1 // Copyright (c) 2009-2010 Satoshi Nakamoto 2 // Copyright (c) 2009-2020 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 #ifndef BITCOIN_WALLET_DB_H 7 #define BITCOIN_WALLET_DB_H 8 9 #include <clientversion.h> 10 #include <fs.h> 11 #include <optional.h> 12 #include <streams.h> 13 #include <support/allocators/secure.h> 14 #include <util/memory.h> 15 16 #include <atomic> 17 #include <memory> 18 #include <string> 19 20 struct bilingual_str; 21 22 void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename); 23 24 /** RAII class that provides access to a WalletDatabase */ 25 class DatabaseBatch 26 { 27 private: 28 virtual bool ReadKey(CDataStream&& key, CDataStream& value) = 0; 29 virtual bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) = 0; 30 virtual bool EraseKey(CDataStream&& key) = 0; 31 virtual bool HasKey(CDataStream&& key) = 0; 32 33 public: DatabaseBatch()34 explicit DatabaseBatch() {} ~DatabaseBatch()35 virtual ~DatabaseBatch() {} 36 37 DatabaseBatch(const DatabaseBatch&) = delete; 38 DatabaseBatch& operator=(const DatabaseBatch&) = delete; 39 40 virtual void Flush() = 0; 41 virtual void Close() = 0; 42 43 template <typename K, typename T> Read(const K & key,T & value)44 bool Read(const K& key, T& value) 45 { 46 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 47 ssKey.reserve(1000); 48 ssKey << key; 49 50 CDataStream ssValue(SER_DISK, CLIENT_VERSION); 51 if (!ReadKey(std::move(ssKey), ssValue)) return false; 52 try { 53 ssValue >> value; 54 return true; 55 } catch (const std::exception&) { 56 return false; 57 } 58 } 59 60 template <typename K, typename T> 61 bool Write(const K& key, const T& value, bool fOverwrite = true) 62 { 63 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 64 ssKey.reserve(1000); 65 ssKey << key; 66 67 CDataStream ssValue(SER_DISK, CLIENT_VERSION); 68 ssValue.reserve(10000); 69 ssValue << value; 70 71 return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite); 72 } 73 74 template <typename K> Erase(const K & key)75 bool Erase(const K& key) 76 { 77 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 78 ssKey.reserve(1000); 79 ssKey << key; 80 81 return EraseKey(std::move(ssKey)); 82 } 83 84 template <typename K> Exists(const K & key)85 bool Exists(const K& key) 86 { 87 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 88 ssKey.reserve(1000); 89 ssKey << key; 90 91 return HasKey(std::move(ssKey)); 92 } 93 94 virtual bool StartCursor() = 0; 95 virtual bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) = 0; 96 virtual void CloseCursor() = 0; 97 virtual bool TxnBegin() = 0; 98 virtual bool TxnCommit() = 0; 99 virtual bool TxnAbort() = 0; 100 }; 101 102 /** An instance of this class represents one database. 103 **/ 104 class WalletDatabase 105 { 106 public: 107 /** Create dummy DB handle */ WalletDatabase()108 WalletDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0) {} ~WalletDatabase()109 virtual ~WalletDatabase() {}; 110 111 /** Open the database if it is not already opened. */ 112 virtual void Open() = 0; 113 114 //! Counts the number of active database users to be sure that the database is not closed while someone is using it 115 std::atomic<int> m_refcount{0}; 116 /** Indicate the a new database user has began using the database. Increments m_refcount */ 117 virtual void AddRef() = 0; 118 /** Indicate that database user has stopped using the database and that it could be flushed or closed. Decrement m_refcount */ 119 virtual void RemoveRef() = 0; 120 121 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero 122 */ 123 virtual bool Rewrite(const char* pszSkip=nullptr) = 0; 124 125 /** Back up the entire database to a file. 126 */ 127 virtual bool Backup(const std::string& strDest) const = 0; 128 129 /** Make sure all changes are flushed to database file. 130 */ 131 virtual void Flush() = 0; 132 /** Flush to the database file and close the database. 133 * Also close the environment if no other databases are open in it. 134 */ 135 virtual void Close() = 0; 136 /* flush the wallet passively (TRY_LOCK) 137 ideal to be called periodically */ 138 virtual bool PeriodicFlush() = 0; 139 140 virtual void IncrementUpdateCounter() = 0; 141 142 virtual void ReloadDbEnv() = 0; 143 144 /** Return path to main database file for logs and error messages. */ 145 virtual std::string Filename() = 0; 146 147 virtual std::string Format() = 0; 148 149 std::atomic<unsigned int> nUpdateCounter; 150 unsigned int nLastSeen; 151 unsigned int nLastFlushed; 152 int64_t nLastWalletUpdate; 153 154 /** Make a DatabaseBatch connected to this database */ 155 virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0; 156 }; 157 158 /** RAII class that provides access to a DummyDatabase. Never fails. */ 159 class DummyBatch : public DatabaseBatch 160 { 161 private: ReadKey(CDataStream && key,CDataStream & value)162 bool ReadKey(CDataStream&& key, CDataStream& value) override { return true; } 163 bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) override { return true; } EraseKey(CDataStream && key)164 bool EraseKey(CDataStream&& key) override { return true; } HasKey(CDataStream && key)165 bool HasKey(CDataStream&& key) override { return true; } 166 167 public: Flush()168 void Flush() override {} Close()169 void Close() override {} 170 StartCursor()171 bool StartCursor() override { return true; } ReadAtCursor(CDataStream & ssKey,CDataStream & ssValue,bool & complete)172 bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override { return true; } CloseCursor()173 void CloseCursor() override {} TxnBegin()174 bool TxnBegin() override { return true; } TxnCommit()175 bool TxnCommit() override { return true; } TxnAbort()176 bool TxnAbort() override { return true; } 177 }; 178 179 /** A dummy WalletDatabase that does nothing and never fails. Only used by unit tests. 180 **/ 181 class DummyDatabase : public WalletDatabase 182 { 183 public: Open()184 void Open() override {}; AddRef()185 void AddRef() override {} RemoveRef()186 void RemoveRef() override {} 187 bool Rewrite(const char* pszSkip=nullptr) override { return true; } Backup(const std::string & strDest)188 bool Backup(const std::string& strDest) const override { return true; } Close()189 void Close() override {} Flush()190 void Flush() override {} PeriodicFlush()191 bool PeriodicFlush() override { return true; } IncrementUpdateCounter()192 void IncrementUpdateCounter() override { ++nUpdateCounter; } ReloadDbEnv()193 void ReloadDbEnv() override {} Filename()194 std::string Filename() override { return "dummy"; } Format()195 std::string Format() override { return "dummy"; } 196 std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); } 197 }; 198 199 enum class DatabaseFormat { 200 BERKELEY, 201 SQLITE, 202 }; 203 204 struct DatabaseOptions { 205 bool require_existing = false; 206 bool require_create = false; 207 Optional<DatabaseFormat> require_format; 208 uint64_t create_flags = 0; 209 SecureString create_passphrase; 210 bool verify = true; 211 }; 212 213 enum class DatabaseStatus { 214 SUCCESS, 215 FAILED_BAD_PATH, 216 FAILED_BAD_FORMAT, 217 FAILED_ALREADY_LOADED, 218 FAILED_ALREADY_EXISTS, 219 FAILED_NOT_FOUND, 220 FAILED_CREATE, 221 FAILED_LOAD, 222 FAILED_VERIFY, 223 FAILED_ENCRYPT, 224 }; 225 226 std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); 227 228 #endif // BITCOIN_WALLET_DB_H 229