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