feat(rpc/wallet): allow skipping/forcing blockchain rescan on upgradetohd (#4822)

* wallet: allow skipping/forcing blockchain rescan on upgradetohd

* tests: test new use cases in wallet_upgradetohd.py

* add release notes
This commit is contained in:
UdjinM6 2022-05-17 01:51:54 +03:00 committed by GitHub
parent d561e3c726
commit 56898d2085
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 2 deletions

View File

@ -0,0 +1,3 @@
Miscellaneous RPC Changes
-------------------------
- In rpc `upgradetohd` new parameter `rescan` was added which allows users to skip or force blockchain rescan. This params defaults to `false` when `mnemonic` parameter is empty and `true` otherwise.

View File

@ -196,6 +196,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "rescanblockchain", 1, "stop_height"}, { "rescanblockchain", 1, "stop_height"},
{ "createwallet", 1, "disable_private_keys"}, { "createwallet", 1, "disable_private_keys"},
{ "createwallet", 2, "blank"}, { "createwallet", 2, "blank"},
{ "upgradetohd", 3, "rescan"},
{ "getnodeaddresses", 0, "count"}, { "getnodeaddresses", 0, "count"},
{ "stop", 0, "wait" }, { "stop", 0, "wait" },
}; };

View File

@ -2567,6 +2567,7 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
{"mnemonic", RPCArg::Type::STR, /* default */ "", "Mnemonic as defined in BIP39 to use for the new HD wallet. Use an empty string \"\" to generate a new random mnemonic."}, {"mnemonic", RPCArg::Type::STR, /* default */ "", "Mnemonic as defined in BIP39 to use for the new HD wallet. Use an empty string \"\" to generate a new random mnemonic."},
{"mnemonicpassphrase", RPCArg::Type::STR, /* default */ "", "Optional mnemonic passphrase as defined in BIP39"}, {"mnemonicpassphrase", RPCArg::Type::STR, /* default */ "", "Optional mnemonic passphrase as defined in BIP39"},
{"walletpassphrase", RPCArg::Type::STR, /* default */ "", "If your wallet is encrypted you must have your wallet passphrase here. If your wallet is not encrypted specifying wallet passphrase will trigger wallet encryption."}, {"walletpassphrase", RPCArg::Type::STR, /* default */ "", "If your wallet is encrypted you must have your wallet passphrase here. If your wallet is not encrypted specifying wallet passphrase will trigger wallet encryption."},
{"rescan", RPCArg::Type::BOOL, /* default */ "false if mnemonic is empty", "Whether to rescan the blockchain for missing transactions or not"},
}, },
RPCResult{ RPCResult{
RPCResult::Type::BOOL, "", "true if successful" RPCResult::Type::BOOL, "", "true if successful"
@ -2650,7 +2651,8 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
} }
// If you are generating new mnemonic it is assumed that the addresses have never gotten a transaction before, so you don't need to rescan for transactions // If you are generating new mnemonic it is assumed that the addresses have never gotten a transaction before, so you don't need to rescan for transactions
if (!generate_mnemonic) { bool rescan = request.params[3].isNull() ? !generate_mnemonic : request.params[3].get_bool();
if (rescan) {
WalletRescanReserver reserver(pwallet); WalletRescanReserver reserver(pwallet);
if (!reserver.reserve()) { if (!reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait."); throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
@ -3926,7 +3928,7 @@ static const CRPCCommand commands[] =
{ "wallet", "signmessage", &signmessage, {"address","message"} }, { "wallet", "signmessage", &signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} }, { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} }, { "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
{ "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase"} }, { "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase", "rescan"} },
{ "wallet", "walletlock", &walletlock, {} }, { "wallet", "walletlock", &walletlock, {} },
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout","mixingonly"} }, { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout","mixingonly"} },

View File

@ -90,6 +90,17 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
self.recover_non_hd() self.recover_non_hd()
self.log.info("No mnemonic, no mnemonic passphrase, no wallet passphrase, should result in completely different keys")
self.stop_node(0)
self.start_node(0, extra_args=['-keypool=10'])
assert node.upgradetohd("", "", "", True)
# Completely different keys, no HD coins should be recovered
assert mnemonic != node.dumphdinfo()['mnemonic']
assert chainid != node.getwalletinfo()['hdchainid']
assert_equal(balance_non_HD, node.getbalance())
self.recover_non_hd()
self.log.info("Same mnemonic, another mnemonic passphrase, no wallet passphrase, should result in a different set of keys") self.log.info("Same mnemonic, another mnemonic passphrase, no wallet passphrase, should result in a different set of keys")
new_mnemonic_passphrase = "somewords" new_mnemonic_passphrase = "somewords"
assert node.upgradetohd(mnemonic, new_mnemonic_passphrase) assert node.upgradetohd(mnemonic, new_mnemonic_passphrase)
@ -123,6 +134,7 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
assert_equal(mnemonic, node.dumphdinfo()['mnemonic']) assert_equal(mnemonic, node.dumphdinfo()['mnemonic'])
assert_equal(chainid, node.getwalletinfo()['hdchainid']) assert_equal(chainid, node.getwalletinfo()['hdchainid'])
node.keypoolrefill(5) node.keypoolrefill(5)
assert balance_after != node.getbalance()
node.rescanblockchain() node.rescanblockchain()
assert_equal(balance_after, node.getbalance()) assert_equal(balance_after, node.getbalance())
@ -139,6 +151,19 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
self.recover_non_hd() self.recover_non_hd()
self.log.info("Same mnemonic, no mnemonic passphrase, no wallet passphrase, large enough keepool, rescan is skipped initially, should recover all coins after rescanblockchain")
self.stop_node(0)
self.start_node(0, extra_args=['-keypool=10'])
assert node.upgradetohd(mnemonic, "", "", False)
assert_equal(mnemonic, node.dumphdinfo()['mnemonic'])
assert_equal(chainid, node.getwalletinfo()['hdchainid'])
assert balance_after != node.getbalance()
node.rescanblockchain()
# All coins should be recovered
assert_equal(balance_after, node.getbalance())
self.recover_non_hd()
self.log.info("Same mnemonic, same mnemonic passphrase, encrypt wallet on upgrade, should recover all coins after rescan") self.log.info("Same mnemonic, same mnemonic passphrase, encrypt wallet on upgrade, should recover all coins after rescan")
walletpass = "111pass222" walletpass = "111pass222"
assert node.upgradetohd(mnemonic, "", walletpass) assert node.upgradetohd(mnemonic, "", walletpass)