LCOV - code coverage report
Current view: top level - src/wallet - salvage.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 0 87 0.0 %
Date: 2026-06-25 07:23:51 Functions: 0 4 0.0 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <fs.h>
       7             : #include <streams.h>
       8             : #include <util/translation.h>
       9             : #include <wallet/bdb.h>
      10             : #include <wallet/salvage.h>
      11             : #include <wallet/wallet.h>
      12             : #include <wallet/walletdb.h>
      13             : 
      14             : namespace wallet {
      15             : /* End of headers, beginning of key/value data */
      16             : static const char *HEADER_END = "HEADER=END";
      17             : /* End of key/value data */
      18             : static const char *DATA_END = "DATA=END";
      19             : typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
      20             : 
      21           0 : static bool KeyFilter(const std::string& type)
      22             : {
      23           0 :     return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
      24             : }
      25             : 
      26           0 : bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
      27             : {
      28           0 :     DatabaseOptions options;
      29             :     DatabaseStatus status;
      30           0 :     ReadDatabaseArgs(args, options);
      31           0 :     options.require_existing = true;
      32           0 :     options.verify = false;
      33           0 :     options.require_format = DatabaseFormat::BERKELEY;
      34           0 :     std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
      35           0 :     if (!database) return false;
      36             : 
      37           0 :     BerkeleyDatabase& berkeley_database = static_cast<BerkeleyDatabase&>(*database);
      38           0 :     std::string filename = berkeley_database.Filename();
      39           0 :     std::shared_ptr<BerkeleyEnvironment> env = berkeley_database.env;
      40             : 
      41           0 :     if (!env->Open(error)) {
      42           0 :         return false;
      43             :     }
      44             : 
      45             :     // Recovery procedure:
      46             :     // move wallet file to walletfilename.timestamp.bak
      47             :     // Call Salvage with fAggressive=true to
      48             :     // get as much data as possible.
      49             :     // Rewrite salvaged data to fresh wallet file
      50             :     // Set -rescan so any missing transactions will be
      51             :     // found.
      52           0 :     int64_t now = GetTime();
      53           0 :     std::string newFilename = strprintf("%s.%d.bak", filename, now);
      54             : 
      55           0 :     int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
      56           0 :                                        newFilename.c_str(), DB_AUTO_COMMIT);
      57           0 :     if (result != 0)
      58             :     {
      59           0 :         error = strprintf(Untranslated("Failed to rename %s to %s"), filename, newFilename);
      60           0 :         return false;
      61             :     }
      62             : 
      63             :     /**
      64             :      * Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation).
      65             :      * key/value pairs are appended to salvagedData which are then written out to a new wallet file.
      66             :      * NOTE: reads the entire database into memory, so cannot be used
      67             :      * for huge databases.
      68             :      */
      69           0 :     std::vector<KeyValPair> salvagedData;
      70             : 
      71           0 :     std::stringstream strDump;
      72             : 
      73           0 :     Db db(env->dbenv.get(), 0);
      74           0 :     result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
      75           0 :     if (result == DB_VERIFY_BAD) {
      76           0 :         warnings.push_back(Untranslated("Salvage: Database salvage found errors, all data may not be recoverable."));
      77           0 :     }
      78           0 :     if (result != 0 && result != DB_VERIFY_BAD) {
      79           0 :         error = strprintf(Untranslated("Salvage: Database salvage failed with result %d."), result);
      80           0 :         return false;
      81             :     }
      82             : 
      83             :     // Format of bdb dump is ascii lines:
      84             :     // header lines...
      85             :     // HEADER=END
      86             :     //  hexadecimal key
      87             :     //  hexadecimal value
      88             :     //  ... repeated
      89             :     // DATA=END
      90             : 
      91           0 :     std::string strLine;
      92           0 :     while (!strDump.eof() && strLine != HEADER_END)
      93           0 :         getline(strDump, strLine); // Skip past header
      94             : 
      95           0 :     std::string keyHex, valueHex;
      96           0 :     while (!strDump.eof() && keyHex != DATA_END) {
      97           0 :         getline(strDump, keyHex);
      98           0 :         if (keyHex != DATA_END) {
      99           0 :             if (strDump.eof())
     100           0 :                 break;
     101           0 :             getline(strDump, valueHex);
     102           0 :             if (valueHex == DATA_END) {
     103           0 :                 warnings.push_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values."));
     104           0 :                 break;
     105             :             }
     106           0 :             salvagedData.emplace_back(ParseHex(keyHex), ParseHex(valueHex));
     107           0 :         }
     108             :     }
     109             : 
     110             :     bool fSuccess;
     111           0 :     if (keyHex != DATA_END) {
     112           0 :         warnings.push_back(Untranslated("Salvage: WARNING: Unexpected end of file while reading salvage output."));
     113           0 :         fSuccess = false;
     114           0 :     } else {
     115           0 :         fSuccess = (result == 0);
     116             :     }
     117             : 
     118           0 :     if (salvagedData.empty())
     119             :     {
     120           0 :         error = strprintf(Untranslated("Salvage(aggressive) found no records in %s."), newFilename);
     121           0 :         return false;
     122             :     }
     123             : 
     124           0 :     std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
     125           0 :     int ret = pdbCopy->open(nullptr,               // Txn pointer
     126           0 :                             filename.c_str(),   // Filename
     127             :                             "main",             // Logical db name
     128             :                             DB_BTREE,           // Database type
     129             :                             DB_CREATE,          // Flags
     130             :                             0);
     131           0 :     if (ret > 0) {
     132           0 :         error = strprintf(Untranslated("Cannot create database file %s"), filename);
     133           0 :         pdbCopy->close(0);
     134           0 :         return false;
     135             :     }
     136             : 
     137           0 :     DbTxn* ptxn = env->TxnBegin();
     138           0 :     CWallet dummyWallet(/*chain=*/nullptr, /*coinjoin_loader=*/nullptr, "", gArgs, CreateDummyWalletDatabase());
     139           0 :     dummyWallet.SetupLegacyScriptPubKeyMan();
     140           0 :     for (KeyValPair& row : salvagedData)
     141             :     {
     142             :         /* Filter for only private key type KV pairs to be added to the salvaged wallet */
     143           0 :         CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
     144           0 :         CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
     145           0 :         std::string strType, strErr;
     146             :         bool fReadOK;
     147             :         {
     148             :             // Required in LoadKeyMetadata():
     149           0 :             LOCK(dummyWallet.cs_wallet);
     150           0 :             fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr, KeyFilter);
     151           0 :         }
     152           0 :         if (!KeyFilter(strType)) {
     153           0 :             continue;
     154             :         }
     155           0 :         if (!fReadOK)
     156             :         {
     157           0 :             warnings.push_back(strprintf(Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"), strType, strErr));
     158           0 :             continue;
     159             :         }
     160           0 :         Dbt datKey(row.first.data(), row.first.size());
     161           0 :         Dbt datValue(row.second.data(), row.second.size());
     162           0 :         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
     163           0 :         if (ret2 > 0)
     164           0 :             fSuccess = false;
     165           0 :     }
     166           0 :     ptxn->commit(0);
     167           0 :     pdbCopy->close(0);
     168             : 
     169           0 :     return fSuccess;
     170           0 : }
     171             : } // namespace wallet

Generated by: LCOV version 1.16