mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
merge #15937: Add loadwallet and createwallet load_on_startup options
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
parent
e1d8dfba06
commit
7f3c8c399e
12
doc/release-notes-15937.md
Normal file
12
doc/release-notes-15937.md
Normal file
@ -0,0 +1,12 @@
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The `createwallet`, `loadwallet`, and `unloadwallet` RPCs now accept
|
||||
`load_on_startup` options that modify bitcoin's dynamic configuration in
|
||||
`\<datadir\>/settings.json`, and can add or remove a wallet from the list of
|
||||
wallets automatically loaded at startup. Unless these options are explicitly
|
||||
set to true or false, the load on startup wallet list is not modified, so this
|
||||
change is backwards compatible.
|
||||
|
||||
In the future, the GUI will start updating the same startup wallet list as the
|
||||
RPCs to automatically reopen wallets previously opened in the GUI.
|
@ -360,6 +360,27 @@ public:
|
||||
{
|
||||
RPCRunLater(name, std::move(fn), seconds);
|
||||
}
|
||||
util::SettingsValue getRwSetting(const std::string& name) override
|
||||
{
|
||||
util::SettingsValue result;
|
||||
gArgs.LockSettings([&](const util::Settings& settings) {
|
||||
if (const util::SettingsValue* value = util::FindKey(settings.rw_settings, name)) {
|
||||
result = *value;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
bool updateRwSetting(const std::string& name, const util::SettingsValue& value) override
|
||||
{
|
||||
gArgs.LockSettings([&](util::Settings& settings) {
|
||||
if (value.isNull()) {
|
||||
settings.rw_settings.erase(name);
|
||||
} else {
|
||||
settings.rw_settings[name] = value;
|
||||
}
|
||||
});
|
||||
return gArgs.WriteSettingsFile();
|
||||
}
|
||||
void requestMempoolTransactions(Notifications& notifications) override
|
||||
{
|
||||
LOCK2(::cs_main, ::mempool.cs);
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <optional.h> // For Optional and nullopt
|
||||
#include <primitives/transaction.h> // For CTransactionRef
|
||||
#include <util/settings.h> // For util::SettingsValue
|
||||
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
@ -244,6 +245,12 @@ public:
|
||||
//! Run function after given number of seconds. Cancel any previous calls with same name.
|
||||
virtual void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) = 0;
|
||||
|
||||
//! Return <datadir>/settings.json setting value.
|
||||
virtual util::SettingsValue getRwSetting(const std::string& name) = 0;
|
||||
|
||||
//! Write a setting to <datadir>/settings.json.
|
||||
virtual bool updateRwSetting(const std::string& name, const util::SettingsValue& value) = 0;
|
||||
|
||||
//! Synchronously send TransactionAddedToMempool notifications about all
|
||||
//! current mempool transactions to the specified handler and return after
|
||||
//! the last one is sent. These notifications aren't coordinated with async
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
#include <wallet/db.h>
|
||||
#include <wallet/walletutil.h>
|
||||
#endif
|
||||
|
||||
#include <QButtonGroup>
|
||||
@ -499,8 +500,18 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags
|
||||
connect(ui->btn_reindex, &QPushButton::clicked, this, &RPCConsole::walletReindex);
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
std::string walletPath = GetDataDir().string();
|
||||
walletPath += QDir::separator().toLatin1() + gArgs.GetArg("-wallet", "wallet.dat");
|
||||
// If there's no -wallet setting with a list of wallets to load, set it to
|
||||
// load the default "" wallet.
|
||||
if (!gArgs.IsArgSet("wallet")) {
|
||||
gArgs.LockSettings([&](util::Settings& settings) {
|
||||
util::SettingsValue wallets(util::SettingsValue::VARR);
|
||||
wallets.push_back(""); // Default wallet name is ""
|
||||
settings.rw_settings["wallet"] = wallets;
|
||||
});
|
||||
}
|
||||
std::string walletName = gArgs.GetArgs("-wallet").at(0);
|
||||
std::string walletPath = GetWalletDir().string();
|
||||
walletPath += QDir::separator().toLatin1() + (walletName == "" ? "wallet.dat" : walletName);
|
||||
ui->wallet_path->setText(QString::fromStdString(walletPath));
|
||||
#endif
|
||||
|
||||
|
@ -200,6 +200,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "createwallet", 2, "blank"},
|
||||
{ "createwallet", 4, "avoid_reuse"},
|
||||
{ "upgradetohd", 3, "rescan"},
|
||||
{ "createwallet", 5, "load_on_startup"},
|
||||
{ "loadwallet", 1, "load_on_startup"},
|
||||
{ "unloadwallet", 1, "load_on_startup"},
|
||||
{ "upgradetohd", 3, "rescan"},
|
||||
{ "getnodeaddresses", 0, "count"},
|
||||
{ "stop", 0, "wait" },
|
||||
};
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <interfaces/chain.h>
|
||||
#include <net.h>
|
||||
#include <node/context.h>
|
||||
#include <univalue.h>
|
||||
#include <util/error.h>
|
||||
#include <util/system.h>
|
||||
#include <util/moneystr.h>
|
||||
@ -162,7 +163,15 @@ void WalletInit::Construct(NodeContext& node) const
|
||||
LogPrintf("Wallet disabled!\n");
|
||||
return;
|
||||
}
|
||||
gArgs.SoftSetArg("-wallet", "");
|
||||
// If there's no -wallet setting with a list of wallets to load, set it to
|
||||
// load the default "" wallet.
|
||||
if (!gArgs.IsArgSet("wallet")) {
|
||||
gArgs.LockSettings([&](util::Settings& settings) {
|
||||
util::SettingsValue wallets(util::SettingsValue::VARR);
|
||||
wallets.push_back(""); // Default wallet name is ""
|
||||
settings.rw_settings["wallet"] = wallets;
|
||||
});
|
||||
}
|
||||
node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, gArgs.GetArgs("-wallet")));
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/walletdb.h>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
|
||||
{
|
||||
if (gArgs.IsArgSet("-walletdir")) {
|
||||
@ -129,3 +131,26 @@ void UnloadWallets()
|
||||
UnloadWallet(std::move(wallet));
|
||||
}
|
||||
}
|
||||
|
||||
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
|
||||
{
|
||||
util::SettingsValue setting_value = chain.getRwSetting("wallet");
|
||||
if (!setting_value.isArray()) setting_value.setArray();
|
||||
for (const util::SettingsValue& value : setting_value.getValues()) {
|
||||
if (value.isStr() && value.get_str() == wallet_name) return true;
|
||||
}
|
||||
setting_value.push_back(wallet_name);
|
||||
return chain.updateRwSetting("wallet", setting_value);
|
||||
}
|
||||
|
||||
bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
|
||||
{
|
||||
util::SettingsValue setting_value = chain.getRwSetting("wallet");
|
||||
if (!setting_value.isArray()) return true;
|
||||
util::SettingsValue new_value(util::SettingsValue::VARR);
|
||||
for (const util::SettingsValue& value : setting_value.getValues()) {
|
||||
if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value);
|
||||
}
|
||||
if (new_value.size() == setting_value.size()) return true;
|
||||
return chain.updateRwSetting("wallet", new_value);
|
||||
}
|
||||
|
@ -34,4 +34,10 @@ void StopWallets();
|
||||
//! Close all wallets.
|
||||
void UnloadWallets();
|
||||
|
||||
//! Add wallet name to persistent configuration so it will be loaded on startup.
|
||||
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
|
||||
|
||||
//! Remove wallet name from persistent configuration so it will not be loaded on startup.
|
||||
bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
|
||||
|
||||
#endif // BITCOIN_WALLET_LOAD_H
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <wallet/coincontrol.h>
|
||||
#include <wallet/context.h>
|
||||
#include <wallet/psbtwallet.h>
|
||||
#include <wallet/load.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/walletdb.h>
|
||||
@ -160,6 +161,18 @@ static std::string LabelFromValue(const UniValue& value)
|
||||
return label;
|
||||
}
|
||||
|
||||
static void UpdateWalletSetting(interfaces::Chain& chain,
|
||||
const std::string& wallet_name,
|
||||
const UniValue& load_on_startup,
|
||||
std::vector<bilingual_str>& warnings)
|
||||
{
|
||||
if (load_on_startup.isTrue() && !AddWalletSetting(chain, wallet_name)) {
|
||||
warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
|
||||
} else if (load_on_startup.isFalse() && !RemoveWalletSetting(chain, wallet_name)) {
|
||||
warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup."));
|
||||
}
|
||||
}
|
||||
|
||||
UniValue getnewaddress(const JSONRPCRequest& request)
|
||||
{
|
||||
RPCHelpMan{"getnewaddress",
|
||||
@ -2647,6 +2660,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
|
||||
"\napplied to the new wallet (eg -upgradewallet, rescan, etc).\n",
|
||||
{
|
||||
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
|
||||
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "",
|
||||
@ -2679,6 +2693,8 @@ static UniValue loadwallet(const JSONRPCRequest& request)
|
||||
std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, location, error, warnings);
|
||||
if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original);
|
||||
|
||||
UpdateWalletSetting(*context.chain, location.GetName(), request.params[1], warnings);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("name", wallet->GetName());
|
||||
obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
|
||||
@ -2763,6 +2779,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
|
||||
{"blank", RPCArg::Type::BOOL, /* default */ "false", "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
|
||||
{"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
|
||||
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
|
||||
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "",
|
||||
@ -2814,6 +2831,8 @@ static UniValue createwallet(const JSONRPCRequest& request)
|
||||
// no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
|
||||
UpdateWalletSetting(*context.chain, request.params[0].get_str(), request.params[6], warnings);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("name", wallet->GetName());
|
||||
obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
|
||||
@ -2828,8 +2847,11 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
|
||||
"Specifying the wallet name on a wallet endpoint is invalid.",
|
||||
{
|
||||
{"wallet_name", RPCArg::Type::STR, /* default */ "the wallet name from the RPC request", "The name of the wallet to unload."},
|
||||
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
|
||||
},
|
||||
RPCResult{RPCResult::Type::NONE, "", ""},
|
||||
RPCResult{RPCResult::Type::OBJ, "", "", {
|
||||
{RPCResult::Type::STR, "warning", "Warning message if wallet was not unloaded cleanly."},
|
||||
}},
|
||||
RPCExamples{
|
||||
HelpExampleCli("unloadwallet", "wallet_name")
|
||||
+ HelpExampleRpc("unloadwallet", "wallet_name")
|
||||
@ -2857,9 +2879,15 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
|
||||
}
|
||||
|
||||
UnloadWallet(std::move(wallet));
|
||||
interfaces::Chain& chain = wallet->chain();
|
||||
std::vector<bilingual_str> warnings;
|
||||
|
||||
return NullUniValue;
|
||||
UnloadWallet(std::move(wallet));
|
||||
UpdateWalletSetting(chain, wallet_name, request.params[1], warnings);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
|
||||
return result;
|
||||
}
|
||||
|
||||
static UniValue listunspent(const JSONRPCRequest& request)
|
||||
@ -3932,7 +3960,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "abortrescan", &abortrescan, {} },
|
||||
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label"} },
|
||||
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
||||
{ "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse"} },
|
||||
{ "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "load_on_startup"} },
|
||||
{ "wallet", "dumphdinfo", &dumphdinfo, {} },
|
||||
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
||||
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
|
||||
@ -3966,7 +3994,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
|
||||
{ "wallet", "listwalletdir", &listwalletdir, {} },
|
||||
{ "wallet", "listwallets", &listwallets, {} },
|
||||
{ "wallet", "loadwallet", &loadwallet, {"filename"} },
|
||||
{ "wallet", "loadwallet", &loadwallet, {"filename", "load_on_startup"} },
|
||||
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
|
||||
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
|
||||
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
|
||||
@ -3979,7 +4007,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "setwalletflag", &setwalletflag, {"flag","value"} },
|
||||
{ "wallet", "signmessage", &signmessage, {"address","message"} },
|
||||
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
|
||||
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
|
||||
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name", "load_on_startup"} },
|
||||
{ "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase", "rescan"} },
|
||||
{ "wallet", "walletlock", &walletlock, {} },
|
||||
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
|
||||
|
@ -247,6 +247,7 @@ BASE_SCRIPTS = [
|
||||
'p2p_node_network_limited.py',
|
||||
'p2p_permissions.py',
|
||||
'feature_blocksdir.py',
|
||||
'wallet_startup.py',
|
||||
'feature_config_args.py',
|
||||
'feature_settings.py',
|
||||
'rpc_help.py',
|
||||
|
48
test/functional/wallet_startup.py
Executable file
48
test/functional/wallet_startup.py
Executable file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2017-2019 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 wallet load on startup.
|
||||
|
||||
Verify that a dashd node can maintain list of wallets loading on startup
|
||||
"""
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
)
|
||||
|
||||
|
||||
class WalletStartupTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
self.supports_cli = True
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def setup_nodes(self):
|
||||
self.add_nodes(self.num_nodes)
|
||||
self.start_nodes()
|
||||
|
||||
def run_test(self):
|
||||
self.nodes[0].createwallet(wallet_name='w0', load_on_startup=True)
|
||||
self.nodes[0].createwallet(wallet_name='w1', load_on_startup=False)
|
||||
self.nodes[0].createwallet(wallet_name='w2', load_on_startup=True)
|
||||
self.nodes[0].createwallet(wallet_name='w3', load_on_startup=False)
|
||||
self.nodes[0].createwallet(wallet_name='w4', load_on_startup=False)
|
||||
self.nodes[0].unloadwallet(wallet_name='w0', load_on_startup=False)
|
||||
self.nodes[0].unloadwallet(wallet_name='w4', load_on_startup=False)
|
||||
self.nodes[0].loadwallet(filename='w4', load_on_startup=True)
|
||||
assert_equal(set(self.nodes[0].listwallets()), set(('', 'w1', 'w2', 'w3', 'w4')))
|
||||
self.restart_node(0)
|
||||
assert_equal(set(self.nodes[0].listwallets()), set(('', 'w2', 'w4')))
|
||||
self.nodes[0].unloadwallet(wallet_name='', load_on_startup=False)
|
||||
self.nodes[0].unloadwallet(wallet_name='w4', load_on_startup=False)
|
||||
self.nodes[0].loadwallet(filename='w3', load_on_startup=True)
|
||||
self.nodes[0].loadwallet(filename='')
|
||||
self.restart_node(0)
|
||||
assert_equal(set(self.nodes[0].listwallets()), set(('w2', 'w3')))
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletStartupTest().main()
|
Loading…
Reference in New Issue
Block a user