LCOV - code coverage report
Current view: top level - src/wallet - dump.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 163 191 85.3 %
Date: 2026-06-25 07:23:43 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2020-2021 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <wallet/dump.h>
       6             : 
       7             : #include <fs.h>
       8             : #include <util/translation.h>
       9             : #include <wallet/wallet.h>
      10             : #include <wallet/walletdb.h>
      11             : 
      12             : #include <algorithm>
      13             : #include <fstream>
      14             : #include <memory>
      15             : #include <string>
      16             : #include <utility>
      17             : #include <vector>
      18             : 
      19             : namespace wallet {
      20         136 : static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
      21             : uint32_t DUMP_VERSION = 1;
      22             : 
      23          24 : bool DumpWallet(const ArgsManager& args, WalletDatabase& db, bilingual_str& error)
      24             : {
      25             :     // Get the dumpfile
      26          24 :     std::string dump_filename = args.GetArg("-dumpfile", "");
      27          24 :     if (dump_filename.empty()) {
      28           4 :         error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided.");
      29           4 :         return false;
      30             :     }
      31             : 
      32          20 :     fs::path path = fs::PathFromString(dump_filename);
      33          20 :     path = fs::absolute(path);
      34          20 :     if (fs::exists(path)) {
      35           4 :         error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), fs::PathToString(path));
      36           4 :         return false;
      37             :     }
      38          16 :     std::ofstream dump_file;
      39          16 :     dump_file.open(path);
      40          16 :     if (dump_file.fail()) {
      41           0 :         error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
      42           0 :         return false;
      43             :     }
      44             : 
      45          16 :     HashWriter hasher{};
      46             : 
      47          16 :     std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
      48             : 
      49          16 :     bool ret = true;
      50          16 :     if (!batch->StartCursor()) {
      51           0 :         error = _("Error: Couldn't create cursor into database");
      52           0 :         ret = false;
      53           0 :     }
      54             : 
      55             :     // Write out a magic string with version
      56          16 :     std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
      57          16 :     dump_file.write(line.data(), line.size());
      58          16 :     hasher << Span{line};
      59             : 
      60             :     // Write out the file format
      61          16 :     line = strprintf("%s,%s\n", "format", db.Format());
      62          16 :     dump_file.write(line.data(), line.size());
      63          16 :     hasher << Span{line};
      64             : 
      65          16 :     if (ret) {
      66             : 
      67             :         // Read the records
      68         280 :         while (true) {
      69         280 :             CDataStream ss_key(SER_DISK, CLIENT_VERSION);
      70         280 :             CDataStream ss_value(SER_DISK, CLIENT_VERSION);
      71             :             bool complete;
      72         280 :             ret = batch->ReadAtCursor(ss_key, ss_value, complete);
      73         280 :             if (complete) {
      74          16 :                 ret = true;
      75          16 :                 break;
      76         264 :             } else if (!ret) {
      77           0 :                 error = _("Error reading next record from wallet database");
      78           0 :                 break;
      79             :             }
      80         264 :             std::string key_str = HexStr(ss_key);
      81         264 :             std::string value_str = HexStr(ss_value);
      82         264 :             line = strprintf("%s,%s\n", key_str, value_str);
      83         264 :             dump_file.write(line.data(), line.size());
      84         264 :             hasher << Span{line};
      85         280 :         }
      86          16 :     }
      87             : 
      88          16 :     batch->CloseCursor();
      89          16 :     batch.reset();
      90             : 
      91          16 :     if (ret) {
      92             :         // Write the hash
      93          16 :         tfm::format(dump_file, "checksum,%s\n", HexStr(hasher.GetHash()));
      94          16 :         dump_file.close();
      95          16 :     } else {
      96             :         // Remove the dumpfile on failure
      97           0 :         dump_file.close();
      98           0 :         fs::remove(path);
      99             :     }
     100             : 
     101          16 :     return ret;
     102          24 : }
     103             : 
     104             : // The standard wallet deleter function blocks on the validation interface
     105             : // queue, which doesn't exist for the bitcoin-wallet. Define our own
     106             : // deleter here.
     107          28 : static void WalletToolReleaseWallet(CWallet* wallet)
     108             : {
     109          28 :     wallet->WalletLogPrintf("Releasing wallet\n");
     110          28 :     wallet->Close();
     111          28 :     delete wallet;
     112          28 : }
     113             : 
     114          56 : bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
     115             : {
     116             :     // Get the dumpfile
     117          56 :     std::string dump_filename = args.GetArg("-dumpfile", "");
     118          56 :     if (dump_filename.empty()) {
     119           4 :         error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
     120           4 :         return false;
     121             :     }
     122             : 
     123          52 :     fs::path dump_path = fs::PathFromString(dump_filename);
     124          52 :     dump_path = fs::absolute(dump_path);
     125          52 :     if (!fs::exists(dump_path)) {
     126           4 :         error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
     127           4 :         return false;
     128             :     }
     129          48 :     std::ifstream dump_file{dump_path};
     130             : 
     131             :     // Compute the checksum
     132          48 :     HashWriter hasher{};
     133          48 :     uint256 checksum;
     134             : 
     135             :     // Check the magic and version
     136          48 :     std::string magic_key;
     137          48 :     std::getline(dump_file, magic_key, ',');
     138          48 :     std::string version_value;
     139          48 :     std::getline(dump_file, version_value, '\n');
     140          48 :     if (magic_key != DUMP_MAGIC) {
     141           4 :         error = strprintf(_("Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."), magic_key, DUMP_MAGIC);
     142           4 :         dump_file.close();
     143           4 :         return false;
     144             :     }
     145             :     // Check the version number (value of first record)
     146             :     uint32_t ver;
     147          44 :     if (!ParseUInt32(version_value, &ver)) {
     148           0 :         error =strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value);
     149           0 :         dump_file.close();
     150           0 :         return false;
     151             :     }
     152          44 :     if (ver != DUMP_VERSION) {
     153           8 :         error = strprintf(_("Error: Dumpfile version is not supported. This version of dash-wallet only supports version 1 dumpfiles. Got dumpfile with version %s"), version_value);
     154           8 :         dump_file.close();
     155           8 :         return false;
     156             :     }
     157          36 :     std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
     158          36 :     hasher << Span{magic_hasher_line};
     159             : 
     160             :     // Get the stored file format
     161          36 :     std::string format_key;
     162          36 :     std::getline(dump_file, format_key, ',');
     163          36 :     std::string format_value;
     164          36 :     std::getline(dump_file, format_value, '\n');
     165          36 :     if (format_key != "format") {
     166           0 :         error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key);
     167           0 :         dump_file.close();
     168           0 :         return false;
     169             :     }
     170             :     // Get the data file format with format_value as the default
     171          36 :     std::string file_format = args.GetArg("-format", format_value);
     172          36 :     if (file_format.empty()) {
     173           0 :         error = _("No wallet file format provided. To use createfromdump, -format=<format> must be provided.");
     174           0 :         return false;
     175             :     }
     176             :     DatabaseFormat data_format;
     177          36 :     if (file_format == "bdb") {
     178          16 :         data_format = DatabaseFormat::BERKELEY;
     179          36 :     } else if (file_format == "sqlite") {
     180          16 :         data_format = DatabaseFormat::SQLITE;
     181          16 :     } else {
     182           4 :         error = strprintf(_("Unknown wallet file format \"%s\" provided. Please provide one of \"bdb\" or \"sqlite\"."), file_format);
     183           4 :         return false;
     184             :     }
     185          32 :     if (file_format != format_value) {
     186           4 :         warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format));
     187           4 :     }
     188          32 :     std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
     189          32 :     hasher << Span{format_hasher_line};
     190             : 
     191          32 :     DatabaseOptions options;
     192             :     DatabaseStatus status;
     193          32 :     ReadDatabaseArgs(args, options);
     194          32 :     options.require_create = true;
     195          32 :     options.require_format = data_format;
     196          32 :     std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);
     197          32 :     if (!database) return false;
     198             : 
     199             :     // dummy chain interface
     200          28 :     bool ret = true;
     201          28 :     std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, /*coinjoin_loader=*/nullptr, name, gArgs, std::move(database)), WalletToolReleaseWallet);
     202             :     {
     203          28 :         LOCK(wallet->cs_wallet);
     204          28 :         DBErrors load_wallet_ret = wallet->LoadWallet();
     205          28 :         if (load_wallet_ret != DBErrors::LOAD_OK) {
     206           0 :             error = strprintf(_("Error creating %s"), name);
     207           0 :             return false;
     208             :         }
     209             : 
     210             :         // Get the database handle
     211          28 :         WalletDatabase& db = wallet->GetDatabase();
     212          28 :         std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
     213          28 :         batch->TxnBegin();
     214             : 
     215             :         // Read the records from the dump file and write them to the database
     216         494 :         while (dump_file.good()) {
     217         490 :             std::string key;
     218         490 :             std::getline(dump_file, key, ',');
     219         490 :             std::string value;
     220         490 :             std::getline(dump_file, value, '\n');
     221             : 
     222         490 :             if (key == "checksum") {
     223          24 :                 std::vector<unsigned char> parsed_checksum = ParseHex(value);
     224          24 :                 if (parsed_checksum.size() != checksum.size()) {
     225           8 :                     error = Untranslated("Error: Checksum is not the correct size");
     226           8 :                     ret = false;
     227           8 :                     break;
     228             :                 }
     229          16 :                 std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
     230          16 :                 break;
     231          24 :             }
     232             : 
     233         466 :             std::string line = strprintf("%s,%s\n", key, value);
     234         466 :             hasher << Span{line};
     235             : 
     236         466 :             if (key.empty() || value.empty()) {
     237           4 :                 continue;
     238             :             }
     239             : 
     240         462 :             if (!IsHex(key)) {
     241           0 :                 error = strprintf(_("Error: Got key that was not hex: %s"), key);
     242           0 :                 ret = false;
     243           0 :                 break;
     244             :             }
     245         462 :             if (!IsHex(value)) {
     246           0 :                 error = strprintf(_("Error: Got value that was not hex: %s"), value);
     247           0 :                 ret = false;
     248           0 :                 break;
     249             :             }
     250             : 
     251         462 :             std::vector<unsigned char> k = ParseHex(key);
     252         462 :             std::vector<unsigned char> v = ParseHex(value);
     253             : 
     254         462 :             CDataStream ss_key(k, SER_DISK, CLIENT_VERSION);
     255         462 :             CDataStream ss_value(v, SER_DISK, CLIENT_VERSION);
     256             : 
     257         462 :             if (!batch->Write(ss_key, ss_value)) {
     258           0 :                 error = strprintf(_("Error: Unable to write record to new wallet"));
     259           0 :                 ret = false;
     260           0 :                 break;
     261             :             }
     262         490 :         }
     263             : 
     264          28 :         if (ret) {
     265          20 :             uint256 comp_checksum = hasher.GetHash();
     266          20 :             if (checksum.IsNull()) {
     267           4 :                 error = _("Error: Missing checksum");
     268           4 :                 ret = false;
     269          20 :             } else if (checksum != comp_checksum) {
     270           4 :                 error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum));
     271           4 :                 ret = false;
     272           4 :             }
     273          20 :         }
     274             : 
     275          28 :         if (ret) {
     276          12 :             batch->TxnCommit();
     277          12 :         } else {
     278          16 :             batch->TxnAbort();
     279             :         }
     280             : 
     281          28 :         batch.reset();
     282             : 
     283          28 :         dump_file.close();
     284          28 :     }
     285          28 :     wallet.reset(); // The pointer deleter will close the wallet for us.
     286             : 
     287             :     // Remove the wallet dir if we have a failure
     288          28 :     if (!ret) {
     289          16 :         fs::remove_all(wallet_path);
     290          16 :     }
     291             : 
     292          28 :     return ret;
     293          56 : }
     294             : } // namespace wallet

Generated by: LCOV version 1.16