LCOV - code coverage report
Current view: top level - src/wallet - salvage.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 0.0 % 114 0
Test Date: 2024-08-28 04:44:32 Functions: 0.0 % 27 0
Branches: 0.0 % 166 0

             Branch data     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 <streams.h>
       7                 :             : #include <util/fs.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                 :             : #include <db_cxx.h>
      15                 :             : 
      16                 :             : namespace wallet {
      17                 :             : /* End of headers, beginning of key/value data */
      18                 :             : static const char *HEADER_END = "HEADER=END";
      19                 :             : /* End of key/value data */
      20                 :             : static const char *DATA_END = "DATA=END";
      21                 :             : typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
      22                 :             : 
      23                 :           0 : class DummyCursor : public DatabaseCursor
      24                 :             : {
      25                 :           0 :     Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
      26                 :             : };
      27                 :             : 
      28                 :             : /** RAII class that provides access to a DummyDatabase. Never fails. */
      29                 :           0 : class DummyBatch : public DatabaseBatch
      30                 :             : {
      31                 :             : private:
      32                 :           0 :     bool ReadKey(DataStream&& key, DataStream& value) override { return true; }
      33                 :           0 :     bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite=true) override { return true; }
      34                 :           0 :     bool EraseKey(DataStream&& key) override { return true; }
      35                 :           0 :     bool HasKey(DataStream&& key) override { return true; }
      36                 :           0 :     bool ErasePrefix(Span<const std::byte> prefix) override { return true; }
      37                 :             : 
      38                 :             : public:
      39                 :           0 :     void Flush() override {}
      40                 :           0 :     void Close() override {}
      41                 :             : 
      42                 :           0 :     std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<DummyCursor>(); }
      43                 :           0 :     std::unique_ptr<DatabaseCursor> GetNewPrefixCursor(Span<const std::byte> prefix) override { return GetNewCursor(); }
      44                 :           0 :     bool TxnBegin() override { return true; }
      45                 :           0 :     bool TxnCommit() override { return true; }
      46                 :           0 :     bool TxnAbort() override { return true; }
      47                 :             : };
      48                 :             : 
      49                 :             : /** A dummy WalletDatabase that does nothing and never fails. Only used by salvage.
      50                 :             :  **/
      51                 :           0 : class DummyDatabase : public WalletDatabase
      52                 :             : {
      53                 :             : public:
      54                 :           0 :     void Open() override {};
      55                 :           0 :     void AddRef() override {}
      56                 :           0 :     void RemoveRef() override {}
      57                 :           0 :     bool Rewrite(const char* pszSkip=nullptr) override { return true; }
      58                 :           0 :     bool Backup(const std::string& strDest) const override { return true; }
      59                 :           0 :     void Close() override {}
      60                 :           0 :     void Flush() override {}
      61                 :           0 :     bool PeriodicFlush() override { return true; }
      62                 :           0 :     void IncrementUpdateCounter() override { ++nUpdateCounter; }
      63                 :           0 :     void ReloadDbEnv() override {}
      64                 :           0 :     std::string Filename() override { return "dummy"; }
      65                 :           0 :     std::string Format() override { return "dummy"; }
      66                 :           0 :     std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
      67                 :             : };
      68                 :             : 
      69                 :           0 : bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
      70                 :             : {
      71         [ #  # ]:           0 :     DatabaseOptions options;
      72                 :           0 :     DatabaseStatus status;
      73         [ #  # ]:           0 :     ReadDatabaseArgs(args, options);
      74                 :           0 :     options.require_existing = true;
      75                 :           0 :     options.verify = false;
      76         [ #  # ]:           0 :     options.require_format = DatabaseFormat::BERKELEY;
      77         [ #  # ]:           0 :     std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
      78         [ #  # ]:           0 :     if (!database) return false;
      79                 :             : 
      80         [ #  # ]:           0 :     BerkeleyDatabase& berkeley_database = static_cast<BerkeleyDatabase&>(*database);
      81         [ #  # ]:           0 :     std::string filename = berkeley_database.Filename();
      82         [ #  # ]:           0 :     std::shared_ptr<BerkeleyEnvironment> env = berkeley_database.env;
      83                 :             : 
      84   [ #  #  #  # ]:           0 :     if (!env->Open(error)) {
      85                 :             :         return false;
      86                 :             :     }
      87                 :             : 
      88                 :             :     // Recovery procedure:
      89                 :             :     // move wallet file to walletfilename.timestamp.bak
      90                 :             :     // Call Salvage with fAggressive=true to
      91                 :             :     // get as much data as possible.
      92                 :             :     // Rewrite salvaged data to fresh wallet file
      93                 :             :     // Rescan so any missing transactions will be
      94                 :             :     // found.
      95         [ #  # ]:           0 :     int64_t now = GetTime();
      96         [ #  # ]:           0 :     std::string newFilename = strprintf("%s.%d.bak", filename, now);
      97                 :             : 
      98         [ #  # ]:           0 :     int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
      99                 :           0 :                                        newFilename.c_str(), DB_AUTO_COMMIT);
     100         [ #  # ]:           0 :     if (result != 0)
     101                 :             :     {
     102   [ #  #  #  #  :           0 :         error = strprintf(Untranslated("Failed to rename %s to %s"), filename, newFilename);
                   #  # ]
     103                 :           0 :         return false;
     104                 :             :     }
     105                 :             : 
     106                 :             :     /**
     107                 :             :      * Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation).
     108                 :             :      * key/value pairs are appended to salvagedData which are then written out to a new wallet file.
     109                 :             :      * NOTE: reads the entire database into memory, so cannot be used
     110                 :             :      * for huge databases.
     111                 :             :      */
     112                 :           0 :     std::vector<KeyValPair> salvagedData;
     113                 :             : 
     114         [ #  # ]:           0 :     std::stringstream strDump;
     115                 :             : 
     116         [ #  # ]:           0 :     Db db(env->dbenv.get(), 0);
     117         [ #  # ]:           0 :     result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
     118         [ #  # ]:           0 :     if (result == DB_VERIFY_BAD) {
     119   [ #  #  #  # ]:           0 :         warnings.push_back(Untranslated("Salvage: Database salvage found errors, all data may not be recoverable."));
     120                 :             :     }
     121         [ #  # ]:           0 :     if (result != 0 && result != DB_VERIFY_BAD) {
     122   [ #  #  #  #  :           0 :         error = strprintf(Untranslated("Salvage: Database salvage failed with result %d."), result);
                   #  # ]
     123                 :           0 :         return false;
     124                 :             :     }
     125                 :             : 
     126                 :             :     // Format of bdb dump is ascii lines:
     127                 :             :     // header lines...
     128                 :             :     // HEADER=END
     129                 :             :     //  hexadecimal key
     130                 :             :     //  hexadecimal value
     131                 :             :     //  ... repeated
     132                 :             :     // DATA=END
     133                 :             : 
     134                 :           0 :     std::string strLine;
     135   [ #  #  #  # ]:           0 :     while (!strDump.eof() && strLine != HEADER_END)
     136         [ #  # ]:           0 :         getline(strDump, strLine); // Skip past header
     137                 :             : 
     138                 :           0 :     std::string keyHex, valueHex;
     139   [ #  #  #  # ]:           0 :     while (!strDump.eof() && keyHex != DATA_END) {
     140         [ #  # ]:           0 :         getline(strDump, keyHex);
     141         [ #  # ]:           0 :         if (keyHex != DATA_END) {
     142         [ #  # ]:           0 :             if (strDump.eof())
     143                 :             :                 break;
     144         [ #  # ]:           0 :             getline(strDump, valueHex);
     145         [ #  # ]:           0 :             if (valueHex == DATA_END) {
     146   [ #  #  #  # ]:           0 :                 warnings.push_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values."));
     147                 :           0 :                 break;
     148                 :             :             }
     149   [ #  #  #  #  :           0 :             salvagedData.emplace_back(ParseHex(keyHex), ParseHex(valueHex));
                   #  # ]
     150                 :             :         }
     151                 :             :     }
     152                 :             : 
     153                 :           0 :     bool fSuccess;
     154         [ #  # ]:           0 :     if (keyHex != DATA_END) {
     155   [ #  #  #  # ]:           0 :         warnings.push_back(Untranslated("Salvage: WARNING: Unexpected end of file while reading salvage output."));
     156                 :           0 :         fSuccess = false;
     157                 :             :     } else {
     158                 :           0 :         fSuccess = (result == 0);
     159                 :             :     }
     160                 :             : 
     161         [ #  # ]:           0 :     if (salvagedData.empty())
     162                 :             :     {
     163   [ #  #  #  #  :           0 :         error = strprintf(Untranslated("Salvage(aggressive) found no records in %s."), newFilename);
                   #  # ]
     164                 :           0 :         return false;
     165                 :             :     }
     166                 :             : 
     167         [ #  # ]:           0 :     std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
     168         [ #  # ]:           0 :     int ret = pdbCopy->open(nullptr,               // Txn pointer
     169                 :             :                             filename.c_str(),   // Filename
     170                 :             :                             "main",             // Logical db name
     171                 :             :                             DB_BTREE,           // Database type
     172                 :             :                             DB_CREATE,          // Flags
     173                 :             :                             0);
     174         [ #  # ]:           0 :     if (ret > 0) {
     175   [ #  #  #  #  :           0 :         error = strprintf(Untranslated("Cannot create database file %s"), filename);
                   #  # ]
     176         [ #  # ]:           0 :         pdbCopy->close(0);
     177                 :             :         return false;
     178                 :             :     }
     179                 :             : 
     180         [ #  # ]:           0 :     DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
     181   [ #  #  #  #  :           0 :     CWallet dummyWallet(nullptr, "", std::make_unique<DummyDatabase>());
                   #  # ]
     182         [ #  # ]:           0 :     for (KeyValPair& row : salvagedData)
     183                 :             :     {
     184                 :             :         /* Filter for only private key type KV pairs to be added to the salvaged wallet */
     185         [ #  # ]:           0 :         DataStream ssKey{row.first};
     186         [ #  # ]:           0 :         DataStream ssValue(row.second);
     187         [ #  # ]:           0 :         std::string strType, strErr;
     188                 :             : 
     189                 :             :         // We only care about KEY, MASTER_KEY, CRYPTED_KEY, and HDCHAIN types
     190         [ #  # ]:           0 :         ssKey >> strType;
     191                 :           0 :         bool fReadOK = false;
     192         [ #  # ]:           0 :         if (strType == DBKeys::KEY) {
     193         [ #  # ]:           0 :             fReadOK = LoadKey(&dummyWallet, ssKey, ssValue, strErr);
     194         [ #  # ]:           0 :         } else if (strType == DBKeys::CRYPTED_KEY) {
     195         [ #  # ]:           0 :             fReadOK = LoadCryptedKey(&dummyWallet, ssKey, ssValue, strErr);
     196         [ #  # ]:           0 :         } else if (strType == DBKeys::MASTER_KEY) {
     197         [ #  # ]:           0 :             fReadOK = LoadEncryptionKey(&dummyWallet, ssKey, ssValue, strErr);
     198         [ #  # ]:           0 :         } else if (strType == DBKeys::HDCHAIN) {
     199         [ #  # ]:           0 :             fReadOK = LoadHDChain(&dummyWallet, ssValue, strErr);
     200                 :             :         } else {
     201                 :           0 :             continue;
     202                 :             :         }
     203                 :             : 
     204         [ #  # ]:           0 :         if (!fReadOK)
     205                 :             :         {
     206   [ #  #  #  #  :           0 :             warnings.push_back(strprintf(Untranslated("WARNING: WalletBatch::Recover skipping %s: %s"), strType, strErr));
                   #  # ]
     207                 :           0 :             continue;
     208                 :             :         }
     209         [ #  # ]:           0 :         Dbt datKey(row.first.data(), row.first.size());
     210         [ #  # ]:           0 :         Dbt datValue(row.second.data(), row.second.size());
     211         [ #  # ]:           0 :         int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
     212         [ #  # ]:           0 :         if (ret2 > 0)
     213                 :           0 :             fSuccess = false;
     214                 :           0 :     }
     215         [ #  # ]:           0 :     ptxn->commit(0);
     216         [ #  # ]:           0 :     pdbCopy->close(0);
     217                 :             : 
     218                 :           0 :     return fSuccess;
     219                 :           0 : }
     220                 :             : } // namespace wallet
        

Generated by: LCOV version 2.0-1