test: Fix intermittent issue in wallet_assumeutxo.py
What changed, and why it matters
This commit fixes a flaky automated test in Bitcoin Core. It replaces a complicated node restart/sync dance with a simpler helper that manually copies blocks between test nodes via RPC. There is no change to production code, no security fix, and no vulnerability.
No security action needed. Treat as routine test reliability improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch modifies only test/functional/test_framework/util.py and test/functional/wallet_assumeutxo.py. It adds a test-only helper dumb_sync_blocks() that submits blocks from one regtest node to another using submitblock, and uses it in wallet_assumeutxo.py to bring node 1 to PAUSE_HEIGHT without relying on -stopatheight and restart timing. No consensus, networking, wallet, or RPC production code is touched.
Changed components
test/functional/test_framework/util.pytest/functional/wallet_assumeutxo.pyInspect captured patch +19 / −23
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index d246a744..3a6346a3 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -721,6 +721,16 @@ def find_vout_for_address(node, txid, addr):
raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr))
+def dumb_sync_blocks(*, src, dst, height=None):
+ """Sync blocks between `src` and `dst` nodes via RPC submitblock up to height."""
+ height = height or src.getblockcount()
+ for i in range(dst.getblockcount() + 1, height + 1):
+ block_hash = src.getblockhash(i)
+ block = src.getblock(blockhash=block_hash, verbose=0)
+ dst.submitblock(block)
+ assert_equal(dst.getblockcount(), height)
+
+
def sync_txindex(test_framework, node):
test_framework.log.debug("Waiting for node txindex to sync")
sync_start = int(time.time())
diff --git a/test/functional/wallet_assumeutxo.py b/test/functional/wallet_assumeutxo.py
index 926d4b16..355fc32b 100755
--- a/test/functional/wallet_assumeutxo.py
+++ b/test/functional/wallet_assumeutxo.py
@@ -12,8 +12,8 @@ from test_framework.messages import COIN
from test_framework.util import (
assert_equal,
assert_greater_than,
- assert_greater_than_or_equal,
assert_raises_rpc_error,
+ dumb_sync_blocks,
ensure_for,
)
from test_framework.wallet import MiniWallet
@@ -42,8 +42,8 @@ class AssumeutxoTest(BitcoinTestFramework):
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(4)
- self.start_nodes(extra_args=self.extra_args)
+ self.add_nodes(self.num_nodes, self.extra_args)
+ self.start_nodes()
def import_descriptor(self, node, wallet_name, key, timestamp):
import_request = [{"desc": descsum_create("pkh(" + key.pubkey + ")"),
@@ -227,32 +227,18 @@ class AssumeutxoTest(BitcoinTestFramework):
PAUSE_HEIGHT = FINAL_HEIGHT - 40
- self.log.info("Restarting node to stop at height %d", PAUSE_HEIGHT)
- self.restart_node(1, extra_args=[
- f"-stopatheight={PAUSE_HEIGHT}", *self.extra_args[1]])
+ self.log.info(f"Unload wallets and sync node up to height {PAUSE_HEIGHT}")
+ n1.unloadwallet("w")
+ n1.unloadwallet(wallet_name)
+ dumb_sync_blocks(src=n0, dst=n1, height=PAUSE_HEIGHT)
- # Finally connect the nodes and let them sync.
- #
- # Set `wait_for_connect=False` to avoid a race between performing connection
- # assertions and the -stopatheight tripping.
- self.connect_nodes(0, 1, wait_for_connect=False)
-
- n1.wait_until_stopped(timeout=5)
-
- self.log.info(
- "Restarted node before snapshot validation completed, reloading...")
- self.restart_node(1, extra_args=self.extra_args[1])
-
- self.log.info("Verify node state after restart during background sync")
+ self.log.info("Verify node state during background sync")
# Verify there are still two chainstates (background validation not complete)
chainstates = n1.getchainstates()['chainstates']
assert_equal(len(chainstates), 2)
# The background chainstate should still be at START_HEIGHT
assert_equal(chainstates[0]['blocks'], START_HEIGHT)
- # The snapshot chainstate should be at least PAUSE_HEIGHT. It may be
- # higher because stopatheight may allow additional blocks to be
- # processed during shutdown (per stopatheight documentation).
- assert_greater_than_or_equal(chainstates[1]['blocks'], PAUSE_HEIGHT)
+ assert_equal(chainstates[1]["blocks"], PAUSE_HEIGHT)
# After restart, wallets that existed before cannot be loaded because
# the wallet loading code checks if required blocks are available for
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.