@@ -60,6 +60,7 @@ use stacks_common::util::sleep_ms;
60
60
use stacks_signer:: chainstate:: { ProposalEvalConfig , SortitionsView } ;
61
61
use stacks_signer:: client:: { SignerSlotID , StackerDB } ;
62
62
use stacks_signer:: config:: { build_signer_config_tomls, GlobalConfig as SignerConfig , Network } ;
63
+ use stacks_signer:: signerdb:: SignerDb ;
63
64
use stacks_signer:: v0:: signer:: {
64
65
TEST_IGNORE_ALL_BLOCK_PROPOSALS , TEST_PAUSE_BLOCK_BROADCAST , TEST_REJECT_ALL_BLOCK_PROPOSAL ,
65
66
TEST_SKIP_BLOCK_BROADCAST ,
@@ -7767,6 +7768,178 @@ fn block_validation_response_timeout() {
7767
7768
) ;
7768
7769
}
7769
7770
7771
+ /// Test that, when a signer submit a block validation request and
7772
+ /// gets a 429 the signer stores the pending request and submits
7773
+ /// it again after the current block validation request finishes.
7774
+ #[ test]
7775
+ #[ ignore]
7776
+ fn block_validation_pending_table ( ) {
7777
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
7778
+ return ;
7779
+ }
7780
+
7781
+ tracing_subscriber:: registry ( )
7782
+ . with ( fmt:: layer ( ) )
7783
+ . with ( EnvFilter :: from_default_env ( ) )
7784
+ . init ( ) ;
7785
+
7786
+ info ! ( "------------------------- Test Setup -------------------------" ) ;
7787
+ let num_signers = 5 ;
7788
+ let timeout = Duration :: from_secs ( 30 ) ;
7789
+ let sender_sk = Secp256k1PrivateKey :: new ( ) ;
7790
+ let sender_addr = tests:: to_addr ( & sender_sk) ;
7791
+ let send_amt = 100 ;
7792
+ let send_fee = 180 ;
7793
+ let recipient = PrincipalData :: from ( StacksAddress :: burn_address ( false ) ) ;
7794
+ let short_timeout = Duration :: from_secs ( 20 ) ;
7795
+
7796
+ let mut signer_test: SignerTest < SpawnedSigner > = SignerTest :: new_with_config_modifications (
7797
+ num_signers,
7798
+ vec ! [ ( sender_addr, send_amt + send_fee) ] ,
7799
+ |config| {
7800
+ config. block_proposal_validation_timeout = timeout;
7801
+ } ,
7802
+ |_| { } ,
7803
+ None ,
7804
+ None ,
7805
+ ) ;
7806
+ let db_path = signer_test. signer_configs [ 0 ] . db_path . clone ( ) ;
7807
+ let http_origin = format ! ( "http://{}" , & signer_test. running_nodes. conf. node. rpc_bind) ;
7808
+ signer_test. boot_to_epoch_3 ( ) ;
7809
+
7810
+ info ! ( "----- Starting test -----" ;
7811
+ "db_path" => db_path. clone( ) . to_str( ) ,
7812
+ ) ;
7813
+ signer_test. mine_and_verify_confirmed_naka_block ( timeout, num_signers, true ) ;
7814
+ TEST_VALIDATE_DELAY_DURATION_SECS
7815
+ . lock ( )
7816
+ . unwrap ( )
7817
+ . replace ( 30 ) ;
7818
+
7819
+ let signer_db = SignerDb :: new ( db_path) . unwrap ( ) ;
7820
+
7821
+ let proposals_before = signer_test. get_miner_proposal_messages ( ) . len ( ) ;
7822
+
7823
+ let peer_info = signer_test. get_peer_info ( ) ;
7824
+
7825
+ // submit a tx so that the miner will attempt to mine an extra block
7826
+ let sender_nonce = 0 ;
7827
+ let transfer_tx = make_stacks_transfer (
7828
+ & sender_sk,
7829
+ sender_nonce,
7830
+ send_fee,
7831
+ signer_test. running_nodes . conf . burnchain . chain_id ,
7832
+ & recipient,
7833
+ send_amt,
7834
+ ) ;
7835
+ submit_tx ( & http_origin, & transfer_tx) ;
7836
+
7837
+ info ! ( "----- Waiting for miner to propose a block -----" ) ;
7838
+
7839
+ // Wait for the miner to propose a block
7840
+ wait_for ( 30 , || {
7841
+ Ok ( signer_test. get_miner_proposal_messages ( ) . len ( ) > proposals_before)
7842
+ } )
7843
+ . expect ( "Timed out waiting for miner to propose a block" ) ;
7844
+
7845
+ info ! ( "----- Proposing a concurrent block -----" ) ;
7846
+ let proposal_conf = ProposalEvalConfig {
7847
+ first_proposal_burn_block_timing : Duration :: from_secs ( 0 ) ,
7848
+ block_proposal_timeout : Duration :: from_secs ( 100 ) ,
7849
+ tenure_last_block_proposal_timeout : Duration :: from_secs ( 30 ) ,
7850
+ tenure_idle_timeout : Duration :: from_secs ( 300 ) ,
7851
+ } ;
7852
+ let mut block = NakamotoBlock {
7853
+ header : NakamotoBlockHeader :: empty ( ) ,
7854
+ txs : vec ! [ ] ,
7855
+ } ;
7856
+ block. header . timestamp = get_epoch_time_secs ( ) ;
7857
+
7858
+ let view = SortitionsView :: fetch_view ( proposal_conf, & signer_test. stacks_client ) . unwrap ( ) ;
7859
+ block. header . pox_treatment = BitVec :: ones ( 1 ) . unwrap ( ) ;
7860
+ block. header . consensus_hash = view. cur_sortition . consensus_hash ;
7861
+ block. header . chain_length = peer_info. stacks_tip_height + 1 ;
7862
+ let block_signer_signature_hash = block. header . signer_signature_hash ( ) ;
7863
+ signer_test. propose_block ( block. clone ( ) , short_timeout) ;
7864
+
7865
+ info ! (
7866
+ "----- Waiting for a pending block proposal in SignerDb -----" ;
7867
+ "signer_signature_hash" => block_signer_signature_hash. to_hex( ) ,
7868
+ ) ;
7869
+ let mut last_log = Instant :: now ( ) ;
7870
+ last_log -= Duration :: from_secs ( 5 ) ;
7871
+ wait_for ( 120 , || {
7872
+ let sighash = match signer_db. get_pending_block_validation ( ) {
7873
+ Ok ( Some ( sighash) ) => sighash,
7874
+ Err ( e) => {
7875
+ error ! ( "Failed to get pending block validation: {e}" ) ;
7876
+ panic ! ( "Failed to get pending block validation" ) ;
7877
+ }
7878
+ Ok ( None ) => {
7879
+ if last_log. elapsed ( ) > Duration :: from_secs ( 5 ) {
7880
+ info ! ( "----- No pending block validations found -----" ) ;
7881
+ last_log = Instant :: now ( ) ;
7882
+ }
7883
+ return Ok ( false ) ;
7884
+ }
7885
+ } ;
7886
+ if last_log. elapsed ( ) > Duration :: from_secs ( 5 ) && sighash != block_signer_signature_hash {
7887
+ let pending_block_validations = signer_db
7888
+ . get_all_pending_block_validations ( )
7889
+ . expect ( "Failed to get pending block validations" ) ;
7890
+ info ! (
7891
+ "----- Received a different pending block proposal -----" ;
7892
+ "db_signer_signature_hash" => sighash. to_hex( ) ,
7893
+ "proposed_signer_signature_hash" => block_signer_signature_hash. to_hex( ) ,
7894
+ "pending_block_validations" => pending_block_validations. iter( )
7895
+ . map( |p| p. signer_signature_hash. to_hex( ) )
7896
+ . collect:: <Vec <String >>( )
7897
+ . join( ", " ) ,
7898
+ ) ;
7899
+ last_log = Instant :: now ( ) ;
7900
+ }
7901
+ Ok ( sighash == block_signer_signature_hash)
7902
+ } )
7903
+ . expect ( "Timed out waiting for pending block proposal" ) ;
7904
+
7905
+ // Set the delay to 0 so that the block validation finishes quickly
7906
+ TEST_VALIDATE_DELAY_DURATION_SECS . lock ( ) . unwrap ( ) . take ( ) ;
7907
+
7908
+ info ! ( "----- Waiting for pending block validation to be submitted -----" ) ;
7909
+
7910
+ wait_for ( 30 , || {
7911
+ let proposal_responses = test_observer:: get_proposal_responses ( ) ;
7912
+ let found_proposal = proposal_responses
7913
+ . iter ( )
7914
+ . any ( |p| p. signer_signature_hash ( ) == block_signer_signature_hash) ;
7915
+ Ok ( found_proposal)
7916
+ } )
7917
+ . expect ( "Timed out waiting for pending block validation to be submitted" ) ;
7918
+
7919
+ info ! ( "----- Waiting for pending block validation to be removed -----" ) ;
7920
+ wait_for ( 30 , || {
7921
+ let Ok ( Some ( sighash) ) = signer_db. get_pending_block_validation ( ) else {
7922
+ // There are no pending block validations
7923
+ return Ok ( true ) ;
7924
+ } ;
7925
+ Ok ( sighash != block_signer_signature_hash)
7926
+ } )
7927
+ . expect ( "Timed out waiting for pending block validation to be removed" ) ;
7928
+
7929
+ // for test cleanup we need to wait for block rejections
7930
+ let signer_keys = signer_test
7931
+ . signer_configs
7932
+ . iter ( )
7933
+ . map ( |c| StacksPublicKey :: from_private ( & c. stacks_private_key ) )
7934
+ . collect :: < Vec < _ > > ( ) ;
7935
+ signer_test
7936
+ . wait_for_block_rejections ( 30 , & signer_keys)
7937
+ . expect ( "Timed out waiting for block rejections" ) ;
7938
+
7939
+ info ! ( "------------------------- Shutdown -------------------------" ) ;
7940
+ signer_test. shutdown ( ) ;
7941
+ }
7942
+
7770
7943
#[ test]
7771
7944
#[ ignore]
7772
7945
/// Test that a miner will extend its tenure after the succeding miner fails to mine a block.
0 commit comments