LCOV - code coverage report
Current view: top level - src/wallet - dump.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 0 198 0.0 %
Date: 2023-10-05 15:40:34 Functions: 0 13 0.0 %
Branches: 0 0 -

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

Generated by: LCOV version 1.14