LCOV - code coverage report
Current view: top level - src/wallet - dump.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 0.0 % 172 0
Test Date: 2026-02-07 04:52:48 Functions: 0.0 % 3 0
Branches: 0.0 % 324 0

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

Generated by: LCOV version 2.0-1