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