2010-08-29 18:58:15 +02:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
2016-12-31 19:01:21 +01:00
|
|
|
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
2014-10-31 01:43:19 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2012-05-18 16:02:28 +02:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2015-03-21 18:15:31 +01:00
|
|
|
#ifndef BITCOIN_WALLET_DB_H
|
|
|
|
#define BITCOIN_WALLET_DB_H
|
2011-05-15 09:11:04 +02:00
|
|
|
|
2014-10-29 02:33:23 +01:00
|
|
|
#include "clientversion.h"
|
2013-01-06 13:30:00 +01:00
|
|
|
#include "serialize.h"
|
2014-10-22 21:08:30 +02:00
|
|
|
#include "streams.h"
|
2013-04-13 07:13:08 +02:00
|
|
|
#include "sync.h"
|
|
|
|
#include "version.h"
|
2011-05-15 09:11:04 +02:00
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2013-04-13 07:13:08 +02:00
|
|
|
#include <boost/filesystem/path.hpp>
|
2014-09-14 12:43:56 +02:00
|
|
|
|
2011-05-15 09:11:04 +02:00
|
|
|
#include <db_cxx.h>
|
2010-08-29 18:58:15 +02:00
|
|
|
|
2015-06-28 01:15:11 +02:00
|
|
|
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
|
2015-06-27 21:21:41 +02:00
|
|
|
static const bool DEFAULT_WALLET_PRIVDB = true;
|
2015-06-28 01:15:11 +02:00
|
|
|
|
2012-05-14 03:37:39 +02:00
|
|
|
class CDBEnv
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
bool fDbEnvInit;
|
2012-05-22 21:51:13 +02:00
|
|
|
bool fMockDb;
|
2015-06-15 07:46:51 +02:00
|
|
|
// Don't change into boost::filesystem::path, as that can result in
|
|
|
|
// shutdown problems/crashes caused by a static initialized internal pointer.
|
|
|
|
std::string strPath;
|
2012-05-14 03:37:39 +02:00
|
|
|
|
|
|
|
void EnvShutdown();
|
|
|
|
|
|
|
|
public:
|
|
|
|
mutable CCriticalSection cs_db;
|
2015-03-03 16:49:12 +01:00
|
|
|
DbEnv *dbenv;
|
2012-05-18 08:49:50 +02:00
|
|
|
std::map<std::string, int> mapFileUseCount;
|
|
|
|
std::map<std::string, Db*> mapDb;
|
2012-05-14 03:37:39 +02:00
|
|
|
|
|
|
|
CDBEnv();
|
|
|
|
~CDBEnv();
|
2015-03-03 16:49:12 +01:00
|
|
|
void Reset();
|
|
|
|
|
2012-05-22 21:51:13 +02:00
|
|
|
void MakeMock();
|
2012-11-09 22:51:40 +01:00
|
|
|
bool IsMock() { return fMockDb; }
|
2012-09-18 20:30:47 +02:00
|
|
|
|
2014-10-31 01:43:19 +01:00
|
|
|
/**
|
2012-09-18 20:30:47 +02:00
|
|
|
* Verify that database file strFile is OK. If it is not,
|
|
|
|
* call the callback to try to recover.
|
|
|
|
* This must be called BEFORE strFile is opened.
|
|
|
|
* Returns true if strFile is OK.
|
|
|
|
*/
|
2014-09-19 19:21:46 +02:00
|
|
|
enum VerifyResult { VERIFY_OK,
|
|
|
|
RECOVER_OK,
|
|
|
|
RECOVER_FAIL };
|
2016-08-24 09:57:23 +02:00
|
|
|
VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile));
|
2014-10-31 01:43:19 +01:00
|
|
|
/**
|
2012-09-18 20:30:47 +02:00
|
|
|
* Salvage data from a file that Verify says is bad.
|
|
|
|
* fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
|
|
|
|
* Appends binary key/value pairs to vResult, returns true if successful.
|
|
|
|
* NOTE: reads the entire database into memory, so cannot be used
|
|
|
|
* for huge databases.
|
|
|
|
*/
|
|
|
|
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
|
2015-03-21 18:40:51 +01:00
|
|
|
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
|
2012-09-18 20:30:47 +02:00
|
|
|
|
2014-09-19 19:21:46 +02:00
|
|
|
bool Open(const boost::filesystem::path& path);
|
2012-05-14 03:37:39 +02:00
|
|
|
void Close();
|
|
|
|
void Flush(bool fShutdown);
|
2014-09-16 15:16:29 +02:00
|
|
|
void CheckpointLSN(const std::string& strFile);
|
2012-05-14 18:33:34 +02:00
|
|
|
|
2012-05-18 08:49:50 +02:00
|
|
|
void CloseDb(const std::string& strFile);
|
2012-09-18 20:30:47 +02:00
|
|
|
bool RemoveDb(const std::string& strFile);
|
2012-05-18 08:49:50 +02:00
|
|
|
|
2014-09-19 19:21:46 +02:00
|
|
|
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
|
2012-05-14 18:33:34 +02:00
|
|
|
{
|
|
|
|
DbTxn* ptxn = NULL;
|
2015-03-03 16:49:12 +01:00
|
|
|
int ret = dbenv->txn_begin(NULL, &ptxn, flags);
|
2012-05-14 18:33:34 +02:00
|
|
|
if (!ptxn || ret != 0)
|
|
|
|
return NULL;
|
|
|
|
return ptxn;
|
|
|
|
}
|
2012-05-14 03:37:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
extern CDBEnv bitdb;
|
|
|
|
|
|
|
|
|
2012-03-26 16:48:23 +02:00
|
|
|
/** RAII class that provides access to a Berkeley database */
|
2010-08-29 18:58:15 +02:00
|
|
|
class CDB
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
Db* pdb;
|
2011-05-15 09:11:04 +02:00
|
|
|
std::string strFile;
|
2014-09-19 19:21:46 +02:00
|
|
|
DbTxn* activeTxn;
|
2010-08-29 18:58:15 +02:00
|
|
|
bool fReadOnly;
|
2014-08-31 05:55:27 +02:00
|
|
|
bool fFlushOnClose;
|
2010-08-29 18:58:15 +02:00
|
|
|
|
2014-08-31 05:55:27 +02:00
|
|
|
explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
|
2010-08-29 18:58:15 +02:00
|
|
|
~CDB() { Close(); }
|
2014-09-16 15:16:29 +02:00
|
|
|
|
2010-08-29 18:58:15 +02:00
|
|
|
public:
|
2012-07-06 16:33:34 +02:00
|
|
|
void Flush();
|
2010-08-29 18:58:15 +02:00
|
|
|
void Close();
|
2016-08-24 09:57:23 +02:00
|
|
|
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
|
|
|
|
|
|
|
|
/* flush the wallet passively (TRY_LOCK)
|
|
|
|
ideal to be called periodically */
|
|
|
|
static bool PeriodicFlush(std::string strFile);
|
|
|
|
/* verifies the database environment */
|
|
|
|
static bool VerifyEnvironment(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr);
|
|
|
|
/* verifies the database file */
|
|
|
|
static bool VerifyDatabaseFile(const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
|
2014-09-16 15:16:29 +02:00
|
|
|
|
2010-08-29 18:58:15 +02:00
|
|
|
private:
|
|
|
|
CDB(const CDB&);
|
|
|
|
void operator=(const CDB&);
|
|
|
|
|
|
|
|
protected:
|
2014-09-19 19:21:46 +02:00
|
|
|
template <typename K, typename T>
|
2010-08-29 18:58:15 +02:00
|
|
|
bool Read(const K& key, T& value)
|
|
|
|
{
|
|
|
|
if (!pdb)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Key
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
2010-08-29 18:58:15 +02:00
|
|
|
ssKey.reserve(1000);
|
|
|
|
ssKey << key;
|
2016-12-15 17:34:59 +01:00
|
|
|
Dbt datKey(ssKey.data(), ssKey.size());
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Read
|
|
|
|
Dbt datValue;
|
|
|
|
datValue.set_flags(DB_DBT_MALLOC);
|
2012-05-14 18:39:29 +02:00
|
|
|
int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
|
2010-08-29 18:58:15 +02:00
|
|
|
memset(datKey.get_data(), 0, datKey.get_size());
|
|
|
|
if (datValue.get_data() == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Unserialize value
|
2012-05-22 21:12:52 +02:00
|
|
|
try {
|
|
|
|
CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
|
|
|
|
ssValue >> value;
|
2014-09-19 19:21:46 +02:00
|
|
|
} catch (const std::exception&) {
|
2012-05-22 21:12:52 +02:00
|
|
|
return false;
|
|
|
|
}
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Clear and free memory
|
|
|
|
memset(datValue.get_data(), 0, datValue.get_size());
|
|
|
|
free(datValue.get_data());
|
|
|
|
return (ret == 0);
|
|
|
|
}
|
|
|
|
|
2014-09-19 19:21:46 +02:00
|
|
|
template <typename K, typename T>
|
|
|
|
bool Write(const K& key, const T& value, bool fOverwrite = true)
|
2010-08-29 18:58:15 +02:00
|
|
|
{
|
|
|
|
if (!pdb)
|
|
|
|
return false;
|
|
|
|
if (fReadOnly)
|
2011-06-24 19:56:23 +02:00
|
|
|
assert(!"Write called on database in read-only mode");
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Key
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
2010-08-29 18:58:15 +02:00
|
|
|
ssKey.reserve(1000);
|
|
|
|
ssKey << key;
|
2016-12-15 17:34:59 +01:00
|
|
|
Dbt datKey(ssKey.data(), ssKey.size());
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Value
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
2010-08-29 18:58:15 +02:00
|
|
|
ssValue.reserve(10000);
|
|
|
|
ssValue << value;
|
2016-12-15 17:34:59 +01:00
|
|
|
Dbt datValue(ssValue.data(), ssValue.size());
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Write
|
2012-05-14 18:39:29 +02:00
|
|
|
int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Clear memory in case it was a private key
|
|
|
|
memset(datKey.get_data(), 0, datKey.get_size());
|
|
|
|
memset(datValue.get_data(), 0, datValue.get_size());
|
|
|
|
return (ret == 0);
|
|
|
|
}
|
|
|
|
|
2014-09-19 19:21:46 +02:00
|
|
|
template <typename K>
|
2010-08-29 18:58:15 +02:00
|
|
|
bool Erase(const K& key)
|
|
|
|
{
|
|
|
|
if (!pdb)
|
|
|
|
return false;
|
|
|
|
if (fReadOnly)
|
2011-06-24 19:56:23 +02:00
|
|
|
assert(!"Erase called on database in read-only mode");
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Key
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
2010-08-29 18:58:15 +02:00
|
|
|
ssKey.reserve(1000);
|
|
|
|
ssKey << key;
|
2016-12-15 17:34:59 +01:00
|
|
|
Dbt datKey(ssKey.data(), ssKey.size());
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Erase
|
2012-05-14 18:39:29 +02:00
|
|
|
int ret = pdb->del(activeTxn, &datKey, 0);
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Clear memory
|
|
|
|
memset(datKey.get_data(), 0, datKey.get_size());
|
|
|
|
return (ret == 0 || ret == DB_NOTFOUND);
|
|
|
|
}
|
|
|
|
|
2014-09-19 19:21:46 +02:00
|
|
|
template <typename K>
|
2010-08-29 18:58:15 +02:00
|
|
|
bool Exists(const K& key)
|
|
|
|
{
|
|
|
|
if (!pdb)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Key
|
2012-04-16 14:56:45 +02:00
|
|
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
2010-08-29 18:58:15 +02:00
|
|
|
ssKey.reserve(1000);
|
|
|
|
ssKey << key;
|
2016-12-15 17:34:59 +01:00
|
|
|
Dbt datKey(ssKey.data(), ssKey.size());
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Exists
|
2012-05-14 18:39:29 +02:00
|
|
|
int ret = pdb->exists(activeTxn, &datKey, 0);
|
2010-08-29 18:58:15 +02:00
|
|
|
|
|
|
|
// Clear memory
|
|
|
|
memset(datKey.get_data(), 0, datKey.get_size());
|
|
|
|
return (ret == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Dbc* GetCursor()
|
|
|
|
{
|
|
|
|
if (!pdb)
|
|
|
|
return NULL;
|
|
|
|
Dbc* pcursor = NULL;
|
|
|
|
int ret = pdb->cursor(NULL, &pcursor, 0);
|
|
|
|
if (ret != 0)
|
|
|
|
return NULL;
|
|
|
|
return pcursor;
|
|
|
|
}
|
|
|
|
|
2016-08-23 15:36:23 +02:00
|
|
|
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false)
|
2010-08-29 18:58:15 +02:00
|
|
|
{
|
|
|
|
// Read at cursor
|
|
|
|
Dbt datKey;
|
2016-08-23 15:36:23 +02:00
|
|
|
unsigned int fFlags = DB_NEXT;
|
|
|
|
if (setRange) {
|
2016-12-15 17:34:59 +01:00
|
|
|
datKey.set_data(ssKey.data());
|
2010-08-29 18:58:15 +02:00
|
|
|
datKey.set_size(ssKey.size());
|
2016-08-23 15:36:23 +02:00
|
|
|
fFlags = DB_SET_RANGE;
|
2010-08-29 18:58:15 +02:00
|
|
|
}
|
|
|
|
Dbt datValue;
|
|
|
|
datKey.set_flags(DB_DBT_MALLOC);
|
|
|
|
datValue.set_flags(DB_DBT_MALLOC);
|
|
|
|
int ret = pcursor->get(&datKey, &datValue, fFlags);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
else if (datKey.get_data() == NULL || datValue.get_data() == NULL)
|
|
|
|
return 99999;
|
|
|
|
|
|
|
|
// Convert to streams
|
|
|
|
ssKey.SetType(SER_DISK);
|
|
|
|
ssKey.clear();
|
|
|
|
ssKey.write((char*)datKey.get_data(), datKey.get_size());
|
|
|
|
ssValue.SetType(SER_DISK);
|
|
|
|
ssValue.clear();
|
|
|
|
ssValue.write((char*)datValue.get_data(), datValue.get_size());
|
|
|
|
|
|
|
|
// Clear and free memory
|
|
|
|
memset(datKey.get_data(), 0, datKey.get_size());
|
|
|
|
memset(datValue.get_data(), 0, datValue.get_size());
|
|
|
|
free(datKey.get_data());
|
|
|
|
free(datValue.get_data());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool TxnBegin()
|
|
|
|
{
|
2012-05-14 18:39:29 +02:00
|
|
|
if (!pdb || activeTxn)
|
2010-08-29 18:58:15 +02:00
|
|
|
return false;
|
2012-05-14 18:39:29 +02:00
|
|
|
DbTxn* ptxn = bitdb.TxnBegin();
|
2012-05-14 18:33:34 +02:00
|
|
|
if (!ptxn)
|
2010-08-29 18:58:15 +02:00
|
|
|
return false;
|
2012-05-14 18:39:29 +02:00
|
|
|
activeTxn = ptxn;
|
2010-08-29 18:58:15 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TxnCommit()
|
|
|
|
{
|
2012-05-14 18:39:29 +02:00
|
|
|
if (!pdb || !activeTxn)
|
2010-08-29 18:58:15 +02:00
|
|
|
return false;
|
2012-05-14 18:39:29 +02:00
|
|
|
int ret = activeTxn->commit(0);
|
|
|
|
activeTxn = NULL;
|
2010-08-29 18:58:15 +02:00
|
|
|
return (ret == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TxnAbort()
|
|
|
|
{
|
2012-05-14 18:39:29 +02:00
|
|
|
if (!pdb || !activeTxn)
|
2010-08-29 18:58:15 +02:00
|
|
|
return false;
|
2012-05-14 18:39:29 +02:00
|
|
|
int ret = activeTxn->abort();
|
|
|
|
activeTxn = NULL;
|
2010-08-29 18:58:15 +02:00
|
|
|
return (ret == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadVersion(int& nVersion)
|
|
|
|
{
|
|
|
|
nVersion = 0;
|
2011-05-15 09:11:04 +02:00
|
|
|
return Read(std::string("version"), nVersion);
|
2010-08-29 18:58:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool WriteVersion(int nVersion)
|
|
|
|
{
|
2011-05-15 09:11:04 +02:00
|
|
|
return Write(std::string("version"), nVersion);
|
2010-08-29 18:58:15 +02:00
|
|
|
}
|
2011-11-10 21:29:23 +01:00
|
|
|
|
2011-11-11 03:12:46 +01:00
|
|
|
bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL);
|
2010-08-29 18:58:15 +02:00
|
|
|
};
|
|
|
|
|
2015-03-21 18:15:31 +01:00
|
|
|
#endif // BITCOIN_WALLET_DB_H
|