Skip to content

Commit b470c75

Browse files
author
MarcoFalke
committed
Merge #15761: Replace -upgradewallet startup option with upgradewallet RPC
0d32d66 Remove -upgradewallet startup option (Andrew Chow) 92263cc Add upgradewallet RPC (Andrew Chow) 1e48796 Make UpgradeWallet a member function of CWallet (Andrew Chow) c988f27 Have UpgradeWallet take the version to upgrade to and an error message out parameter (Andrew Chow) 1833237 Only run UpgradeWallet if the wallet needs to be upgraded (Andrew Chow) 9c16b17 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 ACK 0d32d66 darosior: ACK 0d32d66 MarcoFalke: ACK 0d32d66 🚵 Tree-SHA512: b425bf6f5d605e26506889d63c780895482f07cbc086193218e031e8504d3072d41e90d65cd41bcc98ee4c1eb048954bc5d4ac85435f7394892373aac89a3b0a
2 parents a998c51 + 0d32d66 commit b470c75

File tree

6 files changed

+82
-64
lines changed

6 files changed

+82
-64
lines changed

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
153153
{ "logging", 0, "include" },
154154
{ "logging", 1, "exclude" },
155155
{ "disconnectnode", 1, "nodeid" },
156+
{ "upgradewallet", 0, "version" },
156157
// Echo with conversion (For testing only)
157158
{ "echojson", 0, "arg0" },
158159
{ "echojson", 1, "arg1" },

src/wallet/init.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ void WalletInit::AddWalletOptions() const
5757
gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
5858
gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
5959
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);
60-
gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
6160
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);
6261
gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
6362
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
116115
}
117116
}
118117

119-
if (is_multiwallet) {
120-
if (gArgs.GetBoolArg("-upgradewallet", false)) {
121-
return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet"));
122-
}
123-
}
124-
125118
if (gArgs.GetBoolArg("-sysperms", false))
126119
return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
127120

src/wallet/rpcwallet.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,7 +2591,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
25912591
RPCHelpMan{"loadwallet",
25922592
"\nLoads a wallet from a wallet file or directory."
25932593
"\nNote that all wallet command-line options used when starting bitcoind will be"
2594-
"\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n",
2594+
"\napplied to the new wallet (eg -zapwallettxes, rescan, etc).\n",
25952595
{
25962596
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
25972597
},
@@ -4017,7 +4017,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
40174017

40184018
// Do not do anything to non-HD wallets
40194019
if (!pwallet->CanSupportFeature(FEATURE_HD)) {
4020-
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");
4020+
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");
40214021
}
40224022

40234023
EnsureWalletIsUnlocked(pwallet);
@@ -4241,6 +4241,45 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
42414241
return result;
42424242
}
42434243

4244+
static UniValue upgradewallet(const JSONRPCRequest& request)
4245+
{
4246+
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
4247+
CWallet* const pwallet = wallet.get();
4248+
4249+
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
4250+
return NullUniValue;
4251+
}
4252+
4253+
RPCHelpMan{"upgradewallet",
4254+
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
4255+
"New keys may be generated and a new wallet backup will need to be made.",
4256+
{
4257+
{"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version"}
4258+
},
4259+
RPCResults{},
4260+
RPCExamples{
4261+
HelpExampleCli("upgradewallet", "169900")
4262+
+ HelpExampleRpc("upgradewallet", "169900")
4263+
}
4264+
}.Check(request);
4265+
4266+
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
4267+
4268+
EnsureWalletIsUnlocked(pwallet);
4269+
4270+
int version = 0;
4271+
if (!request.params[0].isNull()) {
4272+
version = request.params[0].get_int();
4273+
}
4274+
4275+
std::string error;
4276+
std::vector<std::string> warnings;
4277+
if (!pwallet->UpgradeWallet(version, error, warnings)) {
4278+
throw JSONRPCError(RPC_WALLET_ERROR, error);
4279+
}
4280+
return error;
4281+
}
4282+
42444283
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
42454284
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
42464285
UniValue importprivkey(const JSONRPCRequest& request);
@@ -4309,6 +4348,7 @@ static const CRPCCommand commands[] =
43094348
{ "wallet", "signmessage", &signmessage, {"address","message"} },
43104349
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
43114350
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
4351+
{ "wallet", "upgradewallet", &upgradewallet, {"version"} },
43124352
{ "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} },
43134353
{ "wallet", "walletlock", &walletlock, {} },
43144354
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },

src/wallet/wallet.cpp

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3827,44 +3827,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
38273827
}
38283828
}
38293829

