1 // Copyright (c) 2009-2010 Satoshi Nakamoto 2 // Copyright (c) 2009-2018 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 <serialize.h> 12 #include <streams.h> 13 #include <sync.h> 14 #include <util/system.h> 15 #include <version.h> 16 17 #include <atomic> 18 #include <map> 19 #include <memory> 20 #include <string> 21 #include <unordered_map> 22 #include <vector> 23 24 #include <db_cxx.h> 25 26 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; 27 static const bool DEFAULT_WALLET_PRIVDB = true; 28 29 struct WalletDatabaseFileId { 30 u_int8_t value[DB_FILE_ID_LEN]; 31 bool operator==(const WalletDatabaseFileId& rhs) const; 32 }; 33 34 class BerkeleyDatabase; 35 36 class BerkeleyEnvironment 37 { 38 private: 39 bool fDbEnvInit; 40 bool fMockDb; 41 // Don't change into fs::path, as that can result in 42 // shutdown problems/crashes caused by a static initialized internal pointer. 43 std::string strPath; 44 45 public: 46 std::unique_ptr<DbEnv> dbenv; 47 std::map<std::string, int> mapFileUseCount; 48 std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases; 49 std::unordered_map<std::string, WalletDatabaseFileId> m_fileids; 50 std::condition_variable_any m_db_in_use; 51 52 BerkeleyEnvironment(const fs::path& env_directory); 53 BerkeleyEnvironment(); 54 ~BerkeleyEnvironment(); 55 void Reset(); 56 IsMock()57 bool IsMock() const { return fMockDb; } IsInitialized()58 bool IsInitialized() const { return fDbEnvInit; } IsDatabaseLoaded(const std::string & db_filename)59 bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); } Directory()60 fs::path Directory() const { return strPath; } 61 62 /** 63 * Verify that database file strFile is OK. If it is not, 64 * call the callback to try to recover. 65 * This must be called BEFORE strFile is opened. 66 * Returns true if strFile is OK. 67 */ 68 enum class VerifyResult { VERIFY_OK, 69 RECOVER_OK, 70 RECOVER_FAIL }; 71 typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename); 72 VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename); 73 /** 74 * Salvage data from a file that Verify says is bad. 75 * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). 76 * Appends binary key/value pairs to vResult, returns true if successful. 77 * NOTE: reads the entire database into memory, so cannot be used 78 * for huge databases. 79 */ 80 typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair; 81 bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult); 82 83 bool Open(bool retry); 84 void Close(); 85 void Flush(bool fShutdown); 86 void CheckpointLSN(const std::string& strFile); 87 88 void CloseDb(const std::string& strFile); 89 void ReloadDbEnv(); 90 91 DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) 92 { 93 DbTxn* ptxn = nullptr; 94 int ret = dbenv->txn_begin(nullptr, &ptxn, flags); 95 if (!ptxn || ret != 0) 96 return nullptr; 97 return ptxn; 98 } 99 }; 100 101 /** Return whether a wallet database is currently loaded. */ 102 bool IsWalletLoaded(const fs::path& wallet_path); 103 104 /** Get BerkeleyEnvironment and database filename given a wallet path. */ 105 std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename); 106 107 /** An instance of this class represents one database. 108 * For BerkeleyDB this is just a (env, strFile) tuple. 109 **/ 110 class BerkeleyDatabase 111 { 112 friend class BerkeleyBatch; 113 public: 114 /** Create dummy DB handle */ BerkeleyDatabase()115 BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) 116 { 117 } 118 119 /** Create DB handle to real database */ BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env,std::string filename)120 BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) : 121 nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename)) 122 { 123 auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this)); 124 assert(inserted.second); 125 } 126 ~BerkeleyDatabase()127 ~BerkeleyDatabase() { 128 if (env) { 129 size_t erased = env->m_databases.erase(strFile); 130 assert(erased == 1); 131 } 132 } 133 134 /** Return object for accessing database at specified path. */ Create(const fs::path & path)135 static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path) 136 { 137 std::string filename; 138 return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename)); 139 } 140 141 /** Return object for accessing dummy database with no read/write capabilities. */ CreateDummy()142 static std::unique_ptr<BerkeleyDatabase> CreateDummy() 143 { 144 return MakeUnique<BerkeleyDatabase>(); 145 } 146 147 /** Return object for accessing temporary in-memory database. */ CreateMock()148 static std::unique_ptr<BerkeleyDatabase> CreateMock() 149 { 150 return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), ""); 151 } 152 153 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero 154 */ 155 bool Rewrite(const char* pszSkip=nullptr); 156 157 /** Back up the entire database to a file. 158 */ 159 bool Backup(const std::string& strDest); 160 161 /** Make sure all changes are flushed to disk. 162 */ 163 void Flush(bool shutdown); 164 165 void IncrementUpdateCounter(); 166 167 void ReloadDbEnv(); 168 169 std::atomic<unsigned int> nUpdateCounter; 170 unsigned int nLastSeen; 171 unsigned int nLastFlushed; 172 int64_t nLastWalletUpdate; 173 174 /** 175 * Pointer to shared database environment. 176 * 177 * Normally there is only one BerkeleyDatabase object per 178 * BerkeleyEnvivonment, but in the special, backwards compatible case where 179 * multiple wallet BDB data files are loaded from the same directory, this 180 * will point to a shared instance that gets freed when the last data file 181 * is closed. 182 */ 183 std::shared_ptr<BerkeleyEnvironment> env; 184 185 /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */ 186 std::unique_ptr<Db> m_db; 187 188 private: 189 std::string strFile; 190 191 /** Return whether this database handle is a dummy for testing. 192 * Only to be used at a low level, application should ideally not care 193 * about this. 194 */ IsDummy()195 bool IsDummy() { return env == nullptr; } 196 }; 197 198 /** RAII class that provides access to a Berkeley database */ 199 class BerkeleyBatch 200 { 201 /** RAII class that automatically cleanses its data on destruction */ 202 class SafeDbt final 203 { 204 Dbt m_dbt; 205 206 public: 207 // construct Dbt with internally-managed data 208 SafeDbt(); 209 // construct Dbt with provided data 210 SafeDbt(void* data, size_t size); 211 ~SafeDbt(); 212 213 // delegate to Dbt 214 const void* get_data() const; 215 u_int32_t get_size() const; 216 217 // conversion operator to access the underlying Dbt 218 operator Dbt*(); 219 }; 220 221 protected: 222 Db* pdb; 223 std::string strFile; 224 DbTxn* activeTxn; 225 bool fReadOnly; 226 bool fFlushOnClose; 227 BerkeleyEnvironment *env; 228 229 public: 230 explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true); ~BerkeleyBatch()231 ~BerkeleyBatch() { Close(); } 232 233 BerkeleyBatch(const BerkeleyBatch&) = delete; 234 BerkeleyBatch& operator=(const BerkeleyBatch&) = delete; 235 236 void Flush(); 237 void Close(); 238 static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); 239 240 /* flush the wallet passively (TRY_LOCK) 241 ideal to be called periodically */ 242 static bool PeriodicFlush(BerkeleyDatabase& database); 243 /* verifies the database environment */ 244 static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr); 245 /* verifies the database file */ 246 static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc); 247 248 template <typename K, typename T> Read(const K & key,T & value)249 bool Read(const K& key, T& value) 250 { 251 if (!pdb) 252 return false; 253 254 // Key 255 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 256 ssKey.reserve(1000); 257 ssKey << key; 258 SafeDbt datKey(ssKey.data(), ssKey.size()); 259 260 // Read 261 SafeDbt datValue; 262 int ret = pdb->get(activeTxn, datKey, datValue, 0); 263 bool success = false; 264 if (datValue.get_data() != nullptr) { 265 // Unserialize value 266 try { 267 CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); 268 ssValue >> value; 269 success = true; 270 } catch (const std::exception&) { 271 // In this case success remains 'false' 272 } 273 } 274 return ret == 0 && success; 275 } 276 277 template <typename K, typename T> 278 bool Write(const K& key, const T& value, bool fOverwrite = true) 279 { 280 if (!pdb) 281 return true; 282 if (fReadOnly) 283 assert(!"Write called on database in read-only mode"); 284 285 // Key 286 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 287 ssKey.reserve(1000); 288 ssKey << key; 289 SafeDbt datKey(ssKey.data(), ssKey.size()); 290 291 // Value 292 CDataStream ssValue(SER_DISK, CLIENT_VERSION); 293 ssValue.reserve(10000); 294 ssValue << value; 295 SafeDbt datValue(ssValue.data(), ssValue.size()); 296 297 // Write 298 int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); 299 return (ret == 0); 300 } 301 302 template <typename K> Erase(const K & key)303 bool Erase(const K& key) 304 { 305 if (!pdb) 306 return false; 307 if (fReadOnly) 308 assert(!"Erase called on database in read-only mode"); 309 310 // Key 311 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 312 ssKey.reserve(1000); 313 ssKey << key; 314 SafeDbt datKey(ssKey.data(), ssKey.size()); 315 316 // Erase 317 int ret = pdb->del(activeTxn, datKey, 0); 318 return (ret == 0 || ret == DB_NOTFOUND); 319 } 320 321 template <typename K> Exists(const K & key)322 bool Exists(const K& key) 323 { 324 if (!pdb) 325 return false; 326 327 // Key 328 CDataStream ssKey(SER_DISK, CLIENT_VERSION); 329 ssKey.reserve(1000); 330 ssKey << key; 331 SafeDbt datKey(ssKey.data(), ssKey.size()); 332 333 // Exists 334 int ret = pdb->exists(activeTxn, datKey, 0); 335 return (ret == 0); 336 } 337 GetCursor()338 Dbc* GetCursor() 339 { 340 if (!pdb) 341 return nullptr; 342 Dbc* pcursor = nullptr; 343 int ret = pdb->cursor(nullptr, &pcursor, 0); 344 if (ret != 0) 345 return nullptr; 346 return pcursor; 347 } 348 ReadAtCursor(Dbc * pcursor,CDataStream & ssKey,CDataStream & ssValue)349 int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue) 350 { 351 // Read at cursor 352 SafeDbt datKey; 353 SafeDbt datValue; 354 int ret = pcursor->get(datKey, datValue, DB_NEXT); 355 if (ret != 0) 356 return ret; 357 else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) 358 return 99999; 359 360 // Convert to streams 361 ssKey.SetType(SER_DISK); 362 ssKey.clear(); 363 ssKey.write((char*)datKey.get_data(), datKey.get_size()); 364 ssValue.SetType(SER_DISK); 365 ssValue.clear(); 366 ssValue.write((char*)datValue.get_data(), datValue.get_size()); 367 return 0; 368 } 369 TxnBegin()370 bool TxnBegin() 371 { 372 if (!pdb || activeTxn) 373 return false; 374 DbTxn* ptxn = env->TxnBegin(); 375 if (!ptxn) 376 return false; 377 activeTxn = ptxn; 378 return true; 379 } 380 TxnCommit()381 bool TxnCommit() 382 { 383 if (!pdb || !activeTxn) 384 return false; 385 int ret = activeTxn->commit(0); 386 activeTxn = nullptr; 387 return (ret == 0); 388 } 389 TxnAbort()390 bool TxnAbort() 391 { 392 if (!pdb || !activeTxn) 393 return false; 394 int ret = activeTxn->abort(); 395 activeTxn = nullptr; 396 return (ret == 0); 397 } 398 ReadVersion(int & nVersion)399 bool ReadVersion(int& nVersion) 400 { 401 nVersion = 0; 402 return Read(std::string("version"), nVersion); 403 } 404 WriteVersion(int nVersion)405 bool WriteVersion(int nVersion) 406 { 407 return Write(std::string("version"), nVersion); 408 } 409 410 bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr); 411 }; 412 413 #endif // BITCOIN_WALLET_DB_H 414