Use MakeUnique<Db>(...)
[bitcoinplatinum.git] / src / wallet / db.h
blobe6d5a9f293b7a68ae8c036de5786131352e0a377
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 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.
6 #ifndef BITCOIN_WALLET_DB_H
7 #define BITCOIN_WALLET_DB_H
9 #include "clientversion.h"
10 #include "fs.h"
11 #include "serialize.h"
12 #include "streams.h"
13 #include "sync.h"
14 #include "version.h"
16 #include <atomic>
17 #include <map>
18 #include <string>
19 #include <vector>
21 #include <db_cxx.h>
23 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
24 static const bool DEFAULT_WALLET_PRIVDB = true;
26 class CDBEnv
28 private:
29 bool fDbEnvInit;
30 bool fMockDb;
31 // Don't change into fs::path, as that can result in
32 // shutdown problems/crashes caused by a static initialized internal pointer.
33 std::string strPath;
35 void EnvShutdown();
37 public:
38 mutable CCriticalSection cs_db;
39 std::unique_ptr<DbEnv> dbenv;
40 std::map<std::string, int> mapFileUseCount;
41 std::map<std::string, Db*> mapDb;
43 CDBEnv();
44 ~CDBEnv();
45 void Reset();
47 void MakeMock();
48 bool IsMock() const { return fMockDb; }
50 /**
51 * Verify that database file strFile is OK. If it is not,
52 * call the callback to try to recover.
53 * This must be called BEFORE strFile is opened.
54 * Returns true if strFile is OK.
56 enum VerifyResult { VERIFY_OK,
57 RECOVER_OK,
58 RECOVER_FAIL };
59 typedef bool (*recoverFunc_type)(const std::string& strFile, std::string& out_backup_filename);
60 VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
61 /**
62 * Salvage data from a file that Verify says is bad.
63 * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
64 * Appends binary key/value pairs to vResult, returns true if successful.
65 * NOTE: reads the entire database into memory, so cannot be used
66 * for huge databases.
68 typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
69 bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
71 bool Open(const fs::path& path);
72 void Close();
73 void Flush(bool fShutdown);
74 void CheckpointLSN(const std::string& strFile);
76 void CloseDb(const std::string& strFile);
78 DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
80 DbTxn* ptxn = nullptr;
81 int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
82 if (!ptxn || ret != 0)
83 return nullptr;
84 return ptxn;
88 extern CDBEnv bitdb;
90 /** An instance of this class represents one database.
91 * For BerkeleyDB this is just a (env, strFile) tuple.
92 **/
93 class CWalletDBWrapper
95 friend class CDB;
96 public:
97 /** Create dummy DB handle */
98 CWalletDBWrapper() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
102 /** Create DB handle to real database */
103 CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) :
104 nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in)
108 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
110 bool Rewrite(const char* pszSkip=nullptr);
112 /** Back up the entire database to a file.
114 bool Backup(const std::string& strDest);
116 /** Get a name for this database, for debugging etc.
118 std::string GetName() const { return strFile; }
120 /** Make sure all changes are flushed to disk.
122 void Flush(bool shutdown);
124 void IncrementUpdateCounter();
126 std::atomic<unsigned int> nUpdateCounter;
127 unsigned int nLastSeen;
128 unsigned int nLastFlushed;
129 int64_t nLastWalletUpdate;
131 private:
132 /** BerkeleyDB specific */
133 CDBEnv *env;
134 std::string strFile;
136 /** Return whether this database handle is a dummy for testing.
137 * Only to be used at a low level, application should ideally not care
138 * about this.
140 bool IsDummy() { return env == nullptr; }
144 /** RAII class that provides access to a Berkeley database */
145 class CDB
147 protected:
148 Db* pdb;
149 std::string strFile;
150 DbTxn* activeTxn;
151 bool fReadOnly;
152 bool fFlushOnClose;
153 CDBEnv *env;
155 public:
156 explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
157 ~CDB() { Close(); }
159 CDB(const CDB&) = delete;
160 CDB& operator=(const CDB&) = delete;
162 void Flush();
163 void Close();
164 static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
166 /* flush the wallet passively (TRY_LOCK)
167 ideal to be called periodically */
168 static bool PeriodicFlush(CWalletDBWrapper& dbw);
169 /* verifies the database environment */
170 static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
171 /* verifies the database file */
172 static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
174 public:
175 template <typename K, typename T>
176 bool Read(const K& key, T& value)
178 if (!pdb)
179 return false;
181 // Key
182 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
183 ssKey.reserve(1000);
184 ssKey << key;
185 Dbt datKey(ssKey.data(), ssKey.size());
187 // Read
188 Dbt datValue;
189 datValue.set_flags(DB_DBT_MALLOC);
190 int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
191 memory_cleanse(datKey.get_data(), datKey.get_size());
192 bool success = false;
193 if (datValue.get_data() != nullptr) {
194 // Unserialize value
195 try {
196 CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
197 ssValue >> value;
198 success = true;
199 } catch (const std::exception&) {
200 // In this case success remains 'false'
203 // Clear and free memory
204 memory_cleanse(datValue.get_data(), datValue.get_size());
205 free(datValue.get_data());
207 return ret == 0 && success;
210 template <typename K, typename T>
211 bool Write(const K& key, const T& value, bool fOverwrite = true)
213 if (!pdb)
214 return true;
215 if (fReadOnly)
216 assert(!"Write called on database in read-only mode");
218 // Key
219 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
220 ssKey.reserve(1000);
221 ssKey << key;
222 Dbt datKey(ssKey.data(), ssKey.size());
224 // Value
225 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
226 ssValue.reserve(10000);
227 ssValue << value;
228 Dbt datValue(ssValue.data(), ssValue.size());
230 // Write
231 int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
233 // Clear memory in case it was a private key
234 memory_cleanse(datKey.get_data(), datKey.get_size());
235 memory_cleanse(datValue.get_data(), datValue.get_size());
236 return (ret == 0);
239 template <typename K>
240 bool Erase(const K& key)
242 if (!pdb)
243 return false;
244 if (fReadOnly)
245 assert(!"Erase called on database in read-only mode");
247 // Key
248 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
249 ssKey.reserve(1000);
250 ssKey << key;
251 Dbt datKey(ssKey.data(), ssKey.size());
253 // Erase
254 int ret = pdb->del(activeTxn, &datKey, 0);
256 // Clear memory
257 memory_cleanse(datKey.get_data(), datKey.get_size());
258 return (ret == 0 || ret == DB_NOTFOUND);
261 template <typename K>
262 bool Exists(const K& key)
264 if (!pdb)
265 return false;
267 // Key
268 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
269 ssKey.reserve(1000);
270 ssKey << key;
271 Dbt datKey(ssKey.data(), ssKey.size());
273 // Exists
274 int ret = pdb->exists(activeTxn, &datKey, 0);
276 // Clear memory
277 memory_cleanse(datKey.get_data(), datKey.get_size());
278 return (ret == 0);
281 Dbc* GetCursor()
283 if (!pdb)
284 return nullptr;
285 Dbc* pcursor = nullptr;
286 int ret = pdb->cursor(nullptr, &pcursor, 0);
287 if (ret != 0)
288 return nullptr;
289 return pcursor;
292 int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false)
294 // Read at cursor
295 Dbt datKey;
296 unsigned int fFlags = DB_NEXT;
297 if (setRange) {
298 datKey.set_data(ssKey.data());
299 datKey.set_size(ssKey.size());
300 fFlags = DB_SET_RANGE;
302 Dbt datValue;
303 datKey.set_flags(DB_DBT_MALLOC);
304 datValue.set_flags(DB_DBT_MALLOC);
305 int ret = pcursor->get(&datKey, &datValue, fFlags);
306 if (ret != 0)
307 return ret;
308 else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
309 return 99999;
311 // Convert to streams
312 ssKey.SetType(SER_DISK);
313 ssKey.clear();
314 ssKey.write((char*)datKey.get_data(), datKey.get_size());
315 ssValue.SetType(SER_DISK);
316 ssValue.clear();
317 ssValue.write((char*)datValue.get_data(), datValue.get_size());
319 // Clear and free memory
320 memory_cleanse(datKey.get_data(), datKey.get_size());
321 memory_cleanse(datValue.get_data(), datValue.get_size());
322 free(datKey.get_data());
323 free(datValue.get_data());
324 return 0;
327 public:
328 bool TxnBegin()
330 if (!pdb || activeTxn)
331 return false;
332 DbTxn* ptxn = bitdb.TxnBegin();
333 if (!ptxn)
334 return false;
335 activeTxn = ptxn;
336 return true;
339 bool TxnCommit()
341 if (!pdb || !activeTxn)
342 return false;
343 int ret = activeTxn->commit(0);
344 activeTxn = nullptr;
345 return (ret == 0);
348 bool TxnAbort()
350 if (!pdb || !activeTxn)
351 return false;
352 int ret = activeTxn->abort();
353 activeTxn = nullptr;
354 return (ret == 0);
357 bool ReadVersion(int& nVersion)
359 nVersion = 0;
360 return Read(std::string("version"), nVersion);
363 bool WriteVersion(int nVersion)
365 return Write(std::string("version"), nVersion);
368 bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = nullptr);
371 #endif // BITCOIN_WALLET_DB_H