diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h index bb6bc0f3c..d1e488167 100644 --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -1,289 +1,244 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2020 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_BDB_H #define BITCOIN_WALLET_BDB_H #include #include #include #include #include #include #include #include #include #include #include #include #include struct bilingual_str; static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; struct WalletDatabaseFileId { u_int8_t value[DB_FILE_ID_LEN]; bool operator==(const WalletDatabaseFileId &rhs) const; }; class BerkeleyDatabase; class BerkeleyEnvironment { 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; public: std::unique_ptr dbenv; std::map mapFileUseCount; std::map> m_databases; std::unordered_map m_fileids; std::condition_variable_any m_db_in_use; BerkeleyEnvironment(const fs::path &env_directory); BerkeleyEnvironment(); ~BerkeleyEnvironment(); void Reset(); void MakeMock(); bool IsMock() const { return fMockDb; } bool IsInitialized() const { return fDbEnvInit; } bool IsDatabaseLoaded(const std::string &db_filename) const { return m_databases.find(db_filename) != m_databases.end(); } fs::path Directory() const { return strPath; } bool Verify(const std::string &strFile); bool Open(bilingual_str &error); void Close(); void Flush(bool fShutdown); void CheckpointLSN(const std::string &strFile); void CloseDb(const std::string &strFile); void ReloadDbEnv(); 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; } }; /** Get BerkeleyEnvironment and database filename given a wallet path. */ std::shared_ptr GetWalletEnv(const fs::path &wallet_path, std::string &database_filename); /** Return whether a BDB wallet database is currently loaded. */ bool IsBDBWalletLoaded(const fs::path &wallet_path); /** * An instance of this class represents one database. * For BerkeleyDB this is just a (env, strFile) tuple. */ class BerkeleyDatabase { friend class BerkeleyBatch; public: /** Create dummy DB handle */ BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) {} /** Create DB handle to real database */ BerkeleyDatabase(std::shared_ptr envIn, std::string filename) : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(envIn)), strFile(std::move(filename)) { auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this)); assert(inserted.second); } ~BerkeleyDatabase() { if (env) { size_t erased = env->m_databases.erase(strFile); assert(erased == 1); } } /** * Rewrite the entire database on disk, with the exception of key pszSkip if * non-zero */ bool Rewrite(const char *pszSkip = nullptr); /** * Back up the entire database to a file. */ bool Backup(const std::string &strDest) const; /** * Make sure all changes are flushed to disk. */ void Flush(bool shutdown); /* * flush the wallet passively (TRY_LOCK) * ideal to be called periodically */ bool PeriodicFlush(); void IncrementUpdateCounter(); void ReloadDbEnv(); std::atomic nUpdateCounter; unsigned int nLastSeen; unsigned int nLastFlushed; int64_t nLastWalletUpdate; /** Verifies the environment and database file */ bool Verify(bilingual_str &error); /** * Pointer to shared database environment. * * Normally there is only one BerkeleyDatabase object per * BerkeleyEnvivonment, but in the special, backwards compatible case where * multiple wallet BDB data files are loaded from the same directory, this * will point to a shared instance that gets freed when the last data file * is closed. */ std::shared_ptr env; /** * Database pointer. This is initialized lazily and reset during flushes, * so it can be null. */ std::unique_ptr m_db; private: std::string strFile; /** * Return whether this database handle is a dummy for testing. * Only to be used at a low level, application should ideally not care * about this. */ bool IsDummy() const { return env == nullptr; } }; /** RAII class that provides access to a Berkeley database */ -class BerkeleyBatch { +class BerkeleyBatch : public DatabaseBatch { /** RAII class that automatically cleanses its data on destruction */ class SafeDbt final { Dbt m_dbt; public: // construct Dbt with internally-managed data SafeDbt(); // construct Dbt with provided data SafeDbt(void *data, size_t size); ~SafeDbt(); // delegate to Dbt const void *get_data() const; u_int32_t get_size() const; // conversion operator to access the underlying Dbt operator Dbt *(); }; private: - bool ReadKey(CDataStream &&key, CDataStream &value); + bool ReadKey(CDataStream &&key, CDataStream &value) override; bool WriteKey(CDataStream &&key, CDataStream &&value, - bool overwrite = true); - bool EraseKey(CDataStream &&key); - bool HasKey(CDataStream &&key); + bool overwrite = true) override; + bool EraseKey(CDataStream &&key) override; + bool HasKey(CDataStream &&key) override; protected: Db *pdb; std::string strFile; DbTxn *activeTxn; Dbc *m_cursor; bool fReadOnly; bool fFlushOnClose; BerkeleyEnvironment *env; public: explicit BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode = "r+", bool fFlushOnCloseIn = true); - ~BerkeleyBatch() { Close(); } + ~BerkeleyBatch() override { Close(); } BerkeleyBatch(const BerkeleyBatch &) = delete; BerkeleyBatch &operator=(const BerkeleyBatch &) = delete; - void Flush(); - void Close(); - - template bool Read(const K &key, T &value) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - if (!ReadKey(std::move(ssKey), ssValue)) { - return false; - } - try { - ssValue >> value; - return true; - } catch (const std::exception &) { - return false; - } - } - - template - bool Write(const K &key, const T &value, bool fOverwrite = true) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - ssValue.reserve(10000); - ssValue << value; - - return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite); - } - - template bool Erase(const K &key) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - - return EraseKey(std::move(ssKey)); - } - - template bool Exists(const K &key) { - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - ssKey.reserve(1000); - ssKey << key; - - return HasKey(std::move(ssKey)); - } + void Flush() override; + void Close() override; - bool StartCursor(); - bool ReadAtCursor(CDataStream &ssKey, CDataStream &ssValue, bool &complete); - void CloseCursor(); - bool TxnBegin(); - bool TxnCommit(); - bool TxnAbort(); + bool StartCursor() override; + bool ReadAtCursor(CDataStream &ssKey, CDataStream &ssValue, + bool &complete) override; + void CloseCursor() override; + bool TxnBegin() override; + bool TxnCommit() override; + bool TxnAbort() override; }; #endif // BITCOIN_WALLET_BDB_H diff --git a/src/wallet/db.h b/src/wallet/db.h index 874182b23..57273d6cb 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -1,20 +1,96 @@ // 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 #include +#include #include /** * Given a wallet directory path or legacy file path, return path to main data * file in the wallet database. */ fs::path WalletDataFilePath(const fs::path &wallet_path); void SplitWalletPath(const fs::path &wallet_path, fs::path &env_directory, std::string &database_filename); +/** RAII class that provides access to a WalletDatabase */ +class DatabaseBatch { +private: + virtual bool ReadKey(CDataStream &&key, CDataStream &value) = 0; + virtual bool WriteKey(CDataStream &&key, CDataStream &&value, + bool overwrite = true) = 0; + virtual bool EraseKey(CDataStream &&key) = 0; + virtual bool HasKey(CDataStream &&key) = 0; + +public: + explicit DatabaseBatch() {} + virtual ~DatabaseBatch() {} + + DatabaseBatch(const DatabaseBatch &) = delete; + DatabaseBatch &operator=(const DatabaseBatch &) = delete; + + virtual void Flush() = 0; + virtual void Close() = 0; + + template bool Read(const K &key, T &value) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + if (!ReadKey(std::move(ssKey), ssValue)) { + return false; + } + try { + ssValue >> value; + return true; + } catch (const std::exception &) { + return false; + } + } + + template + bool Write(const K &key, const T &value, bool fOverwrite = true) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + + CDataStream ssValue(SER_DISK, CLIENT_VERSION); + ssValue.reserve(10000); + ssValue << value; + + return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite); + } + + template bool Erase(const K &key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + + return EraseKey(std::move(ssKey)); + } + + template bool Exists(const K &key) { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + ssKey.reserve(1000); + ssKey << key; + + return HasKey(std::move(ssKey)); + } + + virtual bool StartCursor() = 0; + virtual bool ReadAtCursor(CDataStream &ssKey, CDataStream &ssValue, + bool &complete) = 0; + virtual void CloseCursor() = 0; + virtual bool TxnBegin() = 0; + virtual bool TxnCommit() = 0; + virtual bool TxnAbort() = 0; +}; + #endif // BITCOIN_WALLET_DB_H