3830-
int prev_version = walletInstance->GetVersion();
3831-
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
3832-
{
3833-
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
3834-
if (nMaxVersion == 0) // the -upgradewallet without argument case
3835-
{
3836-
walletInstance->WalletLogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
3837-
nMaxVersion = FEATURE_LATEST;
3838-
walletInstance->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
3839-
}
3840-
else
3841-
walletInstance->WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
3842-
if (nMaxVersion < walletInstance->GetVersion())
3843-
{
3844-
error = _("Cannot downgrade wallet").translated;
3845-
return nullptr;
3846-
}
3847-
walletInstance->SetMaxVersion(nMaxVersion);
3848-
}
3849-
3850-
// Upgrade to HD if explicit upgrade
3851-
if (gArgs.GetBoolArg("-upgradewallet", false)) {
3852-
LOCK(walletInstance->cs_wallet);
3853-
3854-
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
3855-
int max_version = walletInstance->GetVersion();
3856-
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
3857-
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;
3858-
return nullptr;
3859-
}
3860-
3861-
for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) {
3862-
if (!spk_man->Upgrade(prev_version, error)) {
3863-
return nullptr;
3864-
}
3865-
}
3866-
}
3867-
38683830
if (fFirstRun)
38693831
{
38703832
// 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
41264088
return &address_book_it->second;
41274089
}
41284090

4091+
bool CWallet::UpgradeWallet(int version, std::string& error, std::vector<std::string>& warnings)
4092+
{
4093+
int prev_version = GetVersion();
4094+
int nMaxVersion = version;
4095+
if (nMaxVersion == 0) // the -upgradewallet without argument case
4096+
{
4097+
WalletLogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
4098+
nMaxVersion = FEATURE_LATEST;
4099+
SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately
4100+
}
4101+
else
4102+
WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
4103+
if (nMaxVersion < GetVersion())
4104+
{
4105+
error = _("Cannot downgrade wallet").translated;
4106+
return false;
4107+
}
4108+
SetMaxVersion(nMaxVersion);
4109+
4110+
LOCK(cs_wallet);
4111+
4112+
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
4113+
int max_version = GetVersion();
4114+
if (!CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
4115+
error = _("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use version 169900 or no version specified.").translated;
4116+
return false;
4117+
}
4118+
4119+
for (auto spk_man : GetActiveScriptPubKeyMans()) {
4120+
if (!spk_man->Upgrade(prev_version, error)) {
4121+
return false;
4122+
}
4123+
}
4124+
return true;
4125+
}
4126+
41294127
void CWallet::postInitProcess()
41304128
{
41314129
auto locked_chain = chain().lock();

src/wallet/wallet.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
11751175
LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...);
11761176
};
11771177

1178+
/** Upgrade the wallet */
1179+
bool UpgradeWallet(int version, std::string& error, std::vector<std::string>& warnings);
1180+
11781181
//! Returns all unique ScriptPubKeyMans in m_internal_spk_managers and m_external_spk_managers
11791182
std::set<ScriptPubKeyMan*> GetActiveScriptPubKeyMans() const;
11801183

test/functional/wallet_multiwallet.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ def wallet_file(name):
126126
self.nodes[0].assert_start_raises_init_error(['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
127127
self.nodes[0].assert_start_raises_init_error(['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file")
128128

129-
self.log.info("Do not allow -upgradewallet with multiwallet")
130-
self.nodes[0].assert_start_raises_init_error(['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
131-
self.nodes[0].assert_start_raises_init_error(['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
132-
133129
# if wallets/ doesn't exist, datadir should be the default wallet dir
134130
wallet_dir2 = data_dir('walletdir')
135131
os.rename(wallet_dir(), wallet_dir2)
@@ -333,18 +329,5 @@ def wallet_file(name):
333329
self.nodes[0].unloadwallet(wallet)
334330
self.nodes[1].loadwallet(wallet)
335331

336-
# Fail to load if wallet is downgraded
337-
shutil.copytree(os.path.join(self.options.data_wallets_dir, 'high_minversion'), wallet_dir('high_minversion'))
338-
self.restart_node(0, extra_args=['-upgradewallet={}'.format(FEATURE_LATEST)])
339-
assert {'name': 'high_minversion'} in self.nodes[0].listwalletdir()['wallets']
340-
self.log.info("Fail -upgradewallet that results in downgrade")
341-
assert_raises_rpc_error(
342-
-4,
343-
'Wallet loading failed: Error loading {}: Wallet requires newer version of {}'.format(
344-
wallet_dir('high_minversion', 'wallet.dat'), self.config['environment']['PACKAGE_NAME']),
345-
lambda: self.nodes[0].loadwallet(filename='high_minversion'),
346-
)
347-
348-
349332
if __name__ == '__main__':
350333
MultiWalletTest().main()

0 commit comments

Comments
 (0)