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