Allow wallet files in multiple directories
Remove requirement that two wallet files can only be opened at the same time if they are contained in the same directory. This change mostly consists of updates to function signatures (updating functions to take fs::path arguments, instead of combinations of strings, fs::path, and CDBEnv / CWalletDBWrapper arguments).
This commit is contained in:
parent
6012f1caf7
commit
d8a99f65e5
13 changed files with 201 additions and 151 deletions
|
@ -32,7 +32,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO
|
||||||
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
|
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
|
||||||
static void CoinSelection(benchmark::State& state)
|
static void CoinSelection(benchmark::State& state)
|
||||||
{
|
{
|
||||||
const CWallet wallet;
|
const CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
std::vector<COutput> vCoins;
|
std::vector<COutput> vCoins;
|
||||||
LOCK(wallet.cs_wallet);
|
LOCK(wallet.cs_wallet);
|
||||||
|
|
||||||
|
|
|
@ -157,9 +157,7 @@ void TestGUI()
|
||||||
for (int i = 0; i < 5; ++i) {
|
for (int i = 0; i < 5; ++i) {
|
||||||
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
|
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
|
||||||
}
|
}
|
||||||
bitdb.MakeMock();
|
CWallet wallet("mock", CWalletDBWrapper::CreateMock());
|
||||||
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
|
|
||||||
CWallet wallet(std::move(dbw));
|
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
wallet.LoadWallet(firstRun);
|
wallet.LoadWallet(firstRun);
|
||||||
{
|
{
|
||||||
|
@ -260,9 +258,6 @@ void TestGUI()
|
||||||
QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
|
QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
|
||||||
removeRequestButton->click();
|
removeRequestButton->click();
|
||||||
QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
|
QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
|
||||||
|
|
||||||
bitdb.Flush(true);
|
|
||||||
bitdb.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,20 +52,44 @@ void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CCriticalSection cs_db;
|
||||||
|
std::map<std::string, CDBEnv> g_dbenvs; //!< Map from directory name to open db environment.
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
|
||||||
|
{
|
||||||
|
fs::path env_directory = wallet_path.parent_path();
|
||||||
|
database_filename = wallet_path.filename().string();
|
||||||
|
LOCK(cs_db);
|
||||||
|
// Note: An ununsed temporary CDBEnv object may be created inside the
|
||||||
|
// emplace function if the key already exists. This is a little inefficient,
|
||||||
|
// but not a big concern since the map will be changed in the future to hold
|
||||||
|
// pointers instead of objects, anyway.
|
||||||
|
return &g_dbenvs.emplace(std::piecewise_construct, std::forward_as_tuple(env_directory.string()), std::forward_as_tuple(env_directory)).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// CDB
|
// CDB
|
||||||
//
|
//
|
||||||
|
|
||||||
CDBEnv bitdb;
|
void CDBEnv::Close()
|
||||||
|
|
||||||
void CDBEnv::EnvShutdown()
|
|
||||||
{
|
{
|
||||||
if (!fDbEnvInit)
|
if (!fDbEnvInit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fDbEnvInit = false;
|
fDbEnvInit = false;
|
||||||
|
|
||||||
|
for (auto& db : mapDb) {
|
||||||
|
auto count = mapFileUseCount.find(db.first);
|
||||||
|
assert(count == mapFileUseCount.end() || count->second == 0);
|
||||||
|
if (db.second) {
|
||||||
|
db.second->close(0);
|
||||||
|
delete db.second;
|
||||||
|
db.second = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ret = dbenv->close(0);
|
int ret = dbenv->close(0);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
|
LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
|
||||||
|
@ -80,29 +104,24 @@ void CDBEnv::Reset()
|
||||||
fMockDb = false;
|
fMockDb = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CDBEnv::CDBEnv()
|
CDBEnv::CDBEnv(const fs::path& dir_path) : strPath(dir_path.string())
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
CDBEnv::~CDBEnv()
|
CDBEnv::~CDBEnv()
|
||||||
{
|
{
|
||||||
EnvShutdown();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDBEnv::Close()
|
bool CDBEnv::Open(bool retry)
|
||||||
{
|
|
||||||
EnvShutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CDBEnv::Open(const fs::path& pathIn, bool retry)
|
|
||||||
{
|
{
|
||||||
if (fDbEnvInit)
|
if (fDbEnvInit)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
boost::this_thread::interruption_point();
|
boost::this_thread::interruption_point();
|
||||||
|
|
||||||
strPath = pathIn.string();
|
fs::path pathIn = strPath;
|
||||||
if (!LockDirectory(pathIn, ".walletlock")) {
|
if (!LockDirectory(pathIn, ".walletlock")) {
|
||||||
LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
|
LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
|
||||||
return false;
|
return false;
|
||||||
|
@ -150,7 +169,7 @@ bool CDBEnv::Open(const fs::path& pathIn, bool retry)
|
||||||
// failure is ok (well, not really, but it's not worse than what we started with)
|
// failure is ok (well, not really, but it's not worse than what we started with)
|
||||||
}
|
}
|
||||||
// try opening it again one more time
|
// try opening it again one more time
|
||||||
if (!Open(pathIn, false)) {
|
if (!Open(false /* retry */)) {
|
||||||
// if it still fails, it probably means we can't even create the database env
|
// if it still fails, it probably means we can't even create the database env
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -209,12 +228,15 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type
|
||||||
return RECOVER_FAIL;
|
return RECOVER_FAIL;
|
||||||
|
|
||||||
// Try to recover:
|
// Try to recover:
|
||||||
bool fRecovered = (*recoverFunc)(strFile, out_backup_filename);
|
bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename);
|
||||||
return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
|
return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
|
bool CDB::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
|
||||||
{
|
{
|
||||||
|
std::string filename;
|
||||||
|
CDBEnv* env = GetWalletEnv(file_path, filename);
|
||||||
|
|
||||||
// Recovery procedure:
|
// Recovery procedure:
|
||||||
// move wallet file to walletfilename.timestamp.bak
|
// move wallet file to walletfilename.timestamp.bak
|
||||||
// Call Salvage with fAggressive=true to
|
// Call Salvage with fAggressive=true to
|
||||||
|
@ -225,7 +247,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
|
||||||
int64_t now = GetTime();
|
int64_t now = GetTime();
|
||||||
newFilename = strprintf("%s.%d.bak", filename, now);
|
newFilename = strprintf("%s.%d.bak", filename, now);
|
||||||
|
|
||||||
int result = bitdb.dbenv->dbrename(nullptr, filename.c_str(), nullptr,
|
int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
|
||||||
newFilename.c_str(), DB_AUTO_COMMIT);
|
newFilename.c_str(), DB_AUTO_COMMIT);
|
||||||
if (result == 0)
|
if (result == 0)
|
||||||
LogPrintf("Renamed %s to %s\n", filename, newFilename);
|
LogPrintf("Renamed %s to %s\n", filename, newFilename);
|
||||||
|
@ -236,7 +258,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CDBEnv::KeyValPair> salvagedData;
|
std::vector<CDBEnv::KeyValPair> salvagedData;
|
||||||
bool fSuccess = bitdb.Salvage(newFilename, true, salvagedData);
|
bool fSuccess = env->Salvage(newFilename, true, salvagedData);
|
||||||
if (salvagedData.empty())
|
if (salvagedData.empty())
|
||||||
{
|
{
|
||||||
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
|
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
|
||||||
|
@ -244,7 +266,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
|
||||||
}
|
}
|
||||||
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
|
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
|
||||||
|
|
||||||
std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(bitdb.dbenv.get(), 0);
|
std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
|
||||||
int ret = pdbCopy->open(nullptr, // Txn pointer
|
int ret = pdbCopy->open(nullptr, // Txn pointer
|
||||||
filename.c_str(), // Filename
|
filename.c_str(), // Filename
|
||||||
"main", // Logical db name
|
"main", // Logical db name
|
||||||
|
@ -257,7 +279,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DbTxn* ptxn = bitdb.TxnBegin();
|
DbTxn* ptxn = env->TxnBegin();
|
||||||
for (CDBEnv::KeyValPair& row : salvagedData)
|
for (CDBEnv::KeyValPair& row : salvagedData)
|
||||||
{
|
{
|
||||||
if (recoverKVcallback)
|
if (recoverKVcallback)
|
||||||
|
@ -279,8 +301,12 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
|
||||||
return fSuccess;
|
return fSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr)
|
bool CDB::VerifyEnvironment(const fs::path& file_path, std::string& errorStr)
|
||||||
{
|
{
|
||||||
|
std::string walletFile;
|
||||||
|
CDBEnv* env = GetWalletEnv(file_path, walletFile);
|
||||||
|
fs::path walletDir = env->Directory();
|
||||||
|
|
||||||
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
|
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
|
||||||
LogPrintf("Using wallet %s\n", walletFile);
|
LogPrintf("Using wallet %s\n", walletFile);
|
||||||
|
|
||||||
|
@ -291,7 +317,7 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bitdb.Open(walletDir, true)) {
|
if (!env->Open(true /* retry */)) {
|
||||||
errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
|
errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -299,12 +325,16 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
|
bool CDB::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
|
||||||
{
|
{
|
||||||
|
std::string walletFile;
|
||||||
|
CDBEnv* env = GetWalletEnv(file_path, walletFile);
|
||||||
|
fs::path walletDir = env->Directory();
|
||||||
|
|
||||||
if (fs::exists(walletDir / walletFile))
|
if (fs::exists(walletDir / walletFile))
|
||||||
{
|
{
|
||||||
std::string backup_filename;
|
std::string backup_filename;
|
||||||
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename);
|
CDBEnv::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename);
|
||||||
if (r == CDBEnv::RECOVER_OK)
|
if (r == CDBEnv::RECOVER_OK)
|
||||||
{
|
{
|
||||||
warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
|
warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
|
||||||
|
@ -414,8 +444,8 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
|
||||||
nFlags |= DB_CREATE;
|
nFlags |= DB_CREATE;
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(env->cs_db);
|
LOCK(cs_db);
|
||||||
if (!env->Open(GetWalletDir()))
|
if (!env->Open(false /* retry */))
|
||||||
throw std::runtime_error("CDB: Failed to open database environment.");
|
throw std::runtime_error("CDB: Failed to open database environment.");
|
||||||
|
|
||||||
pdb = env->mapDb[strFilename];
|
pdb = env->mapDb[strFilename];
|
||||||
|
@ -442,7 +472,25 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
|
throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
|
||||||
}
|
}
|
||||||
CheckUniqueFileid(*env, strFilename, *pdb_temp);
|
|
||||||
|
// Call CheckUniqueFileid on the containing BDB environment to
|
||||||
|
// avoid BDB data consistency bugs that happen when different data
|
||||||
|
// files in the same environment have the same fileid.
|
||||||
|
//
|
||||||
|
// Also call CheckUniqueFileid on all the other g_dbenvs to prevent
|
||||||
|
// bitcoin from opening the same data file through another
|
||||||
|
// environment when the file is referenced through equivalent but
|
||||||
|
// not obviously identical symlinked or hard linked or bind mounted
|
||||||
|
// paths. In the future a more relaxed check for equal inode and
|
||||||
|
// device ids could be done instead, which would allow opening
|
||||||
|
// different backup copies of a wallet at the same time. Maybe even
|
||||||
|
// more ideally, an exclusive lock for accessing the database could
|
||||||
|
// be implemented, so no equality checks are needed at all. (Newer
|
||||||
|
// versions of BDB have an set_lk_exclusive method for this
|
||||||
|
// purpose, but the older version we use does not.)
|
||||||
|
for (auto& env : g_dbenvs) {
|
||||||
|
CheckUniqueFileid(env.second, strFilename, *pdb_temp);
|
||||||
|
}
|
||||||
|
|
||||||
pdb = pdb_temp.release();
|
pdb = pdb_temp.release();
|
||||||
env->mapDb[strFilename] = pdb;
|
env->mapDb[strFilename] = pdb;
|
||||||
|
@ -490,7 +538,7 @@ void CDB::Close()
|
||||||
Flush();
|
Flush();
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(env->cs_db);
|
LOCK(cs_db);
|
||||||
--env->mapFileUseCount[strFile];
|
--env->mapFileUseCount[strFile];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -518,7 +566,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
|
||||||
const std::string& strFile = dbw.strFile;
|
const std::string& strFile = dbw.strFile;
|
||||||
while (true) {
|
while (true) {
|
||||||
{
|
{
|
||||||
LOCK(env->cs_db);
|
LOCK(cs_db);
|
||||||
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
|
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
|
||||||
// Flush log data to the dat file
|
// Flush log data to the dat file
|
||||||
env->CloseDb(strFile);
|
env->CloseDb(strFile);
|
||||||
|
@ -646,7 +694,7 @@ bool CDB::PeriodicFlush(CWalletDBWrapper& dbw)
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
CDBEnv *env = dbw.env;
|
CDBEnv *env = dbw.env;
|
||||||
const std::string& strFile = dbw.strFile;
|
const std::string& strFile = dbw.strFile;
|
||||||
TRY_LOCK(bitdb.cs_db,lockDb);
|
TRY_LOCK(cs_db, lockDb);
|
||||||
if (lockDb)
|
if (lockDb)
|
||||||
{
|
{
|
||||||
// Don't do this if any databases are in use
|
// Don't do this if any databases are in use
|
||||||
|
@ -694,7 +742,7 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
LOCK(env->cs_db);
|
LOCK(cs_db);
|
||||||
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
|
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
|
||||||
{
|
{
|
||||||
// Flush log data to the dat file
|
// Flush log data to the dat file
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
|
#include <util.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
@ -32,20 +33,19 @@ private:
|
||||||
// shutdown problems/crashes caused by a static initialized internal pointer.
|
// shutdown problems/crashes caused by a static initialized internal pointer.
|
||||||
std::string strPath;
|
std::string strPath;
|
||||||
|
|
||||||
void EnvShutdown();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
mutable CCriticalSection cs_db;
|
|
||||||
std::unique_ptr<DbEnv> dbenv;
|
std::unique_ptr<DbEnv> dbenv;
|
||||||
std::map<std::string, int> mapFileUseCount;
|
std::map<std::string, int> mapFileUseCount;
|
||||||
std::map<std::string, Db*> mapDb;
|
std::map<std::string, Db*> mapDb;
|
||||||
|
|
||||||
CDBEnv();
|
CDBEnv(const fs::path& env_directory);
|
||||||
~CDBEnv();
|
~CDBEnv();
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
void MakeMock();
|
void MakeMock();
|
||||||
bool IsMock() const { return fMockDb; }
|
bool IsMock() const { return fMockDb; }
|
||||||
|
bool IsInitialized() const { return fDbEnvInit; }
|
||||||
|
fs::path Directory() const { return strPath; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify that database file strFile is OK. If it is not,
|
* Verify that database file strFile is OK. If it is not,
|
||||||
|
@ -56,7 +56,7 @@ public:
|
||||||
enum VerifyResult { VERIFY_OK,
|
enum VerifyResult { VERIFY_OK,
|
||||||
RECOVER_OK,
|
RECOVER_OK,
|
||||||
RECOVER_FAIL };
|
RECOVER_FAIL };
|
||||||
typedef bool (*recoverFunc_type)(const std::string& strFile, std::string& out_backup_filename);
|
typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename);
|
||||||
VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
|
VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
|
||||||
/**
|
/**
|
||||||
* Salvage data from a file that Verify says is bad.
|
* Salvage data from a file that Verify says is bad.
|
||||||
|
@ -68,7 +68,7 @@ public:
|
||||||
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
|
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
|
||||||
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
|
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
|
||||||
|
|
||||||
bool Open(const fs::path& path, bool retry = 0);
|
bool Open(bool retry);
|
||||||
void Close();
|
void Close();
|
||||||
void Flush(bool fShutdown);
|
void Flush(bool fShutdown);
|
||||||
void CheckpointLSN(const std::string& strFile);
|
void CheckpointLSN(const std::string& strFile);
|
||||||
|
@ -85,7 +85,8 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CDBEnv bitdb;
|
/** Get CDBEnv and database filename given a wallet path. */
|
||||||
|
CDBEnv* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
|
||||||
|
|
||||||
/** An instance of this class represents one database.
|
/** An instance of this class represents one database.
|
||||||
* For BerkeleyDB this is just a (env, strFile) tuple.
|
* For BerkeleyDB this is just a (env, strFile) tuple.
|
||||||
|
@ -100,9 +101,33 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create DB handle to real database */
|
/** Create DB handle to real database */
|
||||||
CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) :
|
CWalletDBWrapper(const fs::path& wallet_path, bool mock = false) :
|
||||||
nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in)
|
nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
|
||||||
{
|
{
|
||||||
|
env = GetWalletEnv(wallet_path, strFile);
|
||||||
|
if (mock) {
|
||||||
|
env->Close();
|
||||||
|
env->Reset();
|
||||||
|
env->MakeMock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return object for accessing database at specified path. */
|
||||||
|
static std::unique_ptr<CWalletDBWrapper> Create(const fs::path& path)
|
||||||
|
{
|
||||||
|
return MakeUnique<CWalletDBWrapper>(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return object for accessing dummy database with no read/write capabilities. */
|
||||||
|
static std::unique_ptr<CWalletDBWrapper> CreateDummy()
|
||||||
|
{
|
||||||
|
return MakeUnique<CWalletDBWrapper>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return object for accessing temporary in-memory database. */
|
||||||
|
static std::unique_ptr<CWalletDBWrapper> CreateMock()
|
||||||
|
{
|
||||||
|
return MakeUnique<CWalletDBWrapper>("", true /* mock */);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
|
/** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
|
||||||
|
@ -113,10 +138,6 @@ public:
|
||||||
*/
|
*/
|
||||||
bool Backup(const std::string& strDest);
|
bool Backup(const std::string& strDest);
|
||||||
|
|
||||||
/** Get a name for this database, for debugging etc.
|
|
||||||
*/
|
|
||||||
std::string GetName() const { return strFile; }
|
|
||||||
|
|
||||||
/** Make sure all changes are flushed to disk.
|
/** Make sure all changes are flushed to disk.
|
||||||
*/
|
*/
|
||||||
void Flush(bool shutdown);
|
void Flush(bool shutdown);
|
||||||
|
@ -161,15 +182,15 @@ public:
|
||||||
|
|
||||||
void Flush();
|
void Flush();
|
||||||
void Close();
|
void Close();
|
||||||
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
|
static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
|
||||||
|
|
||||||
/* flush the wallet passively (TRY_LOCK)
|
/* flush the wallet passively (TRY_LOCK)
|
||||||
ideal to be called periodically */
|
ideal to be called periodically */
|
||||||
static bool PeriodicFlush(CWalletDBWrapper& dbw);
|
static bool PeriodicFlush(CWalletDBWrapper& dbw);
|
||||||
/* verifies the database environment */
|
/* verifies the database environment */
|
||||||
static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr);
|
static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr);
|
||||||
/* verifies the database file */
|
/* verifies the database file */
|
||||||
static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
|
static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename K, typename T>
|
template <typename K, typename T>
|
||||||
|
@ -329,7 +350,7 @@ public:
|
||||||
{
|
{
|
||||||
if (!pdb || activeTxn)
|
if (!pdb || activeTxn)
|
||||||
return false;
|
return false;
|
||||||
DbTxn* ptxn = bitdb.TxnBegin();
|
DbTxn* ptxn = env->TxnBegin();
|
||||||
if (!ptxn)
|
if (!ptxn)
|
||||||
return false;
|
return false;
|
||||||
activeTxn = ptxn;
|
activeTxn = ptxn;
|
||||||
|
|
|
@ -249,21 +249,21 @@ bool VerifyWallets()
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string strError;
|
std::string strError;
|
||||||
if (!CWalletDB::VerifyEnvironment(walletFile, GetWalletDir().string(), strError)) {
|
if (!CWalletDB::VerifyEnvironment(wallet_path, strError)) {
|
||||||
return InitError(strError);
|
return InitError(strError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gArgs.GetBoolArg("-salvagewallet", false)) {
|
if (gArgs.GetBoolArg("-salvagewallet", false)) {
|
||||||
// Recover readable keypairs:
|
// Recover readable keypairs:
|
||||||
CWallet dummyWallet;
|
CWallet dummyWallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
std::string backup_filename;
|
std::string backup_filename;
|
||||||
if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) {
|
if (!CWalletDB::Recover(wallet_path, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string strWarning;
|
std::string strWarning;
|
||||||
bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetWalletDir().string(), strWarning, strError);
|
bool dbV = CWalletDB::VerifyDatabaseFile(wallet_path, strWarning, strError);
|
||||||
if (!strWarning.empty()) {
|
if (!strWarning.empty()) {
|
||||||
InitWarning(strWarning);
|
InitWarning(strWarning);
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ bool OpenWallets()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
||||||
CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile);
|
CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()));
|
||||||
if (!pwallet) {
|
if (!pwallet) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@
|
||||||
BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup)
|
BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results)
|
GetResults(CWallet& wallet, std::map<CAmount, CAccountingEntry>& results)
|
||||||
{
|
{
|
||||||
std::list<CAccountingEntry> aes;
|
std::list<CAccountingEntry> aes;
|
||||||
|
|
||||||
results.clear();
|
results.clear();
|
||||||
BOOST_CHECK(wallet->ReorderTransactions() == DB_LOAD_OK);
|
BOOST_CHECK(wallet.ReorderTransactions() == DB_LOAD_OK);
|
||||||
wallet->ListAccountCreditDebit("", aes);
|
wallet.ListAccountCreditDebit("", aes);
|
||||||
for (CAccountingEntry& ae : aes)
|
for (CAccountingEntry& ae : aes)
|
||||||
{
|
{
|
||||||
results[ae.nOrderPos] = ae;
|
results[ae.nOrderPos] = ae;
|
||||||
|
@ -33,28 +33,28 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
CAccountingEntry ae;
|
CAccountingEntry ae;
|
||||||
std::map<CAmount, CAccountingEntry> results;
|
std::map<CAmount, CAccountingEntry> results;
|
||||||
|
|
||||||
LOCK(pwalletMain->cs_wallet);
|
LOCK(m_wallet.cs_wallet);
|
||||||
|
|
||||||
ae.strAccount = "";
|
ae.strAccount = "";
|
||||||
ae.nCreditDebit = 1;
|
ae.nCreditDebit = 1;
|
||||||
ae.nTime = 1333333333;
|
ae.nTime = 1333333333;
|
||||||
ae.strOtherAccount = "b";
|
ae.strOtherAccount = "b";
|
||||||
ae.strComment = "";
|
ae.strComment = "";
|
||||||
pwalletMain->AddAccountingEntry(ae);
|
m_wallet.AddAccountingEntry(ae);
|
||||||
|
|
||||||
wtx.mapValue["comment"] = "z";
|
wtx.mapValue["comment"] = "z";
|
||||||
pwalletMain->AddToWallet(wtx);
|
m_wallet.AddToWallet(wtx);
|
||||||
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
|
vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
|
||||||
vpwtx[0]->nTimeReceived = (unsigned int)1333333335;
|
vpwtx[0]->nTimeReceived = (unsigned int)1333333335;
|
||||||
vpwtx[0]->nOrderPos = -1;
|
vpwtx[0]->nOrderPos = -1;
|
||||||
|
|
||||||
ae.nTime = 1333333336;
|
ae.nTime = 1333333336;
|
||||||
ae.strOtherAccount = "c";
|
ae.strOtherAccount = "c";
|
||||||
pwalletMain->AddAccountingEntry(ae);
|
m_wallet.AddAccountingEntry(ae);
|
||||||
|
|
||||||
GetResults(pwalletMain.get(), results);
|
GetResults(m_wallet, results);
|
||||||
|
|
||||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 3);
|
BOOST_CHECK(m_wallet.nOrderPosNext == 3);
|
||||||
BOOST_CHECK(2 == results.size());
|
BOOST_CHECK(2 == results.size());
|
||||||
BOOST_CHECK(results[0].nTime == 1333333333);
|
BOOST_CHECK(results[0].nTime == 1333333333);
|
||||||
BOOST_CHECK(results[0].strComment.empty());
|
BOOST_CHECK(results[0].strComment.empty());
|
||||||
|
@ -65,13 +65,13 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
|
|
||||||
ae.nTime = 1333333330;
|
ae.nTime = 1333333330;
|
||||||
ae.strOtherAccount = "d";
|
ae.strOtherAccount = "d";
|
||||||
ae.nOrderPos = pwalletMain->IncOrderPosNext();
|
ae.nOrderPos = m_wallet.IncOrderPosNext();
|
||||||
pwalletMain->AddAccountingEntry(ae);
|
m_wallet.AddAccountingEntry(ae);
|
||||||
|
|
||||||
GetResults(pwalletMain.get(), results);
|
GetResults(m_wallet, results);
|
||||||
|
|
||||||
BOOST_CHECK(results.size() == 3);
|
BOOST_CHECK(results.size() == 3);
|
||||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 4);
|
BOOST_CHECK(m_wallet.nOrderPosNext == 4);
|
||||||
BOOST_CHECK(results[0].nTime == 1333333333);
|
BOOST_CHECK(results[0].nTime == 1333333333);
|
||||||
BOOST_CHECK(1 == vpwtx[0]->nOrderPos);
|
BOOST_CHECK(1 == vpwtx[0]->nOrderPos);
|
||||||
BOOST_CHECK(results[2].nTime == 1333333336);
|
BOOST_CHECK(results[2].nTime == 1333333336);
|
||||||
|
@ -85,8 +85,8 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
--tx.nLockTime; // Just to change the hash :)
|
--tx.nLockTime; // Just to change the hash :)
|
||||||
wtx.SetTx(MakeTransactionRef(std::move(tx)));
|
wtx.SetTx(MakeTransactionRef(std::move(tx)));
|
||||||
}
|
}
|
||||||
pwalletMain->AddToWallet(wtx);
|
m_wallet.AddToWallet(wtx);
|
||||||
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
|
vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
|
||||||
vpwtx[1]->nTimeReceived = (unsigned int)1333333336;
|
vpwtx[1]->nTimeReceived = (unsigned int)1333333336;
|
||||||
|
|
||||||
wtx.mapValue["comment"] = "x";
|
wtx.mapValue["comment"] = "x";
|
||||||
|
@ -95,15 +95,15 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
--tx.nLockTime; // Just to change the hash :)
|
--tx.nLockTime; // Just to change the hash :)
|
||||||
wtx.SetTx(MakeTransactionRef(std::move(tx)));
|
wtx.SetTx(MakeTransactionRef(std::move(tx)));
|
||||||
}
|
}
|
||||||
pwalletMain->AddToWallet(wtx);
|
m_wallet.AddToWallet(wtx);
|
||||||
vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
|
vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
|
||||||
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
|
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
|
||||||
vpwtx[2]->nOrderPos = -1;
|
vpwtx[2]->nOrderPos = -1;
|
||||||
|
|
||||||
GetResults(pwalletMain.get(), results);
|
GetResults(m_wallet, results);
|
||||||
|
|
||||||
BOOST_CHECK(results.size() == 3);
|
BOOST_CHECK(results.size() == 3);
|
||||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 6);
|
BOOST_CHECK(m_wallet.nOrderPosNext == 6);
|
||||||
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
|
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
|
||||||
BOOST_CHECK(results[1].nTime == 1333333333);
|
BOOST_CHECK(results[1].nTime == 1333333333);
|
||||||
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
|
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
|
||||||
|
@ -116,12 +116,12 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
|
||||||
ae.nTime = 1333333334;
|
ae.nTime = 1333333334;
|
||||||
ae.strOtherAccount = "e";
|
ae.strOtherAccount = "e";
|
||||||
ae.nOrderPos = -1;
|
ae.nOrderPos = -1;
|
||||||
pwalletMain->AddAccountingEntry(ae);
|
m_wallet.AddAccountingEntry(ae);
|
||||||
|
|
||||||
GetResults(pwalletMain.get(), results);
|
GetResults(m_wallet, results);
|
||||||
|
|
||||||
BOOST_CHECK(results.size() == 4);
|
BOOST_CHECK(results.size() == 4);
|
||||||
BOOST_CHECK(pwalletMain->nOrderPosNext == 7);
|
BOOST_CHECK(m_wallet.nOrderPosNext == 7);
|
||||||
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
|
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
|
||||||
BOOST_CHECK(results[1].nTime == 1333333333);
|
BOOST_CHECK(results[1].nTime == 1333333333);
|
||||||
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
|
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
|
||||||
|
|
|
@ -6,26 +6,21 @@
|
||||||
|
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <wallet/db.h>
|
#include <wallet/db.h>
|
||||||
|
#include <wallet/wallet.h>
|
||||||
|
|
||||||
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
||||||
TestingSetup(chainName)
|
TestingSetup(chainName), m_wallet("mock", CWalletDBWrapper::CreateMock())
|
||||||
{
|
{
|
||||||
bitdb.MakeMock();
|
|
||||||
bool fFirstRun;
|
bool fFirstRun;
|
||||||
g_address_type = OUTPUT_TYPE_DEFAULT;
|
g_address_type = OUTPUT_TYPE_DEFAULT;
|
||||||
g_change_type = OUTPUT_TYPE_DEFAULT;
|
g_change_type = OUTPUT_TYPE_DEFAULT;
|
||||||
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
|
m_wallet.LoadWallet(fFirstRun);
|
||||||
pwalletMain = MakeUnique<CWallet>(std::move(dbw));
|
RegisterValidationInterface(&m_wallet);
|
||||||
pwalletMain->LoadWallet(fFirstRun);
|
|
||||||
RegisterValidationInterface(pwalletMain.get());
|
|
||||||
|
|
||||||
RegisterWalletRPCCommands(tableRPC);
|
RegisterWalletRPCCommands(tableRPC);
|
||||||
}
|
}
|
||||||
|
|
||||||
WalletTestingSetup::~WalletTestingSetup()
|
WalletTestingSetup::~WalletTestingSetup()
|
||||||
{
|
{
|
||||||
UnregisterValidationInterface(pwalletMain.get());
|
UnregisterValidationInterface(&m_wallet);
|
||||||
|
|
||||||
bitdb.Flush(true);
|
|
||||||
bitdb.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ struct WalletTestingSetup: public TestingSetup {
|
||||||
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
|
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
|
||||||
~WalletTestingSetup();
|
~WalletTestingSetup();
|
||||||
|
|
||||||
std::unique_ptr<CWallet> pwalletMain;
|
CWallet m_wallet;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,7 +36,7 @@ typedef std::set<CInputCoin> CoinSet;
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
|
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
|
||||||
|
|
||||||
static const CWallet testWallet;
|
static const CWallet testWallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
static std::vector<COutput> vCoins;
|
static std::vector<COutput> vCoins;
|
||||||
|
|
||||||
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
|
static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
|
||||||
|
@ -382,7 +382,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
||||||
// Verify ScanForWalletTransactions picks up transactions in both the old
|
// Verify ScanForWalletTransactions picks up transactions in both the old
|
||||||
// and new block files.
|
// and new block files.
|
||||||
{
|
{
|
||||||
CWallet wallet;
|
CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
AddKey(wallet, coinbaseKey);
|
AddKey(wallet, coinbaseKey);
|
||||||
WalletRescanReserver reserver(&wallet);
|
WalletRescanReserver reserver(&wallet);
|
||||||
reserver.reserve();
|
reserver.reserve();
|
||||||
|
@ -397,7 +397,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
||||||
// Verify ScanForWalletTransactions only picks transactions in the new block
|
// Verify ScanForWalletTransactions only picks transactions in the new block
|
||||||
// file.
|
// file.
|
||||||
{
|
{
|
||||||
CWallet wallet;
|
CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
AddKey(wallet, coinbaseKey);
|
AddKey(wallet, coinbaseKey);
|
||||||
WalletRescanReserver reserver(&wallet);
|
WalletRescanReserver reserver(&wallet);
|
||||||
reserver.reserve();
|
reserver.reserve();
|
||||||
|
@ -409,7 +409,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
||||||
// before the missing block, and success for a key whose creation time is
|
// before the missing block, and success for a key whose creation time is
|
||||||
// after.
|
// after.
|
||||||
{
|
{
|
||||||
CWallet wallet;
|
CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
vpwallets.insert(vpwallets.begin(), &wallet);
|
vpwallets.insert(vpwallets.begin(), &wallet);
|
||||||
UniValue keys;
|
UniValue keys;
|
||||||
keys.setArray();
|
keys.setArray();
|
||||||
|
@ -471,7 +471,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||||
|
|
||||||
// Import key into wallet and call dumpwallet to create backup file.
|
// Import key into wallet and call dumpwallet to create backup file.
|
||||||
{
|
{
|
||||||
CWallet wallet;
|
CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
LOCK(wallet.cs_wallet);
|
LOCK(wallet.cs_wallet);
|
||||||
wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
|
wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
|
||||||
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
|
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
|
||||||
|
@ -486,7 +486,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||||
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
|
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
|
||||||
// were scanned, and no prior blocks were scanned.
|
// were scanned, and no prior blocks were scanned.
|
||||||
{
|
{
|
||||||
CWallet wallet;
|
CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
|
|
||||||
JSONRPCRequest request;
|
JSONRPCRequest request;
|
||||||
request.params.setArray();
|
request.params.setArray();
|
||||||
|
@ -516,7 +516,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||||
// debit functions.
|
// debit functions.
|
||||||
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
|
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
|
||||||
{
|
{
|
||||||
CWallet wallet;
|
CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
|
||||||
CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
|
CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
|
||||||
LOCK2(cs_main, wallet.cs_wallet);
|
LOCK2(cs_main, wallet.cs_wallet);
|
||||||
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
|
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
|
||||||
|
@ -562,27 +562,25 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
|
||||||
// expanded to cover more corner cases of smart time logic.
|
// expanded to cover more corner cases of smart time logic.
|
||||||
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
|
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
|
||||||
{
|
{
|
||||||
CWallet wallet;
|
|
||||||
|
|
||||||
// New transaction should use clock time if lower than block time.
|
// New transaction should use clock time if lower than block time.
|
||||||
BOOST_CHECK_EQUAL(AddTx(wallet, 1, 100, 120), 100);
|
BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 100, 120), 100);
|
||||||
|
|
||||||
// Test that updating existing transaction does not change smart time.
|
// Test that updating existing transaction does not change smart time.
|
||||||
BOOST_CHECK_EQUAL(AddTx(wallet, 1, 200, 220), 100);
|
BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 200, 220), 100);
|
||||||
|
|
||||||
// New transaction should use clock time if there's no block time.
|
// New transaction should use clock time if there's no block time.
|
||||||
BOOST_CHECK_EQUAL(AddTx(wallet, 2, 300, 0), 300);
|
BOOST_CHECK_EQUAL(AddTx(m_wallet, 2, 300, 0), 300);
|
||||||
|
|
||||||
// New transaction should use block time if lower than clock time.
|
// New transaction should use block time if lower than clock time.
|
||||||
BOOST_CHECK_EQUAL(AddTx(wallet, 3, 420, 400), 400);
|
BOOST_CHECK_EQUAL(AddTx(m_wallet, 3, 420, 400), 400);
|
||||||
|
|
||||||
// New transaction should use latest entry time if higher than
|
// New transaction should use latest entry time if higher than
|
||||||
// min(block time, clock time).
|
// min(block time, clock time).
|
||||||
BOOST_CHECK_EQUAL(AddTx(wallet, 4, 500, 390), 400);
|
BOOST_CHECK_EQUAL(AddTx(m_wallet, 4, 500, 390), 400);
|
||||||
|
|
||||||
// If there are future entries, new transaction should use time of the
|
// If there are future entries, new transaction should use time of the
|
||||||
// newest entry that is no more than 300 seconds ahead of the clock time.
|
// newest entry that is no more than 300 seconds ahead of the clock time.
|
||||||
BOOST_CHECK_EQUAL(AddTx(wallet, 5, 50, 600), 300);
|
BOOST_CHECK_EQUAL(AddTx(m_wallet, 5, 50, 600), 300);
|
||||||
|
|
||||||
// Reset mock time for other tests.
|
// Reset mock time for other tests.
|
||||||
SetMockTime(0);
|
SetMockTime(0);
|
||||||
|
@ -591,12 +589,12 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
|
||||||
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
|
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
|
||||||
{
|
{
|
||||||
CTxDestination dest = CKeyID();
|
CTxDestination dest = CKeyID();
|
||||||
LOCK(pwalletMain->cs_wallet);
|
LOCK(m_wallet.cs_wallet);
|
||||||
pwalletMain->AddDestData(dest, "misc", "val_misc");
|
m_wallet.AddDestData(dest, "misc", "val_misc");
|
||||||
pwalletMain->AddDestData(dest, "rr0", "val_rr0");
|
m_wallet.AddDestData(dest, "rr0", "val_rr0");
|
||||||
pwalletMain->AddDestData(dest, "rr1", "val_rr1");
|
m_wallet.AddDestData(dest, "rr1", "val_rr1");
|
||||||
|
|
||||||
auto values = pwalletMain->GetDestValues("rr");
|
auto values = m_wallet.GetDestValues("rr");
|
||||||
BOOST_CHECK_EQUAL(values.size(), 2);
|
BOOST_CHECK_EQUAL(values.size(), 2);
|
||||||
BOOST_CHECK_EQUAL(values[0], "val_rr0");
|
BOOST_CHECK_EQUAL(values[0], "val_rr0");
|
||||||
BOOST_CHECK_EQUAL(values[1], "val_rr1");
|
BOOST_CHECK_EQUAL(values[1], "val_rr1");
|
||||||
|
@ -608,10 +606,9 @@ public:
|
||||||
ListCoinsTestingSetup()
|
ListCoinsTestingSetup()
|
||||||
{
|
{
|
||||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||||
::bitdb.MakeMock();
|
|
||||||
g_address_type = OUTPUT_TYPE_DEFAULT;
|
g_address_type = OUTPUT_TYPE_DEFAULT;
|
||||||
g_change_type = OUTPUT_TYPE_DEFAULT;
|
g_change_type = OUTPUT_TYPE_DEFAULT;
|
||||||
wallet.reset(new CWallet(std::unique_ptr<CWalletDBWrapper>(new CWalletDBWrapper(&bitdb, "wallet_test.dat"))));
|
wallet = MakeUnique<CWallet>("mock", CWalletDBWrapper::CreateMock());
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
wallet->LoadWallet(firstRun);
|
wallet->LoadWallet(firstRun);
|
||||||
AddKey(*wallet, coinbaseKey);
|
AddKey(*wallet, coinbaseKey);
|
||||||
|
@ -623,8 +620,6 @@ public:
|
||||||
~ListCoinsTestingSetup()
|
~ListCoinsTestingSetup()
|
||||||
{
|
{
|
||||||
wallet.reset();
|
wallet.reset();
|
||||||
::bitdb.Flush(true);
|
|
||||||
::bitdb.Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CWalletTx& AddTx(CRecipient recipient)
|
CWalletTx& AddTx(CRecipient recipient)
|
||||||
|
|
|
@ -3908,16 +3908,17 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
|
CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
|
||||||
{
|
{
|
||||||
|
const std::string& walletFile = name;
|
||||||
|
|
||||||
// needed to restore wallet transaction meta data after -zapwallettxes
|
// needed to restore wallet transaction meta data after -zapwallettxes
|
||||||
std::vector<CWalletTx> vWtx;
|
std::vector<CWalletTx> vWtx;
|
||||||
|
|
||||||
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
|
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
|
||||||
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
|
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
|
||||||
|
|
||||||
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
|
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, CWalletDBWrapper::Create(path));
|
||||||
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(std::move(dbw));
|
|
||||||
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
|
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
|
||||||
if (nZapWalletRet != DB_LOAD_OK) {
|
if (nZapWalletRet != DB_LOAD_OK) {
|
||||||
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
|
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
|
||||||
|
@ -3929,8 +3930,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
|
||||||
|
|
||||||
int64_t nStart = GetTimeMillis();
|
int64_t nStart = GetTimeMillis();
|
||||||
bool fFirstRun = true;
|
bool fFirstRun = true;
|
||||||
std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
|
CWallet *walletInstance = new CWallet(name, CWalletDBWrapper::Create(path));
|
||||||
CWallet *walletInstance = new CWallet(std::move(dbw));
|
|
||||||
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
|
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
|
||||||
if (nLoadWalletRet != DB_LOAD_OK)
|
if (nLoadWalletRet != DB_LOAD_OK)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
#include <script/ismine.h>
|
#include <script/ismine.h>
|
||||||
#include <script/sign.h>
|
#include <script/sign.h>
|
||||||
|
#include <util.h>
|
||||||
#include <wallet/crypter.h>
|
#include <wallet/crypter.h>
|
||||||
#include <wallet/walletdb.h>
|
#include <wallet/walletdb.h>
|
||||||
#include <wallet/rpcwallet.h>
|
#include <wallet/rpcwallet.h>
|
||||||
|
@ -737,6 +738,14 @@ private:
|
||||||
*/
|
*/
|
||||||
bool AddWatchOnly(const CScript& dest) override;
|
bool AddWatchOnly(const CScript& dest) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wallet filename from wallet=<path> command line or config option.
|
||||||
|
* Used in debug logs and to send RPCs to the right wallet instance when
|
||||||
|
* more than one wallet is loaded.
|
||||||
|
*/
|
||||||
|
std::string m_name;
|
||||||
|
|
||||||
|
/** Internal database handle. */
|
||||||
std::unique_ptr<CWalletDBWrapper> dbw;
|
std::unique_ptr<CWalletDBWrapper> dbw;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -768,14 +777,7 @@ public:
|
||||||
|
|
||||||
/** Get a name for this wallet for logging/debugging purposes.
|
/** Get a name for this wallet for logging/debugging purposes.
|
||||||
*/
|
*/
|
||||||
std::string GetName() const
|
const std::string& GetName() const { return m_name; }
|
||||||
{
|
|
||||||
if (dbw) {
|
|
||||||
return dbw->GetName();
|
|
||||||
} else {
|
|
||||||
return "dummy";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
|
||||||
|
|
||||||
|
@ -789,14 +791,8 @@ public:
|
||||||
MasterKeyMap mapMasterKeys;
|
MasterKeyMap mapMasterKeys;
|
||||||
unsigned int nMasterKeyMaxID;
|
unsigned int nMasterKeyMaxID;
|
||||||
|
|
||||||
// Create wallet with dummy database handle
|
/** Construct wallet with specified name and database implementation. */
|
||||||
CWallet(): dbw(new CWalletDBWrapper())
|
CWallet(std::string name, std::unique_ptr<CWalletDBWrapper> dbw) : m_name(std::move(name)), dbw(std::move(dbw))
|
||||||
{
|
|
||||||
SetNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create wallet with passed-in database handle
|
|
||||||
explicit CWallet(std::unique_ptr<CWalletDBWrapper> dbw_in) : dbw(std::move(dbw_in))
|
|
||||||
{
|
{
|
||||||
SetNull();
|
SetNull();
|
||||||
}
|
}
|
||||||
|
@ -1116,7 +1112,7 @@ public:
|
||||||
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
|
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
|
||||||
|
|
||||||
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
|
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
|
||||||
static CWallet* CreateWalletFromFile(const std::string walletFile);
|
static CWallet* CreateWalletFromFile(const std::string& name, const fs::path& path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wallet post-init setup
|
* Wallet post-init setup
|
||||||
|
|
|
@ -771,16 +771,16 @@ void MaybeCompactWalletDB()
|
||||||
//
|
//
|
||||||
// Try to (very carefully!) recover wallet file if there is a problem.
|
// Try to (very carefully!) recover wallet file if there is a problem.
|
||||||
//
|
//
|
||||||
bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
|
bool CWalletDB::Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
|
||||||
{
|
{
|
||||||
return CDB::Recover(filename, callbackDataIn, recoverKVcallback, out_backup_filename);
|
return CDB::Recover(wallet_path, callbackDataIn, recoverKVcallback, out_backup_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::Recover(const std::string& filename, std::string& out_backup_filename)
|
bool CWalletDB::Recover(const fs::path& wallet_path, std::string& out_backup_filename)
|
||||||
{
|
{
|
||||||
// recover without a key filter callback
|
// recover without a key filter callback
|
||||||
// results in recovering all record types
|
// results in recovering all record types
|
||||||
return CWalletDB::Recover(filename, nullptr, nullptr, out_backup_filename);
|
return CWalletDB::Recover(wallet_path, nullptr, nullptr, out_backup_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
|
bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
|
||||||
|
@ -806,14 +806,14 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr)
|
bool CWalletDB::VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr)
|
||||||
{
|
{
|
||||||
return CDB::VerifyEnvironment(walletFile, walletDir, errorStr);
|
return CDB::VerifyEnvironment(wallet_path, errorStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr)
|
bool CWalletDB::VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr)
|
||||||
{
|
{
|
||||||
return CDB::VerifyDatabaseFile(walletFile, walletDir, warningStr, errorStr, CWalletDB::Recover);
|
return CDB::VerifyDatabaseFile(wallet_path, warningStr, errorStr, CWalletDB::Recover);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
|
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
|
||||||
|
|
|
@ -218,17 +218,17 @@ public:
|
||||||
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
|
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
|
||||||
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
|
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
|
||||||
/* Try to (very carefully!) recover wallet database (with a possible key type filter) */
|
/* Try to (very carefully!) recover wallet database (with a possible key type filter) */
|
||||||
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
|
static bool Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
|
||||||
/* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */
|
/* Recover convenience-function to bypass the key filter callback, called when verify fails, recovers everything */
|
||||||
static bool Recover(const std::string& filename, std::string& out_backup_filename);
|
static bool Recover(const fs::path& wallet_path, std::string& out_backup_filename);
|
||||||
/* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
|
/* Recover filter (used as callback), will only let keys (cryptographical keys) as KV/key-type pass through */
|
||||||
static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
|
static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
|
||||||
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
|
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
|
||||||
static bool IsKeyType(const std::string& strType);
|
static bool IsKeyType(const std::string& strType);
|
||||||
/* verifies the database environment */
|
/* verifies the database environment */
|
||||||
static bool VerifyEnvironment(const std::string& walletFile, const fs::path& walletDir, std::string& errorStr);
|
static bool VerifyEnvironment(const fs::path& wallet_path, std::string& errorStr);
|
||||||
/* verifies the database file */
|
/* verifies the database file */
|
||||||
static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& walletDir, std::string& warningStr, std::string& errorStr);
|
static bool VerifyDatabaseFile(const fs::path& wallet_path, std::string& warningStr, std::string& errorStr);
|
||||||
|
|
||||||
//! write the hdchain model (external chain child index counter)
|
//! write the hdchain model (external chain child index counter)
|
||||||
bool WriteHDChain(const CHDChain& chain);
|
bool WriteHDChain(const CHDChain& chain);
|
||||||
|
|
Loading…
Reference in a new issue