@@ -247,6 +247,11 @@ def test_snapshot_not_on_most_work_chain(self, dump_output_path):
247
247
node1 .submitheader (main_block1 )
248
248
node1 .submitheader (main_block2 )
249
249
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
+
250
255
def run_test (self ):
251
256
"""
252
257
Bring up two (disconnected) nodes, mine some new blocks on the first,
@@ -343,13 +348,20 @@ def run_test(self):
343
348
self .test_snapshot_block_invalidated (dump_output ['path' ])
344
349
self .test_snapshot_not_on_most_work_chain (dump_output ['path' ])
345
350
351
+ # Prune-node sanity check
352
+ assert 'NETWORK' not in n1 .getnetworkinfo ()['localservicesnames' ]
353
+
346
354
self .log .info (f"Loading snapshot into second node from { dump_output ['path' ]} " )
347
355
# This node's tip is on an ancestor block of the snapshot, which should
348
356
# be the normal case
349
357
loaded = n1 .loadtxoutset (dump_output ['path' ])
350
358
assert_equal (loaded ['coins_loaded' ], SNAPSHOT_BASE_HEIGHT )
351
359
assert_equal (loaded ['base_height' ], SNAPSHOT_BASE_HEIGHT )
352
360
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
+
353
365
self .log .info ("Check that UTXO-querying RPCs operate on snapshot chainstate" )
354
366
snapshot_hash = loaded ['tip_hash' ]
355
367
snapshot_num_coins = loaded ['coins_loaded' ]
@@ -453,6 +465,9 @@ def check_tx_counts(final: bool) -> None:
453
465
self .restart_node (1 , extra_args = [
454
466
f"-stopatheight={ PAUSE_HEIGHT } " , * self .extra_args [1 ]])
455
467
468
+ # Upon restart during snapshot tip sync, the node must remain in 'limited' mode.
469
+ self .assert_only_network_limited_service (n1 )
470
+
456
471
# Finally connect the nodes and let them sync.
457
472
#
458
473
# Set `wait_for_connect=False` to avoid a race between performing connection
@@ -469,6 +484,9 @@ def check_tx_counts(final: bool) -> None:
469
484
self .log .info ("Restarted node before snapshot validation completed, reloading..." )
470
485
self .restart_node (1 , extra_args = self .extra_args [1 ])
471
486
487
+ # Upon restart, the node must remain in 'limited' mode
488
+ self .assert_only_network_limited_service (n1 )
489
+
472
490
# Send snapshot block to n1 out of order. This makes the test less
473
491
# realistic because normally the snapshot block is one of the last
474
492
# blocks downloaded, but its useful to test because it triggers more
@@ -487,6 +505,10 @@ def check_tx_counts(final: bool) -> None:
487
505
self .log .info ("Ensuring background validation completes" )
488
506
self .wait_until (lambda : len (n1 .getchainstates ()['chainstates' ]) == 1 )
489
507
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
+
490
512
# Ensure indexes have synced.
491
513
completed_idx_state = {
492
514
'basic block filter index' : COMPLETE_IDX ,
@@ -517,12 +539,18 @@ def check_tx_counts(final: bool) -> None:
517
539
518
540
self .log .info ("-- Testing all indexes + reindex" )
519
541
assert_equal (n2 .getblockcount (), START_HEIGHT )
542
+ assert 'NETWORK' in n2 .getnetworkinfo ()['localservicesnames' ] # sanity check
520
543
521
544
self .log .info (f"Loading snapshot into third node from { dump_output ['path' ]} " )
522
545
loaded = n2 .loadtxoutset (dump_output ['path' ])
523
546
assert_equal (loaded ['coins_loaded' ], SNAPSHOT_BASE_HEIGHT )
524
547
assert_equal (loaded ['base_height' ], SNAPSHOT_BASE_HEIGHT )
525
548
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
+
526
554
for reindex_arg in ['-reindex=1' , '-reindex-chainstate=1' ]:
527
555
self .log .info (f"Check that restarting with { reindex_arg } will delete the snapshot chainstate" )
528
556
self .restart_node (2 , extra_args = [reindex_arg , * self .extra_args [2 ]])
@@ -546,13 +574,21 @@ def check_tx_counts(final: bool) -> None:
546
574
msg = "Unable to load UTXO snapshot: Can't activate a snapshot-based chainstate more than once"
547
575
assert_raises_rpc_error (- 32603 , msg , n2 .loadtxoutset , dump_output ['path' ])
548
576
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
+
549
582
self .connect_nodes (0 , 2 )
550
583
self .wait_until (lambda : n2 .getchainstates ()['chainstates' ][- 1 ]['blocks' ] == FINAL_HEIGHT )
551
584
self .sync_blocks (nodes = (n0 , n2 ))
552
585
553
586
self .log .info ("Ensuring background validation completes" )
554
587
self .wait_until (lambda : len (n2 .getchainstates ()['chainstates' ]) == 1 )
555
588
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
+
556
592
completed_idx_state = {
557
593
'basic block filter index' : COMPLETE_IDX ,
558
594
'coinstatsindex' : COMPLETE_IDX ,
0 commit comments