@@ -11546,9 +11546,39 @@ fn reorg_attempts_activity_timeout_exceeded() {
11546
11546
block_proposal
11547
11547
} ;
11548
11548
11549
+ let wait_for_block_rejections = |hash : Sha512Trunc256Sum | {
11550
+ wait_for ( 30 , || {
11551
+ let stackerdb_events = test_observer:: get_stackerdb_chunks ( ) ;
11552
+ let block_rejections = stackerdb_events
11553
+ . into_iter ( )
11554
+ . flat_map ( |chunk| chunk. modified_slots )
11555
+ . filter_map ( |chunk| {
11556
+ let message = SignerMessage :: consensus_deserialize ( & mut chunk. data . as_slice ( ) )
11557
+ . expect ( "Failed to deserialize SignerMessage" ) ;
11558
+ match message {
11559
+ SignerMessage :: BlockResponse ( BlockResponse :: Rejected ( rejection) ) => {
11560
+ if rejection. signer_signature_hash == hash {
11561
+ assert_eq ! (
11562
+ rejection. reason_code,
11563
+ RejectCode :: SortitionViewMismatch
11564
+ ) ;
11565
+ Some ( rejection)
11566
+ } else {
11567
+ None
11568
+ }
11569
+ }
11570
+ _ => None ,
11571
+ }
11572
+ } )
11573
+ . collect :: < Vec < _ > > ( ) ;
11574
+ Ok ( block_rejections. len ( ) >= num_signers * 3 / 10 )
11575
+ } )
11576
+ } ;
11577
+
11549
11578
info ! ( "------------------------- Test Mine Block N -------------------------" ) ;
11550
11579
let chain_before = get_chain_info ( & signer_test. running_nodes . conf ) ;
11551
11580
// Stall validation so signers will be unable to process the tenure change block for Tenure B.
11581
+ // And so the incoming miner proposes a block N' (the reorging block).
11552
11582
TEST_VALIDATE_STALL . set ( true ) ;
11553
11583
test_observer:: clear ( ) ;
11554
11584
// submit a tx so that the miner will mine an extra block
@@ -11573,7 +11603,7 @@ fn reorg_attempts_activity_timeout_exceeded() {
11573
11603
. running_nodes
11574
11604
. commits_submitted
11575
11605
. load ( Ordering :: SeqCst ) ;
11576
-
11606
+ let chain_before = get_chain_info ( & signer_test . running_nodes . conf ) ;
11577
11607
next_block_and (
11578
11608
& mut signer_test. running_nodes . btc_regtest_controller ,
11579
11609
60 ,
@@ -11582,67 +11612,74 @@ fn reorg_attempts_activity_timeout_exceeded() {
11582
11612
. running_nodes
11583
11613
. commits_submitted
11584
11614
. load ( Ordering :: SeqCst ) ;
11585
- Ok ( commits_count > commits_before)
11615
+ let chain_info = get_chain_info ( & signer_test. running_nodes . conf ) ;
11616
+ Ok ( commits_count > commits_before
11617
+ && chain_info. burn_block_height > chain_before. burn_block_height )
11586
11618
} ,
11587
11619
)
11588
11620
. unwrap ( ) ;
11589
11621
11590
- std :: thread :: sleep ( reorg_attempts_activity_timeout . add ( Duration :: from_secs ( 1 ) ) ) ;
11622
+ info ! ( "------------------------- Wait for block N' to arrive late -------------------------" ) ;
11591
11623
test_observer:: clear ( ) ;
11592
- TEST_BROADCAST_STALL . set ( false ) ;
11593
- let block_proposal_n_prime =
11594
- wait_for_block_proposal ( ) . expect ( "Failed to get block proposal N'" ) ;
11595
- std:: thread:: sleep ( block_proposal_timeout. add ( Duration :: from_secs ( 1 ) ) ) ;
11596
-
11597
- assert_ne ! ( block_proposal_n, block_proposal_n_prime) ;
11598
- let chain_before = get_chain_info ( & signer_test. running_nodes . conf ) ;
11624
+ // Allow block N validation to finish.
11599
11625
TEST_VALIDATE_STALL . set ( false ) ;
11600
-
11601
11626
wait_for ( 30 , || {
11602
11627
let chain_info = get_chain_info ( & signer_test. running_nodes . conf ) ;
11603
11628
Ok ( chain_info. stacks_tip_height > chain_before. stacks_tip_height )
11604
11629
} )
11605
- . expect ( "Timed out waiting for stacks tip to advance to block N" ) ;
11606
-
11630
+ . expect ( "Tiemd out waiting for stacks tip to advance to block N" ) ;
11607
11631
let chain_after = get_chain_info ( & signer_test. running_nodes . conf ) ;
11632
+ TEST_VALIDATE_STALL . set ( true ) ;
11633
+ // Allow incoming mine to propose block N'
11634
+ // Make sure to wait the reorg_attempts_activity_timeout AFTER the block is globally signed over
11635
+ // as this is the point where signers start considering from.
11636
+ std:: thread:: sleep ( reorg_attempts_activity_timeout. add ( Duration :: from_secs ( 1 ) ) ) ;
11637
+ TEST_BROADCAST_STALL . set ( false ) ;
11638
+ let block_proposal_n_prime =
11639
+ wait_for_block_proposal ( ) . expect ( "Failed to get block proposal N'" ) ;
11640
+ assert_eq ! (
11641
+ block_proposal_n_prime. block. header. chain_length,
11642
+ chain_after. stacks_tip_height
11643
+ ) ;
11644
+ // Make sure that no subsequent proposal arrives before the block_proposal_timeout is exceeded
11645
+ TEST_BROADCAST_STALL . set ( true ) ;
11646
+ TEST_VALIDATE_STALL . set ( false ) ;
11647
+ // We only need to wait the difference between the two timeouts now since we already slept for a min of reorg_attempts_activity_timeout + 1
11648
+ std:: thread:: sleep ( block_proposal_timeout. saturating_sub ( reorg_attempts_activity_timeout) ) ;
11649
+ assert_ne ! ( block_proposal_n, block_proposal_n_prime) ;
11608
11650
assert_eq ! (
11609
11651
chain_after. stacks_tip_height,
11610
11652
block_proposal_n. block. header. chain_length
11611
11653
) ;
11612
-
11613
11654
let chain_before = chain_after;
11655
+
11614
11656
info ! ( "------------------------- Wait for Block N' Rejection -------------------------" ) ;
11657
+ wait_for_block_rejections ( block_proposal_n_prime. block . header . signer_signature_hash ( ) )
11658
+ . expect ( "FAIL: Timed out waiting for block proposal rejections of N'" ) ;
11659
+
11660
+ info ! ( "------------------------- Wait for Block N+1 Proposal -------------------------" ) ;
11661
+ test_observer:: clear ( ) ;
11662
+ TEST_BROADCAST_STALL . set ( false ) ;
11615
11663
wait_for ( 30 , || {
11616
- let stackerdb_events = test_observer:: get_stackerdb_chunks ( ) ;
11617
- let block_rejections = stackerdb_events
11618
- . into_iter ( )
11619
- . flat_map ( |chunk| chunk. modified_slots )
11620
- . filter_map ( |chunk| {
11621
- let message = SignerMessage :: consensus_deserialize ( & mut chunk. data . as_slice ( ) )
11622
- . expect ( "Failed to deserialize SignerMessage" ) ;
11623
- match message {
11624
- SignerMessage :: BlockResponse ( BlockResponse :: Rejected ( rejection) ) => {
11625
- if rejection. signer_signature_hash
11626
- == block_proposal_n_prime. block . header . signer_signature_hash ( )
11627
- {
11628
- assert_eq ! ( rejection. reason_code, RejectCode :: SortitionViewMismatch ) ;
11629
- Some ( rejection)
11630
- } else {
11631
- None
11632
- }
11633
- }
11634
- _ => None ,
11635
- }
11636
- } )
11637
- . collect :: < Vec < _ > > ( ) ;
11638
- Ok ( block_rejections. len ( ) >= num_signers * 3 / 10 )
11639
- } )
11640
- . expect ( "FAIL: Timed out waiting for block proposal rejections of N'" ) ;
11664
+ let block_proposal_n_1 =
11665
+ wait_for_block_proposal ( ) . expect ( "Failed to get block proposal N+1" ) ;
11666
+ Ok ( block_proposal_n_1. block . header . chain_length
11667
+ == block_proposal_n. block . header . chain_length + 1 )
11668
+ } )
11669
+ . expect ( "Timed out waiting for block N+1 to be proposed" ) ;
11670
+
11671
+ info ! ( "------------------------- Wait for Block N+1 Rejection -------------------------" ) ;
11672
+ // The miner will automatically reattempt to mine a block N+1 once it sees the stacks tip advance to block N.
11673
+ // N+1 will still be rejected however as the signers will have already marked the miner as invalid since the reorg
11674
+ // block N' arrived AFTER the reorg_attempts_activity_timeout and the subsequent block N+1 arrived AFTER the
11675
+ // block_proposal_timeout.
11676
+ let block_proposal_n_1 = wait_for_block_proposal ( ) . expect ( "Failed to get block proposal N+1'" ) ;
11677
+ wait_for_block_rejections ( block_proposal_n_1. block . header . signer_signature_hash ( ) )
11678
+ . expect ( "FAIL: Timed out waiting for block proposal rejections of N+1'" ) ;
11641
11679
11642
11680
info ! ( "------------------------- Ensure chain halts -------------------------" ) ;
11643
- // The signer should automatically attempt to mine a new block once the signers eventually tell it to abandon the previous block
11644
- // It will reject it though because the block proposal timeout is exceeded and its first block proposal arrived AFTER the reorg activity timeout
11645
- assert ! ( wait_for( 30 , || {
11681
+ // Just in case, wait again and ensure that the chain is still halted (once marked invalid, the miner can do nothing to satisfy the signers)
11682
+ assert ! ( wait_for( reorg_attempts_activity_timeout. as_secs( ) , || {
11646
11683
let chain_info = get_chain_info( & signer_test. running_nodes. conf) ;
11647
11684
assert_eq!( chain_info. stacks_tip_height, chain_before. stacks_tip_height) ;
11648
11685
Ok ( false )
0 commit comments