LCOV - code coverage report
Current view: top level - src/wallet - dump.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 84.4 % 179 151
Test Date: 2024-11-04 05:10:19 Functions: 100.0 % 3 3
Branches: 47.0 % 332 156

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2020-2022 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                 :           6 : bool DumpWallet(const ArgsManager& args, WalletDatabase& db, bilingual_str& error)
      25                 :             : {
      26                 :             :     // Get the dumpfile
      27   [ +  -  +  - ]:          12 :     std::string dump_filename = args.GetArg("-dumpfile", "");
      28         [ +  + ]:           6 :     if (dump_filename.empty()) {
      29         [ +  - ]:           1 :         error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided.");
      30                 :           1 :         return false;
      31                 :             :     }
      32                 :             : 
      33         [ +  - ]:           5 :     fs::path path = fs::PathFromString(dump_filename);
      34         [ +  - ]:          10 :     path = fs::absolute(path);
      35   [ +  -  +  + ]:           5 :     if (fs::exists(path)) {
      36   [ +  -  +  -  :           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));
                   +  - ]
      37                 :           1 :         return false;
      38                 :             :     }
      39         [ +  - ]:           4 :     std::ofstream dump_file;
      40         [ +  - ]:           4 :     dump_file.open(path);
      41         [ -  + ]:           4 :     if (dump_file.fail()) {
      42   [ #  #  #  #  :           0 :         error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
                   #  # ]
      43                 :           0 :         return false;
      44                 :             :     }
      45                 :             : 
      46         [ +  - ]:           4 :     HashWriter hasher{};
      47                 :             : 
      48         [ +  - ]:           4 :     std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
      49                 :             : 
      50                 :           4 :     bool ret = true;
      51         [ +  - ]:           4 :     std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();
      52         [ -  + ]:           4 :     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         [ +  - ]:           4 :     std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
      59         [ +  - ]:           4 :     dump_file.write(line.data(), line.size());
      60         [ +  - ]:           4 :     hasher << Span{line};
      61                 :             : 
      62                 :             :     // Write out the file format
      63         [ +  - ]:           4 :     std::string format = db.Format();
      64                 :             :     // BDB files that are opened using BerkeleyRODatabase have it's format as "bdb_ro"
      65                 :             :     // We want to override that format back to "bdb"
      66         [ -  + ]:           4 :     if (format == "bdb_ro") {
      67         [ #  # ]:           0 :         format = "bdb";
      68                 :             :     }
      69         [ +  - ]:           4 :     line = strprintf("%s,%s\n", "format", format);
      70         [ +  - ]:           4 :     dump_file.write(line.data(), line.size());
      71         [ +  - ]:           4 :     hasher << Span{line};
      72                 :             : 
      73         [ +  - ]:           4 :     if (ret) {
      74                 :             : 
      75                 :             :         // Read the records
      76                 :        2370 :         while (true) {
      77                 :        1187 :             DataStream ss_key{};
      78                 :        1187 :             DataStream ss_value{};
      79         [ +  - ]:        1187 :             DatabaseCursor::Status status = cursor->Next(ss_key, ss_value);
      80         [ +  + ]:        1187 :             if (status == DatabaseCursor::Status::DONE) {
      81                 :             :                 ret = true;
      82                 :             :                 break;
      83         [ -  + ]:        1183 :             } 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         [ +  - ]:        1183 :             std::string key_str = HexStr(ss_key);
      89         [ +  - ]:        1183 :             std::string value_str = HexStr(ss_value);
      90         [ +  - ]:        1183 :             line = strprintf("%s,%s\n", key_str, value_str);
      91         [ +  - ]:        1183 :             dump_file.write(line.data(), line.size());
      92         [ +  - ]:        2366 :             hasher << Span{line};
      93                 :        1187 :         }
      94                 :             :     }
      95                 :             : 
      96         [ +  - ]:           4 :     cursor.reset();
      97         [ +  - ]:           4 :     batch.reset();
      98                 :             : 
      99         [ +  - ]:           4 :     if (ret) {
     100                 :             :         // Write the hash
     101   [ +  -  +  -  :           4 :         tfm::format(dump_file, "checksum,%s\n", HexStr(hasher.GetHash()));
                   +  - ]
     102         [ +  - ]:           4 :         dump_file.close();
     103                 :             :     } else {
     104                 :             :         // Remove the dumpfile on failure
     105         [ #  # ]:           0 :         dump_file.close();
     106         [ #  # ]:           0 :         fs::remove(path);
     107                 :             :     }
     108                 :             : 
     109                 :           4 :     return ret;
     110                 :          15 : }
     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                 :           6 : static void WalletToolReleaseWallet(CWallet* wallet)
     116                 :             : {
     117                 :           6 :     wallet->WalletLogPrintf("Releasing wallet\n");
     118                 :           6 :     wallet->Close();
     119         [ +  - ]:           6 :     delete wallet;
     120                 :           6 : }
     121                 :             : 
     122                 :          13 : bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
     123                 :             : {
     124                 :             :     // Get the dumpfile
     125   [ +  -  +  - ]:          26 :     std::string dump_filename = args.GetArg("-dumpfile", "");
     126         [ +  + ]:          13 :     if (dump_filename.empty()) {
     127         [ +  - ]:           1 :         error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
     128                 :           1 :         return false;
     129                 :             :     }
     130                 :             : 
     131         [ +  - ]:          12 :     fs::path dump_path = fs::PathFromString(dump_filename);
     132         [ +  - ]:          24 :     dump_path = fs::absolute(dump_path);
     133   [ +  -  +  + ]:          12 :     if (!fs::exists(dump_path)) {
     134   [ +  -  +  -  :           4 :         error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
                   +  - ]
     135                 :           1 :         return false;
     136                 :             :     }
     137         [ +  - ]:          11 :     std::ifstream dump_file{dump_path};
     138                 :             : 
     139                 :             :     // Compute the checksum
     140         [ +  - ]:          11 :     HashWriter hasher{};
     141                 :          11 :     uint256 checksum;
     142                 :             : 
     143                 :             :     // Check the magic and version
     144         [ +  - ]:          11 :     std::string magic_key;
     145         [ +  - ]:          11 :     std::getline(dump_file, magic_key, ',');
     146         [ +  - ]:          11 :     std::string version_value;
     147         [ +  - ]:          11 :     std::getline(dump_file, version_value, '\n');
     148         [ +  + ]:          11 :     if (magic_key != DUMP_MAGIC) {
     149   [ +  -  +  - ]:           2 :         error = strprintf(_("Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."), magic_key, DUMP_MAGIC);
     150         [ +  - ]:           1 :         dump_file.close();
     151                 :             :         return false;
     152                 :             :     }
     153                 :             :     // Check the version number (value of first record)
     154                 :          10 :     uint32_t ver;
     155   [ +  -  -  + ]:          10 :     if (!ParseUInt32(version_value, &ver)) {
     156   [ #  #  #  # ]:           0 :         error =strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value);
     157         [ #  # ]:           0 :         dump_file.close();
     158                 :             :         return false;
     159                 :             :     }
     160         [ +  + ]:          10 :     if (ver != DUMP_VERSION) {
     161   [ +  -  +  - ]:           4 :         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);
     162         [ +  - ]:           2 :         dump_file.close();
     163                 :             :         return false;
     164                 :             :     }
     165         [ +  - ]:           8 :     std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
     166         [ +  - ]:           8 :     hasher << Span{magic_hasher_line};
     167                 :             : 
     168                 :             :     // Get the stored file format
     169         [ +  - ]:           8 :     std::string format_key;
     170         [ +  - ]:           8 :     std::getline(dump_file, format_key, ',');
     171         [ +  - ]:           8 :     std::string format_value;
     172         [ +  - ]:           8 :     std::getline(dump_file, format_value, '\n');
     173         [ -  + ]:           8 :     if (format_key != "format") {
     174   [ #  #  #  # ]:           0 :         error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key);
     175         [ #  # ]:           0 :         dump_file.close();
     176                 :             :         return false;
     177                 :             :     }
     178                 :             :     // Get the data file format with format_value as the default
     179   [ +  -  +  - ]:           8 :     std::string file_format = args.GetArg("-format", format_value);
     180         [ -  + ]:           8 :     if (file_format.empty()) {
     181         [ #  # ]:           0 :         error = _("No wallet file format provided. To use createfromdump, -format=<format> must be provided.");
     182                 :           0 :         return false;
     183                 :             :     }
     184                 :           8 :     DatabaseFormat data_format;
     185         [ +  - ]:           8 :     if (file_format == "bdb") {
     186                 :             :         data_format = DatabaseFormat::BERKELEY;
     187         [ +  + ]:           8 :     } else if (file_format == "sqlite") {
     188                 :             :         data_format = DatabaseFormat::SQLITE;
     189         [ +  - ]:           1 :     } else if (file_format == "bdb_swap") {
     190                 :             :         data_format = DatabaseFormat::BERKELEY_SWAP;
     191                 :             :     } else {
     192   [ +  -  +  - ]:           2 :         error = strprintf(_("Unknown wallet file format \"%s\" provided. Please provide one of \"bdb\" or \"sqlite\"."), file_format);
     193                 :           1 :         return false;
     194                 :             :     }
     195         [ -  + ]:           7 :     if (file_format != format_value) {
     196   [ #  #  #  # ]:           0 :         warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format));
     197                 :             :     }
     198         [ +  - ]:           7 :     std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
     199         [ +  - ]:           7 :     hasher << Span{format_hasher_line};
     200                 :             : 
     201         [ +  - ]:           7 :     DatabaseOptions options;
     202                 :           7 :     DatabaseStatus status;
     203         [ +  - ]:           7 :     ReadDatabaseArgs(args, options);
     204                 :           7 :     options.require_create = true;
     205         [ +  - ]:           7 :     options.require_format = data_format;
     206         [ +  - ]:           7 :     std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);
     207         [ +  + ]:           7 :     if (!database) return false;
     208                 :             : 
     209                 :             :     // dummy chain interface
     210                 :           6 :     bool ret = true;
     211   [ +  -  +  -  :          12 :     std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet);
             +  -  -  - ]
     212                 :           6 :     {
     213         [ +  - ]:           6 :         LOCK(wallet->cs_wallet);
     214         [ +  - ]:           6 :         DBErrors load_wallet_ret = wallet->LoadWallet();
     215         [ -  + ]:           6 :         if (load_wallet_ret != DBErrors::LOAD_OK) {
     216   [ #  #  #  # ]:           0 :             error = strprintf(_("Error creating %s"), name);
     217         [ #  # ]:           0 :             return false;
     218                 :             :         }
     219                 :             : 
     220                 :             :         // Get the database handle
     221                 :           6 :         WalletDatabase& db = wallet->GetDatabase();
     222         [ +  - ]:           6 :         std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
     223         [ +  - ]:           6 :         batch->TxnBegin();
     224                 :             : 
     225                 :             :         // Read the records from the dump file and write them to the database
     226         [ +  + ]:         277 :         while (dump_file.good()) {
     227         [ +  - ]:         276 :             std::string key;
     228         [ +  - ]:         276 :             std::getline(dump_file, key, ',');
     229         [ +  - ]:         276 :             std::string value;
     230         [ +  - ]:         276 :             std::getline(dump_file, value, '\n');
     231                 :             : 
     232         [ +  + ]:         276 :             if (key == "checksum") {
     233         [ +  - ]:           5 :                 std::vector<unsigned char> parsed_checksum = ParseHex(value);
     234         [ +  + ]:           5 :                 if (parsed_checksum.size() != checksum.size()) {
     235   [ +  -  +  - ]:           4 :                     error = Untranslated("Error: Checksum is not the correct size");
     236                 :           2 :                     ret = false;
     237                 :           2 :                     break;
     238                 :             :                 }
     239                 :           3 :                 std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
     240                 :             :                 break;
     241                 :           5 :             }
     242                 :             : 
     243         [ +  - ]:         271 :             std::string line = strprintf("%s,%s\n", key, value);
     244         [ +  - ]:         271 :             hasher << Span{line};
     245                 :             : 
     246   [ +  +  -  + ]:         271 :             if (key.empty() || value.empty()) {
     247                 :           1 :                 continue;
     248                 :             :             }
     249                 :             : 
     250   [ +  -  -  + ]:         270 :             if (!IsHex(key)) {
     251   [ #  #  #  # ]:           0 :                 error = strprintf(_("Error: Got key that was not hex: %s"), key);
     252                 :           0 :                 ret = false;
     253                 :           0 :                 break;
     254                 :             :             }
     255   [ +  -  -  + ]:         270 :             if (!IsHex(value)) {
     256   [ #  #  #  # ]:           0 :                 error = strprintf(_("Error: Got value that was not hex: %s"), value);
     257                 :           0 :                 ret = false;
     258                 :           0 :                 break;
     259                 :             :             }
     260                 :             : 
     261         [ +  - ]:         270 :             std::vector<unsigned char> k = ParseHex(key);
     262         [ +  - ]:         270 :             std::vector<unsigned char> v = ParseHex(value);
     263   [ +  -  -  + ]:         270 :             if (!batch->Write(Span{k}, Span{v})) {
     264   [ #  #  #  # ]:           0 :                 error = strprintf(_("Error: Unable to write record to new wallet"));
     265                 :           0 :                 ret = false;
     266                 :           0 :                 break;
     267                 :             :             }
     268                 :         276 :         }
     269                 :             : 
     270         [ +  + ]:           6 :         if (ret) {
     271         [ +  - ]:           4 :             uint256 comp_checksum = hasher.GetHash();
     272         [ +  + ]:           4 :             if (checksum.IsNull()) {
     273         [ +  - ]:           1 :                 error = _("Error: Missing checksum");
     274                 :           1 :                 ret = false;
     275         [ +  + ]:           3 :             } else if (checksum != comp_checksum) {
     276   [ +  -  +  -  :           3 :                 error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum));
             +  -  +  - ]
     277                 :           1 :                 ret = false;
     278                 :             :             }
     279                 :             :         }
     280                 :             : 
     281                 :           2 :         if (ret) {
     282         [ +  - ]:           2 :             batch->TxnCommit();
     283                 :             :         } else {
     284         [ +  - ]:           4 :             batch->TxnAbort();
     285                 :             :         }
     286                 :             : 
     287         [ +  - ]:           6 :         batch.reset();
     288                 :             : 
     289         [ +  - ]:           6 :         dump_file.close();
     290         [ +  - ]:           6 :     }
     291                 :           6 :     wallet.reset(); // The pointer deleter will close the wallet for us.
     292                 :             : 
     293                 :             :     // Remove the wallet dir if we have a failure
     294         [ +  + ]:           6 :     if (!ret) {
     295         [ +  - ]:           4 :         fs::remove_all(wallet_path);
     296                 :             :     }
     297                 :             : 
     298                 :             :     return ret;
     299                 :          59 : }
     300                 :             : } // namespace wallet
        

Generated by: LCOV version 2.0-1