Bitcoin Core  0.20.99
P2P Digital Currency
db.h
Go to the documentation of this file.
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 <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 #if defined(__GNUC__) && !defined(__clang__)
23 #pragma GCC diagnostic push
24 #pragma GCC diagnostic ignored "-Wsuggest-override"
25 #endif
26 #include <db_cxx.h>
27 #if defined(__GNUC__) && !defined(__clang__)
28 #pragma GCC diagnostic pop
29 #endif
30 
31 struct bilingual_str;
32 
33 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
34 static const bool DEFAULT_WALLET_PRIVDB = true;
35 
37  u_int8_t value[DB_FILE_ID_LEN];
38  bool operator==(const WalletDatabaseFileId& rhs) const;
39 };
40 
41 class BerkeleyDatabase;
42 
44 {
45 private:
46  bool fDbEnvInit;
47  bool fMockDb;
48  // Don't change into fs::path, as that can result in
49  // shutdown problems/crashes caused by a static initialized internal pointer.
50  std::string strPath;
51 
52 public:
53  std::unique_ptr<DbEnv> dbenv;
54  std::map<std::string, int> mapFileUseCount;
55  std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
56  std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
57  std::condition_variable_any m_db_in_use;
58 
59  BerkeleyEnvironment(const fs::path& env_directory);
62  void Reset();
63 
64  bool IsMock() const { return fMockDb; }
65  bool IsInitialized() const { return fDbEnvInit; }
66  bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
67  fs::path Directory() const { return strPath; }
68 
69  bool Verify(const std::string& strFile);
70 
71  bool Open(bool retry);
72  void Close();
73  void Flush(bool fShutdown);
74  void CheckpointLSN(const std::string& strFile);
75 
76  void CloseDb(const std::string& strFile);
77  void ReloadDbEnv();
78 
79  DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
80  {
81  DbTxn* ptxn = nullptr;
82  int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
83  if (!ptxn || ret != 0)
84  return nullptr;
85  return ptxn;
86  }
87 };
88 
90 bool IsWalletLoaded(const fs::path& wallet_path);
91 
93 fs::path WalletDataFilePath(const fs::path& wallet_path);
94 
96 std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
97 
102 {
103  friend class BerkeleyBatch;
104 public:
106  BerkeleyDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
107  {
108  }
109 
111  BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
112  nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(std::move(env)), strFile(std::move(filename))
113  {
114  auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
115  assert(inserted.second);
116  }
117 
119  if (env) {
120  size_t erased = env->m_databases.erase(strFile);
121  assert(erased == 1);
122  }
123  }
124 
126  static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
127  {
128  std::string filename;
129  return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
130  }
131 
133  static std::unique_ptr<BerkeleyDatabase> CreateDummy()
134  {
135  return MakeUnique<BerkeleyDatabase>();
136  }
137 
139  static std::unique_ptr<BerkeleyDatabase> CreateMock()
140  {
141  return MakeUnique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
142  }
143 
146  bool Rewrite(const char* pszSkip=nullptr);
147 
150  bool Backup(const std::string& strDest) const;
151 
154  void Flush(bool shutdown);
155 
156  void IncrementUpdateCounter();
157 
158  void ReloadDbEnv();
159 
160  std::atomic<unsigned int> nUpdateCounter;
161  unsigned int nLastSeen;
162  unsigned int nLastFlushed;
164 
174  std::shared_ptr<BerkeleyEnvironment> env;
175 
177  std::unique_ptr<Db> m_db;
178 
179 private:
180  std::string strFile;
181 
186  bool IsDummy() const { return env == nullptr; }
187 };
188 
191 {
193  class SafeDbt final
194  {
195  Dbt m_dbt;
196 
197  public:
198  // construct Dbt with internally-managed data
199  SafeDbt();
200  // construct Dbt with provided data
201  SafeDbt(void* data, size_t size);
202  ~SafeDbt();
203 
204  // delegate to Dbt
205  const void* get_data() const;
206  u_int32_t get_size() const;
207 
208  // conversion operator to access the underlying Dbt
209  operator Dbt*();
210  };
211 
212 protected:
213  Db* pdb;
214  std::string strFile;
215  DbTxn* activeTxn;
216  bool fReadOnly;
219 
220 public:
221  explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
222  ~BerkeleyBatch() { Close(); }
223 
224  BerkeleyBatch(const BerkeleyBatch&) = delete;
225  BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
226 
227  void Flush();
228  void Close();
229 
230  /* flush the wallet passively (TRY_LOCK)
231  ideal to be called periodically */
232  static bool PeriodicFlush(BerkeleyDatabase& database);
233  /* verifies the database environment */
234  static bool VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr);
235  /* verifies the database file */
236  static bool VerifyDatabaseFile(const fs::path& file_path, bilingual_str& errorStr);
237 
238  template <typename K, typename T>
239  bool Read(const K& key, T& value)
240  {
241  if (!pdb)
242  return false;
243 
244  // Key
246  ssKey.reserve(1000);
247  ssKey << key;
248  SafeDbt datKey(ssKey.data(), ssKey.size());
249 
250  // Read
251  SafeDbt datValue;
252  int ret = pdb->get(activeTxn, datKey, datValue, 0);
253  bool success = false;
254  if (datValue.get_data() != nullptr) {
255  // Unserialize value
256  try {
257  CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
258  ssValue >> value;
259  success = true;
260  } catch (const std::exception&) {
261  // In this case success remains 'false'
262  }
263  }
264  return ret == 0 && success;
265  }
266 
267  template <typename K, typename T>
268  bool Write(const K& key, const T& value, bool fOverwrite = true)
269  {
270  if (!pdb)
271  return true;
272  if (fReadOnly)
273  assert(!"Write called on database in read-only mode");
274 
275  // Key
277  ssKey.reserve(1000);
278  ssKey << key;
279  SafeDbt datKey(ssKey.data(), ssKey.size());
280 
281  // Value
283  ssValue.reserve(10000);
284  ssValue << value;
285  SafeDbt datValue(ssValue.data(), ssValue.size());
286 
287  // Write
288  int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
289  return (ret == 0);
290  }
291 
292  template <typename K>
293  bool Erase(const K& key)
294  {
295  if (!pdb)
296  return false;
297  if (fReadOnly)
298  assert(!"Erase called on database in read-only mode");
299 
300  // Key
302  ssKey.reserve(1000);
303  ssKey << key;
304  SafeDbt datKey(ssKey.data(), ssKey.size());
305 
306  // Erase
307  int ret = pdb->del(activeTxn, datKey, 0);
308  return (ret == 0 || ret == DB_NOTFOUND);
309  }
310 
311  template <typename K>
312  bool Exists(const K& key)
313  {
314  if (!pdb)
315  return false;
316 
317  // Key
319  ssKey.reserve(1000);
320  ssKey << key;
321  SafeDbt datKey(ssKey.data(), ssKey.size());
322 
323  // Exists
324  int ret = pdb->exists(activeTxn, datKey, 0);
325  return (ret == 0);
326  }
327 
328  Dbc* GetCursor()
329  {
330  if (!pdb)
331  return nullptr;
332  Dbc* pcursor = nullptr;
333  int ret = pdb->cursor(nullptr, &pcursor, 0);
334  if (ret != 0)
335  return nullptr;
336  return pcursor;
337  }
338 
339  int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
340  {
341  // Read at cursor
342  SafeDbt datKey;
343  SafeDbt datValue;
344  int ret = pcursor->get(datKey, datValue, DB_NEXT);
345  if (ret != 0)
346  return ret;
347  else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
348  return 99999;
349 
350  // Convert to streams
351  ssKey.SetType(SER_DISK);
352  ssKey.clear();
353  ssKey.write((char*)datKey.get_data(), datKey.get_size());
354  ssValue.SetType(SER_DISK);
355  ssValue.clear();
356  ssValue.write((char*)datValue.get_data(), datValue.get_size());
357  return 0;
358  }
359 
360  bool TxnBegin()
361  {
362  if (!pdb || activeTxn)
363  return false;
364  DbTxn* ptxn = env->TxnBegin();
365  if (!ptxn)
366  return false;
367  activeTxn = ptxn;
368  return true;
369  }
370 
371  bool TxnCommit()
372  {
373  if (!pdb || !activeTxn)
374  return false;
375  int ret = activeTxn->commit(0);
376  activeTxn = nullptr;
377  return (ret == 0);
378  }
379 
380  bool TxnAbort()
381  {
382  if (!pdb || !activeTxn)
383  return false;
384  int ret = activeTxn->abort();
385  activeTxn = nullptr;
386  return (ret == 0);
387  }
388 
389  bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
390 };
391 
392 std::string BerkeleyDatabaseVersion();
393 
394 #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:133
bool fReadOnly
Definition: db.h:216
static std::unique_ptr< BerkeleyDatabase > CreateMock()
Return object for accessing temporary in-memory database.
Definition: db.h:139
bool IsInitialized() const
Definition: db.h:65
bool fDbEnvInit
Definition: db.h:46
bool TxnCommit()
Definition: db.h:371
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
Definition: db.h:56
Bilingual messages:
Definition: translation.h:16
unsigned int nLastSeen
Definition: db.h:161
bool fFlushOnClose
Definition: db.h:217
bool TxnBegin()
Definition: db.h:360
bool Exists(const K &key)
Definition: db.h:312
bool Write(const K &key, const T &value, bool fOverwrite=true)
Definition: db.h:268
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:101
bool IsDatabaseLoaded(const std::string &db_filename) const
Definition: db.h:66
Dbc * GetCursor()
Definition: db.h:328
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:67
u_int32_t get_size() const
Definition: db.cpp:310
int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue)
Definition: db.h:339
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
Definition: db.h:79
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Definition: db.h:174
std::map< std::string, int > mapFileUseCount
Definition: db.h:54
std::map< std::string, std::reference_wrapper< BerkeleyDatabase > > m_databases
Definition: db.h:55
std::unique_ptr< DbEnv > dbenv
Definition: db.h:53
bool Erase(const K &key)
Definition: db.h:293
bool TxnAbort()
Definition: db.h:380
bool Read(const K &key, T &value)
Definition: db.h:239
BerkeleyDatabase(std::shared_ptr< BerkeleyEnvironment > env, std::string filename)
Create DB handle to real database.
Definition: db.h:111
bool IsDummy() const
Return whether this database handle is a dummy for testing.
Definition: db.h:186
size_type size() const
Definition: streams.h:292
std::string strFile
Definition: db.h:214
~BerkeleyBatch()
Definition: db.h:222
BerkeleyDatabase()
Create dummy DB handle.
Definition: db.h:106
DbTxn * activeTxn
Definition: db.h:215
RAII class that provides access to a Berkeley database.
Definition: db.h:190
void Flush()
Definition: db.cpp:442
std::string strFile
Definition: db.h:180
static const unsigned int DEFAULT_WALLET_DBLOGSIZE
Definition: db.h:33
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:126
std::string BerkeleyDatabaseVersion()
Definition: db.cpp:767
int flags
Definition: bitcoin-tx.cpp:509
static bool Rewrite(BerkeleyDatabase &database, const char *pszSkip=nullptr)
Definition: db.cpp:522
int64_t nLastWalletUpdate
Definition: db.h:163
void reserve(size_type n)
Definition: streams.h:295
std::string strPath
Definition: db.h:50
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:162
BerkeleyEnvironment * env
Definition: db.h:218
u_int8_t value[DB_FILE_ID_LEN]
Definition: db.h:37
Db * pdb
Definition: db.h:213
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:160
~BerkeleyDatabase()
Definition: db.h:118
static const bool DEFAULT_WALLET_PRIVDB
Definition: db.h:34
void clear()
Definition: streams.h:298
bool IsMock() const
Definition: db.h:64
bool fMockDb
Definition: db.h:47
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:177
const void * get_data() const
Definition: db.cpp:305
RAII class that automatically cleanses its data on destruction.
Definition: db.h:193
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:57