CWalletDB: Store the update counter per wallet
[bitcoinplatinum.git] / src / wallet / db.h
blob1a583c3ce991e61ff0e1d45714609c0477e92c1c
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 <map>
17 #include <string>
18 #include <vector>
20 #include <db_cxx.h>
22 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
23 static const bool DEFAULT_WALLET_PRIVDB = true;
25 class CDBEnv
27 private:
28 bool fDbEnvInit;
29 bool fMockDb;
30 // Don't change into fs::path, as that can result in
31 // shutdown problems/crashes caused by a static initialized internal pointer.
32 std::string strPath;
34 void EnvShutdown();
36 public:
37 mutable CCriticalSection cs_db;
38 DbEnv *dbenv;
39 std::map<std::string, int> mapFileUseCount;
40 std::map<std::string, Db*> mapDb;
42 CDBEnv();
43 ~CDBEnv();
44 void Reset();
46 void MakeMock();
47 bool IsMock() { return fMockDb; }
49 /**
50 * Verify that database file strFile is OK. If it is not,
51 * call the callback to try to recover.
52 * This must be called BEFORE strFile is opened.
53 * Returns true if strFile is OK.
55 enum VerifyResult { VERIFY_OK,
56 RECOVER_OK,
57 RECOVER_FAIL };
58 VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile));
59 /**
60 * Salvage data from a file that Verify says is bad.
61 * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
62 * Appends binary key/value pairs to vResult, returns true if successful.
63 * NOTE: reads the entire database into memory, so cannot be used
64 * for huge databases.
66 typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
67 bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
69 bool Open(const fs::path& path);
70 void Close();
71 void Flush(bool fShutdown);
72 void CheckpointLSN(const std::string& strFile);
74 void CloseDb(const std::string& strFile);
76 DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
78 DbTxn* ptxn = NULL;
79 int ret = dbenv->txn_begin(NULL, &ptxn, flags);
80 if (!ptxn || ret != 0)
81 return NULL;
82 return ptxn;
86 extern CDBEnv bitdb;
88 /** An instance of this class represents one database.
89 * For BerkeleyDB this is just a (env, strFile) tuple.
90 **/
91 class CWalletDBWrapper
93 friend class CDB;
94 public:
95 /** Create dummy DB handle */
96 CWalletDBWrapper(): env(nullptr)
100 /** Create DB handle to real database */
101 CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in):
102 env(env_in), strFile(strFile_in)
106 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
108 bool Rewrite(const char* pszSkip=nullptr);
110 /** Back up the entire database to a file.
112 bool Backup(const std::string& strDest);
114 /** Get a name for this database, for debugging etc.
116 std::string GetName() const { return strFile; }
118 /** Make sure all changes are flushed to disk.
120 void Flush(bool shutdown);
122 void IncrementUpdateCounter();
123 unsigned int GetUpdateCounter();
125 private:
126 /** BerkeleyDB specific */
127 CDBEnv *env;
128 std::string strFile;
129 std::atomic<unsigned int> nUpdateCounter;
131 /** Return whether this database handle is a dummy for testing.
132 * Only to be used at a low level, application should ideally not care
133 * about this.
135 bool IsDummy() { return env == nullptr; }
139 /** RAII class that provides access to a Berkeley database */
140 class CDB
142 protected:
143 Db* pdb;
144 std::string strFile;
145 DbTxn* activeTxn;
146 bool fReadOnly;
147 bool fFlushOnClose;
148 CDBEnv *env;
150 public:
151 explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
152 ~CDB() { Close(); }
154 void Flush();
155 void Close();
156 static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
158 /* flush the wallet passively (TRY_LOCK)
159 ideal to be called periodically */
160 static bool PeriodicFlush(CWalletDBWrapper& dbw);
161 /* verifies the database environment */
162 static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
163 /* verifies the database file */
164 static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
166 private:
167 CDB(const CDB&);
168 void operator=(const CDB&);
170 public:
171 template <typename K, typename T>
172 bool Read(const K& key, T& value)
174 if (!pdb)
175 return false;
177 // Key
178 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
179 ssKey.reserve(1000);
180 ssKey << key;
181 Dbt datKey(ssKey.data(), ssKey.size());
183 // Read
184 Dbt datValue;
185 datValue.set_flags(DB_DBT_MALLOC);
186 int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
187 memory_cleanse(datKey.get_data(), datKey.get_size());
188 bool success = false;
189 if (datValue.get_data() != NULL) {
190 // Unserialize value
191 try {
192 CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
193 ssValue >> value;
194 success = true;
195 } catch (const std::exception&) {
196 // In this case success remains 'false'
199 // Clear and free memory
200 memory_cleanse(datValue.get_data(), datValue.get_size());
201 free(datValue.get_data());
203 return ret == 0 && success;
206 template <typename K, typename T>
207 bool Write(const K& key, const T& value, bool fOverwrite = true)
209 if (!pdb)
210 return true;
211 if (fReadOnly)
212 assert(!"Write called on database in read-only mode");
214 // Key
215 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
216 ssKey.reserve(1000);
217 ssKey << key;
218 Dbt datKey(ssKey.data(), ssKey.size());
220 // Value
221 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
222 ssValue.reserve(10000);
223 ssValue << value;
224 Dbt datValue(ssValue.data(), ssValue.size());
226 // Write
227 int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
229 // Clear memory in case it was a private key
230 memory_cleanse(datKey.get_data(), datKey.get_size());
231 memory_cleanse(datValue.get_data(), datValue.get_size());
232 return (ret == 0);
235 template <typename K>
236 bool Erase(const K& key)
238 if (!pdb)
239 return false;
240 if (fReadOnly)
241 assert(!"Erase called on database in read-only mode");
243 // Key
244 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
245 ssKey.reserve(1000);
246 ssKey << key;
247 Dbt datKey(ssKey.data(), ssKey.size());
249 // Erase
250 int ret = pdb->del(activeTxn, &datKey, 0);
252 // Clear memory
253 memory_cleanse(datKey.get_data(), datKey.get_size());
254 return (ret == 0 || ret == DB_NOTFOUND);
257 template <typename K>
258 bool Exists(const K& key)
260 if (!pdb)
261 return false;
263 // Key
264 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
265 ssKey.reserve(1000);
266 ssKey << key;
267 Dbt datKey(ssKey.data(), ssKey.size());
269 // Exists
270 int ret = pdb->exists(activeTxn, &datKey, 0);
272 // Clear memory
273 memory_cleanse(datKey.get_data(), datKey.get_size());
274 return (ret == 0);
277 Dbc* GetCursor()
279 if (!pdb)
280 return NULL;
281 Dbc* pcursor = NULL;
282 int ret = pdb->cursor(NULL, &pcursor, 0);
283 if (ret != 0)
284 return NULL;
285 return pcursor;
288 int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false)
290 // Read at cursor
291 Dbt datKey;
292 unsigned int fFlags = DB_NEXT;
293 if (setRange) {
294 datKey.set_data(ssKey.data());
295 datKey.set_size(ssKey.size());
296 fFlags = DB_SET_RANGE;
298 Dbt datValue;
299 datKey.set_flags(DB_DBT_MALLOC);
300 datValue.set_flags(DB_DBT_MALLOC);
301 int ret = pcursor->get(&datKey, &datValue, fFlags);
302 if (ret != 0)
303 return ret;
304 else if (datKey.get_data() == NULL || datValue.get_data() == NULL)
305 return 99999;
307 // Convert to streams
308 ssKey.SetType(SER_DISK);
309 ssKey.clear();
310 ssKey.write((char*)datKey.get_data(), datKey.get_size());
311 ssValue.SetType(SER_DISK);
312 ssValue.clear();
313 ssValue.write((char*)datValue.get_data(), datValue.get_size());
315 // Clear and free memory
316 memory_cleanse(datKey.get_data(), datKey.get_size());
317 memory_cleanse(datValue.get_data(), datValue.get_size());
318 free(datKey.get_data());
319 free(datValue.get_data());
320 return 0;
323 public:
324 bool TxnBegin()
326 if (!pdb || activeTxn)
327 return false;
328 DbTxn* ptxn = bitdb.TxnBegin();
329 if (!ptxn)
330 return false;
331 activeTxn = ptxn;
332 return true;
335 bool TxnCommit()
337 if (!pdb || !activeTxn)
338 return false;
339 int ret = activeTxn->commit(0);
340 activeTxn = NULL;
341 return (ret == 0);
344 bool TxnAbort()
346 if (!pdb || !activeTxn)
347 return false;
348 int ret = activeTxn->abort();
349 activeTxn = NULL;
350 return (ret == 0);
353 bool ReadVersion(int& nVersion)
355 nVersion = 0;
356 return Read(std::string("version"), nVersion);
359 bool WriteVersion(int nVersion)
361 return Write(std::string("version"), nVersion);
364 bool static Rewrite(CWalletDBWrapper& dbw, const char* pszSkip = NULL);
367 #endif // BITCOIN_WALLET_DB_H