mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-18 22:35:39 +01:00
Merge bitcoin/bitcoin#26347: wallet: ensure the wallet is unlocked when needed for rescanning
6a5b348f2etest: test rescanning encrypted wallets (ishaanam)493b813e17wallet: ensure that the passphrase is not deleted from memory when being used to rescan (ishaanam)66a86ebabbwallet: keep track of when the passphrase is needed when rescanning (ishaanam) Pull request description: Wallet passphrases are needed to top up the keypool of encrypted wallets during a rescan. The following RPCs need the passphrase when rescanning: - `importdescriptors` - `rescanblockchain` The following RPCs use the information about whether or not the passphrase is being used to ensure that full rescans are able to take place (meaning the following RPCs should not be able to run if a rescan requiring the wallet to be unlocked is taking place): - `walletlock` - `encryptwallet` - `walletpassphrasechange` `m_relock_mutex` is also introduced so that the passphrase is not deleted from memory when the timeout provided in `walletpassphrase` is up and the wallet is still rescanning. Fixes #25702, #11249 Thanks to achow101 for coming up with the idea of using a new mutex to solve this issue and for answering related questions. ACKs for top commit: achow101: ACK6a5b348f2ehernanmarino: ACK6a5b348f2efurszy: Tested ACK6a5b348fTree-SHA512: 0b6db692714f6f94594fa47249f5ee24f85713bfa70ac295a7e84b9ca6c07dda65df7b47781a2dc73e5b603a8725343a2f864428ae20d3e126c5b4802abc4ab5
This commit is contained in:
@@ -667,5 +667,33 @@ class ImportDescriptorsTest(BitcoinTestFramework):
|
||||
success=True,
|
||||
warnings=["Unknown output type, cannot set descriptor to active."])
|
||||
|
||||
self.log.info("Test importing a descriptor to an encrypted wallet")
|
||||
|
||||
descriptor = {"desc": descsum_create("pkh(" + xpriv + "/1h/*h)"),
|
||||
"timestamp": "now",
|
||||
"active": True,
|
||||
"range": [0,4000],
|
||||
"next_index": 4000}
|
||||
|
||||
self.nodes[0].createwallet("temp_wallet", blank=True, descriptors=True)
|
||||
temp_wallet = self.nodes[0].get_wallet_rpc("temp_wallet")
|
||||
temp_wallet.importdescriptors([descriptor])
|
||||
self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
|
||||
self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
|
||||
|
||||
self.nodes[0].createwallet("encrypted_wallet", blank=True, descriptors=True, passphrase="passphrase")
|
||||
encrypted_wallet = self.nodes[0].get_wallet_rpc("encrypted_wallet")
|
||||
|
||||
descriptor["timestamp"] = 0
|
||||
descriptor["next_index"] = 0
|
||||
|
||||
batch = []
|
||||
batch.append(encrypted_wallet.walletpassphrase.get_request("passphrase", 3))
|
||||
batch.append(encrypted_wallet.importdescriptors.get_request([descriptor]))
|
||||
|
||||
encrypted_wallet.batch(batch)
|
||||
|
||||
assert_equal(temp_wallet.getbalance(), encrypted_wallet.getbalance())
|
||||
|
||||
if __name__ == '__main__':
|
||||
ImportDescriptorsTest().main()
|
||||
|
||||
@@ -14,6 +14,9 @@ from test_framework.util import (
|
||||
assert_raises_rpc_error,
|
||||
set_node_times,
|
||||
)
|
||||
from test_framework.wallet_util import (
|
||||
get_generate_key,
|
||||
)
|
||||
|
||||
|
||||
class TransactionTimeRescanTest(BitcoinTestFramework):
|
||||
@@ -23,6 +26,10 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = False
|
||||
self.num_nodes = 3
|
||||
self.extra_args = [["-keypool=400"],
|
||||
["-keypool=400"],
|
||||
[]
|
||||
]
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
@@ -167,6 +174,38 @@ class TransactionTimeRescanTest(BitcoinTestFramework):
|
||||
assert_raises_rpc_error(-8, "Invalid stop_height", restorewo_wallet.rescanblockchain, 1, -1)
|
||||
assert_raises_rpc_error(-8, "stop_height must be greater than start_height", restorewo_wallet.rescanblockchain, 20, 10)
|
||||
|
||||
self.log.info("Test `rescanblockchain` fails when wallet is encrypted and locked")
|
||||
usernode.createwallet(wallet_name="enc_wallet", passphrase="passphrase")
|
||||
enc_wallet = usernode.get_wallet_rpc("enc_wallet")
|
||||
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", enc_wallet.rescanblockchain)
|
||||
|
||||
if not self.options.descriptors:
|
||||
self.log.info("Test rescanning an encrypted wallet")
|
||||
hd_seed = get_generate_key().privkey
|
||||
|
||||
usernode.createwallet(wallet_name="temp_wallet", blank=True, descriptors=False)
|
||||
temp_wallet = usernode.get_wallet_rpc("temp_wallet")
|
||||
temp_wallet.sethdseed(seed=hd_seed)
|
||||
|
||||
for i in range(399):
|
||||
temp_wallet.getnewaddress()
|
||||
|
||||
self.generatetoaddress(usernode, COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
|
||||
self.generatetoaddress(usernode, COINBASE_MATURITY + 1, temp_wallet.getnewaddress())
|
||||
|
||||
minernode.createwallet("encrypted_wallet", blank=True, passphrase="passphrase", descriptors=False)
|
||||
encrypted_wallet = minernode.get_wallet_rpc("encrypted_wallet")
|
||||
|
||||
encrypted_wallet.walletpassphrase("passphrase", 1)
|
||||
encrypted_wallet.sethdseed(seed=hd_seed)
|
||||
|
||||
batch = []
|
||||
batch.append(encrypted_wallet.walletpassphrase.get_request("passphrase", 3))
|
||||
batch.append(encrypted_wallet.rescanblockchain.get_request())
|
||||
|
||||
encrypted_wallet.batch(batch)
|
||||
|
||||
assert_equal(encrypted_wallet.getbalance(), temp_wallet.getbalance())
|
||||
|
||||
if __name__ == '__main__':
|
||||
TransactionTimeRescanTest().main()
|
||||
|
||||
Reference in New Issue
Block a user