@@ -7892,6 +7892,177 @@ fn block_validation_pending_table() {
7892
7892
signer_test. shutdown ( ) ;
7893
7893
}
7894
7894
7895
+ /// Test scenario:
7896
+ ///
7897
+ /// - Miner A proposes a block in tenure A
7898
+ /// - While that block is pending validation,
7899
+ /// Miner B proposes a new block in tenure B
7900
+ /// - After A's block is validated, Miner B's block is
7901
+ /// rejected (because it's a sister block)
7902
+ /// - Miner B retries and successfully mines a block
7903
+ #[ test]
7904
+ #[ ignore]
7905
+ fn new_tenure_while_validating_previous_scenario ( ) {
7906
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
7907
+ return ;
7908
+ }
7909
+
7910
+ tracing_subscriber:: registry ( )
7911
+ . with ( fmt:: layer ( ) )
7912
+ . with ( EnvFilter :: from_default_env ( ) )
7913
+ . init ( ) ;
7914
+
7915
+ info ! ( "------------------------- Test Setup -------------------------" ) ;
7916
+ let num_signers = 5 ;
7917
+ let timeout = Duration :: from_secs ( 30 ) ;
7918
+ let sender_sk = Secp256k1PrivateKey :: new ( ) ;
7919
+ let sender_addr = tests:: to_addr ( & sender_sk) ;
7920
+ let send_amt = 100 ;
7921
+ let send_fee = 180 ;
7922
+ let recipient = PrincipalData :: from ( StacksAddress :: burn_address ( false ) ) ;
7923
+
7924
+ let mut signer_test: SignerTest < SpawnedSigner > = SignerTest :: new_with_config_modifications (
7925
+ num_signers,
7926
+ vec ! [ ( sender_addr, send_amt + send_fee) ] ,
7927
+ |_| { } ,
7928
+ |_| { } ,
7929
+ None ,
7930
+ None ,
7931
+ ) ;
7932
+ let db_path = signer_test. signer_configs [ 0 ] . db_path . clone ( ) ;
7933
+ let http_origin = format ! ( "http://{}" , & signer_test. running_nodes. conf. node. rpc_bind) ;
7934
+ signer_test. boot_to_epoch_3 ( ) ;
7935
+
7936
+ info ! ( "----- Starting test -----" ;
7937
+ "db_path" => db_path. clone( ) . to_str( ) ,
7938
+ ) ;
7939
+ signer_test. mine_and_verify_confirmed_naka_block ( timeout, num_signers, true ) ;
7940
+ TEST_VALIDATE_DELAY_DURATION_SECS . set ( 30 ) ;
7941
+
7942
+ let proposals_before = signer_test. get_miner_proposal_messages ( ) . len ( ) ;
7943
+
7944
+ let peer_info_before_stall = signer_test. get_peer_info ( ) ;
7945
+ let burn_height_before_stall = peer_info_before_stall. burn_block_height ;
7946
+ let stacks_height_before_stall = peer_info_before_stall. stacks_tip_height ;
7947
+
7948
+ // STEP 1: Miner A proposes a block in tenure A
7949
+
7950
+ // submit a tx so that the miner will attempt to mine an extra block
7951
+ let sender_nonce = 0 ;
7952
+ let transfer_tx = make_stacks_transfer (
7953
+ & sender_sk,
7954
+ sender_nonce,
7955
+ send_fee,
7956
+ signer_test. running_nodes . conf . burnchain . chain_id ,
7957
+ & recipient,
7958
+ send_amt,
7959
+ ) ;
7960
+ submit_tx ( & http_origin, & transfer_tx) ;
7961
+
7962
+ info ! ( "----- Waiting for miner to propose a block -----" ) ;
7963
+
7964
+ // Wait for the miner to propose a block
7965
+ wait_for ( 30 , || {
7966
+ Ok ( signer_test. get_miner_proposal_messages ( ) . len ( ) > proposals_before)
7967
+ } )
7968
+ . expect ( "Timed out waiting for miner to propose a block" ) ;
7969
+
7970
+ let proposals_before = signer_test. get_miner_proposal_messages ( ) . len ( ) ;
7971
+ let info_before = signer_test. get_peer_info ( ) ;
7972
+
7973
+ // STEP 2: Miner B proposes a block in tenure B, while A's block is pending validation
7974
+
7975
+ info ! ( "----- Mining a new BTC block -----" ) ;
7976
+ signer_test
7977
+ . running_nodes
7978
+ . btc_regtest_controller
7979
+ . build_next_block ( 1 ) ;
7980
+
7981
+ let mut last_log = Instant :: now ( ) ;
7982
+ last_log -= Duration :: from_secs ( 5 ) ;
7983
+ let mut new_block_hash = None ;
7984
+ wait_for ( 120 , || {
7985
+ let proposals = signer_test. get_miner_proposal_messages ( ) ;
7986
+ let new_proposal = proposals. iter ( ) . find ( |p| {
7987
+ p. burn_height > burn_height_before_stall
7988
+ && p. block . header . chain_length == info_before. stacks_tip_height + 1
7989
+ } ) ;
7990
+
7991
+ let has_new_proposal = new_proposal. is_some ( ) && proposals. len ( ) > proposals_before;
7992
+ if last_log. elapsed ( ) > Duration :: from_secs ( 5 ) && !has_new_proposal {
7993
+ info ! (
7994
+ "----- Waiting for a new proposal -----" ;
7995
+ "proposals_len" => proposals. len( ) ,
7996
+ "burn_height_before" => info_before. burn_block_height,
7997
+ ) ;
7998
+ last_log = Instant :: now ( ) ;
7999
+ }
8000
+ if let Some ( proposal) = new_proposal {
8001
+ new_block_hash = Some ( proposal. block . header . signer_signature_hash ( ) ) ;
8002
+ }
8003
+ Ok ( has_new_proposal)
8004
+ } )
8005
+ . expect ( "Timed out waiting for pending block proposal" ) ;
8006
+
8007
+ info ! ( "----- Waiting for pending block validation to be submitted -----" ) ;
8008
+ let new_block_hash = new_block_hash. unwrap ( ) ;
8009
+
8010
+ // Set the delay to 0 so that the block validation finishes quickly
8011
+ TEST_VALIDATE_DELAY_DURATION_SECS . set ( 0 ) ;
8012
+
8013
+ wait_for ( 30 , || {
8014
+ let proposal_responses = test_observer:: get_proposal_responses ( ) ;
8015
+ let found_proposal = proposal_responses
8016
+ . iter ( )
8017
+ . any ( |p| p. signer_signature_hash ( ) == new_block_hash) ;
8018
+ Ok ( found_proposal)
8019
+ } )
8020
+ . expect ( "Timed out waiting for pending block validation to be submitted" ) ;
8021
+
8022
+ // STEP 3: Miner B is rejected, retries, and mines a block
8023
+
8024
+ // Now, wait for miner B to propose a new block
8025
+ let mut last_log = Instant :: now ( ) ;
8026
+ last_log -= Duration :: from_secs ( 5 ) ;
8027
+ wait_for ( 30 , || {
8028
+ let proposals = signer_test. get_miner_proposal_messages ( ) ;
8029
+ let new_proposal = proposals. iter ( ) . find ( |p| {
8030
+ p. burn_height > burn_height_before_stall
8031
+ && p. block . header . chain_length == stacks_height_before_stall + 2
8032
+ } ) ;
8033
+ if last_log. elapsed ( ) > Duration :: from_secs ( 5 ) && !new_proposal. is_some ( ) {
8034
+ let last_proposal = proposals. last ( ) . unwrap ( ) ;
8035
+ info ! (
8036
+ "----- Waiting for a new proposal -----" ;
8037
+ "proposals_len" => proposals. len( ) ,
8038
+ "burn_height_before" => burn_height_before_stall,
8039
+ "stacks_height_before" => stacks_height_before_stall,
8040
+ "last_proposal_burn_height" => last_proposal. burn_height,
8041
+ "last_proposal_stacks_height" => last_proposal. block. header. chain_length,
8042
+ ) ;
8043
+ last_log = Instant :: now ( ) ;
8044
+ }
8045
+ Ok ( new_proposal. is_some ( ) )
8046
+ } )
8047
+ . expect ( "Timed out waiting for miner to try a new block proposal" ) ;
8048
+
8049
+ // Wait for the new block to be mined
8050
+ wait_for ( 30 , || {
8051
+ let peer_info = signer_test. get_peer_info ( ) ;
8052
+ Ok (
8053
+ peer_info. stacks_tip_height == stacks_height_before_stall + 2
8054
+ && peer_info. burn_block_height == burn_height_before_stall + 1 ,
8055
+ )
8056
+ } )
8057
+ . expect ( "Timed out waiting for new block to be mined" ) ;
8058
+
8059
+ // Ensure that we didn't tenure extend
8060
+ verify_last_block_contains_tenure_change_tx ( TenureChangeCause :: BlockFound ) ;
8061
+
8062
+ info ! ( "------------------------- Shutdown -------------------------" ) ;
8063
+ signer_test. shutdown ( ) ;
8064
+ }
8065
+
7895
8066
#[ test]
7896
8067
#[ ignore]
7897
8068
/// Test that a miner will extend its tenure after the succeding miner fails to mine a block.
0 commit comments