Skip to content

Commit 394651f

Browse files
committed
Merge bitcoin/bitcoin#29996: Assumeutxo: bugfix on loadtxoutset with a divergent chain + test
5b7f70b test: loadtxoutset in divergent chain with less work (Alfonso Roman Zubeldia) d35efe1 p2p: Start downloading historical blocks from common ancestor (Martin Zumsande) Pull request description: This PR adds a test to cover the scenario of loading an assumeutxo snapshot when the current chain tip is not an ancestor of the snapshot block but has less work. During the review process, a bug was discovered where blocks between the last common ancestor and the background tip were not being requested if the background tip was not an ancestor of the snapshot block. mzumsande suggested a fix (65343ec49a6b73c4197dfc38e1c2f433b0a3838a) to start downloading historical blocks from the last common ancestor to address this issue. This fix has been incorporated into the PR with a slight modification. Related to bitcoin/bitcoin#28648 ACKs for top commit: fjahr: tACK 5b7f70b achow101: ACK 5b7f70b mzumsande: Code Review ACK 5b7f70b Tree-SHA512: f8957349686a6a1292165ea9e0fd8c912d21466072632a10f8ef9d852a5f430bc6b2a531e6884a4dbf2e3adb28b3d512b25919e78f5804a67320ef54c3b1aaf6
2 parents 45f757c + 5b7f70b commit 394651f

File tree

2 files changed

+35
-5
lines changed

2 files changed

+35
-5
lines changed

src/net_processing.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6244,10 +6244,13 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
62446244
// before the background chainstate to prioritize getting to network tip.
62456245
FindNextBlocksToDownload(*peer, get_inflight_budget(), vToDownload, staller);
62466246
if (m_chainman.BackgroundSyncInProgress() && !IsLimitedPeer(*peer)) {
6247+
// If the background tip is not an ancestor of the snapshot block,
6248+
// we need to start requesting blocks from their last common ancestor.
6249+
const CBlockIndex *from_tip = LastCommonAncestor(m_chainman.GetBackgroundSyncTip(), m_chainman.GetSnapshotBaseBlock());
62476250
TryDownloadingHistoricalBlocks(
62486251
*peer,
62496252
get_inflight_budget(),
6250-
vToDownload, m_chainman.GetBackgroundSyncTip(),
6253+
vToDownload, from_tip,
62516254
Assert(m_chainman.GetSnapshotBaseBlock()));
62526255
}
62536256
for (const CBlockIndex *pindex : vToDownload) {

test/functional/feature_assumeutxo.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
Interesting starting states could be loading a snapshot when the current chain tip is:
2222
2323
- TODO: An ancestor of snapshot block
24-
- TODO: Not an ancestor of the snapshot block but has less work
2524
- TODO: The snapshot block
2625
- TODO: A descendant of the snapshot block
2726
- TODO: Not an ancestor or a descendant of the snapshot block and has more work
@@ -52,18 +51,19 @@ class AssumeutxoTest(BitcoinTestFramework):
5251

5352
def set_test_params(self):
5453
"""Use the pregenerated, deterministic chain up to height 199."""
55-
self.num_nodes = 3
54+
self.num_nodes = 4
5655
self.rpc_timeout = 120
5756
self.extra_args = [
5857
[],
5958
["-fastprune", "-prune=1", "-blockfilterindex=1", "-coinstatsindex=1"],
6059
["-persistmempool=0","-txindex=1", "-blockfilterindex=1", "-coinstatsindex=1"],
60+
[]
6161
]
6262

6363
def setup_network(self):
6464
"""Start with the nodes disconnected so that one can generate a snapshot
6565
including blocks the other hasn't yet seen."""
66-
self.add_nodes(3)
66+
self.add_nodes(4)
6767
self.start_nodes(extra_args=self.extra_args)
6868

6969
def test_invalid_snapshot_scenarios(self, valid_snapshot_path):
@@ -218,6 +218,29 @@ def test_snapshot_block_invalidated(self, dump_output_path):
218218
assert_raises_rpc_error(-32603, msg, node.loadtxoutset, dump_output_path)
219219
node.reconsiderblock(block_hash)
220220

221+
def test_snapshot_in_a_divergent_chain(self, dump_output_path):
222+
n0 = self.nodes[0]
223+
n3 = self.nodes[3]
224+
assert_equal(n0.getblockcount(), FINAL_HEIGHT)
225+
assert_equal(n3.getblockcount(), START_HEIGHT)
226+
227+
self.log.info("Check importing a snapshot where current chain-tip is not an ancestor of the snapshot block but has less work")
228+
# Generate a divergent chain in n3 up to 298
229+
self.generate(n3, nblocks=99, sync_fun=self.no_op)
230+
assert_equal(n3.getblockcount(), SNAPSHOT_BASE_HEIGHT - 1)
231+
232+
# Try importing the snapshot and assert its success
233+
loaded = n3.loadtxoutset(dump_output_path)
234+
assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT)
235+
normal, snapshot = n3.getchainstates()["chainstates"]
236+
assert_equal(normal['blocks'], START_HEIGHT + 99)
237+
assert_equal(snapshot['blocks'], SNAPSHOT_BASE_HEIGHT)
238+
239+
# Now lets sync the nodes and wait for the background validation to finish
240+
self.connect_nodes(0, 3)
241+
self.sync_blocks(nodes=(n0, n3))
242+
self.wait_until(lambda: len(n3.getchainstates()['chainstates']) == 1)
243+
221244
def run_test(self):
222245
"""
223246
Bring up two (disconnected) nodes, mine some new blocks on the first,
@@ -229,6 +252,7 @@ def run_test(self):
229252
n0 = self.nodes[0]
230253
n1 = self.nodes[1]
231254
n2 = self.nodes[2]
255+
n3 = self.nodes[3]
232256

233257
self.mini_wallet = MiniWallet(n0)
234258

@@ -279,6 +303,7 @@ def run_test(self):
279303
# block.
280304
n1.submitheader(block)
281305
n2.submitheader(block)
306+
n3.submitheader(block)
282307

283308
# Ensure everyone is seeing the same headers.
284309
for n in self.nodes:
@@ -484,7 +509,7 @@ def check_tx_counts(final: bool) -> None:
484509

485510
self.connect_nodes(0, 2)
486511
self.wait_until(lambda: n2.getchainstates()['chainstates'][-1]['blocks'] == FINAL_HEIGHT)
487-
self.sync_blocks()
512+
self.sync_blocks(nodes=(n0, n2))
488513

489514
self.log.info("Ensuring background validation completes")
490515
self.wait_until(lambda: len(n2.getchainstates()['chainstates']) == 1)
@@ -521,6 +546,8 @@ def check_tx_counts(final: bool) -> None:
521546
self.connect_nodes(0, 2)
522547
self.wait_until(lambda: n2.getblockcount() == FINAL_HEIGHT)
523548

549+
self.test_snapshot_in_a_divergent_chain(dump_output['path'])
550+
524551
@dataclass
525552
class Block:
526553
hash: str

0 commit comments

Comments
 (0)