LCOV - code coverage report
Current view: top level - src/wallet - salvage.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 63 82 76.8 %
Date: 2020-09-26 01:30:44 Functions: 2 2 100.0 %

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

Generated by: LCOV version 1.15