@@ -35,6 +35,7 @@ use stacks::chainstate::stacks::db::{StacksBlockHeaderTypes, StacksChainState, S
35
35
use stacks:: codec:: StacksMessageCodec ;
36
36
use stacks:: core:: { StacksEpochId , CHAIN_ID_TESTNET } ;
37
37
use stacks:: libstackerdb:: StackerDBChunkData ;
38
+ use stacks:: net:: api:: getsigner:: GetSignerResponse ;
38
39
use stacks:: net:: api:: postblock_proposal:: { ValidateRejectCode , TEST_VALIDATE_STALL } ;
39
40
use stacks:: net:: relay:: fault_injection:: set_ignore_block;
40
41
use stacks:: types:: chainstate:: { StacksAddress , StacksBlockId , StacksPrivateKey , StacksPublicKey } ;
@@ -4813,3 +4814,115 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
4813
4814
assert_eq ! ( info_after. stacks_tip. to_string( ) , block_n_2. block_hash) ;
4814
4815
assert_ne ! ( block_n_2, block_n) ;
4815
4816
}
4817
+
4818
+ #[ test]
4819
+ #[ ignore]
4820
+ /// Test that signers can successfully sign a block proposal in the 0th tenure of a reward cycle
4821
+ /// This ensures there is no race condition in the /v2/pox endpoint which could prevent it from updating
4822
+ /// on time, possibly triggering an "off by one" like behaviour in the 0th tenure.
4823
+ ///
4824
+ fn signing_in_0th_tenure_of_reward_cycle ( ) {
4825
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
4826
+ return ;
4827
+ }
4828
+
4829
+ tracing_subscriber:: registry ( )
4830
+ . with ( fmt:: layer ( ) )
4831
+ . with ( EnvFilter :: from_default_env ( ) )
4832
+ . init ( ) ;
4833
+
4834
+ info ! ( "------------------------- Test Setup -------------------------" ) ;
4835
+ let num_signers = 5 ;
4836
+ let sender_sk = Secp256k1PrivateKey :: new ( ) ;
4837
+ let sender_addr = tests:: to_addr ( & sender_sk) ;
4838
+ let send_amt = 100 ;
4839
+ let send_fee = 180 ;
4840
+ let recipient = PrincipalData :: from ( StacksAddress :: burn_address ( false ) ) ;
4841
+ let mut signer_test: SignerTest < SpawnedSigner > = SignerTest :: new (
4842
+ num_signers,
4843
+ vec ! [ ( sender_addr. clone( ) , send_amt + send_fee) ] ,
4844
+ ) ;
4845
+ let signer_public_keys = signer_test
4846
+ . signer_stacks_private_keys
4847
+ . iter ( )
4848
+ . map ( StacksPublicKey :: from_private)
4849
+ . collect :: < Vec < _ > > ( ) ;
4850
+ let long_timeout = Duration :: from_secs ( 200 ) ;
4851
+ signer_test. boot_to_epoch_3 ( ) ;
4852
+ let curr_reward_cycle = signer_test. get_current_reward_cycle ( ) ;
4853
+ let next_reward_cycle = curr_reward_cycle + 1 ;
4854
+ // Mine until the boundary of the first full Nakamoto reward cycles (epoch 3 starts in the middle of one)
4855
+ let next_reward_cycle_height_boundary = signer_test
4856
+ . running_nodes
4857
+ . btc_regtest_controller
4858
+ . get_burnchain ( )
4859
+ . reward_cycle_to_block_height ( next_reward_cycle)
4860
+ . saturating_sub ( 1 ) ;
4861
+
4862
+ info ! ( "------------------------- Advancing to {next_reward_cycle} Boundary at Block {next_reward_cycle_height_boundary} -------------------------" ) ;
4863
+ signer_test. run_until_burnchain_height_nakamoto (
4864
+ long_timeout,
4865
+ next_reward_cycle_height_boundary,
4866
+ num_signers,
4867
+ ) ;
4868
+
4869
+ let http_origin = format ! ( "http://{}" , & signer_test. running_nodes. conf. node. rpc_bind) ;
4870
+ let get_v3_signer = |pubkey : & Secp256k1PublicKey , reward_cycle : u64 | {
4871
+ let url = & format ! (
4872
+ "{http_origin}/v3/signer/{pk}/{reward_cycle}" ,
4873
+ pk = pubkey. to_hex( )
4874
+ ) ;
4875
+ info ! ( "Send request: GET {url}" ) ;
4876
+ reqwest:: blocking:: get ( url)
4877
+ . unwrap_or_else ( |e| panic ! ( "GET request failed: {e}" ) )
4878
+ . json :: < GetSignerResponse > ( )
4879
+ . unwrap ( )
4880
+ . blocks_signed
4881
+ } ;
4882
+
4883
+ assert_eq ! ( signer_test. get_current_reward_cycle( ) , curr_reward_cycle) ;
4884
+
4885
+ for signer in & signer_public_keys {
4886
+ let blocks_signed = get_v3_signer ( & signer, next_reward_cycle) ;
4887
+ assert_eq ! ( blocks_signed, 0 ) ;
4888
+ }
4889
+
4890
+ info ! ( "------------------------- Enter Reward Cycle {next_reward_cycle} -------------------------" ) ;
4891
+ next_block_and (
4892
+ & mut signer_test. running_nodes . btc_regtest_controller ,
4893
+ 60 ,
4894
+ || Ok ( true ) ,
4895
+ )
4896
+ . unwrap ( ) ;
4897
+
4898
+ for signer in & signer_public_keys {
4899
+ let blocks_signed = get_v3_signer ( & signer, next_reward_cycle) ;
4900
+ assert_eq ! ( blocks_signed, 0 ) ;
4901
+ }
4902
+
4903
+ let blocks_before = signer_test
4904
+ . running_nodes
4905
+ . nakamoto_blocks_mined
4906
+ . load ( Ordering :: SeqCst ) ;
4907
+
4908
+ // submit a tx so that the miner will mine a stacks block in the 0th block of the new reward cycle
4909
+ let sender_nonce = 0 ;
4910
+ let transfer_tx =
4911
+ make_stacks_transfer ( & sender_sk, sender_nonce, send_fee, & recipient, send_amt) ;
4912
+ let _tx = submit_tx ( & http_origin, & transfer_tx) ;
4913
+
4914
+ wait_for ( 30 , || {
4915
+ Ok ( signer_test
4916
+ . running_nodes
4917
+ . nakamoto_blocks_mined
4918
+ . load ( Ordering :: SeqCst )
4919
+ > blocks_before)
4920
+ } )
4921
+ . unwrap ( ) ;
4922
+
4923
+ for signer in & signer_public_keys {
4924
+ let blocks_signed = get_v3_signer ( & signer, next_reward_cycle) ;
4925
+ assert_eq ! ( blocks_signed, 1 ) ;
4926
+ }
4927
+ assert_eq ! ( signer_test. get_current_reward_cycle( ) , next_reward_cycle) ;
4928
+ }
0 commit comments