Merge #8694: Basic multiwallet support
c237bd7
wallet: Update formatting (Luke Dashjr)9cbe8c8
wallet: Forbid -salvagewallet, -zapwallettxes, and -upgradewallet with multiple wallets (Luke Dashjr)a2a5f3f
wallet: Base backup filenames on original wallet filename (Luke Dashjr)b823a4c
wallet: Include actual backup filename in recovery warning message (Luke Dashjr)84dcb45
Bugfix: wallet: Fix warningStr, errorStr argument order (Luke Dashjr)008c360
Wallet: Move multiwallet sanity checks to CWallet::Verify, and do other checks on all wallets (Luke Dashjr)0f08575
Wallet: Support loading multiple wallets if -wallet used more than once (Luke Dashjr)b124cf0
Wallet: Replace pwalletMain with a vector of wallet pointers (Luke Dashjr)19b3648
CWalletDB: Store the update counter per wallet (Luke Dashjr)74e8738
Bugfix: ForceSetArg should replace entr(ies) in mapMultiArgs, not append (Luke Dashjr)23fb9ad
wallet: Move nAccountingEntryNumber from static/global to CWallet (Luke Dashjr)9d15d55
Bugfix: wallet: Increment "update counter" when modifying account stuff (Luke Dashjr)f28eb80
Bugfix: wallet: Increment "update counter" only after actually making the applicable db changes to avoid potential races (Luke Dashjr) Tree-SHA512: 23f5dda58477307bc07997010740f1dc729164cdddefd2f9a2c9c7a877111eb1516d3e2ad4f9b104621f0b7f17369c69fcef13d28b85cb6c01d35f09a8845f23
This commit is contained in:
commit
177433ad22
12 changed files with 197 additions and 166 deletions
21
src/init.cpp
21
src/init.cpp
|
@ -197,8 +197,9 @@ void Shutdown()
|
|||
StopRPC();
|
||||
StopHTTPServer();
|
||||
#ifdef ENABLE_WALLET
|
||||
if (pwalletMain)
|
||||
pwalletMain->Flush(false);
|
||||
for (CWalletRef pwallet : vpwallets) {
|
||||
pwallet->Flush(false);
|
||||
}
|
||||
#endif
|
||||
MapPort(false);
|
||||
UnregisterValidationInterface(peerLogic.get());
|
||||
|
@ -238,8 +239,9 @@ void Shutdown()
|
|||
pblocktree = NULL;
|
||||
}
|
||||
#ifdef ENABLE_WALLET
|
||||
if (pwalletMain)
|
||||
pwalletMain->Flush(true);
|
||||
for (CWalletRef pwallet : vpwallets) {
|
||||
pwallet->Flush(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_ZMQ
|
||||
|
@ -259,8 +261,10 @@ void Shutdown()
|
|||
#endif
|
||||
UnregisterAllValidationInterfaces();
|
||||
#ifdef ENABLE_WALLET
|
||||
delete pwalletMain;
|
||||
pwalletMain = NULL;
|
||||
for (CWalletRef pwallet : vpwallets) {
|
||||
delete pwallet;
|
||||
}
|
||||
vpwallets.clear();
|
||||
#endif
|
||||
globalVerifyHandle.reset();
|
||||
ECC_Stop();
|
||||
|
@ -1672,8 +1676,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||
uiInterface.InitMessage(_("Done loading"));
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
if (pwalletMain)
|
||||
pwalletMain->postInitProcess(scheduler);
|
||||
for (CWalletRef pwallet : vpwallets) {
|
||||
pwallet->postInitProcess(scheduler);
|
||||
}
|
||||
#endif
|
||||
|
||||
return !fRequestShutdown;
|
||||
|
|
|
@ -474,9 +474,10 @@ void BitcoinApplication::initializeResult(bool success)
|
|||
window->setClientModel(clientModel);
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
if(pwalletMain)
|
||||
// TODO: Expose secondary wallets
|
||||
if (!vpwallets.empty())
|
||||
{
|
||||
walletModel = new WalletModel(platformStyle, pwalletMain, optionsModel);
|
||||
walletModel = new WalletModel(platformStyle, vpwallets[0], optionsModel);
|
||||
|
||||
window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel);
|
||||
window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET);
|
||||
|
|
|
@ -474,6 +474,7 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV
|
|||
{
|
||||
LOCK(cs_args);
|
||||
mapArgs[strArg] = strValue;
|
||||
mapMultiArgs[strArg].clear();
|
||||
mapMultiArgs[strArg].push_back(strValue);
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ void CDBEnv::MakeMock()
|
|||
fMockDb = true;
|
||||
}
|
||||
|
||||
CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile))
|
||||
CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename)
|
||||
{
|
||||
LOCK(cs_db);
|
||||
assert(mapFileUseCount.count(strFile) == 0);
|
||||
|
@ -155,21 +155,21 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFu
|
|||
return RECOVER_FAIL;
|
||||
|
||||
// Try to recover:
|
||||
bool fRecovered = (*recoverFunc)(strFile);
|
||||
bool fRecovered = (*recoverFunc)(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))
|
||||
bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
|
||||
{
|
||||
// Recovery procedure:
|
||||
// move wallet file to wallet.timestamp.bak
|
||||
// move wallet file to walletfilename.timestamp.bak
|
||||
// Call Salvage with fAggressive=true to
|
||||
// get as much data as possible.
|
||||
// Rewrite salvaged data to fresh wallet file
|
||||
// Set -rescan so any missing transactions will be
|
||||
// found.
|
||||
int64_t now = GetTime();
|
||||
std::string newFilename = strprintf("wallet.%d.bak", now);
|
||||
newFilename = strprintf("%s.%d.bak", filename, now);
|
||||
|
||||
int result = bitdb.dbenv->dbrename(NULL, filename.c_str(), NULL,
|
||||
newFilename.c_str(), DB_AUTO_COMMIT);
|
||||
|
@ -259,18 +259,19 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataD
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
|
||||
bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
|
||||
{
|
||||
if (fs::exists(dataDir / walletFile))
|
||||
{
|
||||
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc);
|
||||
std::string backup_filename;
|
||||
CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename);
|
||||
if (r == CDBEnv::RECOVER_OK)
|
||||
{
|
||||
warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
|
||||
" Original %s saved as %s in %s; if"
|
||||
" your balance or transactions are incorrect you should"
|
||||
" restore from a backup."),
|
||||
walletFile, "wallet.{timestamp}.bak", dataDir);
|
||||
walletFile, backup_filename, dataDir);
|
||||
}
|
||||
if (r == CDBEnv::RECOVER_FAIL)
|
||||
{
|
||||
|
@ -432,6 +433,11 @@ void CDB::Flush()
|
|||
env->dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
|
||||
}
|
||||
|
||||
void CWalletDBWrapper::IncrementUpdateCounter()
|
||||
{
|
||||
++nUpdateCounter;
|
||||
}
|
||||
|
||||
void CDB::Close()
|
||||
{
|
||||
if (!pdb)
|
||||
|
|
|
@ -55,7 +55,8 @@ public:
|
|||
enum VerifyResult { VERIFY_OK,
|
||||
RECOVER_OK,
|
||||
RECOVER_FAIL };
|
||||
VerifyResult Verify(const std::string& strFile, bool (*recoverFunc)(const std::string& strFile));
|
||||
typedef bool (*recoverFunc_type)(const std::string& strFile, 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.
|
||||
* fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
|
||||
|
@ -93,13 +94,13 @@ class CWalletDBWrapper
|
|||
friend class CDB;
|
||||
public:
|
||||
/** Create dummy DB handle */
|
||||
CWalletDBWrapper(): env(nullptr)
|
||||
CWalletDBWrapper() : nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/** Create DB handle to real database */
|
||||
CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in):
|
||||
env(env_in), strFile(strFile_in)
|
||||
CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) :
|
||||
nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -119,6 +120,13 @@ public:
|
|||
*/
|
||||
void Flush(bool shutdown);
|
||||
|
||||
void IncrementUpdateCounter();
|
||||
|
||||
std::atomic<unsigned int> nUpdateCounter;
|
||||
unsigned int nLastSeen;
|
||||
unsigned int nLastFlushed;
|
||||
int64_t nLastWalletUpdate;
|
||||
|
||||
private:
|
||||
/** BerkeleyDB specific */
|
||||
CDBEnv *env;
|
||||
|
@ -149,7 +157,7 @@ public:
|
|||
|
||||
void Flush();
|
||||
void Close();
|
||||
static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue));
|
||||
static bool Recover(const std::string& filename, 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 */
|
||||
|
@ -157,7 +165,7 @@ public:
|
|||
/* verifies the database environment */
|
||||
static bool VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr);
|
||||
/* verifies the database file */
|
||||
static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile));
|
||||
static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc);
|
||||
|
||||
private:
|
||||
CDB(const CDB&);
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
|
||||
CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
|
||||
{
|
||||
return pwalletMain;
|
||||
// TODO: Some way to access secondary wallets
|
||||
return vpwallets.empty() ? nullptr : vpwallets[0];
|
||||
}
|
||||
|
||||
std::string HelpRequiringPassphrase(CWallet * const pwallet)
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "wallet/db.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
CWallet *pwalletMain;
|
||||
|
||||
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
||||
TestingSetup(chainName)
|
||||
{
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <univalue.h>
|
||||
|
||||
extern CWallet* pwalletMain;
|
||||
|
||||
extern UniValue importmulti(const JSONRPCRequest& request);
|
||||
extern UniValue dumpwallet(const JSONRPCRequest& request);
|
||||
extern UniValue importwallet(const JSONRPCRequest& request);
|
||||
|
@ -401,8 +403,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||
// after.
|
||||
{
|
||||
CWallet wallet;
|
||||
CWallet *backup = ::pwalletMain;
|
||||
::pwalletMain = &wallet;
|
||||
vpwallets.insert(vpwallets.begin(), &wallet);
|
||||
UniValue keys;
|
||||
keys.setArray();
|
||||
UniValue key;
|
||||
|
@ -433,7 +434,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
|
||||
"options).\"}},{\"success\":true}]",
|
||||
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
|
||||
::pwalletMain = backup;
|
||||
vpwallets.erase(vpwallets.begin());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,7 +444,6 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||
// than or equal to key birthday.
|
||||
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||
{
|
||||
CWallet *pwalletMainBackup = ::pwalletMain;
|
||||
LOCK(cs_main);
|
||||
|
||||
// Create two blocks with same timestamp to verify that importwallet rescan
|
||||
|
@ -469,7 +469,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
|||
JSONRPCRequest request;
|
||||
request.params.setArray();
|
||||
request.params.push_back("wallet.backup");
|
||||
::pwalletMain = &wallet;
|
||||
vpwallets.insert(vpwallets.begin(), &wallet);
|
||||
::dumpwallet(request);
|
||||
}
|
||||
|
||||
|
@ -481,7 +481,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
|||
JSONRPCRequest request;
|
||||
request.params.setArray();
|
||||
request.params.push_back("wallet.backup");
|
||||
::pwalletMain = &wallet;
|
||||
vpwallets[0] = &wallet;
|
||||
::importwallet(request);
|
||||
|
||||
BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3);
|
||||
|
@ -494,7 +494,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
|||
}
|
||||
|
||||
SetMockTime(0);
|
||||
::pwalletMain = pwalletMainBackup;
|
||||
vpwallets.erase(vpwallets.begin());
|
||||
}
|
||||
|
||||
// Check that GetImmatureCredit() returns a newly calculated value instead of
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
CWallet* pwalletMain = NULL;
|
||||
std::vector<CWalletRef> vpwallets;
|
||||
/** Transaction fee set by the user */
|
||||
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
|
||||
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
|
||||
|
@ -440,30 +440,40 @@ bool CWallet::Verify()
|
|||
if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
|
||||
return true;
|
||||
|
||||
uiInterface.InitMessage(_("Verifying wallet..."));
|
||||
std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
|
||||
uiInterface.InitMessage(_("Verifying wallet(s)..."));
|
||||
|
||||
std::string strError;
|
||||
if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError))
|
||||
return InitError(strError);
|
||||
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
||||
if (boost::filesystem::path(walletFile).filename() != walletFile) {
|
||||
return InitError(_("-wallet parameter must only specify a filename (not a path)"));
|
||||
} else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
|
||||
return InitError(_("Invalid characters in -wallet filename"));
|
||||
}
|
||||
|
||||
if (GetBoolArg("-salvagewallet", false))
|
||||
{
|
||||
// Recover readable keypairs:
|
||||
CWallet dummyWallet;
|
||||
if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter))
|
||||
std::string strError;
|
||||
if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) {
|
||||
return InitError(strError);
|
||||
}
|
||||
|
||||
if (GetBoolArg("-salvagewallet", false)) {
|
||||
// Recover readable keypairs:
|
||||
CWallet dummyWallet;
|
||||
std::string backup_filename;
|
||||
if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string strWarning;
|
||||
bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError);
|
||||
if (!strWarning.empty()) {
|
||||
InitWarning(strWarning);
|
||||
}
|
||||
if (!dbV) {
|
||||
InitError(strError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string strWarning;
|
||||
bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError);
|
||||
if (!strWarning.empty())
|
||||
InitWarning(strWarning);
|
||||
if (!dbV)
|
||||
{
|
||||
InitError(strError);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2867,8 +2877,9 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry)
|
|||
|
||||
bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwalletdb)
|
||||
{
|
||||
if (!pwalletdb->WriteAccountingEntry_Backend(acentry))
|
||||
if (!pwalletdb->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
laccentries.push_back(acentry);
|
||||
CAccountingEntry & entry = laccentries.back();
|
||||
|
@ -3880,7 +3891,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
|
|||
walletInstance->ScanForWalletTransactions(pindexRescan, true);
|
||||
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
|
||||
walletInstance->SetBestChain(chainActive.GetLocator());
|
||||
CWalletDB::IncrementUpdateCounter();
|
||||
walletInstance->dbw->IncrementUpdateCounter();
|
||||
|
||||
// Restore wallet transaction metadata after -zapwallettxes=1
|
||||
if (GetBoolArg("-zapwallettxes", false) && GetArg("-zapwallettxes", "1") != "2")
|
||||
|
@ -3922,25 +3933,18 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
|
|||
bool CWallet::InitLoadWallet()
|
||||
{
|
||||
if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
||||
pwalletMain = NULL;
|
||||
LogPrintf("Wallet disabled!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
|
||||
|
||||
if (boost::filesystem::path(walletFile).filename() != walletFile) {
|
||||
return InitError(_("-wallet parameter must only specify a filename (not a path)"));
|
||||
} else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {
|
||||
return InitError(_("Invalid characters in -wallet filename"));
|
||||
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
||||
CWallet * const pwallet = CreateWalletFromFile(walletFile);
|
||||
if (!pwallet) {
|
||||
return false;
|
||||
}
|
||||
vpwallets.push_back(pwallet);
|
||||
}
|
||||
|
||||
CWallet * const pwallet = CreateWalletFromFile(walletFile);
|
||||
if (!pwallet) {
|
||||
return false;
|
||||
}
|
||||
pwalletMain = pwallet;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3960,6 +3964,9 @@ void CWallet::postInitProcess(CScheduler& scheduler)
|
|||
|
||||
bool CWallet::ParameterInteraction()
|
||||
{
|
||||
SoftSetArg("-wallet", DEFAULT_WALLET_DAT);
|
||||
const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
|
||||
|
||||
if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
|
||||
return true;
|
||||
|
||||
|
@ -3968,15 +3975,27 @@ bool CWallet::ParameterInteraction()
|
|||
}
|
||||
|
||||
if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) {
|
||||
if (is_multiwallet) {
|
||||
return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet"));
|
||||
}
|
||||
// Rewrite just private keys: rescan to find transactions
|
||||
LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
|
||||
}
|
||||
|
||||
// -zapwallettx implies a rescan
|
||||
if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) {
|
||||
if (is_multiwallet) {
|
||||
return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes"));
|
||||
}
|
||||
LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
|
||||
}
|
||||
|
||||
if (is_multiwallet) {
|
||||
if (GetBoolArg("-upgradewallet", false)) {
|
||||
return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet"));
|
||||
}
|
||||
}
|
||||
|
||||
if (GetBoolArg("-sysperms", false))
|
||||
return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
|
||||
if (GetArg("-prune", 0) && GetBoolArg("-rescan", false))
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
extern CWallet* pwalletMain;
|
||||
typedef CWallet* CWalletRef;
|
||||
extern std::vector<CWalletRef> vpwallets;
|
||||
|
||||
/**
|
||||
* Settings
|
||||
|
@ -782,6 +783,7 @@ public:
|
|||
nMasterKeyMaxID = 0;
|
||||
pwalletdbEncryption = NULL;
|
||||
nOrderPosNext = 0;
|
||||
nAccountingEntryNumber = 0;
|
||||
nNextResend = 0;
|
||||
nLastResend = 0;
|
||||
nTimeFirstKey = 0;
|
||||
|
@ -799,6 +801,7 @@ public:
|
|||
TxItems wtxOrdered;
|
||||
|
||||
int64_t nOrderPosNext;
|
||||
uint64_t nAccountingEntryNumber;
|
||||
std::map<uint256, int> mapRequestCount;
|
||||
|
||||
std::map<CTxDestination, CAddressBookData> mapAddressBook;
|
||||
|
|
|
@ -21,59 +21,47 @@
|
|||
#include <boost/foreach.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
static uint64_t nAccountingEntryNumber = 0;
|
||||
|
||||
static std::atomic<unsigned int> nWalletDBUpdateCounter;
|
||||
|
||||
//
|
||||
// CWalletDB
|
||||
//
|
||||
|
||||
bool CWalletDB::WriteName(const std::string& strAddress, const std::string& strName)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::make_pair(std::string("name"), strAddress), strName);
|
||||
return WriteIC(std::make_pair(std::string("name"), strAddress), strName);
|
||||
}
|
||||
|
||||
bool CWalletDB::EraseName(const std::string& strAddress)
|
||||
{
|
||||
// This should only be used for sending addresses, never for receiving addresses,
|
||||
// receiving addresses must always have an address book entry if they're not change return.
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Erase(std::make_pair(std::string("name"), strAddress));
|
||||
return EraseIC(std::make_pair(std::string("name"), strAddress));
|
||||
}
|
||||
|
||||
bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::make_pair(std::string("purpose"), strAddress), strPurpose);
|
||||
return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose);
|
||||
}
|
||||
|
||||
bool CWalletDB::ErasePurpose(const std::string& strPurpose)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Erase(std::make_pair(std::string("purpose"), strPurpose));
|
||||
return EraseIC(std::make_pair(std::string("purpose"), strPurpose));
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteTx(const CWalletTx& wtx)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
|
||||
return WriteIC(std::make_pair(std::string("tx"), wtx.GetHash()), wtx);
|
||||
}
|
||||
|
||||
bool CWalletDB::EraseTx(uint256 hash)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Erase(std::make_pair(std::string("tx"), hash));
|
||||
return EraseIC(std::make_pair(std::string("tx"), hash));
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
|
||||
if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
|
||||
keyMeta, false))
|
||||
if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// hash pubkey/privkey to accelerate wallet load
|
||||
std::vector<unsigned char> vchKey;
|
||||
|
@ -81,7 +69,7 @@ bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, c
|
|||
vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
|
||||
vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
|
||||
|
||||
return batch.Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
|
||||
return WriteIC(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
|
||||
|
@ -89,55 +77,53 @@ bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey,
|
|||
const CKeyMetadata &keyMeta)
|
||||
{
|
||||
const bool fEraseUnencryptedKey = true;
|
||||
nWalletDBUpdateCounter++;
|
||||
|
||||
if (!batch.Write(std::make_pair(std::string("keymeta"), vchPubKey),
|
||||
keyMeta))
|
||||
if (!WriteIC(std::make_pair(std::string("keymeta"), vchPubKey), keyMeta)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!batch.Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false))
|
||||
if (!WriteIC(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) {
|
||||
return false;
|
||||
}
|
||||
if (fEraseUnencryptedKey)
|
||||
{
|
||||
batch.Erase(std::make_pair(std::string("key"), vchPubKey));
|
||||
batch.Erase(std::make_pair(std::string("wkey"), vchPubKey));
|
||||
EraseIC(std::make_pair(std::string("key"), vchPubKey));
|
||||
EraseIC(std::make_pair(std::string("wkey"), vchPubKey));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
|
||||
return WriteIC(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
|
||||
return WriteIC(std::make_pair(std::string("cscript"), hash), *(const CScriptBase*)(&redeemScript), false);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
if (!batch.Write(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta))
|
||||
if (!WriteIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)), keyMeta)) {
|
||||
return false;
|
||||
return batch.Write(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
|
||||
}
|
||||
return WriteIC(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)), '1');
|
||||
}
|
||||
|
||||
bool CWalletDB::EraseWatchOnly(const CScript &dest)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
if (!batch.Erase(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest))))
|
||||
if (!EraseIC(std::make_pair(std::string("watchmeta"), *(const CScriptBase*)(&dest)))) {
|
||||
return false;
|
||||
return batch.Erase(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
|
||||
}
|
||||
return EraseIC(std::make_pair(std::string("watchs"), *(const CScriptBase*)(&dest)));
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
batch.Write(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
|
||||
return batch.Write(std::string("bestblock_nomerkle"), locator);
|
||||
WriteIC(std::string("bestblock"), CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
|
||||
return WriteIC(std::string("bestblock_nomerkle"), locator);
|
||||
}
|
||||
|
||||
bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
|
||||
|
@ -148,14 +134,12 @@ bool CWalletDB::ReadBestBlock(CBlockLocator& locator)
|
|||
|
||||
bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::string("orderposnext"), nOrderPosNext);
|
||||
return WriteIC(std::string("orderposnext"), nOrderPosNext);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::string("defaultkey"), vchPubKey);
|
||||
return WriteIC(std::string("defaultkey"), vchPubKey);
|
||||
}
|
||||
|
||||
bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
|
||||
|
@ -165,19 +149,17 @@ bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool)
|
|||
|
||||
bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::make_pair(std::string("pool"), nPool), keypool);
|
||||
return WriteIC(std::make_pair(std::string("pool"), nPool), keypool);
|
||||
}
|
||||
|
||||
bool CWalletDB::ErasePool(int64_t nPool)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Erase(std::make_pair(std::string("pool"), nPool));
|
||||
return EraseIC(std::make_pair(std::string("pool"), nPool));
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteMinVersion(int nVersion)
|
||||
{
|
||||
return batch.Write(std::string("minversion"), nVersion);
|
||||
return WriteIC(std::string("minversion"), nVersion);
|
||||
}
|
||||
|
||||
bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account)
|
||||
|
@ -188,17 +170,12 @@ bool CWalletDB::ReadAccount(const std::string& strAccount, CAccount& account)
|
|||
|
||||
bool CWalletDB::WriteAccount(const std::string& strAccount, const CAccount& account)
|
||||
{
|
||||
return batch.Write(std::make_pair(std::string("acc"), strAccount), account);
|
||||
return WriteIC(std::make_pair(std::string("acc"), strAccount), account);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry)
|
||||
{
|
||||
return batch.Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
|
||||
{
|
||||
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
|
||||
return WriteIC(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry);
|
||||
}
|
||||
|
||||
CAmount CWalletDB::GetAccountCreditDebit(const std::string& strAccount)
|
||||
|
@ -337,8 +314,9 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||
ssKey >> strAccount;
|
||||
uint64_t nNumber;
|
||||
ssKey >> nNumber;
|
||||
if (nNumber > nAccountingEntryNumber)
|
||||
nAccountingEntryNumber = nNumber;
|
||||
if (nNumber > pwallet->nAccountingEntryNumber) {
|
||||
pwallet->nAccountingEntryNumber = nNumber;
|
||||
}
|
||||
|
||||
if (!wss.fAnyUnordered)
|
||||
{
|
||||
|
@ -784,38 +762,39 @@ void MaybeCompactWalletDB()
|
|||
return;
|
||||
}
|
||||
|
||||
static unsigned int nLastSeen = CWalletDB::GetUpdateCounter();
|
||||
static unsigned int nLastFlushed = CWalletDB::GetUpdateCounter();
|
||||
static int64_t nLastWalletUpdate = GetTime();
|
||||
for (CWalletRef pwallet : vpwallets) {
|
||||
CWalletDBWrapper& dbh = pwallet->GetDBHandle();
|
||||
|
||||
if (nLastSeen != CWalletDB::GetUpdateCounter())
|
||||
{
|
||||
nLastSeen = CWalletDB::GetUpdateCounter();
|
||||
nLastWalletUpdate = GetTime();
|
||||
}
|
||||
unsigned int nUpdateCounter = dbh.nUpdateCounter;
|
||||
|
||||
if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2)
|
||||
{
|
||||
if (CDB::PeriodicFlush(pwalletMain->GetDBHandle())) {
|
||||
nLastFlushed = CWalletDB::GetUpdateCounter();
|
||||
if (dbh.nLastSeen != nUpdateCounter) {
|
||||
dbh.nLastSeen = nUpdateCounter;
|
||||
dbh.nLastWalletUpdate = GetTime();
|
||||
}
|
||||
|
||||
if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
|
||||
if (CDB::PeriodicFlush(dbh)) {
|
||||
dbh.nLastFlushed = nUpdateCounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fOneThread = false;
|
||||
}
|
||||
|
||||
//
|
||||
// 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))
|
||||
bool CWalletDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename)
|
||||
{
|
||||
return CDB::Recover(filename, callbackDataIn, recoverKVcallback);
|
||||
return CDB::Recover(filename, callbackDataIn, recoverKVcallback, out_backup_filename);
|
||||
}
|
||||
|
||||
bool CWalletDB::Recover(const std::string& filename)
|
||||
bool CWalletDB::Recover(const std::string& filename, std::string& out_backup_filename)
|
||||
{
|
||||
// recover without a key filter callback
|
||||
// results in recovering all record types
|
||||
return CWalletDB::Recover(filename, NULL, NULL);
|
||||
return CWalletDB::Recover(filename, NULL, NULL, out_backup_filename);
|
||||
}
|
||||
|
||||
bool CWalletDB::RecoverKeysOnlyFilter(void *callbackData, CDataStream ssKey, CDataStream ssValue)
|
||||
|
@ -848,36 +827,23 @@ bool CWalletDB::VerifyEnvironment(const std::string& walletFile, const fs::path&
|
|||
|
||||
bool CWalletDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr)
|
||||
{
|
||||
return CDB::VerifyDatabaseFile(walletFile, dataDir, errorStr, warningStr, CWalletDB::Recover);
|
||||
return CDB::VerifyDatabaseFile(walletFile, dataDir, warningStr, errorStr, CWalletDB::Recover);
|
||||
}
|
||||
|
||||
bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
|
||||
return WriteIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value);
|
||||
}
|
||||
|
||||
bool CWalletDB::EraseDestData(const std::string &address, const std::string &key)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
|
||||
return EraseIC(std::make_pair(std::string("destdata"), std::make_pair(address, key)));
|
||||
}
|
||||
|
||||
|
||||
bool CWalletDB::WriteHDChain(const CHDChain& chain)
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
return batch.Write(std::string("hdchain"), chain);
|
||||
}
|
||||
|
||||
void CWalletDB::IncrementUpdateCounter()
|
||||
{
|
||||
nWalletDBUpdateCounter++;
|
||||
}
|
||||
|
||||
unsigned int CWalletDB::GetUpdateCounter()
|
||||
{
|
||||
return nWalletDBUpdateCounter;
|
||||
return WriteIC(std::string("hdchain"), chain);
|
||||
}
|
||||
|
||||
bool CWalletDB::TxnBegin()
|
||||
|
|
|
@ -140,9 +140,31 @@ public:
|
|||
*/
|
||||
class CWalletDB
|
||||
{
|
||||
private:
|
||||
template <typename K, typename T>
|
||||
bool WriteIC(const K& key, const T& value, bool fOverwrite = true)
|
||||
{
|
||||
if (!batch.Write(key, value, fOverwrite)) {
|
||||
return false;
|
||||
}
|
||||
m_dbw.IncrementUpdateCounter();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
bool EraseIC(const K& key)
|
||||
{
|
||||
if (!batch.Erase(key)) {
|
||||
return false;
|
||||
}
|
||||
m_dbw.IncrementUpdateCounter();
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
CWalletDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool _fFlushOnClose = true) :
|
||||
batch(dbw, pszMode, _fFlushOnClose)
|
||||
batch(dbw, pszMode, _fFlushOnClose),
|
||||
m_dbw(dbw)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -180,7 +202,6 @@ public:
|
|||
/// This writes directly to the database, and will not update the CWallet's cached accounting entries!
|
||||
/// Use wallet.AddAccountingEntry instead, to write *and* update its caches.
|
||||
bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry);
|
||||
bool WriteAccountingEntry_Backend(const CAccountingEntry& acentry);
|
||||
bool ReadAccount(const std::string& strAccount, CAccount& account);
|
||||
bool WriteAccount(const std::string& strAccount, const CAccount& account);
|
||||
|
||||
|
@ -197,9 +218,9 @@ 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));
|
||||
static bool Recover(const std::string& filename, 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);
|
||||
static bool Recover(const std::string& filename, 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 */
|
||||
|
@ -212,9 +233,6 @@ public:
|
|||
//! write the hdchain model (external chain child index counter)
|
||||
bool WriteHDChain(const CHDChain& chain);
|
||||
|
||||
static void IncrementUpdateCounter();
|
||||
static unsigned int GetUpdateCounter();
|
||||
|
||||
//! Begin a new transaction
|
||||
bool TxnBegin();
|
||||
//! Commit current transaction
|
||||
|
@ -227,6 +245,7 @@ public:
|
|||
bool WriteVersion(int nVersion);
|
||||
private:
|
||||
CDB batch;
|
||||
CWalletDBWrapper& m_dbw;
|
||||
|
||||
CWalletDB(const CWalletDB&);
|
||||
void operator=(const CWalletDB&);
|
||||
|
|
Loading…
Reference in a new issue