@@ -247,6 +247,11 @@ def test_snapshot_not_on_most_work_chain(self, dump_output_path):
247247 node1 .submitheader (main_block1 )
248248 node1 .submitheader (main_block2 )
249249
250+ def assert_only_network_limited_service (self , node ):
251+ node_services = node .getnetworkinfo ()['localservicesnames' ]
252+ assert 'NETWORK' not in node_services
253+ assert 'NETWORK_LIMITED' in node_services
254+
250255 def run_test (self ):
251256 """
252257 Bring up two (disconnected) nodes, mine some new blocks on the first,
@@ -343,13 +348,20 @@ def run_test(self):
343348 self .test_snapshot_block_invalidated (dump_output ['path' ])
344349 self .test_snapshot_not_on_most_work_chain (dump_output ['path' ])
345350
351+ # Prune-node sanity check
352+ assert 'NETWORK' not in n1 .getnetworkinfo ()['localservicesnames' ]
353+
346354 self .log .info (f"Loading snapshot into second node from { dump_output ['path' ]} " )
347355 # This node's tip is on an ancestor block of the snapshot, which should
348356 # be the normal case
349357 loaded = n1 .loadtxoutset (dump_output ['path' ])
350358 assert_equal (loaded ['coins_loaded' ], SNAPSHOT_BASE_HEIGHT )
351359 assert_equal (loaded ['base_height' ], SNAPSHOT_BASE_HEIGHT )
352360
361+ self .log .info ("Confirm that local services remain unchanged" )
362+ # Since n1 is a pruned node, the 'NETWORK' service flag must always be unset.
363+ self .assert_only_network_limited_service (n1 )
364+
353365 self .log .info ("Check that UTXO-querying RPCs operate on snapshot chainstate" )
354366 snapshot_hash = loaded ['tip_hash' ]
355367 snapshot_num_coins = loaded ['coins_loaded' ]
@@ -453,6 +465,9 @@ def check_tx_counts(final: bool) -> None:
453465 self .restart_node (1 , extra_args = [
454466 f"-stopatheight={ PAUSE_HEIGHT } " , * self .extra_args [1 ]])
455467
468+ # Upon restart during snapshot tip sync, the node must remain in 'limited' mode.
469+ self .assert_only_network_limited_service (n1 )
470+
456471 # Finally connect the nodes and let them sync.
457472 #
458473 # Set `wait_for_connect=False` to avoid a race between performing connection
@@ -469,6 +484,9 @@ def check_tx_counts(final: bool) -> None:
469484 self .log .info ("Restarted node before snapshot validation completed, reloading..." )
470485 self .restart_node (1 , extra_args = self .extra_args [1 ])
471486
487+ # Upon restart, the node must remain in 'limited' mode
488+ self .assert_only_network_limited_service (n1 )
489+
472490 # Send snapshot block to n1 out of order. This makes the test less
473491 # realistic because normally the snapshot block is one of the last
474492 # blocks downloaded, but its useful to test because it triggers more
@@ -487,6 +505,10 @@ def check_tx_counts(final: bool) -> None:
487505 self .log .info ("Ensuring background validation completes" )
488506 self .wait_until (lambda : len (n1 .getchainstates ()['chainstates' ]) == 1 )
489507
508+ # Since n1 is a pruned node, it will not signal NODE_NETWORK after
509+ # completing the background sync.
510+ self .assert_only_network_limited_service (n1 )
511+
490512 # Ensure indexes have synced.
491513 completed_idx_state = {
492514 'basic block filter index' : COMPLETE_IDX ,
@@ -517,12 +539,18 @@ def check_tx_counts(final: bool) -> None:
517539
518540 self .log .info ("-- Testing all indexes + reindex" )
519541 assert_equal (n2 .getblockcount (), START_HEIGHT )
542+ assert 'NETWORK' in n2 .getnetworkinfo ()['localservicesnames' ] # sanity check
520543
521544 self .log .info (f"Loading snapshot into third node from { dump_output ['path' ]} " )
522545 loaded = n2 .loadtxoutset (dump_output ['path' ])
523546 assert_equal (loaded ['coins_loaded' ], SNAPSHOT_BASE_HEIGHT )
524547 assert_equal (loaded ['base_height' ], SNAPSHOT_BASE_HEIGHT )
525548
549+ # Even though n2 is a full node, it will unset the 'NETWORK' service flag during snapshot loading.
550+ # This indicates other peers that the node will temporarily not provide historical blocks.
551+ self .log .info ("Check node2 updated the local services during snapshot load" )
552+ self .assert_only_network_limited_service (n2 )
553+
526554 for reindex_arg in ['-reindex=1' , '-reindex-chainstate=1' ]:
527555 self .log .info (f"Check that restarting with { reindex_arg } will delete the snapshot chainstate" )
528556 self .restart_node (2 , extra_args = [reindex_arg , * self .extra_args [2 ]])
@@ -546,13 +574,21 @@ def check_tx_counts(final: bool) -> None:
546574 msg = "Unable to load UTXO snapshot: Can't activate a snapshot-based chainstate more than once"
547575 assert_raises_rpc_error (- 32603 , msg , n2 .loadtxoutset , dump_output ['path' ])
548576
577+ # Upon restart, the node must stay in 'limited' mode until the background
578+ # chain sync completes.
579+ self .restart_node (2 , extra_args = self .extra_args [2 ])
580+ self .assert_only_network_limited_service (n2 )
581+
549582 self .connect_nodes (0 , 2 )
550583 self .wait_until (lambda : n2 .getchainstates ()['chainstates' ][- 1 ]['blocks' ] == FINAL_HEIGHT )
551584 self .sync_blocks (nodes = (n0 , n2 ))
552585
553586 self .log .info ("Ensuring background validation completes" )
554587 self .wait_until (lambda : len (n2 .getchainstates ()['chainstates' ]) == 1 )
555588
589+ # Once background chain sync completes, the full node must start offering historical blocks again.
590+ assert {'NETWORK' , 'NETWORK_LIMITED' }.issubset (n2 .getnetworkinfo ()['localservicesnames' ])
591+
556592 completed_idx_state = {
557593 'basic block filter index' : COMPLETE_IDX ,
558594 'coinstatsindex' : COMPLETE_IDX ,
0 commit comments