From 754434a32773944bf1a689df82d900ec83547da3 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 15 Jun 2017 13:49:38 -0400 Subject: [PATCH 01/19] [wallet] fix comment for CWallet::Verify() --- src/wallet/wallet.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4f558adc772..c4143b7f652 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1053,7 +1053,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - //! Verify the wallet database and perform salvage if required + //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. + // This function will perform salvage on the wallet if requested, as long as only one wallet is + // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). static bool Verify(); /** From 5d18ee0f1c10561f410ce62aa20372ab109ab54a Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 15 Jun 2017 11:45:08 -0400 Subject: [PATCH 02/19] [wallet] [rpc] print wallet name in getwalletinfo --- src/wallet/rpcwallet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e0c7ab9f0f9..e4dde5d561b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2390,6 +2390,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) "Returns an object containing various wallet state info.\n" "\nResult:\n" "{\n" + " \"walletname\": xxxxx, (string) the wallet name\n" " \"walletversion\": xxxxx, (numeric) the wallet version\n" " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" @@ -2412,6 +2413,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) UniValue obj(UniValue::VOBJ); size_t kpExternalSize = pwallet->KeypoolCountExternalKeys(); + obj.push_back(Pair("walletname", pwallet->GetName())); obj.push_back(Pair("walletversion", pwallet->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance()))); obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()))); From 74882ca0ebc070ba80304e910af4562659e44a1f Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 27 Jun 2017 09:46:55 -0400 Subject: [PATCH 03/19] [wallet] [rpc] Add listwallets RPC This commit adds a listwallets RPC, which lists the names of the currently loaded wallets. This command intentionally shows no information about the wallet other then the name. Information on individual wallets can be obtained using the getwalletinfo RPC. --- src/wallet/rpcwallet.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e4dde5d561b..4c84a48a7e4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2434,6 +2434,41 @@ UniValue getwalletinfo(const JSONRPCRequest& request) return obj; } +UniValue listwallets(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 0) + throw std::runtime_error( + "listwallets\n" + "Returns a list of currently loaded wallets.\n" + "For full information on the wallet, use \"getwalletinfo\"\n" + "\nResult:\n" + "[\n" + " \"walletname\": xxxxx, (string) the wallet name\n" + " ...\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("listwallets", "") + + HelpExampleRpc("listwallets", "") + ); + + LOCK(cs_main); + + UniValue obj(UniValue::VARR); + + for (CWalletRef pwallet : vpwallets) { + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + LOCK(pwallet->cs_wallet); + + obj.push_back(pwallet->GetName()); + } + + return obj; +} + UniValue resendwallettransactions(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -3021,6 +3056,7 @@ static const CRPCCommand commands[] = { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} }, { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, + { "wallet", "listwallets", &listwallets, true, {} }, { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, From a952c04dded9057318379d68d0a2c37981457896 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Thu, 15 Jun 2017 09:05:32 -0400 Subject: [PATCH 04/19] [wallet] [tests] multiwallet test --- test/functional/multiwallet.py | 34 ++++++++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 35 insertions(+) create mode 100755 test/functional/multiwallet.py diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py new file mode 100755 index 00000000000..d855fd987bf --- /dev/null +++ b/test/functional/multiwallet.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test multiwallet + +Verify that a bitcoind node can load multiple wallet files +""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class Multiwallet(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 1 + self.extra_args = [["-wallet=wallet0.dat"]] + + def run_test(self): + wallet0_name = self.nodes[0].getwalletinfo()["walletname"] + + # stop-start the node with a new wallet + self.stop_nodes() + self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-wallet=wallet1.dat"]) + wallet1_name = self.nodes[0].getwalletinfo()["walletname"] + + # Stop the node and load both wallets + self.stop_nodes() + self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-wallet=wallet0.dat", "-wallet=wallet1.dat"]) + + # Assert that node0 has loaded both wallets correctly + assert_equal(self.nodes[0].listwallets(), [wallet0_name, wallet1_name]) + +if __name__ == '__main__': + Multiwallet().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 54f625514bd..6a7bc160295 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -88,6 +88,7 @@ 'mempool_spendcoinbase.py', 'mempool_reorg.py', 'mempool_persist.py', + 'multiwallet.py', 'httpbasics.py', 'multi_rpc.py', 'proxy_test.py', From 851cf9f9b420dc12c5bcb8d5a2b51adc30081b40 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 14:20:36 +0100 Subject: [PATCH 05/19] [wallet] Add walletinit.cpp This commit adds a new walletinit translation unit and moves GetWalletHelpString() to walletinit from a static member function of CWallet. --- src/Makefile.am | 2 ++ src/init.cpp | 3 ++- src/wallet/wallet.cpp | 37 ------------------------------------- src/wallet/wallet.h | 3 --- src/wallet/walletinit.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/wallet/walletinit.h | 6 ++++++ 6 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 src/wallet/walletinit.cpp create mode 100644 src/wallet/walletinit.h diff --git a/src/Makefile.am b/src/Makefile.am index 8ecd3918045..7e957d67d46 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -164,6 +164,7 @@ BITCOIN_CORE_H = \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ + wallet/walletinit.h \ warnings.h \ zmq/zmqabstractnotifier.h \ zmq/zmqconfig.h\ @@ -242,6 +243,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ + wallet/walletinit.cpp \ $(BITCOIN_CORE_H) # crypto primitives library diff --git a/src/init.cpp b/src/init.cpp index 672ef77e80f..befffc4ac10 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -44,6 +44,7 @@ #include "validationinterface.h" #ifdef ENABLE_WALLET #include "wallet/wallet.h" +#include "wallet/walletinit.h" #endif #include "warnings.h" #include @@ -404,7 +405,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-maxuploadtarget=", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); #ifdef ENABLE_WALLET - strUsage += CWallet::GetWalletHelpString(showDebug); + strUsage += GetWalletHelpString(showDebug); #endif #if ENABLE_ZMQ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0d1a86dd244..19ddc468892 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3731,43 +3731,6 @@ std::vector CWallet::GetDestValues(const std::string& prefix) const return values; } -std::string CWallet::GetWalletHelpString(bool showDebug) -{ - std::string strUsage = HelpMessageGroup(_("Wallet options:")); - strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); - strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); - strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); - strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); - strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); - strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); - strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); - strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); - strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); - strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); - strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + - " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); - - if (showDebug) - { - strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); - - strUsage += HelpMessageOpt("-dblogsize=", strprintf("Flush wallet database activity from memory to disk log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); - strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); - strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); - strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); - } - - return strUsage; -} - CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) { // needed to restore wallet transaction meta data after -zapwallettxes diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index c4143b7f652..408f9a9116b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1094,9 +1094,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ bool MarkReplaced(const uint256& originalHash, const uint256& newHash); - /* Returns the wallets help message */ - static std::string GetWalletHelpString(bool showDebug); - /* 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 bool InitLoadWallet(); diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp new file mode 100644 index 00000000000..442f790bd6a --- /dev/null +++ b/src/wallet/walletinit.cpp @@ -0,0 +1,41 @@ +#include "wallet/walletinit.h" +#include "util.h" +#include "utilmoneystr.h" + +std::string GetWalletHelpString(bool showDebug) +{ + std::string strUsage = HelpMessageGroup(_("Wallet options:")); + strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); + strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); + strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); + strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); + strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); + strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); + strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); + strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); + strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); + strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); + strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); + + if (showDebug) + { + strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); + + strUsage += HelpMessageOpt("-dblogsize=", strprintf("Flush wallet database activity from memory to disk log every megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); + strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); + strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); + } + + return strUsage; +} + diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h new file mode 100644 index 00000000000..412f3522508 --- /dev/null +++ b/src/wallet/walletinit.h @@ -0,0 +1,6 @@ +#include "wallet/wallet.h" + +#include + +//!Returns the wallets help message +std::string GetWalletHelpString(bool showDebug); From a863dc3ec3540171eec27c1cb86a1eb70e33efdf Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 14:37:10 +0100 Subject: [PATCH 06/19] [wallet] move WalletParameterInteraction() to walletinit.cpp --- src/init.cpp | 3 +- src/wallet/wallet.cpp | 100 -------------------------------------------- src/wallet/wallet.h | 3 -- src/wallet/walletinit.cpp | 104 +++++++++++++++++++++++++++++++++++++++++++++- src/wallet/walletinit.h | 4 ++ 5 files changed, 108 insertions(+), 106 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index befffc4ac10..19fc2ba96af 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1053,8 +1053,7 @@ bool AppInitParameterInteraction() nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp); #ifdef ENABLE_WALLET - if (!CWallet::ParameterInteraction()) - return false; + if (!WalletParameterInteraction()) return false; #endif fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 19ddc468892..6ee5e528864 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3953,106 +3953,6 @@ 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; - - if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { - LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); - } - - 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= -> 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)) - return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); - - if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-minrelaytxfee") + " " + - _("The wallet will avoid paying less than the minimum relay fee.")); - - if (IsArgSet("-mintxfee")) - { - CAmount n = 0; - if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) - return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", ""))); - if (n > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-mintxfee") + " " + - _("This is the minimum transaction fee you pay on every transaction.")); - CWallet::minTxFee = CFeeRate(n); - } - if (IsArgSet("-fallbackfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), GetArg("-fallbackfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-fallbackfee") + " " + - _("This is the transaction fee you may pay when fee estimates are not available.")); - CWallet::fallbackFee = CFeeRate(nFeePerK); - } - if (IsArgSet("-paytxfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) - return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-paytxfee") + " " + - _("This is the transaction fee you will pay if you send a transaction.")); - - payTxFee = CFeeRate(nFeePerK, 1000); - if (payTxFee < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), - GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); - } - } - if (IsArgSet("-maxtxfee")) - { - CAmount nMaxFee = 0; - if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) - return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", ""))); - if (nMaxFee > HIGH_MAX_TX_FEE) - InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); - maxTxFee = nMaxFee; - if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), - GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); - } - } - nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); - - return true; -} - bool CWallet::BackupWallet(const std::string& strDest) { return dbw->Backup(strDest); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 408f9a9116b..51e16f10b5d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1104,9 +1104,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ void postInitProcess(CScheduler& scheduler); - /* Wallets parameter interaction */ - static bool ParameterInteraction(); - bool BackupWallet(const std::string& strDest); /* Set the HD chain model (chain child index counters) */ diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index 442f790bd6a..ba2fc1be54a 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -1,6 +1,8 @@ -#include "wallet/walletinit.h" +#include "net.h" #include "util.h" #include "utilmoneystr.h" +#include "validation.h" +#include "wallet/walletinit.h" std::string GetWalletHelpString(bool showDebug) { @@ -39,3 +41,103 @@ std::string GetWalletHelpString(bool showDebug) return strUsage; } +bool WalletParameterInteraction() +{ + SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) { + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); + } + + 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= -> 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)) + return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); + + if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-minrelaytxfee") + " " + + _("The wallet will avoid paying less than the minimum relay fee.")); + + if (IsArgSet("-mintxfee")) + { + CAmount n = 0; + if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", ""))); + if (n > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on every transaction.")); + CWallet::minTxFee = CFeeRate(n); + } + if (IsArgSet("-fallbackfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -fallbackfee=: '%s'"), GetArg("-fallbackfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee estimates are not available.")); + CWallet::fallbackFee = CFeeRate(nFeePerK); + } + if (IsArgSet("-paytxfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK)) + return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you send a transaction.")); + + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s' (must be at least %s)"), + GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (IsArgSet("-maxtxfee")) + { + CAmount nMaxFee = 0; + if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee)) + return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", ""))); + if (nMaxFee > HIGH_MAX_TX_FEE) + InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + maxTxFee = nMaxFee; + if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -maxtxfee=: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + } + } + nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + + return true; +} + diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h index 412f3522508..057edb65eab 100644 --- a/src/wallet/walletinit.h +++ b/src/wallet/walletinit.h @@ -4,3 +4,7 @@ //!Returns the wallets help message std::string GetWalletHelpString(bool showDebug); + +//!Wallet parameter interaction +bool WalletParameterInteraction(); + From 3954d7900f69274abccfbe00c6268f7fcb46b3e6 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 14:49:48 +0100 Subject: [PATCH 07/19] [wallet] move VerifyWallets() to walletinit.cpp --- src/init.cpp | 3 +-- src/wallet/wallet.cpp | 42 ------------------------------------------ src/wallet/wallet.h | 5 ----- src/wallet/walletinit.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ src/wallet/walletinit.h | 6 ++++++ 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 19fc2ba96af..cbe6e3443c7 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1219,8 +1219,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 5: verify wallet database integrity #ifdef ENABLE_WALLET - if (!CWallet::Verify()) - return false; + if (!VerifyWallets()) return false; #endif // ********************************************************* Step 6: network initialization // Note that we absolutely cannot open any actual connections diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6ee5e528864..3315ec4ebec 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -438,48 +438,6 @@ void CWallet::Flush(bool shutdown) dbw->Flush(shutdown); } -bool CWallet::Verify() -{ - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) - return true; - - 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)) { - 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; - } - } - - return true; -} - void CWallet::SyncMetaData(std::pair range) { // We want all the wallet transactions in range to have the same metadata as diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 51e16f10b5d..53be4d8b9f4 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1053,11 +1053,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. - // This function will perform salvage on the wallet if requested, as long as only one wallet is - // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - static bool Verify(); - /** * Address book entry changed. * @note called with lock cs_wallet held. diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index ba2fc1be54a..63e2aba5dc7 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -141,3 +141,45 @@ bool WalletParameterInteraction() return true; } +bool VerifyWallets() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + 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)) { + 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; + } + } + + return true; +} + diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h index 057edb65eab..4db05cd71c6 100644 --- a/src/wallet/walletinit.h +++ b/src/wallet/walletinit.h @@ -8,3 +8,9 @@ std::string GetWalletHelpString(bool showDebug); //!Wallet parameter interaction bool WalletParameterInteraction(); +//! Read and validate the -wallet arguments and verify the wallet database. +// This function will perform salvage on the wallet if requested, as long as +// only one wallet is being loaded (CWallet::ParameterInteraction forbids +// -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). +bool VerifyWallets(); + From 4ba8d57f97a7cf9c93c75dd31f4a8c69357d4899 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 15:10:13 +0100 Subject: [PATCH 08/19] [wallet] Allow individual wallets to be verified --- src/wallet/wallet.cpp | 28 ++++++++++++++++++++++++++++ src/wallet/wallet.h | 3 +++ src/wallet/walletinit.cpp | 28 ++++------------------------ src/wallet/walletinit.h | 4 ---- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3315ec4ebec..bf25be0493d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3689,6 +3689,34 @@ std::vector CWallet::GetDestValues(const std::string& prefix) const return values; } +bool VerifyWallet(std::string walletFile, bool salvage_wallet) +{ + std::string strError; + if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { + return InitError(strError); + } + + if (salvage_wallet) { + // 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; + } + return true; +} + CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) { // needed to restore wallet transaction meta data after -zapwallettxes diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 53be4d8b9f4..0174fe17a5a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1118,6 +1118,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SetHDMasterKey(const CPubKey& key); }; +//! Verify the database for a single wallet and perform salvage if required +bool VerifyWallet(std::string walletFile, bool salvage_wallet); + /** A key allocated from the key pool. */ class CReserveKey : public CReserveScript { diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index 63e2aba5dc7..5ddbbde1514 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -155,31 +155,11 @@ bool VerifyWallets() return InitError(_("Invalid characters in -wallet filename")); } - 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; - } + // -salvagewallet is only permitted when loading a single wallet + // (belt-and-suspenders - WalletParameterInteraction() should prevent -salvagewallet with multiwallet) + bool salvage_wallet = GetBoolArg("-salvagewallet", false) && (gArgs.GetArgs("-wallet").size()) == 1; + if (!VerifyWallet(walletFile, salvage_wallet)) return false; } - return true; } diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h index 4db05cd71c6..b327bf75e67 100644 --- a/src/wallet/walletinit.h +++ b/src/wallet/walletinit.h @@ -9,8 +9,4 @@ std::string GetWalletHelpString(bool showDebug); bool WalletParameterInteraction(); //! Read and validate the -wallet arguments and verify the wallet database. -// This function will perform salvage on the wallet if requested, as long as -// only one wallet is being loaded (CWallet::ParameterInteraction forbids -// -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). bool VerifyWallets(); - From 34d0690f4a0f8cccbc10dafac3deaf2c61dd50b4 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 22:09:43 +0100 Subject: [PATCH 09/19] fixup with Allow individual wallets to be verified --- src/wallet/db.cpp | 30 +----------------------------- src/wallet/wallet.cpp | 6 +----- src/wallet/walletinit.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index c8928a3b666..3800e46f645 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -227,40 +227,12 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr) { - LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); - LogPrintf("Using wallet %s\n", walletFile); - - // Wallet file must be a plain filename without a directory - if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) - { - errorStr = strprintf(_("Wallet %s resides outside data directory %s"), walletFile, dataDir.string()); - return false; - } - - if (!bitdb.Open(dataDir)) - { - // try moving the database env out of the way - fs::path pathDatabase = dataDir / "database"; - fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime()); - try { - fs::rename(pathDatabase, pathDatabaseBak); - LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); - } catch (const fs::filesystem_error&) { - // failure is ok (well, not really, but it's not worse than what we started with) - } - - // try again - if (!bitdb.Open(dataDir)) { - // if it still fails, it probably means we can't even create the database env - errorStr = strprintf(_("Error initializing wallet database environment %s!"), GetDataDir()); - return false; - } - } return true; } bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc) { + LogPrintf("Using wallet %s\n", walletFile); if (fs::exists(dataDir / walletFile)) { std::string backup_filename; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index bf25be0493d..810ee65e206 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3691,11 +3691,6 @@ std::vector CWallet::GetDestValues(const std::string& prefix) const bool VerifyWallet(std::string walletFile, bool salvage_wallet) { - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { - return InitError(strError); - } - if (salvage_wallet) { // Recover readable keypairs: CWallet dummyWallet; @@ -3705,6 +3700,7 @@ bool VerifyWallet(std::string walletFile, bool salvage_wallet) } } + std::string strError; std::string strWarning; bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); if (!strWarning.empty()) { diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index 5ddbbde1514..b58d857b4e4 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -1,3 +1,4 @@ +#include "fs.h" #include "net.h" #include "util.h" #include "utilmoneystr.h" @@ -148,6 +149,29 @@ bool VerifyWallets() uiInterface.InitMessage(_("Verifying wallet(s)...")); + LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); + + fs::path dataDir = GetDataDir().string(); + + if (!bitdb.Open(dataDir)) + { + // try moving the database env out of the way + fs::path pathDatabase = dataDir / "database"; + fs::path pathDatabaseBak = dataDir / strprintf("database.%d.bak", GetTime()); + try { + fs::rename(pathDatabase, pathDatabaseBak); + LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); + } catch (const fs::filesystem_error&) { + // failure is ok (well, not really, but it's not worse than what we started with) + } + + // try again + if (!bitdb.Open(dataDir)) { + // if it still fails, it probably means we can't even create the database env + return InitError(strprintf(_("Error initializing wallet database environment %s!"), GetDataDir())); + } + } + 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)")); From 533eb5ed3d69a96599c34682d28e12ee2388f67f Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 15:26:17 +0100 Subject: [PATCH 10/19] [wallet] move InitLoadWallets() to walletinit.cpp --- src/init.cpp | 3 +-- src/wallet/wallet.cpp | 18 ------------------ src/wallet/wallet.h | 1 - src/wallet/walletinit.cpp | 18 ++++++++++++++++++ src/wallet/walletinit.h | 3 +++ 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index cbe6e3443c7..a6a25fc2732 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1506,8 +1506,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET - if (!CWallet::InitLoadWallet()) - return false; + if (!InitLoadWallets()) return false; #else LogPrintf("No wallet support compiled in!\n"); #endif diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 810ee65e206..039113947e4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3903,24 +3903,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) return walletInstance; } -bool CWallet::InitLoadWallet() -{ - if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - LogPrintf("Wallet disabled!\n"); - return true; - } - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - CWallet * const pwallet = CreateWalletFromFile(walletFile); - if (!pwallet) { - return false; - } - vpwallets.push_back(pwallet); - } - - return true; -} - std::atomic CWallet::fFlushScheduled(false); void CWallet::postInitProcess(CScheduler& scheduler) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 0174fe17a5a..284d81372fc 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1091,7 +1091,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* 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 bool InitLoadWallet(); /** * Wallet post-init setup diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index b58d857b4e4..e07451a2b28 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -187,3 +187,21 @@ bool VerifyWallets() return true; } +bool InitLoadWallets() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + LogPrintf("Wallet disabled!\n"); + return true; + } + + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + vpwallets.push_back(pwallet); + } + + return true; +} + diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h index b327bf75e67..021dd25a36b 100644 --- a/src/wallet/walletinit.h +++ b/src/wallet/walletinit.h @@ -10,3 +10,6 @@ bool WalletParameterInteraction(); //! Read and validate the -wallet arguments and verify the wallet database. bool VerifyWallets(); + +//! Load walllets from file on startup +bool InitLoadWallets(); From 4c62008ea59742e63e6b6c38d1a56be210011526 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 15:44:34 +0100 Subject: [PATCH 11/19] [wallet] move calls to postInitProcess() to walletinit.cpp --- src/init.cpp | 4 +--- src/wallet/wallet.h | 2 +- src/wallet/walletinit.cpp | 16 ++++++++++++++++ src/wallet/walletinit.h | 3 +++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index a6a25fc2732..11dba2acfaa 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1636,9 +1636,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Done loading")); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->postInitProcess(scheduler); - } + WalletCompleteStartup(scheduler); #endif return !fRequestShutdown; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 284d81372fc..b3b7bc73a37 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -650,7 +650,6 @@ class CAccountingEntry class CWallet : public CCryptoKeyStore, public CValidationInterface { private: - static std::atomic fFlushScheduled; std::atomic fAbortRescan; std::atomic fScanningWallet; @@ -796,6 +795,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface fScanningWallet = false; } + static std::atomic fFlushScheduled; std::map mapWallet; std::list laccentries; diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index e07451a2b28..5de1ea58836 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -1,5 +1,6 @@ #include "fs.h" #include "net.h" +#include "scheduler.h" #include "util.h" #include "utilmoneystr.h" #include "validation.h" @@ -205,3 +206,18 @@ bool InitLoadWallets() return true; } +void WalletCompleteStartup(CScheduler& scheduler) +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return; + + for (CWalletRef pwallet : vpwallets) { + // Add wallet transactions that aren't already in a block to mempool + // Do this here as mempool requires genesis block to be loaded + pwallet->ReacceptWalletTransactions(); + } + + // Run a thread to flush wallet periodically + if (!CWallet::fFlushScheduled.exchange(true)) { + scheduler.scheduleEvery(MaybeCompactWalletDB, 500); + } +} diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h index 021dd25a36b..a2c9b86a917 100644 --- a/src/wallet/walletinit.h +++ b/src/wallet/walletinit.h @@ -13,3 +13,6 @@ bool VerifyWallets(); //! Load walllets from file on startup bool InitLoadWallets(); + +//! Post-init setup for wallets +void WalletCompleteStartup(CScheduler& scheduler); From 10e781b44aca7117abe4cf049b52173e719baa3f Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 15:56:55 +0100 Subject: [PATCH 12/19] [wallet] move FlushWallets and DeleteWallets to walletinit.cpp This completes the movement of wallet initialization functions to walletinit.cpp. init.cpp now has an interface to the wallet consisting of the following functions: - GetWalletHelpString() - RegisterWalletRPCCommands() - WalletParameterInteraction() - VerifyWallets() - InitLoadWallets() - WalletCompleteStartup() - FlushWallets() - DeleteWallets() --- src/init.cpp | 14 +++----------- src/wallet/walletinit.cpp | 20 ++++++++++++++++++++ src/wallet/walletinit.h | 8 +++++++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 11dba2acfaa..3633d60b256 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -43,7 +43,6 @@ #include "utilmoneystr.h" #include "validationinterface.h" #ifdef ENABLE_WALLET -#include "wallet/wallet.h" #include "wallet/walletinit.h" #endif #include "warnings.h" @@ -189,9 +188,7 @@ void Shutdown() StopRPC(); StopHTTPServer(); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(false); - } + FlushWallets(false); #endif MapPort(false); UnregisterValidationInterface(peerLogic.get()); @@ -231,9 +228,7 @@ void Shutdown() pblocktree = NULL; } #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(true); - } + FlushWallets(true); #endif #if ENABLE_ZMQ @@ -253,10 +248,7 @@ void Shutdown() #endif UnregisterAllValidationInterfaces(); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - delete pwallet; - } - vpwallets.clear(); + DeleteWallets(); #endif globalVerifyHandle.reset(); ECC_Stop(); diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index 5de1ea58836..9d7e8ca9d1a 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -221,3 +221,23 @@ void WalletCompleteStartup(CScheduler& scheduler) scheduler.scheduleEvery(MaybeCompactWalletDB, 500); } } + +void FlushWallets(bool shutdown) +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return; + + LOCK(bitdb.cs_db); + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(shutdown); + } +} + +void DeleteWallets() +{ + if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return; + + for (CWalletRef pwallet : vpwallets) { + delete pwallet; + } + vpwallets.clear(); +} diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h index a2c9b86a917..ea3296764f8 100644 --- a/src/wallet/walletinit.h +++ b/src/wallet/walletinit.h @@ -14,5 +14,11 @@ bool VerifyWallets(); //! Load walllets from file on startup bool InitLoadWallets(); -//! Post-init setup for wallets +//! Post-init setup for wallets void WalletCompleteStartup(CScheduler& scheduler); + +//! Flush wallets +void FlushWallets(bool shutdown); + +//! Delete wallets +void DeleteWallets(); From 0efbd06ea5c705d62b714e2a060971a1efc6ca8b Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 16:32:47 +0100 Subject: [PATCH 13/19] [wallet] add CDBEnv::Shutdown() function --- src/wallet/db.cpp | 20 +++++++++++--------- src/wallet/db.h | 1 + 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 3800e46f645..96954e0f23b 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -530,6 +530,16 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) return false; } +void CDBEnv::Shutdown() +{ + char** listp; + if (mapFileUseCount.empty()) { + dbenv->log_archive(&listp, DB_ARCH_REMOVE); + Close(); + if (!fMockDb) + fs::remove_all(fs::path(strPath) / "database"); + } +} void CDBEnv::Flush(bool fShutdown) { @@ -559,15 +569,7 @@ void CDBEnv::Flush(bool fShutdown) mi++; } LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); - if (fShutdown) { - char** listp; - if (mapFileUseCount.empty()) { - dbenv->log_archive(&listp, DB_ARCH_REMOVE); - Close(); - if (!fMockDb) - fs::remove_all(fs::path(strPath) / "database"); - } - } + if (fShutdown) Shutdown(); } } diff --git a/src/wallet/db.h b/src/wallet/db.h index 7cccc65660d..71330b96eab 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -71,6 +71,7 @@ class CDBEnv bool Open(const fs::path& path); void Close(); void Flush(bool fShutdown); + void Shutdown(); void CheckpointLSN(const std::string& strFile); void CloseDb(const std::string& strFile); From d1a1218b123f6fc526fd8124d37b89ae45c41021 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 17:07:53 +0100 Subject: [PATCH 14/19] [wallet] call CDBEnv.shutdown() from FlushWallets() This commit removes the call to CDBEnv.Shutdown() from CDBEnv.Flush() and calls CDBEnv.Shutdown directly from ShutdownWallets() in walletinit.cpp. This also changes the interface to CDBEnv.Flush() to allow an individual wallet to be flushed. --- src/wallet/db.cpp | 17 ++++++++--------- src/wallet/db.h | 4 ++-- src/wallet/test/wallet_test_fixture.cpp | 3 ++- src/wallet/test/wallet_tests.cpp | 3 ++- src/wallet/wallet.cpp | 4 ++-- src/wallet/wallet.h | 2 +- src/wallet/walletinit.cpp | 3 ++- 7 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 96954e0f23b..6cdfeb010ed 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -541,21 +541,21 @@ void CDBEnv::Shutdown() } } -void CDBEnv::Flush(bool fShutdown) +void CDBEnv::Flush(std::string strFile) { int64_t nStart = GetTimeMillis(); // Flush log data to the actual data file on all files that are not in use - LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); + LogPrint(BCLog::DB, "CDBEnv::Flush: Flush%s\n", fDbEnvInit ? "" : " database not started"); if (!fDbEnvInit) return; { LOCK(cs_db); std::map::iterator mi = mapFileUseCount.begin(); while (mi != mapFileUseCount.end()) { - std::string strFile = (*mi).first; + std::string foundStrFile = (*mi).first; int nRefCount = (*mi).second; - LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); - if (nRefCount == 0) { + if (foundStrFile == strFile && nRefCount == 0) { + LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount); // Move log data to the dat file CloseDb(strFile); LogPrint(BCLog::DB, "CDBEnv::Flush: %s checkpoint\n", strFile); @@ -568,8 +568,7 @@ void CDBEnv::Flush(bool fShutdown) } else mi++; } - LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); - if (fShutdown) Shutdown(); + LogPrint(BCLog::DB, "CDBEnv::Flush: Flush took %15dms\n", GetTimeMillis() - nStart); } } @@ -658,9 +657,9 @@ bool CWalletDBWrapper::Backup(const std::string& strDest) return false; } -void CWalletDBWrapper::Flush(bool shutdown) +void CWalletDBWrapper::Flush() { if (!IsDummy()) { - env->Flush(shutdown); + env->Flush(strFile); } } diff --git a/src/wallet/db.h b/src/wallet/db.h index 71330b96eab..4c2268a12c8 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -70,7 +70,7 @@ class CDBEnv bool Open(const fs::path& path); void Close(); - void Flush(bool fShutdown); + void Flush(std::string strFile); void Shutdown(); void CheckpointLSN(const std::string& strFile); @@ -120,7 +120,7 @@ class CWalletDBWrapper /** Make sure all changes are flushed to disk. */ - void Flush(bool shutdown); + void Flush(); void IncrementUpdateCounter(); diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 922fcc8e898..26ed8a99c00 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -30,6 +30,7 @@ WalletTestingSetup::~WalletTestingSetup() delete pwalletMain; pwalletMain = NULL; - bitdb.Flush(true); + bitdb.Flush("wallet_test.dat"); + bitdb.Shutdown(); bitdb.Reset(); } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 96a1b14b60e..156403d60ed 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -606,7 +606,8 @@ class ListCoinsTestingSetup : public TestChain100Setup ~ListCoinsTestingSetup() { wallet.reset(); - ::bitdb.Flush(true); + ::bitdb.Flush("wallet_test.dat"); + ::bitdb.Shutdown(); ::bitdb.Reset(); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 039113947e4..65a0274d778 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -433,9 +433,9 @@ bool CWallet::HasWalletSpend(const uint256& txid) const return (iter != mapTxSpends.end() && iter->first.hash == txid); } -void CWallet::Flush(bool shutdown) +void CWallet::Flush() { - dbw->Flush(shutdown); + dbw->Flush(); } void CWallet::SyncMetaData(std::pair range) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b3b7bc73a37..4d8f5338af1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1051,7 +1051,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool HasWalletSpend(const uint256& txid) const; //! Flush wallet (bitdb flush) - void Flush(bool shutdown=false); + void Flush(); /** * Address book entry changed. diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index 9d7e8ca9d1a..23b4b393e68 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -228,8 +228,9 @@ void FlushWallets(bool shutdown) LOCK(bitdb.cs_db); for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(shutdown); + pwallet->Flush(); } + if (shutdown) bitdb.Shutdown(); } void DeleteWallets() From 267a74e1a8cd15603847265b66c60c656f1af656 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 17:37:23 +0100 Subject: [PATCH 15/19] [wallet] remove early call to FlushWallets() This commit removes the early call to FlushWallets() in Shutdown() and renames the function in walletinit.cpp to ShutdownWallets(). --- src/init.cpp | 5 +---- src/wallet/walletinit.cpp | 4 ++-- src/wallet/walletinit.h | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 3633d60b256..7896d5fad43 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -187,9 +187,6 @@ void Shutdown() StopREST(); StopRPC(); StopHTTPServer(); -#ifdef ENABLE_WALLET - FlushWallets(false); -#endif MapPort(false); UnregisterValidationInterface(peerLogic.get()); peerLogic.reset(); @@ -228,7 +225,7 @@ void Shutdown() pblocktree = NULL; } #ifdef ENABLE_WALLET - FlushWallets(true); + ShutdownWallets(); #endif #if ENABLE_ZMQ diff --git a/src/wallet/walletinit.cpp b/src/wallet/walletinit.cpp index 23b4b393e68..e3d2b3f6428 100644 --- a/src/wallet/walletinit.cpp +++ b/src/wallet/walletinit.cpp @@ -222,7 +222,7 @@ void WalletCompleteStartup(CScheduler& scheduler) } } -void FlushWallets(bool shutdown) +void ShutdownWallets() { if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return; @@ -230,7 +230,7 @@ void FlushWallets(bool shutdown) for (CWalletRef pwallet : vpwallets) { pwallet->Flush(); } - if (shutdown) bitdb.Shutdown(); + bitdb.Shutdown(); } void DeleteWallets() diff --git a/src/wallet/walletinit.h b/src/wallet/walletinit.h index ea3296764f8..9cb3978ab35 100644 --- a/src/wallet/walletinit.h +++ b/src/wallet/walletinit.h @@ -18,7 +18,7 @@ bool InitLoadWallets(); void WalletCompleteStartup(CScheduler& scheduler); //! Flush wallets -void FlushWallets(bool shutdown); +void ShutdownWallets(); //! Delete wallets void DeleteWallets(); From 17549a43a1a964f5b7a47d493451cc138bb82dfb Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 4 Jul 2017 14:51:22 +0100 Subject: [PATCH 16/19] [wallet] add cs_wallets to protect vpwallets --- src/wallet/rpcwallet.cpp | 2 +- src/wallet/wallet.cpp | 2 ++ src/wallet/wallet.h | 1 + src/wallet/walletdb.cpp | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4c84a48a7e4..5da58cedf42 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2451,7 +2451,7 @@ UniValue listwallets(const JSONRPCRequest& request) + HelpExampleRpc("listwallets", "") ); - LOCK(cs_main); + LOCK2(cs_main, cs_wallets); UniValue obj(UniValue::VARR); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 65a0274d778..4129654c408 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -35,6 +35,8 @@ #include #include +/** Protects the vpwallets vector */ +CCriticalSection cs_wallets; std::vector vpwallets; /** Transaction fee set by the user */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 4d8f5338af1..4b36805eb85 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -30,6 +30,7 @@ #include typedef CWallet* CWalletRef; +extern CCriticalSection cs_wallets; extern std::vector vpwallets; /** diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 8321719b560..bc540636794 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -748,6 +748,8 @@ DBErrors CWalletDB::ZapWalletTx(std::vector& vWtx) void MaybeCompactWalletDB() { + LOCK(cs_wallets); + static std::atomic fOneThread; if (fOneThread.exchange(true)) { return; From d4f6a0ff08300946f516c63b3be161e2cde59a95 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Mon, 3 Jul 2017 18:01:36 +0100 Subject: [PATCH 17/19] [wallet] add loadwallet RPC This commit adds an RPC to load wallet .dat files dynamically at runtime. --- src/wallet/rpcwallet.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5da58cedf42..301282e3e8a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2469,6 +2469,50 @@ UniValue listwallets(const JSONRPCRequest& request) return obj; } +UniValue loadwallet(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "loadwallet \"filename\"\n" + "\nLoads a wallet.dat file.\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The wallet.dat file\n" + "\nResult:\n" + " (string) The wallet name if loaded successfully.\n" + "\nExamples:\n" + + HelpExampleCli("loadwallet", "\"test.dat\"") + + HelpExampleRpc("loadwallet", "\"test.dat\"") + ); + + LOCK2(cs_main, cs_wallets); + + std::string walletFile = request.params[0].get_str(); + + if (boost::filesystem::path(walletFile).filename() != walletFile) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file parameter must only specify a filename (not a path)"); + } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + throw JSONRPCError(RPC_WALLET_ERROR, "Invalid characters in wallet file parameter."); + } + + if (!VerifyWallet(walletFile, false)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed."); + } + + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); + if (!pwallet) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed."); + } + vpwallets.push_back(pwallet); + + pwallet->ReacceptWalletTransactions(); + + UniValue obj(UniValue::VSTR); + obj = walletFile; + + return obj; + +} + UniValue resendwallettransactions(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -3057,6 +3101,7 @@ static const CRPCCommand commands[] = { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, { "wallet", "listwallets", &listwallets, true, {} }, + { "wallet", "loadwallet", &loadwallet, true, {"filename"} }, { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, From 8d32ecc71e3132e7fa1b37bd40b5e9a9d7f89188 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 4 Jul 2017 08:41:36 +0100 Subject: [PATCH 18/19] [wallet] add unloadwallet rpc --- src/wallet/rpcwallet.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 301282e3e8a..477c66fb464 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2513,6 +2513,54 @@ UniValue loadwallet(const JSONRPCRequest& request) } +UniValue unloadwallet(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "unloadwallet \"walletname\"\n" + "\nUnloads a wallet.\n" + "\nArguments:\n" + "1. \"walletname\" (string, required) The wallet name\n" + "\nExamples:\n" + "\nResult:\n" + "true|false (boolean) Whether the wallet was unloaded successfully.\n" + + HelpExampleCli("unloadwallet", "\"test.dat\"") + + HelpExampleRpc("unloadwallet", "\"test.dat\"") + ); + + LOCK2(cs_main, cs_wallets); + + std::string walletName = request.params[0].get_str(); + + bool unloaded = false; + + for (std::vector::iterator wallet_iter = vpwallets.begin(); wallet_iter != vpwallets.end(); wallet_iter++) { + + if (!EnsureWalletIsAvailable(*wallet_iter, request.fHelp)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error accessing wallets"); + } + + if ((*wallet_iter)->GetName() == walletName) { + + (*wallet_iter)->Flush(); + UnregisterValidationInterface(*wallet_iter); + delete *wallet_iter; + vpwallets.erase(wallet_iter); + + unloaded = true; + break; + } + } + + if (!unloaded) { + throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Failed to find wallet %s", walletName)); + } + + UniValue obj(UniValue::VBOOL); + obj = unloaded; + return obj; +} + UniValue resendwallettransactions(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -3110,6 +3158,7 @@ static const CRPCCommand commands[] = { "wallet", "setaccount", &setaccount, true, {"address","account"} }, { "wallet", "settxfee", &settxfee, true, {"amount"} }, { "wallet", "signmessage", &signmessage, true, {"address","message"} }, + { "wallet", "unloadwallet", &unloadwallet, true, {"walletname"} }, { "wallet", "walletlock", &walletlock, true, {} }, { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, From 72c37457cd71a78add957d7f0239ffa260359701 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Tue, 4 Jul 2017 14:57:53 +0100 Subject: [PATCH 19/19] [wallet] [test] update multiwallet.py to use loadwallet and unloadwallet --- test/functional/multiwallet.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index d855fd987bf..d26489d7889 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -23,12 +23,16 @@ def run_test(self): self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-wallet=wallet1.dat"]) wallet1_name = self.nodes[0].getwalletinfo()["walletname"] - # Stop the node and load both wallets - self.stop_nodes() - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-wallet=wallet0.dat", "-wallet=wallet1.dat"]) + # Load wallet0 + self.nodes[0].loadwallet("wallet0.dat") + + # Assert that node0 has both wallets loaded correctly + assert_equal(self.nodes[0].listwallets(), [wallet1_name, wallet0_name]) + + # Unload wallet1 + self.nodes[0].unloadwallet("wallet1.dat") - # Assert that node0 has loaded both wallets correctly - assert_equal(self.nodes[0].listwallets(), [wallet0_name, wallet1_name]) + assert_equal(self.nodes[0].listwallets(), [wallet0_name]) if __name__ == '__main__': Multiwallet().main()