aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/db.cpp108
-rw-r--r--src/wallet/db.h55
-rw-r--r--src/wallet/init.cpp10
-rw-r--r--src/wallet/test/accounting_tests.cpp46
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp15
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp43
-rw-r--r--src/wallet/wallet.cpp10
-rw-r--r--src/wallet/wallet.h30
-rw-r--r--src/wallet/walletdb.cpp16
-rw-r--r--src/wallet/walletdb.h8
11 files changed, 199 insertions, 144 deletions
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 23c6279128..88463d10b2 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -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
+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
//
-CDBEnv bitdb;
-
-void CDBEnv::EnvShutdown()
+void CDBEnv::Close()
{
if (!fDbEnvInit)
return;
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);
if (ret != 0)
LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
@@ -80,29 +104,24 @@ void CDBEnv::Reset()
fMockDb = false;
}
-CDBEnv::CDBEnv()
+CDBEnv::CDBEnv(const fs::path& dir_path) : strPath(dir_path.string())
{
Reset();
}
CDBEnv::~CDBEnv()
{
- EnvShutdown();
+ Close();
}
-void CDBEnv::Close()
-{
- EnvShutdown();
-}
-
-bool CDBEnv::Open(const fs::path& pathIn, bool retry)
+bool CDBEnv::Open(bool retry)
{
if (fDbEnvInit)
return true;
boost::this_thread::interruption_point();
- strPath = pathIn.string();
+ fs::path pathIn = strPath;
if (!LockDirectory(pathIn, ".walletlock")) {
LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
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)
}
// 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
return false;
}
@@ -209,12 +228,15 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type
return RECOVER_FAIL;
// 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);
}
-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:
// move wallet file to walletfilename.timestamp.bak
// 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();
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);
if (result == 0)
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;
- bool fSuccess = bitdb.Salvage(newFilename, true, salvagedData);
+ bool fSuccess = env->Salvage(newFilename, true, salvagedData);
if (salvagedData.empty())
{
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());
- 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
filename.c_str(), // Filename
"main", // Logical db name
@@ -257,7 +279,7 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
return false;
}
- DbTxn* ptxn = bitdb.TxnBegin();
+ DbTxn* ptxn = env->TxnBegin();
for (CDBEnv::KeyValPair& row : salvagedData)
{
if (recoverKVcallback)
@@ -279,8 +301,12 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco
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 wallet %s\n", walletFile);
@@ -291,7 +317,7 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
return false;
}
- if (!bitdb.Open(walletDir, true)) {
+ if (!env->Open(true /* retry */)) {
errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
return false;
}
@@ -299,12 +325,16 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
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))
{
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)
{
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;
{
- LOCK(env->cs_db);
- if (!env->Open(GetWalletDir()))
+ LOCK(cs_db);
+ if (!env->Open(false /* retry */))
throw std::runtime_error("CDB: Failed to open database environment.");
pdb = env->mapDb[strFilename];
@@ -442,7 +472,25 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
if (ret != 0) {
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();
env->mapDb[strFilename] = pdb;
@@ -490,7 +538,7 @@ void CDB::Close()
Flush();
{
- LOCK(env->cs_db);
+ LOCK(cs_db);
--env->mapFileUseCount[strFile];
}
}
@@ -518,7 +566,7 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
const std::string& strFile = dbw.strFile;
while (true) {
{
- LOCK(env->cs_db);
+ LOCK(cs_db);
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
// Flush log data to the dat file
env->CloseDb(strFile);
@@ -646,7 +694,7 @@ bool CDB::PeriodicFlush(CWalletDBWrapper& dbw)
bool ret = false;
CDBEnv *env = dbw.env;
const std::string& strFile = dbw.strFile;
- TRY_LOCK(bitdb.cs_db,lockDb);
+ TRY_LOCK(cs_db, lockDb);
if (lockDb)
{
// Don't do this if any databases are in use
@@ -694,7 +742,7 @@ bool CWalletDBWrapper::Backup(const std::string& strDest)
while (true)
{
{
- LOCK(env->cs_db);
+ LOCK(cs_db);
if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
{
// Flush log data to the dat file
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 787135e400..b1ce451534 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -11,6 +11,7 @@
#include <serialize.h>
#include <streams.h>
#include <sync.h>
+#include <util.h>
#include <version.h>
#include <atomic>
@@ -32,20 +33,19 @@ private:
// shutdown problems/crashes caused by a static initialized internal pointer.
std::string strPath;
- void EnvShutdown();
-
public:
- mutable CCriticalSection cs_db;
std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
std::map<std::string, Db*> mapDb;
- CDBEnv();
+ CDBEnv(const fs::path& env_directory);
~CDBEnv();
void Reset();
void MakeMock();
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,
@@ -56,7 +56,7 @@ public:
enum VerifyResult { VERIFY_OK,
RECOVER_OK,
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);
/**
* 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;
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 Flush(bool fShutdown);
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.
* For BerkeleyDB this is just a (env, strFile) tuple.
@@ -100,9 +101,33 @@ public:
}
/** Create DB handle to real database */
- CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) :
- nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in)
+ CWalletDBWrapper(const fs::path& wallet_path, bool mock = false) :
+ 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
@@ -113,10 +138,6 @@ public:
*/
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.
*/
void Flush(bool shutdown);
@@ -161,15 +182,15 @@ public:
void Flush();
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)
ideal to be called periodically */
static bool PeriodicFlush(CWalletDBWrapper& dbw);
/* 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 */
- 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:
template <typename K, typename T>
@@ -329,7 +350,7 @@ public:
{
if (!pdb || activeTxn)
return false;
- DbTxn* ptxn = bitdb.TxnBegin();
+ DbTxn* ptxn = env->TxnBegin();
if (!ptxn)
return false;
activeTxn = ptxn;
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 9ac48bff77..6e243c0a09 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -249,21 +249,21 @@ bool VerifyWallets()
}
std::string strError;
- if (!CWalletDB::VerifyEnvironment(walletFile, GetWalletDir().string(), strError)) {
+ if (!CWalletDB::VerifyEnvironment(wallet_path, strError)) {
return InitError(strError);
}
if (gArgs.GetBoolArg("-salvagewallet", false)) {
// Recover readable keypairs:
- CWallet dummyWallet;
+ CWallet dummyWallet("dummy", CWalletDBWrapper::CreateDummy());
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;
}
}
std::string strWarning;
- bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetWalletDir().string(), strWarning, strError);
+ bool dbV = CWalletDB::VerifyDatabaseFile(wallet_path, strWarning, strError);
if (!strWarning.empty()) {
InitWarning(strWarning);
}
@@ -284,7 +284,7 @@ bool OpenWallets()
}
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) {
return false;
}
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index cafd69d075..17b8637a8b 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -13,13 +13,13 @@
BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup)
static void
-GetResults(CWallet *wallet, std::map<CAmount, CAccountingEntry>& results)
+GetResults(CWallet& wallet, std::map<CAmount, CAccountingEntry>& results)
{
std::list<CAccountingEntry> aes;
results.clear();
- BOOST_CHECK(wallet->ReorderTransactions() == DB_LOAD_OK);
- wallet->ListAccountCreditDebit("", aes);
+ BOOST_CHECK(wallet.ReorderTransactions() == DB_LOAD_OK);
+ wallet.ListAccountCreditDebit("", aes);
for (CAccountingEntry& ae : aes)
{
results[ae.nOrderPos] = ae;
@@ -33,28 +33,28 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
CAccountingEntry ae;
std::map<CAmount, CAccountingEntry> results;
- LOCK(pwalletMain->cs_wallet);
+ LOCK(m_wallet.cs_wallet);
ae.strAccount = "";
ae.nCreditDebit = 1;
ae.nTime = 1333333333;
ae.strOtherAccount = "b";
ae.strComment = "";
- pwalletMain->AddAccountingEntry(ae);
+ m_wallet.AddAccountingEntry(ae);
wtx.mapValue["comment"] = "z";
- pwalletMain->AddToWallet(wtx);
- vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
+ m_wallet.AddToWallet(wtx);
+ vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
vpwtx[0]->nTimeReceived = (unsigned int)1333333335;
vpwtx[0]->nOrderPos = -1;
ae.nTime = 1333333336;
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(results[0].nTime == 1333333333);
BOOST_CHECK(results[0].strComment.empty());
@@ -65,13 +65,13 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333330;
ae.strOtherAccount = "d";
- ae.nOrderPos = pwalletMain->IncOrderPosNext();
- pwalletMain->AddAccountingEntry(ae);
+ ae.nOrderPos = m_wallet.IncOrderPosNext();
+ m_wallet.AddAccountingEntry(ae);
- GetResults(pwalletMain.get(), results);
+ GetResults(m_wallet, results);
BOOST_CHECK(results.size() == 3);
- BOOST_CHECK(pwalletMain->nOrderPosNext == 4);
+ BOOST_CHECK(m_wallet.nOrderPosNext == 4);
BOOST_CHECK(results[0].nTime == 1333333333);
BOOST_CHECK(1 == vpwtx[0]->nOrderPos);
BOOST_CHECK(results[2].nTime == 1333333336);
@@ -85,8 +85,8 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
--tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
- pwalletMain->AddToWallet(wtx);
- vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
+ m_wallet.AddToWallet(wtx);
+ vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
vpwtx[1]->nTimeReceived = (unsigned int)1333333336;
wtx.mapValue["comment"] = "x";
@@ -95,15 +95,15 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
--tx.nLockTime; // Just to change the hash :)
wtx.SetTx(MakeTransactionRef(std::move(tx)));
}
- pwalletMain->AddToWallet(wtx);
- vpwtx.push_back(&pwalletMain->mapWallet[wtx.GetHash()]);
+ m_wallet.AddToWallet(wtx);
+ vpwtx.push_back(&m_wallet.mapWallet[wtx.GetHash()]);
vpwtx[2]->nTimeReceived = (unsigned int)1333333329;
vpwtx[2]->nOrderPos = -1;
- GetResults(pwalletMain.get(), results);
+ GetResults(m_wallet, results);
BOOST_CHECK(results.size() == 3);
- BOOST_CHECK(pwalletMain->nOrderPosNext == 6);
+ BOOST_CHECK(m_wallet.nOrderPosNext == 6);
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
BOOST_CHECK(results[1].nTime == 1333333333);
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
@@ -116,12 +116,12 @@ BOOST_AUTO_TEST_CASE(acc_orderupgrade)
ae.nTime = 1333333334;
ae.strOtherAccount = "e";
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(pwalletMain->nOrderPosNext == 7);
+ BOOST_CHECK(m_wallet.nOrderPosNext == 7);
BOOST_CHECK(0 == vpwtx[2]->nOrderPos);
BOOST_CHECK(results[1].nTime == 1333333333);
BOOST_CHECK(2 == vpwtx[0]->nOrderPos);
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 18abf9a9db..77ccd0b8d8 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -6,26 +6,21 @@
#include <rpc/server.h>
#include <wallet/db.h>
+#include <wallet/wallet.h>
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
- TestingSetup(chainName)
+ TestingSetup(chainName), m_wallet("mock", CWalletDBWrapper::CreateMock())
{
- bitdb.MakeMock();
bool fFirstRun;
g_address_type = OUTPUT_TYPE_DEFAULT;
g_change_type = OUTPUT_TYPE_DEFAULT;
- std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, "wallet_test.dat"));
- pwalletMain = MakeUnique<CWallet>(std::move(dbw));
- pwalletMain->LoadWallet(fFirstRun);
- RegisterValidationInterface(pwalletMain.get());
+ m_wallet.LoadWallet(fFirstRun);
+ RegisterValidationInterface(&m_wallet);
RegisterWalletRPCCommands(tableRPC);
}
WalletTestingSetup::~WalletTestingSetup()
{
- UnregisterValidationInterface(pwalletMain.get());
-
- bitdb.Flush(true);
- bitdb.Reset();
+ UnregisterValidationInterface(&m_wallet);
}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index c03aec7f87..663836a955 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -15,7 +15,7 @@ struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~WalletTestingSetup();
- std::unique_ptr<CWallet> pwalletMain;
+ CWallet m_wallet;
};
#endif
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 9db5d63922..41348b50a4 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -36,7 +36,7 @@ typedef std::set<CInputCoin> CoinSet;
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
-static const CWallet testWallet;
+static const CWallet testWallet("dummy", CWalletDBWrapper::CreateDummy());
static std::vector<COutput> vCoins;
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
// and new block files.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -397,7 +397,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
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
// after.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
vpwallets.insert(vpwallets.begin(), &wallet);
UniValue keys;
keys.setArray();
@@ -471,7 +471,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
LOCK(wallet.cs_wallet);
wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
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
// were scanned, and no prior blocks were scanned.
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
JSONRPCRequest request;
request.params.setArray();
@@ -516,7 +516,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
- CWallet wallet;
+ CWallet wallet("dummy", CWalletDBWrapper::CreateDummy());
CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
LOCK2(cs_main, wallet.cs_wallet);
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.
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
{
- CWallet wallet;
-
// 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.
- 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.
- 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.
- 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
// 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
// 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.
SetMockTime(0);
@@ -591,12 +589,12 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
{
CTxDestination dest = CKeyID();
- LOCK(pwalletMain->cs_wallet);
- pwalletMain->AddDestData(dest, "misc", "val_misc");
- pwalletMain->AddDestData(dest, "rr0", "val_rr0");
- pwalletMain->AddDestData(dest, "rr1", "val_rr1");
+ LOCK(m_wallet.cs_wallet);
+ m_wallet.AddDestData(dest, "misc", "val_misc");
+ m_wallet.AddDestData(dest, "rr0", "val_rr0");
+ 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[0], "val_rr0");
BOOST_CHECK_EQUAL(values[1], "val_rr1");
@@ -608,10 +606,9 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- ::bitdb.MakeMock();
g_address_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;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
@@ -623,8 +620,6 @@ public:
~ListCoinsTestingSetup()
{
wallet.reset();
- ::bitdb.Flush(true);
- ::bitdb.Reset();
}
CWalletTx& AddTx(CRecipient recipient)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index bb7be2df33..b1e2181ec7 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -3908,16 +3908,17 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
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
std::vector<CWalletTx> vWtx;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
- std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(std::move(dbw));
+ std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, CWalletDBWrapper::Create(path));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@@ -3929,8 +3930,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
- std::unique_ptr<CWalletDBWrapper> dbw(new CWalletDBWrapper(&bitdb, walletFile));
- CWallet *walletInstance = new CWallet(std::move(dbw));
+ CWallet *walletInstance = new CWallet(name, CWalletDBWrapper::Create(path));
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 4db45f16ef..76a411d81b 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -15,6 +15,7 @@
#include <validationinterface.h>
#include <script/ismine.h>
#include <script/sign.h>
+#include <util.h>
#include <wallet/crypter.h>
#include <wallet/walletdb.h>
#include <wallet/rpcwallet.h>
@@ -737,6 +738,14 @@ private:
*/
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;
/**
@@ -768,14 +777,7 @@ public:
/** Get a name for this wallet for logging/debugging purposes.
*/
- std::string GetName() const
- {
- if (dbw) {
- return dbw->GetName();
- } else {
- return "dummy";
- }
- }
+ const std::string& GetName() const { return m_name; }
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
@@ -789,14 +791,8 @@ public:
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
- // Create wallet with dummy database handle
- CWallet(): dbw(new CWalletDBWrapper())
- {
- SetNull();
- }
-
- // Create wallet with passed-in database handle
- explicit CWallet(std::unique_ptr<CWalletDBWrapper> dbw_in) : dbw(std::move(dbw_in))
+ /** Construct wallet with specified name and database implementation. */
+ CWallet(std::string name, std::unique_ptr<CWalletDBWrapper> dbw) : m_name(std::move(name)), dbw(std::move(dbw))
{
SetNull();
}
@@ -1116,7 +1112,7 @@ public:
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 */
- static CWallet* CreateWalletFromFile(const std::string walletFile);
+ static CWallet* CreateWalletFromFile(const std::string& name, const fs::path& path);
/**
* Wallet post-init setup
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index dd6835a06f..46e4c12a7a 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -771,16 +771,16 @@ void MaybeCompactWalletDB()
//
// 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
// 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)
@@ -806,14 +806,14 @@ bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDa
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)
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 3691cfcb57..7d754c7284 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -218,17 +218,17 @@ public:
DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
/* 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 */
- 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 */
static bool RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue);
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
/* 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 */
- 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)
bool WriteHDChain(const CHDChain& chain);