1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2015 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 "serialize.h"
11 #include "streams.h"
12 #include "sync.h"
13 #include "version.h"
14 
15 #include <map>
16 #include <string>
17 #include <vector>
18 
19 #include <boost/filesystem/path.hpp>
20 
21 #include <db_cxx.h>
22 
23 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
24 static const bool DEFAULT_WALLET_PRIVDB = true;
25 
26 extern unsigned int nWalletDBUpdated;
27 
28 class CDBEnv
29 {
30 private:
31     bool fDbEnvInit;
32     bool fMockDb;
33     // Don't change into boost::filesystem::path, as that can result in
34     // shutdown problems/crashes caused by a static initialized internal pointer.
35     std::string strPath;
36 
37     void EnvShutdown();
38 
39 public:
40     mutable CCriticalSection cs_db;
41     DbEnv *dbenv;
42     std::map<std::string, int> mapFileUseCount;
43     std::map<std::string, Db*> mapDb;
44 
45     CDBEnv();
46     ~CDBEnv();
47     void Reset();
48 
49     void MakeMock();
IsMock()50     bool IsMock() { return fMockDb; }
51 
52     /**
53      * Verify that database file strFile is OK. If it is not,
54      * call the callback to try to recover.
55      * This must be called BEFORE strFile is opened.
56      * Returns true if strFile is OK.
57      */
58     enum VerifyResult { VERIFY_OK,
59                         RECOVER_OK,
60                         RECOVER_FAIL };
61     VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(CDBEnv& dbenv, const std::string& strFile));
62     /**
63      * Salvage data from a file that Verify says is bad.
64      * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
65      * Appends binary key/value pairs to vResult, returns true if successful.
66      * NOTE: reads the entire database into memory, so cannot be used
67      * for huge databases.
68      */
69     typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
70     bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
71 
72     bool Open(const boost::filesystem::path& path);
73     void Close();
74     void Flush(bool fShutdown);
75     void CheckpointLSN(const std::string& strFile);
76 
77     void CloseDb(const std::string& strFile);
78     bool RemoveDb(const std::string& strFile);
79 
80     DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
81     {
82         DbTxn* ptxn = NULL;
83         int ret = dbenv->txn_begin(NULL, &ptxn, flags);
84         if (!ptxn || ret != 0)
85             return NULL;
86         return ptxn;
87     }
88 };
89 
90 extern CDBEnv bitdb;
91 
92 
93 /** RAII class that provides access to a Berkeley database */
94 class CDB
95 {
96 protected:
97     Db* pdb;
98     std::string strFile;
99     DbTxn* activeTxn;
100     bool fReadOnly;
101     bool fFlushOnClose;
102 
103     explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
~CDB()104     ~CDB() { Close(); }
105 
106 public:
107     void Flush();
108     void Close();
109 
110 private:
111     CDB(const CDB&);
112     void operator=(const CDB&);
113 
114 protected:
115     template <typename K, typename T>
Read(const K & key,T & value)116     bool Read(const K& key, T& value)
117     {
118         if (!pdb)
119             return false;
120 
121         // Key
122         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
123         ssKey.reserve(1000);
124         ssKey << key;
125         Dbt datKey(&ssKey[0], ssKey.size());
126 
127         // Read
128         Dbt datValue;
129         datValue.set_flags(DB_DBT_MALLOC);
130         int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
131         memset(datKey.get_data(), 0, datKey.get_size());
132         if (datValue.get_data() == NULL)
133             return false;
134 
135         // Unserialize value
136         try {
137             CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
138             ssValue >> value;
139         } catch (const std::exception&) {
140             return false;
141         }
142 
143         // Clear and free memory
144         memset(datValue.get_data(), 0, datValue.get_size());
145         free(datValue.get_data());
146         return (ret == 0);
147     }
148 
149     template <typename K, typename T>
150     bool Write(const K& key, const T& value, bool fOverwrite = true)
151     {
152         if (!pdb)
153             return false;
154         if (fReadOnly)
155             assert(!"Write called on database in read-only mode");
156 
157         // Key
158         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
159         ssKey.reserve(1000);
160         ssKey << key;
161         Dbt datKey(&ssKey[0], ssKey.size());
162 
163         // Value
164         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
165         ssValue.reserve(10000);
166         ssValue << value;
167         Dbt datValue(&ssValue[0], ssValue.size());
168 
169         // Write
170         int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
171 
172         // Clear memory in case it was a private key
173         memset(datKey.get_data(), 0, datKey.get_size());
174         memset(datValue.get_data(), 0, datValue.get_size());
175         return (ret == 0);
176     }
177 
178     template <typename K>
Erase(const K & key)179     bool Erase(const K& key)
180     {
181         if (!pdb)
182             return false;
183         if (fReadOnly)
184             assert(!"Erase called on database in read-only mode");
185 
186         // Key
187         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
188         ssKey.reserve(1000);
189         ssKey << key;
190         Dbt datKey(&ssKey[0], ssKey.size());
191 
192         // Erase
193         int ret = pdb->del(activeTxn, &datKey, 0);
194 
195         // Clear memory
196         memset(datKey.get_data(), 0, datKey.get_size());
197         return (ret == 0 || ret == DB_NOTFOUND);
198     }
199 
200     template <typename K>
Exists(const K & key)201     bool Exists(const K& key)
202     {
203         if (!pdb)
204             return false;
205 
206         // Key
207         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
208         ssKey.reserve(1000);
209         ssKey << key;
210         Dbt datKey(&ssKey[0], ssKey.size());
211 
212         // Exists
213         int ret = pdb->exists(activeTxn, &datKey, 0);
214 
215         // Clear memory
216         memset(datKey.get_data(), 0, datKey.get_size());
217         return (ret == 0);
218     }
219 
GetCursor()220     Dbc* GetCursor()
221     {
222         if (!pdb)
223             return NULL;
224         Dbc* pcursor = NULL;
225         int ret = pdb->cursor(NULL, &pcursor, 0);
226         if (ret != 0)
227             return NULL;
228         return pcursor;
229     }
230 
231     int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags = DB_NEXT)
232     {
233         // Read at cursor
234         Dbt datKey;
235         if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) {
236             datKey.set_data(&ssKey[0]);
237             datKey.set_size(ssKey.size());
238         }
239         Dbt datValue;
240         if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) {
241             datValue.set_data(&ssValue[0]);
242             datValue.set_size(ssValue.size());
243         }
244         datKey.set_flags(DB_DBT_MALLOC);
245         datValue.set_flags(DB_DBT_MALLOC);
246         int ret = pcursor->get(&datKey, &datValue, fFlags);
247         if (ret != 0)
248             return ret;
249         else if (datKey.get_data() == NULL || datValue.get_data() == NULL)
250             return 99999;
251 
252         // Convert to streams
253         ssKey.SetType(SER_DISK);
254         ssKey.clear();
255         ssKey.write((char*)datKey.get_data(), datKey.get_size());
256         ssValue.SetType(SER_DISK);
257         ssValue.clear();
258         ssValue.write((char*)datValue.get_data(), datValue.get_size());
259 
260         // Clear and free memory
261         memset(datKey.get_data(), 0, datKey.get_size());
262         memset(datValue.get_data(), 0, datValue.get_size());
263         free(datKey.get_data());
264         free(datValue.get_data());
265         return 0;
266     }
267 
268 public:
TxnBegin()269     bool TxnBegin()
270     {
271         if (!pdb || activeTxn)
272             return false;
273         DbTxn* ptxn = bitdb.TxnBegin();
274         if (!ptxn)
275             return false;
276         activeTxn = ptxn;
277         return true;
278     }
279 
TxnCommit()280     bool TxnCommit()
281     {
282         if (!pdb || !activeTxn)
283             return false;
284         int ret = activeTxn->commit(0);
285         activeTxn = NULL;
286         return (ret == 0);
287     }
288 
TxnAbort()289     bool TxnAbort()
290     {
291         if (!pdb || !activeTxn)
292             return false;
293         int ret = activeTxn->abort();
294         activeTxn = NULL;
295         return (ret == 0);
296     }
297 
ReadVersion(int & nVersion)298     bool ReadVersion(int& nVersion)
299     {
300         nVersion = 0;
301         return Read(std::string("version"), nVersion);
302     }
303 
WriteVersion(int nVersion)304     bool WriteVersion(int nVersion)
305     {
306         return Write(std::string("version"), nVersion);
307     }
308 
309     bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL);
310 };
311 
312 #endif // BITCOIN_WALLET_DB_H
313