@@ -248,6 +248,11 @@ def test_snapshot_not_on_most_work_chain(self, dump_output_path):
248
248
node1 .submitheader (main_block1 )
249
249
node1 .submitheader (main_block2 )
250
250
251
+ def assert_only_network_limited_service (self , node ):
252
+ node_services = node .getnetworkinfo ()['localservicesnames' ]
253
+ assert 'NETWORK' not in node_services
254
+ assert 'NETWORK_LIMITED' in node_services
255
+
251
256
def run_test (self ):
252
257
"""
253
258
Bring up two (disconnected) nodes, mine some new blocks on the first,
@@ -381,13 +386,20 @@ def check_dump_output(output):
381
386
self .test_snapshot_block_invalidated (dump_output ['path' ])
382
387
self .test_snapshot_not_on_most_work_chain (dump_output ['path' ])
383
388
389
+ # Prune-node sanity check
390
+ assert 'NETWORK' not in n1 .getnetworkinfo ()['localservicesnames' ]
391
+
384
392
self .log .info (f"Loading snapshot into second node from { dump_output ['path' ]} " )
385
393
# This node's tip is on an ancestor block of the snapshot, which should
386
394
# be the normal case
387
395
loaded = n1 .loadtxoutset (dump_output ['path' ])
388
396
assert_equal (loaded ['coins_loaded' ], SNAPSHOT_BASE_HEIGHT )
389
397
assert_equal (loaded ['base_height' ], SNAPSHOT_BASE_HEIGHT )
390
398
399
+ self .log .info ("Confirm that local services remain unchanged" )
400
+ # Since n1 is a pruned node, the 'NETWORK' service flag must always be unset.
401
+ self .assert_only_network_limited_service (n1 )
402
+
391
403
self .log .info ("Check that UTXO-querying RPCs operate on snapshot chainstate" )
392
404
snapshot_hash = loaded ['tip_hash' ]
393
405
snapshot_num_coins = loaded ['coins_loaded' ]
@@ -491,6 +503,9 @@ def check_tx_counts(final: bool) -> None:
491
503
self .restart_node (1 , extra_args = [
492
504
f"-stopatheight={ PAUSE_HEIGHT } " , * self .extra_args [1 ]])
493
505
506
+ # Upon restart during snapshot tip sync, the node must remain in 'limited' mode.
507
+ self .assert_only_network_limited_service (n1 )
508
+
494
509
# Finally connect the nodes and let them sync.
495
510
#
496
511
# Set `wait_for_connect=False` to avoid a race between performing connection
@@ -507,6 +522,9 @@ def check_tx_counts(final: bool) -> None:
507
522
self .log .info ("Restarted node before snapshot validation completed, reloading..." )
508
523
self .restart_node (1 , extra_args = self .extra_args [1 ])
509
524
525
+ # Upon restart, the node must remain in 'limited' mode
526
+ self .assert_only_network_limited_service (n1 )
527
+
510
528
# Send snapshot block to n1 out of order. This makes the test less
511
529
# realistic because normally the snapshot block is one of the last
512
530
# blocks downloaded, but its useful to test because it triggers more
@@ -525,6 +543,10 @@ def check_tx_counts(final: bool) -> None:
525
543
self .log .info ("Ensuring background validation completes" )
526
544
self .wait_until (lambda : len (n1 .getchainstates ()['chainstates' ]) == 1 )
527
545
546
+ # Since n1 is a pruned node, it will not signal NODE_NETWORK after
547
+ # completing the background sync.
548
+ self .assert_only_network_limited_service (n1 )
549
+
528
550
# Ensure indexes have synced.
529
551
completed_idx_state = {
530
552
'basic block filter index' : COMPLETE_IDX ,
@@ -555,12 +577,18 @@ def check_tx_counts(final: bool) -> None:
555
577
556
578
self .log .info ("-- Testing all indexes + reindex" )
557
579
assert_equal (n2 .getblockcount (), START_HEIGHT )
580
+ assert 'NETWORK' in n2 .getnetworkinfo ()['localservicesnames' ] # sanity check
558
581
559
582
self .log .info (f"Loading snapshot into third node from { dump_output ['path' ]} " )
560
583
loaded = n2 .loadtxoutset (dump_output ['path' ])
561
584
assert_equal (loaded ['coins_loaded' ], SNAPSHOT_BASE_HEIGHT )
562
585
assert_equal (loaded ['base_height' ], SNAPSHOT_BASE_HEIGHT )
563
586
587
+ # Even though n2 is a full node, it will unset the 'NETWORK' service flag during snapshot loading.
588
+ # This indicates other peers that the node will temporarily not provide historical blocks.
589
+ self .log .info ("Check node2 updated the local services during snapshot load" )
590
+ self .assert_only_network_limited_service (n2 )
591
+
564
592
for reindex_arg in ['-reindex=1' , '-reindex-chainstate=1' ]:
565
593
self .log .info (f"Check that restarting with { reindex_arg } will delete the snapshot chainstate" )
566
594
self .restart_node (2 , extra_args = [reindex_arg , * self .extra_args [2 ]])
@@ -584,13 +612,21 @@ def check_tx_counts(final: bool) -> None:
584
612
msg = "Unable to load UTXO snapshot: Can't activate a snapshot-based chainstate more than once"
585
613
assert_raises_rpc_error (- 32603 , msg , n2 .loadtxoutset , dump_output ['path' ])
586
614
615
+ # Upon restart, the node must stay in 'limited' mode until the background
616
+ # chain sync completes.
617
+ self .restart_node (2 , extra_args = self .extra_args [2 ])
618
+ self .assert_only_network_limited_service (n2 )
619
+
587
620
self .connect_nodes (0 , 2 )
588
621
self .wait_until (lambda : n2 .getchainstates ()['chainstates' ][- 1 ]['blocks' ] == FINAL_HEIGHT )
589
622
self .sync_blocks (nodes = (n0 , n2 ))
590
623
591
624
self .log .info ("Ensuring background validation completes" )
592
625
self .wait_until (lambda : len (n2 .getchainstates ()['chainstates' ]) == 1 )
593
626
627
+ # Once background chain sync completes, the full node must start offering historical blocks again.
628
+ assert {'NETWORK' , 'NETWORK_LIMITED' }.issubset (n2 .getnetworkinfo ()['localservicesnames' ])
629
+
594
630
completed_idx_state = {
595
631
'basic block filter index' : COMPLETE_IDX ,
596
632
'coinstatsindex' : COMPLETE_IDX ,
0 commit comments