1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2017 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>
11 #include <serialize.h>
23 static const unsigned int DEFAULT_WALLET_DBLOGSIZE
= 100;
24 static const bool DEFAULT_WALLET_PRIVDB
= true;
31 // Don't change into fs::path, as that can result in
32 // shutdown problems/crashes caused by a static initialized internal pointer.
38 mutable CCriticalSection cs_db
;
39 std::unique_ptr
<DbEnv
> dbenv
;
40 std::map
<std::string
, int> mapFileUseCount
;
41 std::map
<std::string
, Db
*> mapDb
;
48 bool IsMock() const { return fMockDb
; }
51 * Verify that database file strFile is OK. If it is not,
52 * call the callback to try to recover.
53 * This must be called BEFORE strFile is opened.
54 * Returns true if strFile is OK.
56 enum VerifyResult
{ VERIFY_OK
,
59 typedef bool (*recoverFunc_type
)(const std::string
& strFile
, std::string
& out_backup_filename
);
60 VerifyResult
Verify(const std::string
& strFile
, recoverFunc_type recoverFunc
, std::string
& out_backup_filename
);
62 * Salvage data from a file that Verify says is bad.
63 * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
64 * Appends binary key/value pairs to vResult, returns true if successful.
65 * NOTE: reads the entire database into memory, so cannot be used
68 typedef std::pair
<std::vector
<unsigned char>, std::vector
<unsigned char> > KeyValPair
;
69 bool Salvage(const std::string
& strFile
, bool fAggressive
, std::vector
<KeyValPair
>& vResult
);
71 bool Open(const fs::path
& path
);
73 void Flush(bool fShutdown
);
74 void CheckpointLSN(const std::string
& strFile
);
76 void CloseDb(const std::string
& strFile
);
78 DbTxn
* TxnBegin(int flags
= DB_TXN_WRITE_NOSYNC
)
80 DbTxn
* ptxn
= nullptr;
81 int ret
= dbenv
->txn_begin(nullptr, &ptxn
, flags
);
82 if (!ptxn
|| ret
!= 0)
90 /** An instance of this class represents one database.
91 * For BerkeleyDB this is just a (env, strFile) tuple.
93 class CWalletDBWrapper
97 /** Create dummy DB handle */
98 CWalletDBWrapper() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
102 /** Create DB handle to real database */
103 CWalletDBWrapper(CDBEnv
*env_in
, const std::string
&strFile_in
) :
104 nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in
), strFile(strFile_in
)
108 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
110 bool Rewrite(const char* pszSkip
=nullptr);
112 /** Back up the entire database to a file.
114 bool Backup(const std::string
& strDest
);
116 /** Get a name for this database, for debugging etc.
118 std::string
GetName() const { return strFile
; }
120 /** Make sure all changes are flushed to disk.
122 void Flush(bool shutdown
);
124 void IncrementUpdateCounter();
126 std::atomic
<unsigned int> nUpdateCounter
;
127 unsigned int nLastSeen
;
128 unsigned int nLastFlushed
;
129 int64_t nLastWalletUpdate
;
132 /** BerkeleyDB specific */
136 /** Return whether this database handle is a dummy for testing.
137 * Only to be used at a low level, application should ideally not care
140 bool IsDummy() { return env
== nullptr; }
144 /** RAII class that provides access to a Berkeley database */
156 explicit CDB(CWalletDBWrapper
& dbw
, const char* pszMode
= "r+", bool fFlushOnCloseIn
=true);
159 CDB(const CDB
&) = delete;
160 CDB
& operator=(const CDB
&) = delete;
164 static bool Recover(const std::string
& filename
, void *callbackDataIn
, bool (*recoverKVcallback
)(void* callbackData
, CDataStream ssKey
, CDataStream ssValue
), std::string
& out_backup_filename
);
166 /* flush the wallet passively (TRY_LOCK)
167 ideal to be called periodically */
168 static bool PeriodicFlush(CWalletDBWrapper
& dbw
);
169 /* verifies the database environment */
170 static bool VerifyEnvironment(const std::string
& walletFile
, const fs::path
& walletDir
, std::string
& errorStr
);
171 /* verifies the database file */
172 static bool VerifyDatabaseFile(const std::string
& walletFile
, const fs::path
& walletDir
, std::string
& warningStr
, std::string
& errorStr
, CDBEnv::recoverFunc_type recoverFunc
);
175 template <typename K
, typename T
>
176 bool Read(const K
& key
, T
& value
)
182 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
185 Dbt
datKey(ssKey
.data(), ssKey
.size());
189 datValue
.set_flags(DB_DBT_MALLOC
);
190 int ret
= pdb
->get(activeTxn
, &datKey
, &datValue
, 0);
191 memory_cleanse(datKey
.get_data(), datKey
.get_size());
192 bool success
= false;
193 if (datValue
.get_data() != nullptr) {
196 CDataStream
ssValue((char*)datValue
.get_data(), (char*)datValue
.get_data() + datValue
.get_size(), SER_DISK
, CLIENT_VERSION
);
199 } catch (const std::exception
&) {
200 // In this case success remains 'false'
203 // Clear and free memory
204 memory_cleanse(datValue
.get_data(), datValue
.get_size());
205 free(datValue
.get_data());
207 return ret
== 0 && success
;
210 template <typename K
, typename T
>
211 bool Write(const K
& key
, const T
& value
, bool fOverwrite
= true)
216 assert(!"Write called on database in read-only mode");
219 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
222 Dbt
datKey(ssKey
.data(), ssKey
.size());
225 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
226 ssValue
.reserve(10000);
228 Dbt
datValue(ssValue
.data(), ssValue
.size());
231 int ret
= pdb
->put(activeTxn
, &datKey
, &datValue
, (fOverwrite
? 0 : DB_NOOVERWRITE
));
233 // Clear memory in case it was a private key
234 memory_cleanse(datKey
.get_data(), datKey
.get_size());
235 memory_cleanse(datValue
.get_data(), datValue
.get_size());
239 template <typename K
>
240 bool Erase(const K
& key
)
245 assert(!"Erase called on database in read-only mode");
248 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
251 Dbt
datKey(ssKey
.data(), ssKey
.size());
254 int ret
= pdb
->del(activeTxn
, &datKey
, 0);
257 memory_cleanse(datKey
.get_data(), datKey
.get_size());
258 return (ret
== 0 || ret
== DB_NOTFOUND
);
261 template <typename K
>
262 bool Exists(const K
& key
)
268 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
271 Dbt
datKey(ssKey
.data(), ssKey
.size());
274 int ret
= pdb
->exists(activeTxn
, &datKey
, 0);
277 memory_cleanse(datKey
.get_data(), datKey
.get_size());
285 Dbc
* pcursor
= nullptr;
286 int ret
= pdb
->cursor(nullptr, &pcursor
, 0);
292 int ReadAtCursor(Dbc
* pcursor
, CDataStream
& ssKey
, CDataStream
& ssValue
, bool setRange
= false)
296 unsigned int fFlags
= DB_NEXT
;
298 datKey
.set_data(ssKey
.data());
299 datKey
.set_size(ssKey
.size());
300 fFlags
= DB_SET_RANGE
;
303 datKey
.set_flags(DB_DBT_MALLOC
);
304 datValue
.set_flags(DB_DBT_MALLOC
);
305 int ret
= pcursor
->get(&datKey
, &datValue
, fFlags
);
308 else if (datKey
.get_data() == nullptr || datValue
.get_data() == nullptr)
311 // Convert to streams
312 ssKey
.SetType(SER_DISK
);
314 ssKey
.write((char*)datKey
.get_data(), datKey
.get_size());
315 ssValue
.SetType(SER_DISK
);
317 ssValue
.write((char*)datValue
.get_data(), datValue
.get_size());
319 // Clear and free memory
320 memory_cleanse(datKey
.get_data(), datKey
.get_size());
321 memory_cleanse(datValue
.get_data(), datValue
.get_size());
322 free(datKey
.get_data());
323 free(datValue
.get_data());
330 if (!pdb
|| activeTxn
)
332 DbTxn
* ptxn
= bitdb
.TxnBegin();
341 if (!pdb
|| !activeTxn
)
343 int ret
= activeTxn
->commit(0);
350 if (!pdb
|| !activeTxn
)
352 int ret
= activeTxn
->abort();
357 bool ReadVersion(int& nVersion
)
360 return Read(std::string("version"), nVersion
);
363 bool WriteVersion(int nVersion
)
365 return Write(std::string("version"), nVersion
);
368 bool static Rewrite(CWalletDBWrapper
& dbw
, const char* pszSkip
= nullptr);
371 #endif // BITCOIN_WALLET_DB_H