Bitcoin Core  0.19.99
P2P Digital Currency
db.h
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2019 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 <util/system.h>
14 
15 #include <atomic>
16 #include <map>
17 #include <memory>
18 #include <string>
19 #include <unordered_map>
20 #include <vector>
21 
22 #include <db_cxx.h>
23 
24 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
25 static const bool DEFAULT_WALLET_PRIVDB = true;
26 
28  u_int8_t value[DB_FILE_ID_LEN];
29  bool operator==(const WalletDatabaseFileId& rhs) const;
30 };
31 
32 class BerkeleyDatabase;
33 
35 {
36 private:
37  bool fDbEnvInit;
38  bool fMockDb;
39  // Don't change into fs::path, as that can result in
40  // shutdown problems/crashes caused by a static initialized internal pointer.
41  std::string strPath;
42 
43 public:
44  std::unique_ptr<DbEnv> dbenv;
45  std::map<std::string, int> mapFileUseCount;
46  std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
47  std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
48  std::condition_variable_any m_db_in_use;
49 
50  BerkeleyEnvironment(const fs::path& env_directory);
53  void Reset();
54 
55  bool IsMock() const { return fMockDb; }
56  bool IsInitialized() const { return fDbEnvInit; }
57  bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
58  fs::path Directory() const { return strPath; }
59 
66  enum class VerifyResult { VERIFY_OK,
67  RECOVER_OK,
68  RECOVER_FAIL };
69  typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename);
70  VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
78  typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
79  bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
80 
81  bool Open(bool retry);
82  void Close();
83  void Flush(bool fShutdown);
84  void CheckpointLSN(const std::string& strFile);
85 
86  void CloseDb(const std::string& strFile);
87  void ReloadDbEnv();
88 
89  DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
90  {
91  DbTxn* ptxn = nullptr;
92  int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
93  if (!ptxn || ret != 0)
94  return nullptr;
95  return ptxn;
96  }
97 };
98 
100 bool IsWalletLoaded(const fs::path& wallet_path);
101 
103 fs::path WalletDataFilePath(const fs::path& wallet_path);
104 
106 std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
107 
112 {
113  friend class BerkeleyBatch;
114 public:
116  BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
117  {
118  }
119 
121  BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
122  nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
123  {
124  auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
125  assert(inserted.second);
126  }
127 
129  if (env) {
130  size_t erased = env->m_databases.erase(strFile);
131  assert(erased == 1);
132  }
133  }
134 
136  static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
137  {
138  std::string filename;
139  return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
140  }
141 
143  static std::unique_ptr<BerkeleyDatabase> CreateDummy()
144  {
145  return MakeUnique<BerkeleyDatabase>();
146  }
147 
149  static std::unique_ptr<BerkeleyDatabase> CreateMock()
150  {
151  return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
152  }
153 
156  bool Rewrite(const char* pszSkip=nullptr);
157 
160  bool Backup(const std::string& strDest);
161 
164  void Flush(bool shutdown);
165 
166  void IncrementUpdateCounter();
167 
168  void ReloadDbEnv();
169 
170  std::atomic<unsigned int> nUpdateCounter;
171  unsigned int nLastSeen;
172  unsigned int nLastFlushed;
174 
184  std::shared_ptr<BerkeleyEnvironment> env;
185 
187  std::unique_ptr<Db> m_db;
188 
189 private:
190  std::string strFile;
191 
196  bool IsDummy() { return env == nullptr; }
197 };
198 
201 {
203  class SafeDbt final
204  {
205  Dbt m_dbt;
206 
207  public:
208  // construct Dbt with internally-managed data
209  SafeDbt();
210  // construct Dbt with provided data
211  SafeDbt(void* data, size_t size);
212  ~SafeDbt();
213 
214  // delegate to Dbt
215  const void* get_data() const;
216  u_int32_t get_size() const;
217 
218  // conversion operator to access the underlying Dbt
219  operator Dbt*();
220  };
221 
222 protected:
223  Db* pdb;
224  std::string strFile;
225  DbTxn* activeTxn;
226  bool fReadOnly;
229 
230 public:
231  explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
232  ~BerkeleyBatch() { Close(); }
233 
234  BerkeleyBatch(const BerkeleyBatch&) = delete;
235  BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
236 
237  void Flush();
238  void Close();
239  static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
240 
241  /* flush the wallet passively (TRY_LOCK)
242  ideal to be called periodically */
243  static bool PeriodicFlush(BerkeleyDatabase& database);
244  /* verifies the database environment */
245  static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr);
246  /* verifies the database file */
247  static bool VerifyDatabaseFile(const fs::path& file_path, std::vector<std::string>& warnings, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
248 
249  template <typename K, typename T>
250  bool Read(const K& key, T& value)
251  {
252  if (!pdb)
253  return false;
254 
255  // Key
257  ssKey.reserve(1000);
258  ssKey << key;
259  SafeDbt datKey(ssKey.data(), ssKey.size());
260 
261  // Read
262  SafeDbt datValue;
263  int ret = pdb->get(activeTxn, datKey, datValue, 0);
264  bool success = false;
265  if (datValue.get_data() != nullptr) {
266  // Unserialize value
267  try {
268  CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
269  ssValue >> value;
270  success = true;
271  } catch (const std::exception&) {
272  // In this case success remains 'false'
273  }
274  }
275  return ret == 0 && success;
276  }
277 
278  template <typename K, typename T>
279  bool Write(const K& key, const T& value, bool fOverwrite = true)
280  {
281  if (!pdb)
282  return true;
283  if (fReadOnly)
284  assert(!"Write called on database in read-only mode");
285 
286  // Key
288  ssKey.reserve(1000);
289  ssKey << key;
290  SafeDbt datKey(ssKey.data(), ssKey.size());
291 
292  // Value
294  ssValue.reserve(10000);
295  ssValue << value;
296  SafeDbt datValue(ssValue.data(), ssValue.size());
297 
298  // Write
299  int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
300  return (ret == 0);
301  }
302 
303  template <typename K>
304  bool Erase(const K& key)
305  {
306  if (!pdb)
307  return false;
308  if (fReadOnly)
309  assert(!"Erase called on database in read-only mode");
310 
311  // Key
313  ssKey.reserve(1000);
314  ssKey << key;
315  SafeDbt datKey(ssKey.data(), ssKey.size());
316 
317  // Erase
318  int ret = pdb->del(activeTxn, datKey, 0);
319  return (ret == 0 || ret == DB_NOTFOUND);
320  }
321 
322  template <typename K>
323  bool Exists(const K& key)
324  {
325  if (!pdb)
326  return false;
327 
328  // Key
330  ssKey.reserve(1000);
331  ssKey << key;
332  SafeDbt datKey(ssKey.data(), ssKey.size());
333 
334  // Exists
335  int ret = pdb->exists(activeTxn, datKey, 0);
336  return (ret == 0);
337  }
338 
339  Dbc* GetCursor()
340  {
341  if (!pdb)
342  return nullptr;
343  Dbc* pcursor = nullptr;
344  int ret = pdb->cursor(nullptr, &pcursor, 0);
345  if (ret != 0)
346  return nullptr;
347  return pcursor;
348  }
349 
350  int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
351  {
352  // Read at cursor
353  SafeDbt datKey;
354  SafeDbt datValue;
355  int ret = pcursor->get(datKey, datValue, DB_NEXT);
356  if (ret != 0)
357  return ret;
358  else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
359  return 99999;
360 
361  // Convert to streams
362  ssKey.SetType(SER_DISK);
363  ssKey.clear();
364  ssKey.write((char*)datKey.get_data(), datKey.get_size());
365  ssValue.SetType(SER_DISK);
366  ssValue.clear();
367  ssValue.write((char*)datValue.get_data(), datValue.get_size());
368  return 0;
369  }
370 
371  bool TxnBegin()
372  {
373  if (!pdb || activeTxn)
374  return false;
375  DbTxn* ptxn = env->TxnBegin();
376  if (!ptxn)
377  return false;
378  activeTxn = ptxn;
379  return true;
380  }
381 
382  bool TxnCommit()
383  {
384  if (!pdb || !activeTxn)
385  return false;
386  int ret = activeTxn->commit(0);
387  activeTxn = nullptr;
388  return (ret == 0);
389  }
390 
391  bool TxnAbort()
392  {
393  if (!pdb || !activeTxn)
394  return false;
395  int ret = activeTxn->abort();
396  activeTxn = nullptr;
397  return (ret == 0);
398  }
399 
400  bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
401 };
402 
403 #endif // BITCOIN_WALLET_DB_H
static std::unique_ptr< BerkeleyDatabase > CreateDummy()
Return object for accessing dummy database with no read/write capabilities.
Definition: db.h:143
bool fReadOnly
Definition: db.h:226
static std::unique_ptr< BerkeleyDatabase > CreateMock()
Return object for accessing temporary in-memory database.
Definition: db.h:149
bool IsInitialized() const
Definition: db.h:56
bool fDbEnvInit
Definition: db.h:37
bool TxnCommit()
Definition: db.h:382
VerifyResult
Verify that database file strFile is OK.
Definition: db.h:66
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
Definition: db.h:47
unsigned int nLastSeen
Definition: db.h:171
bool fFlushOnClose
Definition: db.h:227
bool TxnBegin()
Definition: db.h:371
bool Exists(const K &key)
Definition: db.h:323
bool Write(const K &key, const T &value, bool fOverwrite=true)
Definition: db.h:279
std::shared_ptr< BerkeleyEnvironment > GetWalletEnv(const fs::path &wallet_path, std::string &database_filename)
Get BerkeleyEnvironment and database filename given a wallet path.
Definition: db.cpp:99
value_type * data()
Definition: streams.h:301
An instance of this class represents one database.
Definition: db.h:111
bool IsDatabaseLoaded(const std::string &db_filename) const
Definition: db.h:57
Dbc * GetCursor()
Definition: db.h:339
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:201
void write(const char *pch, size_t nSize)
Definition: streams.h:434
fs::path Directory() const
Definition: db.h:58
u_int32_t get_size() const
Definition: db.cpp:317
int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue)
Definition: db.h:350
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
Definition: db.h:89
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Definition: db.h:184
std::map< std::string, int > mapFileUseCount
Definition: db.h:45
std::map< std::string, std::reference_wrapper< BerkeleyDatabase > > m_databases
Definition: db.h:46
std::unique_ptr< DbEnv > dbenv
Definition: db.h:44
bool Erase(const K &key)
Definition: db.h:304
std::pair< std::vector< unsigned char >, std::vector< unsigned char > > KeyValPair
Salvage data from a file that Verify says is bad.
Definition: db.h:78
bool TxnAbort()
Definition: db.h:391
bool Read(const K &key, T &value)
Definition: db.h:250
BerkeleyDatabase(std::shared_ptr< BerkeleyEnvironment > env, std::string filename)
Create DB handle to real database.
Definition: db.h:121
size_type size() const
Definition: streams.h:292
std::string strFile
Definition: db.h:224
~BerkeleyBatch()
Definition: db.h:232
BerkeleyDatabase()
Create dummy DB handle.
Definition: db.h:116
DbTxn * activeTxn
Definition: db.h:225
RAII class that provides access to a Berkeley database.
Definition: db.h:200
void Flush()
Definition: db.cpp:595
std::string strFile
Definition: db.h:190
static const unsigned int DEFAULT_WALLET_DBLOGSIZE
Definition: db.h:24
bool operator==(const WalletDatabaseFileId &rhs) const
Definition: db.cpp:51
static std::unique_ptr< BerkeleyDatabase > Create(const fs::path &path)
Return object for accessing database at specified path.
Definition: db.h:136
int flags
Definition: bitcoin-tx.cpp:508
static bool Rewrite(BerkeleyDatabase &database, const char *pszSkip=nullptr)
Definition: db.cpp:675
int64_t nLastWalletUpdate
Definition: db.h:173
void reserve(size_type n)
Definition: streams.h:295
std::string strPath
Definition: db.h:41
fs::path WalletDataFilePath(const fs::path &wallet_path)
Given a wallet directory path or legacy file path, return path to main data file in the wallet databa...
Definition: db.cpp:84
unsigned int nLastFlushed
Definition: db.h:172
BerkeleyEnvironment * env
Definition: db.h:228
u_int8_t value[DB_FILE_ID_LEN]
Definition: db.h:28
Db * pdb
Definition: db.h:223
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:170
bool(* recoverFunc_type)(const fs::path &file_path, std::string &out_backup_filename)
Definition: db.h:69
~BerkeleyDatabase()
Definition: db.h:128
static const bool DEFAULT_WALLET_PRIVDB
Definition: db.h:25
void clear()
Definition: streams.h:298
bool IsMock() const
Definition: db.h:55
bool fMockDb
Definition: db.h:38
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
std::unique_ptr< Db > m_db
Database pointer.
Definition: db.h:187
const void * get_data() const
Definition: db.cpp:312
RAII class that automatically cleanses its data on destruction.
Definition: db.h:203
bool IsWalletLoaded(const fs::path &wallet_path)
Return whether a wallet database is currently loaded.
Definition: db.cpp:72
void SetType(int n)
Definition: streams.h:392
std::condition_variable_any m_db_in_use
Definition: db.h:48
bool IsDummy()
Return whether this database handle is a dummy for testing.
Definition: db.h:196