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:
Wladimir J. van der Laan 2017-06-12 13:02:37 +02:00
commit 177433ad22
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
12 changed files with 197 additions and 166 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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)

View file

@ -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)
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&);

View file

@ -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)

View file

@ -8,6 +8,8 @@
#include "wallet/db.h"
#include "wallet/wallet.h"
CWallet *pwalletMain;
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
TestingSetup(chainName)
{

View file

@ -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

View file

@ -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)..."));
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"));
}
std::string strError;
if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError))
if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) {
return InitError(strError);
}
if (GetBoolArg("-salvagewallet", false))
{
if (GetBoolArg("-salvagewallet", false)) {
// Recover readable keypairs:
CWallet dummyWallet;
if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter))
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())
if (!strWarning.empty()) {
InitWarning(strWarning);
if (!dbV)
{
}
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,24 +3933,17 @@ 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;
}
pwalletMain = pwallet;
vpwallets.push_back(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))

View file

@ -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;

View file

@ -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 (dbh.nLastSeen != nUpdateCounter) {
dbh.nLastSeen = nUpdateCounter;
dbh.nLastWalletUpdate = GetTime();
}
if (nLastFlushed != CWalletDB::GetUpdateCounter() && GetTime() - nLastWalletUpdate >= 2)
{
if (CDB::PeriodicFlush(pwalletMain->GetDBHandle())) {
nLastFlushed = CWalletDB::GetUpdateCounter();
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()

View file

@ -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&);