diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index e578433065a..a8baa9a176e 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -45,7 +45,6 @@ static void SetupWalletToolArgs(ArgsManager& argsman) argsman.AddCommand("info", "Get wallet info"); argsman.AddCommand("create", "Create new wallet file"); - argsman.AddCommand("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental."); argsman.AddCommand("dump", "Print out all of the wallet key-value records"); argsman.AddCommand("createfromdump", "Create new wallet file from dumped records"); } diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 0ea85363a6b..0f2fde8cc6d 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -46,6 +46,6 @@ target_link_libraries(bitcoin_wallet ) if(USE_BDB) - target_sources(bitcoin_wallet PRIVATE bdb.cpp salvage.cpp) + target_sources(bitcoin_wallet PRIVATE bdb.cpp) target_link_libraries(bitcoin_wallet PUBLIC BerkeleyDB::BerkeleyDB) endif() diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp deleted file mode 100644 index 30a2dd6dea6..00000000000 --- a/src/wallet/salvage.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-present The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace wallet { -/* End of headers, beginning of key/value data */ -static const char *HEADER_END = "HEADER=END"; -/* End of key/value data */ -static const char *DATA_END = "DATA=END"; -typedef std::pair, std::vector > KeyValPair; - -class DummyCursor : public DatabaseCursor -{ - Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; } -}; - -/** RAII class that provides access to a DummyDatabase. Never fails. */ -class DummyBatch : public DatabaseBatch -{ -private: - bool ReadKey(DataStream&& key, DataStream& value) override { return true; } - bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite=true) override { return true; } - bool EraseKey(DataStream&& key) override { return true; } - bool HasKey(DataStream&& key) override { return true; } - bool ErasePrefix(std::span prefix) override { return true; } - -public: - void Flush() override {} - void Close() override {} - - std::unique_ptr GetNewCursor() override { return std::make_unique(); } - std::unique_ptr GetNewPrefixCursor(std::span prefix) override { return GetNewCursor(); } - bool TxnBegin() override { return true; } - bool TxnCommit() override { return true; } - bool TxnAbort() override { return true; } - bool HasActiveTxn() override { return false; } -}; - -/** A dummy WalletDatabase that does nothing and never fails. Only used by salvage. - **/ -class DummyDatabase : public WalletDatabase -{ -public: - void Open() override {}; - void AddRef() override {} - void RemoveRef() override {} - bool Rewrite(const char* pszSkip=nullptr) override { return true; } - bool Backup(const std::string& strDest) const override { return true; } - void Close() override {} - void Flush() override {} - bool PeriodicFlush() override { return true; } - void IncrementUpdateCounter() override { ++nUpdateCounter; } - void ReloadDbEnv() override {} - std::string Filename() override { return "dummy"; } - std::string Format() override { return "dummy"; } - std::unique_ptr MakeBatch(bool flush_on_close = true) override { return std::make_unique(); } -}; - -bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector& warnings) -{ - DatabaseOptions options; - DatabaseStatus status; - ReadDatabaseArgs(args, options); - options.require_existing = true; - options.verify = false; - options.require_format = DatabaseFormat::BERKELEY; - std::unique_ptr database = MakeDatabase(file_path, options, status, error); - if (!database) return false; - - BerkeleyDatabase& berkeley_database = static_cast(*database); - std::string filename = berkeley_database.Filename(); - std::shared_ptr env = berkeley_database.env; - - if (!env->Open(error)) { - return false; - } - - // Recovery procedure: - // move wallet file to walletfilename.timestamp.bak - // Call Salvage with fAggressive=true to - // get as much data as possible. - // Rewrite salvaged data to fresh wallet file - // Rescan so any missing transactions will be - // found. - int64_t now = GetTime(); - std::string newFilename = strprintf("%s.%d.bak", filename, now); - - int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr, - newFilename.c_str(), DB_AUTO_COMMIT); - if (result != 0) - { - error = Untranslated(strprintf("Failed to rename %s to %s", filename, newFilename)); - return false; - } - - /** - * Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation). - * key/value pairs are appended to salvagedData which are then written out to a new wallet file. - * NOTE: reads the entire database into memory, so cannot be used - * for huge databases. - */ - std::vector salvagedData; - - std::stringstream strDump; - - Db db(env->dbenv.get(), 0); - result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE); - if (result == DB_VERIFY_BAD) { - warnings.emplace_back(Untranslated("Salvage: Database salvage found errors, all data may not be recoverable.")); - } - if (result != 0 && result != DB_VERIFY_BAD) { - error = Untranslated(strprintf("Salvage: Database salvage failed with result %d.", result)); - return false; - } - - // Format of bdb dump is ascii lines: - // header lines... - // HEADER=END - // hexadecimal key - // hexadecimal value - // ... repeated - // DATA=END - - std::string strLine; - while (!strDump.eof() && strLine != HEADER_END) - getline(strDump, strLine); // Skip past header - - std::string keyHex, valueHex; - while (!strDump.eof() && keyHex != DATA_END) { - getline(strDump, keyHex); - if (keyHex != DATA_END) { - if (strDump.eof()) - break; - getline(strDump, valueHex); - if (valueHex == DATA_END) { - warnings.emplace_back(Untranslated("Salvage: WARNING: Number of keys in data does not match number of values.")); - break; - } - salvagedData.emplace_back(ParseHex(keyHex), ParseHex(valueHex)); - } - } - - bool fSuccess; - if (keyHex != DATA_END) { - warnings.emplace_back(Untranslated("Salvage: WARNING: Unexpected end of file while reading salvage output.")); - fSuccess = false; - } else { - fSuccess = (result == 0); - } - - if (salvagedData.empty()) - { - error = Untranslated(strprintf("Salvage(aggressive) found no records in %s.", newFilename)); - return false; - } - - std::unique_ptr pdbCopy = std::make_unique(env->dbenv.get(), 0); - int ret = pdbCopy->open(nullptr, // Txn pointer - filename.c_str(), // Filename - "main", // Logical db name - DB_BTREE, // Database type - DB_CREATE, // Flags - 0); - if (ret > 0) { - error = Untranslated(strprintf("Cannot create database file %s", filename)); - pdbCopy->close(0); - return false; - } - - DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC); - CWallet dummyWallet(nullptr, "", std::make_unique()); - for (KeyValPair& row : salvagedData) - { - /* Filter for only private key type KV pairs to be added to the salvaged wallet */ - DataStream ssKey{row.first}; - DataStream ssValue(row.second); - std::string strType, strErr; - - // We only care about KEY, MASTER_KEY, CRYPTED_KEY, and HDCHAIN types - ssKey >> strType; - bool fReadOK = false; - if (strType == DBKeys::KEY) { - fReadOK = LoadKey(&dummyWallet, ssKey, ssValue, strErr); - } else if (strType == DBKeys::CRYPTED_KEY) { - fReadOK = LoadCryptedKey(&dummyWallet, ssKey, ssValue, strErr); - } else if (strType == DBKeys::MASTER_KEY) { - fReadOK = LoadEncryptionKey(&dummyWallet, ssKey, ssValue, strErr); - } else if (strType == DBKeys::HDCHAIN) { - fReadOK = LoadHDChain(&dummyWallet, ssValue, strErr); - } else { - continue; - } - - if (!fReadOK) - { - warnings.push_back(Untranslated(strprintf("WARNING: WalletBatch::Recover skipping %s: %s", strType, strErr))); - continue; - } - Dbt datKey(row.first.data(), row.first.size()); - Dbt datValue(row.second.data(), row.second.size()); - int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); - if (ret2 > 0) - fSuccess = false; - } - ptxn->commit(0); - pdbCopy->close(0); - - return fSuccess; -} -} // namespace wallet diff --git a/src/wallet/salvage.h b/src/wallet/salvage.h deleted file mode 100644 index fbf152ec792..00000000000 --- a/src/wallet/salvage.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2021 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_WALLET_SALVAGE_H -#define BITCOIN_WALLET_SALVAGE_H - -#include -#include - -class ArgsManager; -struct bilingual_str; - -namespace wallet { -bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector& warnings); -} // namespace wallet - -#endif // BITCOIN_WALLET_SALVAGE_H diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 81a7c25196a..2e40f4feb3f 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -165,24 +164,6 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command) if (!wallet_instance) return false; WalletShowInfo(wallet_instance.get()); wallet_instance->Close(); - } else if (command == "salvage") { -#ifdef USE_BDB - bilingual_str error; - std::vector warnings; - bool ret = RecoverDatabaseFile(args, path, error, warnings); - if (!ret) { - for (const auto& warning : warnings) { - tfm::format(std::cerr, "%s\n", warning.original); - } - if (!error.empty()) { - tfm::format(std::cerr, "%s\n", error.original); - } - } - return ret; -#else - tfm::format(std::cerr, "Salvage command is not available as BDB support is not compiled"); - return false; -#endif } else if (command == "dump") { DatabaseOptions options; ReadDatabaseArgs(args, options);