|
15 | 15 |
|
16 | 16 | - TODO: Valid snapshot file, but referencing a snapshot block that turns out to be
|
17 | 17 | invalid, or has an invalid parent
|
18 |
| -- TODO: Valid snapshot file and snapshot block, but the block is not on the |
19 |
| - most-work chain |
20 | 18 |
|
21 | 19 | Interesting starting states could be loading a snapshot when the current chain tip is:
|
22 | 20 |
|
23 | 21 | - TODO: An ancestor of snapshot block
|
24 | 22 | - TODO: The snapshot block
|
25 | 23 | - TODO: A descendant of the snapshot block
|
26 |
| -- TODO: Not an ancestor or a descendant of the snapshot block and has more work |
27 | 24 |
|
28 | 25 | """
|
29 | 26 | from shutil import rmtree
|
30 | 27 |
|
31 | 28 | from dataclasses import dataclass
|
| 29 | +from test_framework.blocktools import ( |
| 30 | + create_block, |
| 31 | + create_coinbase |
| 32 | +) |
32 | 33 | from test_framework.messages import tx_from_hex
|
33 | 34 | from test_framework.test_framework import BitcoinTestFramework
|
34 | 35 | from test_framework.util import (
|
@@ -241,6 +242,30 @@ def test_snapshot_in_a_divergent_chain(self, dump_output_path):
|
241 | 242 | self.sync_blocks(nodes=(n0, n3))
|
242 | 243 | self.wait_until(lambda: len(n3.getchainstates()['chainstates']) == 1)
|
243 | 244 |
|
| 245 | + def test_snapshot_not_on_most_work_chain(self, dump_output_path): |
| 246 | + self.log.info("Test snapshot is not loaded when the node knows the headers of another chain with more work.") |
| 247 | + node0 = self.nodes[0] |
| 248 | + node1 = self.nodes[1] |
| 249 | + # Create an alternative chain of 2 new blocks, forking off the main chain at the block before the snapshot block. |
| 250 | + # This simulates a longer chain than the main chain when submitting these two block headers to node 1 because it is only aware of |
| 251 | + # the main chain headers up to the snapshot height. |
| 252 | + parent_block_hash = node0.getblockhash(SNAPSHOT_BASE_HEIGHT - 1) |
| 253 | + block_time = node0.getblock(node0.getbestblockhash())['time'] + 1 |
| 254 | + fork_block1 = create_block(int(parent_block_hash, 16), create_coinbase(SNAPSHOT_BASE_HEIGHT), block_time) |
| 255 | + fork_block1.solve() |
| 256 | + fork_block2 = create_block(fork_block1.sha256, create_coinbase(SNAPSHOT_BASE_HEIGHT + 1), block_time + 1) |
| 257 | + fork_block2.solve() |
| 258 | + node1.submitheader(fork_block1.serialize().hex()) |
| 259 | + node1.submitheader(fork_block2.serialize().hex()) |
| 260 | + msg = "A forked headers-chain with more work than the chain with the snapshot base block header exists. Please proceed to sync without AssumeUtxo." |
| 261 | + assert_raises_rpc_error(-32603, msg, node1.loadtxoutset, dump_output_path) |
| 262 | + # Cleanup: submit two more headers of the snapshot chain to node 1, so that it is the most-work chain again and loading |
| 263 | + # the snapshot in future subtests succeeds |
| 264 | + main_block1 = node0.getblock(node0.getblockhash(SNAPSHOT_BASE_HEIGHT + 1), 0) |
| 265 | + main_block2 = node0.getblock(node0.getblockhash(SNAPSHOT_BASE_HEIGHT + 2), 0) |
| 266 | + node1.submitheader(main_block1) |
| 267 | + node1.submitheader(main_block2) |
| 268 | + |
244 | 269 | def run_test(self):
|
245 | 270 | """
|
246 | 271 | Bring up two (disconnected) nodes, mine some new blocks on the first,
|
@@ -330,6 +355,7 @@ def run_test(self):
|
330 | 355 | self.test_invalid_chainstate_scenarios()
|
331 | 356 | self.test_invalid_file_path()
|
332 | 357 | self.test_snapshot_block_invalidated(dump_output['path'])
|
| 358 | + self.test_snapshot_not_on_most_work_chain(dump_output['path']) |
333 | 359 |
|
334 | 360 | self.log.info(f"Loading snapshot into second node from {dump_output['path']}")
|
335 | 361 | loaded = n1.loadtxoutset(dump_output['path'])
|
|
0 commit comments