diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 5951e4180..e3aa1fd48 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1,883 +1,884 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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 "wallet/walletdb.h" #include "base58.h" #include "consensus/validation.h" #include "protocol.h" #include "serialize.h" #include "sync.h" #include "util.h" #include "utiltime.h" #include "validation.h" // For CheckRegularTransaction #include "wallet/wallet.h" #include #include #include #include static uint64_t nAccountingEntryNumber = 0; static std::atomic nWalletDBUpdateCounter; // // CWalletDB // bool CWalletDB::WriteName(const std::string &strAddress, const std::string &strName) { nWalletDBUpdateCounter++; - return Write(make_pair(std::string("name"), strAddress), strName); + return Write(std::make_pair(std::string("name"), strAddress), strName); } bool CWalletDB::EraseName(const std::string &strAddress) { // This should only be used for sending addresses, never for receiving // addresses, receiving addresses must always have an address book entry if // they're not change return. nWalletDBUpdateCounter++; - return Erase(make_pair(std::string("name"), strAddress)); + return Erase(std::make_pair(std::string("name"), strAddress)); } bool CWalletDB::WritePurpose(const std::string &strAddress, const std::string &strPurpose) { nWalletDBUpdateCounter++; - return Write(make_pair(std::string("purpose"), strAddress), strPurpose); + return Write(std::make_pair(std::string("purpose"), strAddress), + strPurpose); } bool CWalletDB::ErasePurpose(const std::string &strPurpose) { nWalletDBUpdateCounter++; - return Erase(make_pair(std::string("purpose"), strPurpose)); + return Erase(std::make_pair(std::string("purpose"), strPurpose)); } bool CWalletDB::WriteTx(const CWalletTx &wtx) { nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("tx"), wtx.GetId()), wtx); } bool CWalletDB::EraseTx(uint256 hash) { nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("tx"), hash)); } bool CWalletDB::WriteKey(const CPubKey &vchPubKey, const CPrivKey &vchPrivKey, const CKeyMetadata &keyMeta) { nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) return false; // hash pubkey/privkey to accelerate wallet load std::vector vchKey; vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); } bool CWalletDB::WriteCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret, const CKeyMetadata &keyMeta) { const bool fEraseUnencryptedKey = true; nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) return false; if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) return false; if (fEraseUnencryptedKey) { Erase(std::make_pair(std::string("key"), vchPubKey)); Erase(std::make_pair(std::string("wkey"), vchPubKey)); } return true; } bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey &kMasterKey) { nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } bool CWalletDB::WriteCScript(const uint160 &hash, const CScript &redeemScript) { nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase *)(&redeemScript), false); } bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata &keyMeta) { nWalletDBUpdateCounter++; if (!Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase *)(&dest)), keyMeta)) return false; return Write( std::make_pair(std::string("watchs"), *(const CScriptBase *)(&dest)), '1'); } bool CWalletDB::EraseWatchOnly(const CScript &dest) { nWalletDBUpdateCounter++; if (!Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase *)(&dest)))) return false; return Erase( std::make_pair(std::string("watchs"), *(const CScriptBase *)(&dest))); } bool CWalletDB::WriteBestBlock(const CBlockLocator &locator) { nWalletDBUpdateCounter++; // Write empty block locator so versions that require a merkle branch // automatically rescan. Write(std::string("bestblock"), CBlockLocator()); return Write(std::string("bestblock_nomerkle"), locator); } bool CWalletDB::ReadBestBlock(CBlockLocator &locator) { if (Read(std::string("bestblock"), locator) && !locator.vHave.empty()) return true; return Read(std::string("bestblock_nomerkle"), locator); } bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) { nWalletDBUpdateCounter++; return Write(std::string("orderposnext"), nOrderPosNext); } bool CWalletDB::WriteDefaultKey(const CPubKey &vchPubKey) { nWalletDBUpdateCounter++; return Write(std::string("defaultkey"), vchPubKey); } bool CWalletDB::ReadPool(int64_t nPool, CKeyPool &keypool) { return Read(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::WritePool(int64_t nPool, const CKeyPool &keypool) { nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("pool"), nPool), keypool); } bool CWalletDB::ErasePool(int64_t nPool) { nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("pool"), nPool)); } bool CWalletDB::WriteMinVersion(int nVersion) { return Write(std::string("minversion"), nVersion); } bool CWalletDB::ReadAccount(const std::string &strAccount, CAccount &account) { account.SetNull(); - return Read(make_pair(std::string("acc"), strAccount), account); + return Read(std::make_pair(std::string("acc"), strAccount), account); } bool CWalletDB::WriteAccount(const std::string &strAccount, const CAccount &account) { - return Write(make_pair(std::string("acc"), strAccount), account); + return Write(std::make_pair(std::string("acc"), strAccount), account); } bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry &acentry) { return Write( std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); } bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry &acentry) { return WriteAccountingEntry(++nAccountingEntryNumber, acentry); } CAmount CWalletDB::GetAccountCreditDebit(const std::string &strAccount) { std::list entries; ListAccountCreditDebit(strAccount, entries); CAmount nCreditDebit = 0; for (const CAccountingEntry &entry : entries) { nCreditDebit += entry.nCreditDebit; } return nCreditDebit; } void CWalletDB::ListAccountCreditDebit(const std::string &strAccount, std::list &entries) { bool fAllAccounts = (strAccount == "*"); Dbc *pcursor = GetCursor(); if (!pcursor) throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor"); bool setRange = true; while (true) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); if (setRange) ssKey << std::make_pair( std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0))); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange); setRange = false; if (ret == DB_NOTFOUND) break; else if (ret != 0) { pcursor->close(); throw std::runtime_error(std::string(__func__) + ": error scanning DB"); } // Unserialize std::string strType; ssKey >> strType; if (strType != "acentry") break; CAccountingEntry acentry; ssKey >> acentry.strAccount; if (!fAllAccounts && acentry.strAccount != strAccount) break; ssValue >> acentry; ssKey >> acentry.nEntryNo; entries.push_back(acentry); } pcursor->close(); } class CWalletScanState { public: unsigned int nKeys; unsigned int nCKeys; unsigned int nWatchKeys; unsigned int nKeyMeta; bool fIsEncrypted; bool fAnyUnordered; int nFileVersion; std::vector vWalletUpgrade; CWalletScanState() { nKeys = nCKeys = nWatchKeys = nKeyMeta = 0; fIsEncrypted = false; fAnyUnordered = false; nFileVersion = 0; } }; bool ReadKeyValue(CWallet *pwallet, CDataStream &ssKey, CDataStream &ssValue, CWalletScanState &wss, std::string &strType, std::string &strErr) { try { // Unserialize // Taking advantage of the fact that pair serialization is just the two // items serialized one after the other. ssKey >> strType; if (strType == "name") { std::string strAddress; ssKey >> strAddress; ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; } else if (strType == "purpose") { std::string strAddress; ssKey >> strAddress; ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()] .purpose; } else if (strType == "tx") { uint256 hash; ssKey >> hash; CWalletTx wtx; ssValue >> wtx; CValidationState state; bool isValid = wtx.IsCoinBase() ? CheckCoinbase(wtx, state) : CheckRegularTransaction(wtx, state); if (wtx.GetId() != hash || !isValid) return false; // Undo serialize changes in 31600 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) { if (!ssValue.empty()) { char fTmp; char fUnused; ssValue >> fTmp >> fUnused >> wtx.strFromAccount; strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString()); wtx.fTimeReceivedIsTxTime = fTmp; } else { strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString()); wtx.fTimeReceivedIsTxTime = 0; } wss.vWalletUpgrade.push_back(hash); } if (wtx.nOrderPos == -1) wss.fAnyUnordered = true; pwallet->LoadToWallet(wtx); } else if (strType == "acentry") { std::string strAccount; ssKey >> strAccount; uint64_t nNumber; ssKey >> nNumber; if (nNumber > nAccountingEntryNumber) nAccountingEntryNumber = nNumber; if (!wss.fAnyUnordered) { CAccountingEntry acentry; ssValue >> acentry; if (acentry.nOrderPos == -1) wss.fAnyUnordered = true; } } else if (strType == "watchs") { wss.nWatchKeys++; CScript script; ssKey >> *(CScriptBase *)(&script); char fYes; ssValue >> fYes; if (fYes == '1') pwallet->LoadWatchOnly(script); } else if (strType == "key" || strType == "wkey") { CPubKey vchPubKey; ssKey >> vchPubKey; if (!vchPubKey.IsValid()) { strErr = "Error reading wallet database: CPubKey corrupt"; return false; } CKey key; CPrivKey pkey; uint256 hash; if (strType == "key") { wss.nKeys++; ssValue >> pkey; } else { CWalletKey wkey; ssValue >> wkey; pkey = wkey.vchPrivKey; } // Old wallets store keys as "key" [pubkey] => [privkey] // ... which was slow for wallets with lots of keys, because the // public key is re-derived from the private key using EC operations // as a checksum. Newer wallets store keys as "key"[pubkey] => // [privkey][hash(pubkey,privkey)], which is much faster while // remaining backwards-compatible. try { ssValue >> hash; } catch (...) { } bool fSkipCheck = false; if (!hash.IsNull()) { // hash pubkey/privkey to accelerate wallet load std::vector vchKey; vchKey.reserve(vchPubKey.size() + pkey.size()); vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); if (Hash(vchKey.begin(), vchKey.end()) != hash) { strErr = "Error reading wallet database: CPubKey/CPrivKey " "corrupt"; return false; } fSkipCheck = true; } if (!key.Load(pkey, vchPubKey, fSkipCheck)) { strErr = "Error reading wallet database: CPrivKey corrupt"; return false; } if (!pwallet->LoadKey(key, vchPubKey)) { strErr = "Error reading wallet database: LoadKey failed"; return false; } } else if (strType == "mkey") { unsigned int nID; ssKey >> nID; CMasterKey kMasterKey; ssValue >> kMasterKey; if (pwallet->mapMasterKeys.count(nID) != 0) { strErr = strprintf( "Error reading wallet database: duplicate CMasterKey id %u", nID); return false; } pwallet->mapMasterKeys[nID] = kMasterKey; if (pwallet->nMasterKeyMaxID < nID) pwallet->nMasterKeyMaxID = nID; } else if (strType == "ckey") { CPubKey vchPubKey; ssKey >> vchPubKey; if (!vchPubKey.IsValid()) { strErr = "Error reading wallet database: CPubKey corrupt"; return false; } std::vector vchPrivKey; ssValue >> vchPrivKey; wss.nCKeys++; if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) { strErr = "Error reading wallet database: LoadCryptedKey failed"; return false; } wss.fIsEncrypted = true; } else if (strType == "keymeta" || strType == "watchmeta") { CTxDestination keyID; if (strType == "keymeta") { CPubKey vchPubKey; ssKey >> vchPubKey; keyID = vchPubKey.GetID(); } else if (strType == "watchmeta") { CScript script; ssKey >> *(CScriptBase *)(&script); keyID = CScriptID(script); } CKeyMetadata keyMeta; ssValue >> keyMeta; wss.nKeyMeta++; pwallet->LoadKeyMetadata(keyID, keyMeta); } else if (strType == "defaultkey") { ssValue >> pwallet->vchDefaultKey; } else if (strType == "pool") { int64_t nIndex; ssKey >> nIndex; CKeyPool keypool; ssValue >> keypool; pwallet->LoadKeyPool(nIndex, keypool); } else if (strType == "version") { ssValue >> wss.nFileVersion; if (wss.nFileVersion == 10300) wss.nFileVersion = 300; } else if (strType == "cscript") { uint160 hash; ssKey >> hash; CScript script; ssValue >> *(CScriptBase *)(&script); if (!pwallet->LoadCScript(script)) { strErr = "Error reading wallet database: LoadCScript failed"; return false; } } else if (strType == "orderposnext") { ssValue >> pwallet->nOrderPosNext; } else if (strType == "destdata") { std::string strAddress, strKey, strValue; ssKey >> strAddress; ssKey >> strKey; ssValue >> strValue; if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) { strErr = "Error reading wallet database: LoadDestData failed"; return false; } } else if (strType == "hdchain") { CHDChain chain; ssValue >> chain; if (!pwallet->SetHDChain(chain, true)) { strErr = "Error reading wallet database: SetHDChain failed"; return false; } } } catch (...) { return false; } return true; } static bool IsKeyType(std::string strType) { return (strType == "key" || strType == "wkey" || strType == "mkey" || strType == "ckey"); } DBErrors CWalletDB::LoadWallet(CWallet *pwallet) { pwallet->vchDefaultKey = CPubKey(); CWalletScanState wss; bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; LOCK(pwallet->cs_wallet); try { int nMinVersion = 0; if (Read((std::string) "minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DB_TOO_NEW; pwallet->LoadMinVersion(nMinVersion); } // Get cursor Dbc *pcursor = GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); return DB_CORRUPT; } while (true) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; else if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); return DB_CORRUPT; } // Try to be tolerant of single corrupt records: std::string strType, strErr; if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) { // losing keys is considered a catastrophic error, anything else // we assume the user can live with: if (IsKeyType(strType)) result = DB_CORRUPT; else { // Leave other errors alone, if we try to fix them we might // make things worse. But do warn the user there is // something wrong. fNoncriticalErrors = true; if (strType == "tx") // Rescan if there is a bad transaction record: SoftSetBoolArg("-rescan", true); } } if (!strErr.empty()) LogPrintf("%s\n", strErr); } pcursor->close(); } catch (const boost::thread_interrupted &) { throw; } catch (...) { result = DB_CORRUPT; } if (fNoncriticalErrors && result == DB_LOAD_OK) result = DB_NONCRITICAL_ERROR; // Any wallet corruption at all: skip any rewriting or upgrading, we don't // want to make it worse. if (result != DB_LOAD_OK) return result; LogPrintf("nFileVersion = %d\n", wss.nFileVersion); LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); // nTimeFirstKey is only reliable if all keys have metadata if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) pwallet->UpdateTimeFirstKey(1); for (uint256 hash : wss.vWalletUpgrade) { WriteTx(pwallet->mapWallet[hash]); } // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) return DB_NEED_REWRITE; if (wss.nFileVersion < CLIENT_VERSION) // Update WriteVersion(CLIENT_VERSION); if (wss.fAnyUnordered) result = pwallet->ReorderTransactions(); pwallet->laccentries.clear(); ListAccountCreditDebit("*", pwallet->laccentries); for (CAccountingEntry &entry : pwallet->laccentries) { - pwallet->wtxOrdered.insert(make_pair( + pwallet->wtxOrdered.insert(std::make_pair( entry.nOrderPos, CWallet::TxPair((CWalletTx *)0, &entry))); } return result; } DBErrors CWalletDB::FindWalletTx(CWallet *pwallet, std::vector &vTxHash, std::vector &vWtx) { pwallet->vchDefaultKey = CPubKey(); bool fNoncriticalErrors = false; DBErrors result = DB_LOAD_OK; try { LOCK(pwallet->cs_wallet); int nMinVersion = 0; if (Read((std::string) "minversion", nMinVersion)) { if (nMinVersion > CLIENT_VERSION) return DB_TOO_NEW; pwallet->LoadMinVersion(nMinVersion); } // Get cursor Dbc *pcursor = GetCursor(); if (!pcursor) { LogPrintf("Error getting wallet database cursor\n"); return DB_CORRUPT; } while (true) { // Read next record CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; else if (ret != 0) { LogPrintf("Error reading next record from wallet database\n"); return DB_CORRUPT; } std::string strType; ssKey >> strType; if (strType == "tx") { uint256 hash; ssKey >> hash; CWalletTx wtx; ssValue >> wtx; vTxHash.push_back(hash); vWtx.push_back(wtx); } } pcursor->close(); } catch (const boost::thread_interrupted &) { throw; } catch (...) { result = DB_CORRUPT; } if (fNoncriticalErrors && result == DB_LOAD_OK) result = DB_NONCRITICAL_ERROR; return result; } DBErrors CWalletDB::ZapSelectTx(CWallet *pwallet, std::vector &vTxHashIn, std::vector &vTxHashOut) { // Build list of wallet TXs and hashes. std::vector vTxHash; std::vector vWtx; DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); if (err != DB_LOAD_OK) { return err; } std::sort(vTxHash.begin(), vTxHash.end()); std::sort(vTxHashIn.begin(), vTxHashIn.end()); // Erase each matching wallet TX. bool delerror = false; std::vector::iterator it = vTxHashIn.begin(); for (uint256 hash : vTxHash) { while (it < vTxHashIn.end() && (*it) < hash) { it++; } if (it == vTxHashIn.end()) { break; } else if ((*it) == hash) { pwallet->mapWallet.erase(hash); if (!EraseTx(hash)) { LogPrint("db", "Transaction was found for deletion but " "returned database error: %s\n", hash.GetHex()); delerror = true; } vTxHashOut.push_back(hash); } } if (delerror) { return DB_CORRUPT; } return DB_LOAD_OK; } DBErrors CWalletDB::ZapWalletTx(CWallet *pwallet, std::vector &vWtx) { // Build list of wallet TXs. std::vector vTxHash; DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); if (err != DB_LOAD_OK) return err; // Erase each wallet TX. for (uint256 &hash : vTxHash) { if (!EraseTx(hash)) return DB_CORRUPT; } return DB_LOAD_OK; } void ThreadFlushWalletDB() { // Make this thread recognisable as the wallet flushing thread. RenameThread("bitcoin-wallet"); static bool fOneThread; if (fOneThread) return; fOneThread = true; if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) return; unsigned int nLastSeen = CWalletDB::GetUpdateCounter(); unsigned int nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nLastWalletUpdate = GetTime(); while (true) { MilliSleep(500); if (nLastSeen != CWalletDB::GetUpdateCounter()) { nLastSeen = CWalletDB::GetUpdateCounter(); nLastWalletUpdate = GetTime(); } if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2) { TRY_LOCK(bitdb.cs_db, lockDb); if (lockDb) { // Don't do this if any databases are in use. int nRefCount = 0; std::map::iterator mi = bitdb.mapFileUseCount.begin(); while (mi != bitdb.mapFileUseCount.end()) { nRefCount += (*mi).second; mi++; } if (nRefCount == 0) { boost::this_thread::interruption_point(); const std::string &strFile = pwalletMain->strWalletFile; std::map::iterator _mi = bitdb.mapFileUseCount.find(strFile); if (_mi != bitdb.mapFileUseCount.end()) { LogPrint("db", "Flushing %s\n", strFile); nLastFlushed = CWalletDB::GetUpdateCounter(); int64_t nStart = GetTimeMillis(); // Flush wallet file so it's self contained. bitdb.CloseDb(strFile); bitdb.CheckpointLSN(strFile); bitdb.mapFileUseCount.erase(_mi++); LogPrint("db", "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart); } } } } } } // // Try to (very carefully!) recover wallet file if there is a problem. // bool CWalletDB::Recover(CDBEnv &dbenv, const std::string &filename, bool fOnlyKeys) { // Recovery procedure: // move wallet file to wallet.timestamp.bak . Call Salvage with // fAggressive=true to get as much data as possible. Rewrite salvaged data // to fresh wallet file. Set -rescan so any missing transactions will be // found. int64_t now = GetTime(); std::string newFilename = strprintf("wallet.%d.bak", now); int result = dbenv.dbenv->dbrename(nullptr, filename.c_str(), nullptr, newFilename.c_str(), DB_AUTO_COMMIT); if (result == 0) LogPrintf("Renamed %s to %s\n", filename, newFilename); else { LogPrintf("Failed to rename %s to %s\n", filename, newFilename); return false; } std::vector salvagedData; bool fSuccess = dbenv.Salvage(newFilename, true, salvagedData); if (salvagedData.empty()) { LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); return false; } LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); std::unique_ptr pdbCopy(new Db(dbenv.dbenv, 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) { LogPrintf("Cannot create database file %s\n", filename); return false; } CWallet dummyWallet; CWalletScanState wss; DbTxn *ptxn = dbenv.TxnBegin(); for (CDBEnv::KeyValPair &row : salvagedData) { if (fOnlyKeys) { CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); std::string strType, strErr; bool fReadOK; { // Required in LoadKeyMetadata(): LOCK(dummyWallet.cs_wallet); fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, wss, strType, strErr); } if (!IsKeyType(strType) && strType != "hdchain") continue; if (!fReadOK) { LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr); continue; } } Dbt datKey(&row.first[0], row.first.size()); Dbt datValue(&row.second[0], 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; } bool CWalletDB::Recover(CDBEnv &dbenv, const std::string &filename) { return CWalletDB::Recover(dbenv, filename, false); } bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) { nWalletDBUpdateCounter++; return Write( std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); } bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) { nWalletDBUpdateCounter++; return Erase( std::make_pair(std::string("destdata"), std::make_pair(address, key))); } bool CWalletDB::WriteHDChain(const CHDChain &chain) { nWalletDBUpdateCounter++; return Write(std::string("hdchain"), chain); } void CWalletDB::IncrementUpdateCounter() { nWalletDBUpdateCounter++; } unsigned int CWalletDB::GetUpdateCounter() { return nWalletDBUpdateCounter; }