mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 20:03:34 -03:00
Merge #15761: Replace -upgradewallet startup option with upgradewallet RPC
0d32d66148
Remove -upgradewallet startup option (Andrew Chow)92263cce5b
Add upgradewallet RPC (Andrew Chow)1e48796c99
Make UpgradeWallet a member function of CWallet (Andrew Chow)c988f27937
Have UpgradeWallet take the version to upgrade to and an error message out parameter (Andrew Chow)1833237123
Only run UpgradeWallet if the wallet needs to be upgraded (Andrew Chow)9c16b1735f
Move wallet upgrading to its own function (Andrew Chow) Pull request description: `-upgradewallet` is largely incompatible with many recent wallet features and versions. For example, it was disabled if multiple wallets were used and would not work with encrypted wallets that were being upgraded to HD. This PR does away with the old method of upgrading upon startup and instead allows users to upgrade their wallets via an `upgradewallet` RPC. This does largely the same thing as the old `-upgradewallet` option but because the wallet is loaded, it can be unlocked to upgrade to HD. Furthermore it is compatible with multiwallet as it works on the individual wallet that is specified by the RPC. ACKs for top commit: meshcollider: Code review ACK0d32d66148
darosior: ACK0d32d66148
MarcoFalke: ACK0d32d66148
🚵 Tree-SHA512: b425bf6f5d605e26506889d63c780895482f07cbc086193218e031e8504d3072d41e90d65cd41bcc98ee4c1eb048954bc5d4ac85435f7394892373aac89a3b0a
This commit is contained in:
commit
b470c75847
6 changed files with 82 additions and 64 deletions
|
@ -153,6 +153,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "logging", 0, "include" },
|
||||
{ "logging", 1, "exclude" },
|
||||
{ "disconnectnode", 1, "nodeid" },
|
||||
{ "upgradewallet", 0, "version" },
|
||||
// Echo with conversion (For testing only)
|
||||
{ "echojson", 0, "arg0" },
|
||||
{ "echojson", 1, "arg1" },
|
||||
|
|
|
@ -57,7 +57,6 @@ void WalletInit::AddWalletOptions() const
|
|||
gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
||||
gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
||||
gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
||||
gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
||||
gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
|
||||
gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
|
||||
gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
|
||||
|
@ -116,12 +115,6 @@ bool WalletInit::ParameterInteraction() const
|
|||
}
|
||||
}
|
||||
|
||||
if (is_multiwallet) {
|
||||
if (gArgs.GetBoolArg("-upgradewallet", false)) {
|
||||
return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet"));
|
||||
}
|
||||
}
|
||||
|
||||
if (gArgs.GetBoolArg("-sysperms", false))
|
||||
return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
|
||||
|
||||
|
|
|
@ -2591,7 +2591,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
|
|||
RPCHelpMan{"loadwallet",
|
||||
"\nLoads a wallet from a wallet file or directory."
|
||||
"\nNote that all wallet command-line options used when starting bitcoind will be"
|
||||
"\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n",
|
||||
"\napplied to the new wallet (eg -zapwallettxes, rescan, etc).\n",
|
||||
{
|
||||
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
|
||||
},
|
||||
|
@ -4017,7 +4017,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
|
|||
|
||||
// Do not do anything to non-HD wallets
|
||||
if (!pwallet->CanSupportFeature(FEATURE_HD)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD");
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
|
||||
}
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
@ -4241,6 +4241,45 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
|
|||
return result;
|
||||
}
|
||||
|
||||
static UniValue upgradewallet(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
RPCHelpMan{"upgradewallet",
|
||||
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
|
||||
"New keys may be generated and a new wallet backup will need to be made.",
|
||||
{
|
||||
{"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version"}
|
||||
},
|
||||
RPCResults{},
|
||||
RPCExamples{
|
||||
HelpExampleCli("upgradewallet", "169900")
|
||||
+ HelpExampleRpc("upgradewallet", "169900")
|
||||
}
|
||||
}.Check(request);
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
int version = 0;
|
||||
if (!request.params[0].isNull()) {
|
||||
version = request.params[0].get_int();
|
||||
}
|
||||
|
||||
std::string error;
|
||||
std::vector<std::string> warnings;
|
||||
if (!pwallet->UpgradeWallet(version, error, warnings)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, error);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
UniValue importprivkey(const JSONRPCRequest& request);
|
||||
|
@ -4309,6 +4348,7 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "signmessage", &signmessage, {"address","message"} },
|
||||
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
|
||||
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
|
||||
{ "wallet", "upgradewallet", &upgradewallet, {"version"} },
|
||||
{ "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} },
|
||||
{ "wallet", "walletlock", &walletlock, {} },
|
||||
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
|
||||
|
|
|
@ -3827,44 +3827,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
|
|||
}
|
||||
}
|
||||
|
||||
int prev_version = walletInstance->GetVersion();
|
||||
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
|
||||
{
|
||||
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
|
||||
if (nMaxVersion == 0) // the -upgradewallet without argument case
|
||||
{
|
||||
walletInstance->WalletLogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
|
||||
nMaxVersion = FEATURE_LATEST;
|
||||
walletInstance->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
|
||||
}
|
||||
else
|
||||
walletInstance->WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
|
||||
if (nMaxVersion < walletInstance->GetVersion())
|
||||
{
|
||||
error = _("Cannot downgrade wallet").translated;
|
||||
return nullptr;
|
||||
}
|
||||
walletInstance->SetMaxVersion(nMaxVersion);
|
||||
}
|
||||
|
||||
// Upgrade to HD if explicit upgrade
|
||||
if (gArgs.GetBoolArg("-upgradewallet", false)) {
|
||||
LOCK(walletInstance->cs_wallet);
|
||||
|
||||
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
|
||||
int max_version = walletInstance->GetVersion();
|
||||
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
|
||||
error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.").translated;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) {
|
||||
if (!spk_man->Upgrade(prev_version, error)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fFirstRun)
|
||||
{
|
||||
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
|
||||
|
@ -4126,6 +4088,42 @@ const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest
|
|||
return &address_book_it->second;
|
||||
}
|
||||
|
||||
bool CWallet::UpgradeWallet(int version, std::string& error, std::vector<std::string>& warnings)
|
||||
{
|
||||
int prev_version = GetVersion();
|
||||
int nMaxVersion = version;
|
||||
if (nMaxVersion == 0) // the -upgradewallet without argument case
|
||||
{
|
||||
WalletLogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
|
||||
nMaxVersion = FEATURE_LATEST;
|
||||
SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
|
||||
}
|
||||
else
|
||||
WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
|
||||
if (nMaxVersion < GetVersion())
|
||||
{
|
||||
error = _("Cannot downgrade wallet").translated;
|
||||
return false;
|
||||
}
|
||||
SetMaxVersion(nMaxVersion);
|
||||
|
||||
LOCK(cs_wallet);
|
||||
|
||||
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
|
||||
int max_version = GetVersion();
|
||||
if (!CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
|
||||
error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified.").translated;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto spk_man : GetActiveScriptPubKeyMans()) {
|
||||
if (!spk_man->Upgrade(prev_version, error)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CWallet::postInitProcess()
|
||||
{
|
||||
auto locked_chain = chain().lock();
|
||||
|
|
|
@ -1175,6 +1175,9 @@ public:
|
|||
LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...);
|
||||
};
|
||||
|
||||
/** Upgrade the wallet */
|
||||
bool UpgradeWallet(int version, std::string& error, std::vector<std::string>& warnings);
|
||||
|
||||
//! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and m_external_spk_managers
|
||||
std::set<ScriptPubKeyMan*> GetActiveScriptPubKeyMans() const;
|
||||
|
||||
|
|
|
@ -126,10 +126,6 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||
self.nodes[0].assert_start_raises_init_error(['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
|
||||
self.nodes[0].assert_start_raises_init_error(['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
|
||||
|
||||
self.log.info("Do not allow -upgradewallet with multiwallet")
|
||||
self.nodes[0].assert_start_raises_init_error(['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
|
||||
self.nodes[0].assert_start_raises_init_error(['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
|
||||
|
||||
# if wallets/ doesn't exist, datadir should be the default wallet dir
|
||||
wallet_dir2 = data_dir('walletdir')
|
||||
os.rename(wallet_dir(), wallet_dir2)
|
||||
|
@ -333,18 +329,5 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||
self.nodes[0].unloadwallet(wallet)
|
||||
self.nodes[1].loadwallet(wallet)
|
||||
|
||||
# Fail to load if wallet is downgraded
|
||||
shutil.copytree(os.path.join(self.options.data_wallets_dir, 'high_minversion'), wallet_dir('high_minversion'))
|
||||
self.restart_node(0, extra_args=['-upgradewallet={}'.format(FEATURE_LATEST)])
|
||||
assert {'name': 'high_minversion'} in self.nodes[0].listwalletdir()['wallets']
|
||||
self.log.info("Fail -upgradewallet that results in downgrade")
|
||||
assert_raises_rpc_error(
|
||||
-4,
|
||||
'Wallet loading failed: Error loading {}: Wallet requires newer version of {}'.format(
|
||||
wallet_dir('high_minversion', 'wallet.dat'), self.config['environment']['PACKAGE_NAME']),
|
||||
lambda: self.nodes[0].loadwallet(filename='high_minversion'),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
MultiWalletTest().main()
|
||||
|
|
Loading…
Reference in a new issue