diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index b70fc8def..70bf77646 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -1,490 +1,516 @@ // 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 "db.h" #include "addrman.h" #include "fs.h" #include "hash.h" #include "protocol.h" #include "util.h" #include "utilstrencodings.h" #include #include #include #ifndef WIN32 #include #endif // // CDB // CDBEnv bitdb; void CDBEnv::EnvShutdown() { if (!fDbEnvInit) { return; } fDbEnvInit = false; int ret = dbenv->close(0); if (ret != 0) { LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database " "environment: %s\n", ret, DbEnv::strerror(ret)); } if (!fMockDb) { DbEnv(uint32_t(0)).remove(strPath.c_str(), 0); } } void CDBEnv::Reset() { delete dbenv; dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS); fDbEnvInit = false; fMockDb = false; } CDBEnv::CDBEnv() : dbenv(nullptr) { Reset(); } CDBEnv::~CDBEnv() { EnvShutdown(); delete dbenv; dbenv = nullptr; } void CDBEnv::Close() { EnvShutdown(); } bool CDBEnv::Open(const fs::path &pathIn) { if (fDbEnvInit) { return true; } boost::this_thread::interruption_point(); strPath = pathIn.string(); fs::path pathLogDir = pathIn / "database"; TryCreateDirectories(pathLogDir); fs::path pathErrorFile = pathIn / "db.log"; LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string()); unsigned int nEnvFlags = 0; if (GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) { nEnvFlags |= DB_PRIVATE; } dbenv->set_lg_dir(pathLogDir.string().c_str()); // 1 MiB should be enough for just the wallet dbenv->set_cachesize(0, 0x100000, 1); dbenv->set_lg_bsize(0x10000); dbenv->set_lg_max(1048576); dbenv->set_lk_max_locks(40000); dbenv->set_lk_max_objects(40000); /// debug dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); dbenv->set_flags(DB_AUTO_COMMIT, 1); dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1); dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1); int ret = dbenv->open(strPath.c_str(), DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD | DB_RECOVER | nEnvFlags, S_IRUSR | S_IWUSR); if (ret != 0) { return error( "CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); } fDbEnvInit = true; fMockDb = false; return true; } void CDBEnv::MakeMock() { if (fDbEnvInit) { throw std::runtime_error("CDBEnv::MakeMock: Already initialized"); } boost::this_thread::interruption_point(); LogPrint(BCLog::DB, "CDBEnv::MakeMock\n"); dbenv->set_cachesize(1, 0, 1); dbenv->set_lg_bsize(10485760 * 4); dbenv->set_lg_max(10485760); dbenv->set_lk_max_locks(10000); dbenv->set_lk_max_objects(10000); dbenv->set_flags(DB_AUTO_COMMIT, 1); dbenv->log_set_config(DB_LOG_IN_MEMORY, 1); int ret = dbenv->open(nullptr, DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD | DB_PRIVATE, S_IRUSR | S_IWUSR); if (ret > 0) { throw std::runtime_error(strprintf( "CDBEnv::MakeMock: Error %d opening database environment.", ret)); } fDbEnvInit = true; fMockDb = true; } CDBEnv::VerifyResult CDBEnv::Verify(const std::string &strFile, bool (*recoverFunc)(CDBEnv &dbenv, const std::string &strFile)) { LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); Db db(dbenv, 0); int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); if (result == 0) { return VERIFY_OK; } else if (recoverFunc == nullptr) { return RECOVER_FAIL; } // Try to recover: bool fRecovered = (*recoverFunc)(*this, strFile); return (fRecovered ? RECOVER_OK : RECOVER_FAIL); } /* 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"; bool CDBEnv::Salvage(const std::string &strFile, bool fAggressive, std::vector &vResult) { LOCK(cs_db); assert(mapFileUseCount.count(strFile) == 0); u_int32_t flags = DB_SALVAGE; - if (fAggressive) flags |= DB_AGGRESSIVE; + if (fAggressive) { + flags |= DB_AGGRESSIVE; + } std::stringstream strDump; Db db(dbenv, 0); int result = db.verify(strFile.c_str(), nullptr, &strDump, flags); if (result == DB_VERIFY_BAD) { LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data " "may not be recoverable.\n"); if (!fAggressive) { LogPrintf("CDBEnv::Salvage: Rerun with aggressive mode to ignore " "errors and continue.\n"); return false; } } if (result != 0 && result != DB_VERIFY_BAD) { LogPrintf("CDBEnv::Salvage: Database salvage failed with result %d.\n", 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) { // Skip past header getline(strDump, strLine); } std::string keyHex, valueHex; while (!strDump.eof() && keyHex != DATA_END) { getline(strDump, keyHex); if (keyHex != DATA_END) { - if (strDump.eof()) break; + if (strDump.eof()) { + break; + } getline(strDump, valueHex); if (valueHex == DATA_END) { LogPrintf("CDBEnv::Salvage: WARNING: Number of keys in data " "does not match number of values.\n"); break; } vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex))); } } if (keyHex != DATA_END) { LogPrintf("CDBEnv::Salvage: WARNING: Unexpected end of file while " "reading salvage output.\n"); return false; } return (result == 0); } void CDBEnv::CheckpointLSN(const std::string &strFile) { dbenv->txn_checkpoint(0, 0, 0); - if (fMockDb) return; + if (fMockDb) { + return; + } dbenv->lsn_reset(strFile.c_str(), 0); } CDB::CDB(const std::string &strFilename, const char *pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr) { int ret; fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); fFlushOnClose = fFlushOnCloseIn; if (strFilename.empty()) { return; } bool fCreate = strchr(pszMode, 'c') != nullptr; unsigned int nFlags = DB_THREAD; if (fCreate) { nFlags |= DB_CREATE; } { LOCK(bitdb.cs_db); if (!bitdb.Open(GetDataDir())) { throw std::runtime_error( "CDB: Failed to open database environment."); } strFile = strFilename; ++bitdb.mapFileUseCount[strFile]; pdb = bitdb.mapDb[strFile]; if (pdb == nullptr) { pdb = new Db(bitdb.dbenv, 0); bool fMockDb = bitdb.IsMock(); if (fMockDb) { DbMpoolFile *mpf = pdb->get_mpf(); ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); if (ret != 0) { throw std::runtime_error( strprintf("CDB: Failed to configure for no temp file " "backing for database %s", strFile)); } } ret = pdb->open(nullptr, // Txn pointer fMockDb ? nullptr : strFile.c_str(), // Filename fMockDb ? strFile.c_str() : "main", // Logical db name DB_BTREE, // Database type nFlags, // Flags 0); if (ret != 0) { delete pdb; pdb = nullptr; --bitdb.mapFileUseCount[strFile]; strFile = ""; throw std::runtime_error(strprintf( "CDB: Error %d, can't open database %s", ret, strFilename)); } if (fCreate && !Exists(std::string("version"))) { bool fTmp = fReadOnly; fReadOnly = false; WriteVersion(CLIENT_VERSION); fReadOnly = fTmp; } bitdb.mapDb[strFile] = pdb; } } } void CDB::Flush() { - if (activeTxn) return; + if (activeTxn) { + return; + } // Flush database activity from memory pool to disk log unsigned int nMinutes = 0; - if (fReadOnly) nMinutes = 1; + if (fReadOnly) { + nMinutes = 1; + } bitdb.dbenv->txn_checkpoint( nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0); } void CDB::Close() { - if (!pdb) return; - if (activeTxn) activeTxn->abort(); + if (!pdb) { + return; + } + if (activeTxn) { + activeTxn->abort(); + } activeTxn = nullptr; pdb = nullptr; - if (fFlushOnClose) Flush(); - - { - LOCK(bitdb.cs_db); - --bitdb.mapFileUseCount[strFile]; + if (fFlushOnClose) { + Flush(); } + + LOCK(bitdb.cs_db); + --bitdb.mapFileUseCount[strFile]; } void CDBEnv::CloseDb(const std::string &strFile) { LOCK(cs_db); if (mapDb[strFile] != nullptr) { // Close the database handle Db *pdb = mapDb[strFile]; pdb->close(0); delete pdb; mapDb[strFile] = nullptr; } } bool CDBEnv::RemoveDb(const std::string &strFile) { this->CloseDb(strFile); LOCK(cs_db); int rc = dbenv->dbremove(nullptr, strFile.c_str(), nullptr, DB_AUTO_COMMIT); return (rc == 0); } bool CDB::Rewrite(const std::string &strFile, const char *pszSkip) { while (true) { { LOCK(bitdb.cs_db); if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) { // Flush log data to the dat file bitdb.CloseDb(strFile); bitdb.CheckpointLSN(strFile); bitdb.mapFileUseCount.erase(strFile); bool fSuccess = true; LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile); std::string strFileRes = strFile + ".rewrite"; { // surround usage of db with extra {} CDB db(strFile.c_str(), "r"); Db *pdbCopy = new Db(bitdb.dbenv, 0); int ret = pdbCopy->open(nullptr, // Txn pointer strFileRes.c_str(), // Filename "main", // Logical db name DB_BTREE, // Database type DB_CREATE, // Flags 0); if (ret > 0) { LogPrintf( "CDB::Rewrite: Can't create database file %s\n", strFileRes); fSuccess = false; } Dbc *pcursor = db.GetCursor(); if (pcursor) while (fSuccess) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); CDataStream ssValue(SER_DISK, CLIENT_VERSION); int ret1 = db.ReadAtCursor(pcursor, ssKey, ssValue); if (ret1 == DB_NOTFOUND) { pcursor->close(); break; - } else if (ret1 != 0) { + } + if (ret1 != 0) { pcursor->close(); fSuccess = false; break; } if (pszSkip && strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), - strlen(pszSkip))) == 0) + strlen(pszSkip))) == 0) { continue; + } if (strncmp(ssKey.data(), "\x07version", 8) == 0) { // Update version: ssValue.clear(); ssValue << CLIENT_VERSION; } Dbt datKey(ssKey.data(), ssKey.size()); Dbt datValue(ssValue.data(), ssValue.size()); int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE); - if (ret2 > 0) fSuccess = false; + if (ret2 > 0) { + fSuccess = false; + } } if (fSuccess) { db.Close(); bitdb.CloseDb(strFile); - if (pdbCopy->close(0)) fSuccess = false; + if (pdbCopy->close(0)) { + fSuccess = false; + } delete pdbCopy; } } if (fSuccess) { Db dbA(bitdb.dbenv, 0); - if (dbA.remove(strFile.c_str(), nullptr, 0)) + if (dbA.remove(strFile.c_str(), nullptr, 0)) { fSuccess = false; + } Db dbB(bitdb.dbenv, 0); if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), - 0)) + 0)) { fSuccess = false; + } } - if (!fSuccess) + if (!fSuccess) { LogPrintf( "CDB::Rewrite: Failed to rewrite database file %s\n", strFileRes); + } return fSuccess; } } MilliSleep(100); } return false; } void CDBEnv::Flush(bool fShutdown) { int64_t nStart = GetTimeMillis(); // Flush log data to the actual data file on all files that are not in use LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); if (!fDbEnvInit) { return; } { LOCK(cs_db); std::map::iterator mi = mapFileUseCount.begin(); while (mi != mapFileUseCount.end()) { std::string strFile = (*mi).first; int nRefCount = (*mi).second; LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); if (nRefCount == 0) { // Move log data to the dat file CloseDb(strFile); LogPrint(BCLog::DB, "CDBEnv::Flush: %s checkpoint\n", strFile); dbenv->txn_checkpoint(0, 0, 0); LogPrint(BCLog::DB, "CDBEnv::Flush: %s detach\n", strFile); - if (!fMockDb) dbenv->lsn_reset(strFile.c_str(), 0); + if (!fMockDb) { + dbenv->lsn_reset(strFile.c_str(), 0); + } LogPrint(BCLog::DB, "CDBEnv::Flush: %s closed\n", strFile); mapFileUseCount.erase(mi++); - } else + } else { mi++; + } } LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); if (fShutdown) { char **listp; if (mapFileUseCount.empty()) { dbenv->log_archive(&listp, DB_ARCH_REMOVE); Close(); if (!fMockDb) { fs::remove_all(fs::path(strPath) / "database"); } } } } } diff --git a/src/wallet/db.h b/src/wallet/db.h index 5c8146062..ed97b0a09 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -1,287 +1,313 @@ // 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. #ifndef BITCOIN_WALLET_DB_H #define BITCOIN_WALLET_DB_H #include "clientversion.h" #include "fs.h" #include "serialize.h" #include "streams.h" #include "sync.h" #include "version.h" #include #include #include #include static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; class CDBEnv { private: bool fDbEnvInit; bool fMockDb; // Don't change into fs::path, as that can result in // shutdown problems/crashes caused by a static initialized internal // pointer. std::string strPath; void EnvShutdown(); public: mutable CCriticalSection cs_db; DbEnv *dbenv; std::map mapFileUseCount; std::map mapDb; CDBEnv(); ~CDBEnv(); void Reset(); void MakeMock(); bool IsMock() { return fMockDb; } /** * Verify that database file strFile is OK. If it is not, call the callback * to try to recover. * This must be called BEFORE strFile is opened. * Returns true if strFile is OK. */ enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; VerifyResult Verify(const std::string &strFile, bool (*recoverFunc)(CDBEnv &dbenv, const std::string &strFile)); /** * Salvage data from a file that Verify says is bad. * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method * documentation). * Appends binary key/value pairs to vResult, returns true if successful. * NOTE: reads the entire database into memory, so cannot be used * for huge databases. */ typedef std::pair, std::vector> KeyValPair; bool Salvage(const std::string &strFile, bool fAggressive, std::vector &vResult); bool Open(const fs::path &path); void Close(); void Flush(bool fShutdown); void CheckpointLSN(const std::string &strFile); void CloseDb(const std::string &strFile); bool RemoveDb(const std::string &strFile); DbTxn *TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { DbTxn *ptxn = nullptr; int ret = dbenv->txn_begin(nullptr, &ptxn, flags); if (!ptxn || ret != 0) return nullptr; return ptxn; } }; extern CDBEnv bitdb; /** RAII class that provides access to a Berkeley database */ class CDB { protected: Db *pdb; std::string strFile; DbTxn *activeTxn; bool fReadOnly; bool fFlushOnClose; explicit CDB(const std::string &strFilename, const char *pszMode = "r+", bool fFlushOnCloseIn = true); ~CDB() { Close(); } public: void Flush(); void Close(); private: CDB(const CDB &); void operator=(const CDB &); protected: template bool Read(const K &key, T &value) { - if (!pdb) return false; + if (!pdb) { + return false; + } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Read Dbt datValue; datValue.set_flags(DB_DBT_MALLOC); int ret = pdb->get(activeTxn, &datKey, &datValue, 0); memset(datKey.get_data(), 0, datKey.get_size()); - if (datValue.get_data() == nullptr) return false; + if (datValue.get_data() == nullptr) { + return false; + } // Unserialize value try { CDataStream ssValue((char *)datValue.get_data(), (char *)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); ssValue >> value; } catch (const std::exception &) { return false; } // Clear and free memory memset(datValue.get_data(), 0, datValue.get_size()); free(datValue.get_data()); return (ret == 0); } template bool Write(const K &key, const T &value, bool fOverwrite = true) { - if (!pdb) return false; - if (fReadOnly) assert(!"Write called on database in read-only mode"); + if (!pdb) { + return false; + } + if (fReadOnly) { + assert(!"Write called on database in read-only mode"); + } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Value CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(10000); ssValue << value; Dbt datValue(ssValue.data(), ssValue.size()); // Write int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); // Clear memory in case it was a private key memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); return (ret == 0); } template bool Erase(const K &key) { - if (!pdb) return false; - if (fReadOnly) assert(!"Erase called on database in read-only mode"); + if (!pdb) { + return false; + } + if (fReadOnly) { + assert(!"Erase called on database in read-only mode"); + } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Erase int ret = pdb->del(activeTxn, &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0 || ret == DB_NOTFOUND); } template bool Exists(const K &key) { - if (!pdb) return false; + if (!pdb) { + return false; + } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Exists int ret = pdb->exists(activeTxn, &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0); } Dbc *GetCursor() { - if (!pdb) return nullptr; + if (!pdb) { + return nullptr; + } Dbc *pcursor = nullptr; int ret = pdb->cursor(nullptr, &pcursor, 0); - if (ret != 0) return nullptr; + if (ret != 0) { + return nullptr; + } return pcursor; } int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue, bool setRange = false) { // Read at cursor Dbt datKey; unsigned int fFlags = DB_NEXT; if (setRange) { datKey.set_data(ssKey.data()); datKey.set_size(ssKey.size()); fFlags = DB_SET_RANGE; } Dbt datValue; datKey.set_flags(DB_DBT_MALLOC); datValue.set_flags(DB_DBT_MALLOC); int ret = pcursor->get(&datKey, &datValue, fFlags); if (ret != 0) { return ret; } else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) { return 99999; } // Convert to streams ssKey.SetType(SER_DISK); ssKey.clear(); ssKey.write((char *)datKey.get_data(), datKey.get_size()); ssValue.SetType(SER_DISK); ssValue.clear(); ssValue.write((char *)datValue.get_data(), datValue.get_size()); // Clear and free memory memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); free(datKey.get_data()); free(datValue.get_data()); return 0; } public: bool TxnBegin() { - if (!pdb || activeTxn) return false; + if (!pdb || activeTxn) { + return false; + } DbTxn *ptxn = bitdb.TxnBegin(); - if (!ptxn) return false; + if (!ptxn) { + return false; + } activeTxn = ptxn; return true; } bool TxnCommit() { - if (!pdb || !activeTxn) return false; + if (!pdb || !activeTxn) { + return false; + } int ret = activeTxn->commit(0); activeTxn = nullptr; return (ret == 0); } bool TxnAbort() { - if (!pdb || !activeTxn) return false; + if (!pdb || !activeTxn) { + return false; + } int ret = activeTxn->abort(); activeTxn = nullptr; return (ret == 0); } bool ReadVersion(int &nVersion) { nVersion = 0; return Read(std::string("version"), nVersion); } bool WriteVersion(int nVersion) { return Write(std::string("version"), nVersion); } static bool Rewrite(const std::string &strFile, const char *pszSkip = nullptr); }; #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 2307701ec..c47cdd59e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1,914 +1,977 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017 The Bitcoin 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 "dstencode.h" #include "fs.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 static uint64_t nAccountingEntryNumber = 0; static std::atomic nWalletDBUpdateCounter; // // CWalletDB // bool CWalletDB::WriteName(const CTxDestination &address, const std::string &strName) { if (!IsValidDestination(address)) { return false; } nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("name"), EncodeLegacyAddr(address, Params())), strName); } bool CWalletDB::EraseName(const CTxDestination &address) { // 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. if (!IsValidDestination(address)) { return false; } nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("name"), EncodeLegacyAddr(address, Params()))); } bool CWalletDB::WritePurpose(const CTxDestination &address, const std::string &strPurpose) { if (!IsValidDestination(address)) { return false; } nWalletDBUpdateCounter++; return Write(std::make_pair(std::string("purpose"), EncodeLegacyAddr(address, Params())), strPurpose); } bool CWalletDB::ErasePurpose(const CTxDestination &address) { if (!IsValidDestination(address)) { return false; } nWalletDBUpdateCounter++; return Erase(std::make_pair(std::string("purpose"), EncodeLegacyAddr(address, Params()))); } 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)) + 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)) + if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) { return false; + } if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, - false)) + 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)) + 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)))) + *(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()) + 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(std::make_pair(std::string("acc"), strAccount), account); } bool CWalletDB::WriteAccount(const std::string &strAccount, const CAccount &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); } Amount CWalletDB::GetAccountCreditDebit(const std::string &strAccount) { std::list entries; ListAccountCreditDebit(strAccount, entries); Amount 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) + 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) + if (ret == DB_NOTFOUND) { break; - else if (ret != 0) { + } + + 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; + if (strType != "acentry") { + break; + } CAccountingEntry acentry; ssKey >> acentry.strAccount; - if (!fAllAccounts && acentry.strAccount != strAccount) break; + 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[DecodeDestination(strAddress)].name; } else if (strType == "purpose") { std::string strAddress; ssKey >> strAddress; ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].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; + 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; + 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) + if (nNumber > nAccountingEntryNumber) { nAccountingEntryNumber = nNumber; + } if (!wss.fAnyUnordered) { CAccountingEntry acentry; ssValue >> acentry; - if (acentry.nOrderPos == -1) wss.fAnyUnordered = true; + 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); + 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; + 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; + 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(DecodeDestination(strAddress), 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; + 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) + if (ret == DB_NOTFOUND) { break; - else if (ret != 0) { + } + + 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)) + if (IsKeyType(strType)) { result = DB_CORRUPT; - else { + } 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") + if (strType == "tx") { // Rescan if there is a bad transaction record: SoftSetBoolArg("-rescan", true); + } } } - if (!strErr.empty()) LogPrintf("%s\n", strErr); + 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) + 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; + 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) + 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)) + (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) { return DB_NEED_REWRITE; + } - if (wss.nFileVersion < CLIENT_VERSION) // Update + if (wss.nFileVersion < CLIENT_VERSION) { + // Update WriteVersion(CLIENT_VERSION); + } - if (wss.fAnyUnordered) result = pwallet->ReorderTransactions(); + if (wss.fAnyUnordered) { + result = pwallet->ReorderTransactions(); + } pwallet->laccentries.clear(); ListAccountCreditDebit("*", pwallet->laccentries); for (CAccountingEntry &entry : pwallet->laccentries) { 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; + 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) + if (ret == DB_NOTFOUND) { break; - else if (ret != 0) { + } + + 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) + 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) { + } + + if ((*it) == hash) { pwallet->mapWallet.erase(hash); if (!EraseTx(hash)) { LogPrint(BCLog::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; + if (fOneThread) { + return; + } + fOneThread = true; - if (!GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) return; + 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(BCLog::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(BCLog::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) + if (result == 0) { LogPrintf("Renamed %s to %s\n", filename, newFilename); - else { + } 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 (!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; + 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 CTxDestination &address, const std::string &key, const std::string &value) { if (!IsValidDestination(address)) { return false; } nWalletDBUpdateCounter++; return Write(std::make_pair( std::string("destdata"), std::make_pair(EncodeLegacyAddr(address, Params()), key)), value); } bool CWalletDB::EraseDestData(const CTxDestination &address, const std::string &key) { if (!IsValidDestination(address)) { return false; } nWalletDBUpdateCounter++; return Erase(std::make_pair( std::string("destdata"), std::make_pair(EncodeLegacyAddr(address, Params()), key))); } bool CWalletDB::WriteHDChain(const CHDChain &chain) { nWalletDBUpdateCounter++; return Write(std::string("hdchain"), chain); } void CWalletDB::IncrementUpdateCounter() { nWalletDBUpdateCounter++; } unsigned int CWalletDB::GetUpdateCounter() { return nWalletDBUpdateCounter; }