mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-13 11:35:20 +01:00
test, assumeutxo: loading a wallet (backup) on a pruned node
This commit is contained in:
parent
85f96b01b7
commit
bdec262bf6
1 changed files with 67 additions and 37 deletions
|
@ -4,11 +4,6 @@
|
|||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test for assumeutxo wallet related behavior.
|
||||
See feature_assumeutxo.py for background.
|
||||
|
||||
## Possible test improvements
|
||||
|
||||
- TODO: test loading a wallet (backup) on a pruned node
|
||||
|
||||
"""
|
||||
from test_framework.address import address_to_scriptpubkey
|
||||
from test_framework.descriptors import descsum_create
|
||||
|
@ -16,6 +11,7 @@ from test_framework.test_framework import BitcoinTestFramework
|
|||
from test_framework.messages import COIN
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_greater_than,
|
||||
assert_raises_rpc_error,
|
||||
ensure_for,
|
||||
)
|
||||
|
@ -36,18 +32,19 @@ class AssumeutxoTest(BitcoinTestFramework):
|
|||
|
||||
def set_test_params(self):
|
||||
"""Use the pregenerated, deterministic chain up to height 199."""
|
||||
self.num_nodes = 3
|
||||
self.num_nodes = 4
|
||||
self.rpc_timeout = 120
|
||||
self.extra_args = [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
["-fastprune", "-prune=1"]
|
||||
]
|
||||
|
||||
def setup_network(self):
|
||||
"""Start with the nodes disconnected so that one can generate a snapshot
|
||||
including blocks the other hasn't yet seen."""
|
||||
self.add_nodes(3)
|
||||
self.add_nodes(4)
|
||||
self.start_nodes(extra_args=self.extra_args)
|
||||
|
||||
def import_descriptor(self, node, wallet_name, key, timestamp):
|
||||
|
@ -57,17 +54,42 @@ class AssumeutxoTest(BitcoinTestFramework):
|
|||
wrpc = node.get_wallet_rpc(wallet_name)
|
||||
return wrpc.importdescriptors(import_request)
|
||||
|
||||
def validate_snapshot_import(self, node, loaded, base_hash):
|
||||
assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT)
|
||||
assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT)
|
||||
|
||||
normal, snapshot = node.getchainstates()["chainstates"]
|
||||
assert_equal(normal['blocks'], START_HEIGHT)
|
||||
assert 'snapshot_blockhash' not in normal
|
||||
assert_equal(normal['validated'], True)
|
||||
assert_equal(snapshot['blocks'], SNAPSHOT_BASE_HEIGHT)
|
||||
assert_equal(snapshot['snapshot_blockhash'], base_hash)
|
||||
assert_equal(snapshot['validated'], False)
|
||||
|
||||
assert_equal(node.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT)
|
||||
|
||||
def complete_background_validation(self, node):
|
||||
self.connect_nodes(0, node.index)
|
||||
|
||||
# Ensuring snapshot chain syncs to tip
|
||||
self.wait_until(lambda: node.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT)
|
||||
self.sync_blocks(nodes=(self.nodes[0], node))
|
||||
|
||||
# Ensuring background validation completes
|
||||
self.wait_until(lambda: len(node.getchainstates()['chainstates']) == 1)
|
||||
|
||||
def run_test(self):
|
||||
"""
|
||||
Bring up two (disconnected) nodes, mine some new blocks on the first,
|
||||
and generate a UTXO snapshot.
|
||||
|
||||
Load the snapshot into the second, ensure it syncs to tip and completes
|
||||
background validation when connected to the first.
|
||||
Bring up four (disconnected) nodes:
|
||||
- n0: mine some blocks and create a UTXO snapshot
|
||||
- n1: load the snapshot and test loading a wallet backup and descriptors during and after background sync
|
||||
- n2: load the snapshot and check the wallet balance during background sync
|
||||
- n3: load the snapshot, prune the chain, and test loading a wallet backup during and after background sync
|
||||
"""
|
||||
n0 = self.nodes[0]
|
||||
n1 = self.nodes[1]
|
||||
n2 = self.nodes[2]
|
||||
n3 = self.nodes[3]
|
||||
|
||||
self.mini_wallet = MiniWallet(n0)
|
||||
|
||||
|
@ -100,7 +122,7 @@ class AssumeutxoTest(BitcoinTestFramework):
|
|||
# make n1 aware of the new header, but don't give it the block.
|
||||
n1.submitheader(newblock)
|
||||
n2.submitheader(newblock)
|
||||
|
||||
n3.submitheader(newblock)
|
||||
# Ensure everyone is seeing the same headers.
|
||||
for n in self.nodes:
|
||||
assert_equal(n.getblockchaininfo()[
|
||||
|
@ -144,26 +166,27 @@ class AssumeutxoTest(BitcoinTestFramework):
|
|||
self.log.info(
|
||||
f"Loading snapshot into second node from {dump_output['path']}")
|
||||
loaded = n1.loadtxoutset(dump_output['path'])
|
||||
assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT)
|
||||
assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT)
|
||||
|
||||
normal, snapshot = n1.getchainstates()["chainstates"]
|
||||
assert_equal(normal['blocks'], START_HEIGHT)
|
||||
assert_equal(normal.get('snapshot_blockhash'), None)
|
||||
assert_equal(normal['validated'], True)
|
||||
assert_equal(snapshot['blocks'], SNAPSHOT_BASE_HEIGHT)
|
||||
assert_equal(snapshot['snapshot_blockhash'], dump_output['base_hash'])
|
||||
assert_equal(snapshot['validated'], False)
|
||||
|
||||
assert_equal(n1.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT)
|
||||
self.validate_snapshot_import(n1, loaded, dump_output['base_hash'])
|
||||
|
||||
self.log.info("Backup from the snapshot height can be loaded during background sync")
|
||||
n1.restorewallet("w", "backup_w.dat")
|
||||
# Balance of w wallet is still still 0 because n1 has not synced yet
|
||||
# Balance of w wallet is still 0 because n1 has not synced yet
|
||||
assert_equal(n1.getbalance(), 0)
|
||||
|
||||
self.log.info("Backup from before the snapshot height can't be loaded during background sync")
|
||||
assert_raises_rpc_error(-4, "Wallet loading failed. Error loading wallet. Wallet requires blocks to be downloaded, and software does not currently support loading wallets while blocks are being downloaded out of order when using assumeutxo snapshots. Wallet should be able to load successfully after node sync reaches height 299", n1.restorewallet, "w2", "backup_w2.dat")
|
||||
error_message = "Wallet loading failed. Error loading wallet. Wallet requires blocks to be downloaded, and software does not currently support loading wallets while blocks are being downloaded out of order when using assumeutxo snapshots. Wallet should be able to load successfully after node sync reaches height 299"
|
||||
assert_raises_rpc_error(-4, error_message, n1.restorewallet, "w2", "backup_w2.dat")
|
||||
|
||||
self.log.info("Backup from the snapshot height can be loaded during background sync (pruned node)")
|
||||
loaded = n3.loadtxoutset(dump_output['path'])
|
||||
assert_greater_than(n3.pruneblockchain(START_HEIGHT), 0)
|
||||
self.validate_snapshot_import(n3, loaded, dump_output['base_hash'])
|
||||
n3.restorewallet("w", "backup_w.dat")
|
||||
# Balance of w wallet is still 0 because n3 has not synced yet
|
||||
assert_equal(n3.getbalance(), 0)
|
||||
|
||||
self.log.info("Backup from before the snapshot height can't be loaded during background sync (pruned node)")
|
||||
assert_raises_rpc_error(-4, error_message, n3.restorewallet, "w2", "backup_w2.dat")
|
||||
|
||||
self.log.info("Test loading descriptors during background sync")
|
||||
wallet_name = "w1"
|
||||
|
@ -199,16 +222,7 @@ class AssumeutxoTest(BitcoinTestFramework):
|
|||
self.restart_node(1, extra_args=self.extra_args[1])
|
||||
|
||||
# TODO: inspect state of e.g. the wallet before reconnecting
|
||||
self.connect_nodes(0, 1)
|
||||
|
||||
self.log.info(
|
||||
f"Ensuring snapshot chain syncs to tip. ({FINAL_HEIGHT})")
|
||||
self.wait_until(lambda: n1.getchainstates()[
|
||||
'chainstates'][-1]['blocks'] == FINAL_HEIGHT)
|
||||
self.sync_blocks(nodes=(n0, n1))
|
||||
|
||||
self.log.info("Ensuring background validation completes")
|
||||
self.wait_until(lambda: len(n1.getchainstates()['chainstates']) == 1)
|
||||
self.complete_background_validation(n1)
|
||||
|
||||
self.log.info("Ensuring wallet can be restored from a backup that was created before the snapshot height")
|
||||
n1.restorewallet("w2", "backup_w2.dat")
|
||||
|
@ -232,6 +246,22 @@ class AssumeutxoTest(BitcoinTestFramework):
|
|||
result = self.import_descriptor(n1, wallet_name, key, timestamp)
|
||||
assert_equal(result[0]['success'], True)
|
||||
|
||||
self.log.info("Ensuring wallet can't be restored from a backup that was created before the pruneheight (pruned node)")
|
||||
self.complete_background_validation(n3)
|
||||
# After background sync, pruneheight is reset to 0, so mine 500 blocks
|
||||
# and prune the chain again
|
||||
self.generate(n3, nblocks=500, sync_fun=self.no_op)
|
||||
assert_equal(n3.pruneblockchain(FINAL_HEIGHT), 298) # 298 is the height of the last block pruned (pruneheight 299)
|
||||
error_message = "Wallet loading failed. Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"
|
||||
# This backup (backup_w2.dat) was created at height 199, so it can't be restored in a node with a pruneheight of 299
|
||||
assert_raises_rpc_error(-4, error_message, n3.restorewallet, "w2", "backup_w2.dat")
|
||||
|
||||
self.log.info("Ensuring wallet can be restored from a backup that was created at the pruneheight (pruned node)")
|
||||
# This backup (backup_w.dat) was created at height 299, so it can be restored in a node with a pruneheight of 299
|
||||
n3.restorewallet("w_alt", "backup_w.dat")
|
||||
# Check balance of w_alt wallet
|
||||
w_alt = n3.get_wallet_rpc("w_alt")
|
||||
assert_equal(w_alt.getbalance(), 34)
|
||||
|
||||
if __name__ == '__main__':
|
||||
AssumeutxoTest(__file__).main()
|
||||
|
|
Loading…
Add table
Reference in a new issue