@@ -766,6 +766,63 @@ impl MultipleMinerTest {
766
766
Ok ( txid)
767
767
}
768
768
769
+ pub fn send_contract_publish ( & mut self , contract_name : & str , contract_src : & str ) -> String {
770
+ let http_origin = format ! (
771
+ "http://{}" ,
772
+ & self . signer_test. running_nodes. conf. node. rpc_bind
773
+ ) ;
774
+ let contract_tx = make_contract_publish (
775
+ & self . sender_sk ,
776
+ self . sender_nonce ,
777
+ self . send_fee ,
778
+ self . signer_test . running_nodes . conf . burnchain . chain_id ,
779
+ contract_name,
780
+ contract_src,
781
+ ) ;
782
+ self . sender_nonce += 1 ;
783
+ submit_tx ( & http_origin, & contract_tx)
784
+ }
785
+
786
+ /// Sends a contract publish tx to the stacks node and waits for the stacks node to mine it
787
+ /// Returns the txid of the transfer tx.
788
+ pub fn send_and_mine_contract_publish (
789
+ & mut self ,
790
+ contract_name : & str ,
791
+ contract_src : & str ,
792
+ timeout_secs : u64 ,
793
+ ) -> Result < String , String > {
794
+ let stacks_height_before = self . get_peer_stacks_tip_height ( ) ;
795
+ let txid = self . send_contract_publish ( contract_name, contract_src) ;
796
+ wait_for ( timeout_secs, || {
797
+ Ok ( self . get_peer_stacks_tip_height ( ) > stacks_height_before)
798
+ } ) ?;
799
+ Ok ( txid)
800
+ }
801
+
802
+ pub fn send_contract_call (
803
+ & mut self ,
804
+ contract_name : & str ,
805
+ function_name : & str ,
806
+ function_args : & [ clarity:: vm:: Value ] ,
807
+ ) -> String {
808
+ let http_origin = format ! (
809
+ "http://{}" ,
810
+ & self . signer_test. running_nodes. conf. node. rpc_bind
811
+ ) ;
812
+ let contract_tx = make_contract_call (
813
+ & self . sender_sk ,
814
+ self . sender_nonce ,
815
+ self . send_fee ,
816
+ self . signer_test . running_nodes . conf . burnchain . chain_id ,
817
+ & tests:: to_addr ( & self . sender_sk ) ,
818
+ contract_name,
819
+ function_name,
820
+ function_args,
821
+ ) ;
822
+ self . sender_nonce += 1 ;
823
+ submit_tx ( & http_origin, & contract_tx)
824
+ }
825
+
769
826
/// Return the Peer Info from node 1
770
827
pub fn get_peer_info ( & self ) -> PeerInfo {
771
828
self . signer_test . get_peer_info ( )
@@ -935,6 +992,23 @@ fn last_block_contains_tenure_change_tx(cause: TenureChangeCause) -> bool {
935
992
}
936
993
}
937
994
995
+ // Returns whether the last block in the test observer contains a tenure change
996
+ /// transaction with the given cause.
997
+ fn last_block_contains_txid ( txid : & str ) -> bool {
998
+ let blocks = test_observer:: get_blocks ( ) ;
999
+ let last_block = & blocks. last ( ) . unwrap ( ) ;
1000
+ let transactions = last_block[ "transactions" ] . as_array ( ) . unwrap ( ) ;
1001
+ for tx in transactions {
1002
+ let raw_tx = tx[ "raw_tx" ] . as_str ( ) . unwrap ( ) ;
1003
+ let tx_bytes = hex_bytes ( & raw_tx[ 2 ..] ) . unwrap ( ) ;
1004
+ let parsed = StacksTransaction :: consensus_deserialize ( & mut & tx_bytes[ ..] ) . unwrap ( ) ;
1005
+ if parsed. txid ( ) . to_string ( ) == txid {
1006
+ return true ;
1007
+ }
1008
+ }
1009
+ false
1010
+ }
1011
+
938
1012
/// Asserts that the last block in the test observer contains a tenure change with the given cause.
939
1013
fn verify_last_block_contains_tenure_change_tx ( cause : TenureChangeCause ) {
940
1014
assert ! ( last_block_contains_tenure_change_tx( cause) ) ;
@@ -12438,3 +12512,159 @@ fn signer_can_accept_rejected_block() {
12438
12512
12439
12513
signer_test. shutdown ( ) ;
12440
12514
}
12515
+
12516
+ /// Test a scenario where:
12517
+ /// Two miners boot to Nakamoto.
12518
+ /// Sortition occurs. Miner 1 wins.
12519
+ /// Miner 1 proposes a block N
12520
+ /// Signers accept and the stacks tip advances to N
12521
+ /// Miner 1's block commits are paused so it cannot confirm the next tenure.
12522
+ /// Sortition occurs. Miner 2 wins.
12523
+ /// Miner 2 successfully mines blocks N+1, N+2, and N+3
12524
+ /// Sortition occurs quickly, within first_proposal_burn_block_timing_secs. Miner 1 wins.
12525
+ /// Miner 1 proposes block N+1' but gets rejected as more than one block has been mined in the current tenure (by miner2)
12526
+ #[ test]
12527
+ #[ ignore]
12528
+ fn miner_rejection_by_contract_call_execution_time_expired ( ) {
12529
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
12530
+ return ;
12531
+ }
12532
+
12533
+ let num_signers = 5 ;
12534
+ let num_txs = 3 ;
12535
+
12536
+ let mut miners = MultipleMinerTest :: new_with_config_modifications (
12537
+ num_signers,
12538
+ num_txs,
12539
+ |signer_config| {
12540
+ // Lets make sure we never time out since we need to stall some things to force our scenario
12541
+ signer_config. block_proposal_validation_timeout = Duration :: from_secs ( 1800 ) ;
12542
+ signer_config. tenure_last_block_proposal_timeout = Duration :: from_secs ( 1800 ) ;
12543
+ signer_config. first_proposal_burn_block_timing = Duration :: from_secs ( 1800 ) ;
12544
+ } ,
12545
+ |config| config. miner . max_execution_time = Some ( 0 ) ,
12546
+ |config| config. miner . max_execution_time = None ,
12547
+ ) ;
12548
+ let rl1_skip_commit_op = miners
12549
+ . signer_test
12550
+ . running_nodes
12551
+ . counters
12552
+ . naka_skip_commit_op
12553
+ . clone ( ) ;
12554
+ let rl2_skip_commit_op = miners. rl2_counters . naka_skip_commit_op . clone ( ) ;
12555
+
12556
+ let ( conf_1, _) = miners. get_node_configs ( ) ;
12557
+ let ( miner_pkh_1, miner_pkh_2) = miners. get_miner_public_key_hashes ( ) ;
12558
+ let ( miner_pk_1, miner_pk_2) = miners. get_miner_public_keys ( ) ;
12559
+
12560
+ info ! ( "------------------------- Pause Miner 2's Block Commits -------------------------" ) ;
12561
+
12562
+ // Make sure Miner 2 cannot win a sortition at first.
12563
+ rl2_skip_commit_op. set ( true ) ;
12564
+
12565
+ miners. boot_to_epoch_3 ( ) ;
12566
+
12567
+ let burnchain = conf_1. get_burnchain ( ) ;
12568
+ let sortdb = burnchain. open_sortition_db ( true ) . unwrap ( ) ;
12569
+
12570
+ info ! ( "------------------------- Pause Miner 1's Block Commits -------------------------" ) ;
12571
+ rl1_skip_commit_op. set ( true ) ;
12572
+
12573
+ // First, lets deploy the contract
12574
+ let dummy_contract_src = "
12575
+ (define-public (run-f)
12576
+ (ok (1)))
12577
+ " ;
12578
+
12579
+ info ! ( "------------------------- Miner 1 Mines a Nakamoto Block N -------------------------" ) ;
12580
+ miners
12581
+ . mine_bitcoin_block_and_tenure_change_tx ( & sortdb, TenureChangeCause :: BlockFound , 60 )
12582
+ . expect ( "Failed to mine BTC block followed by Block N" ) ;
12583
+
12584
+ // First, lets deploy the contract
12585
+ let _contract_publish_txid =
12586
+ miners. send_and_mine_contract_publish ( "dummy-contract" , dummy_contract_src, 60 ) ;
12587
+
12588
+ let stacks_height_before = miners. get_peer_stacks_tip_height ( ) ;
12589
+
12590
+ // try calling it (has to fail)
12591
+ let contract_call_txid = miners. send_contract_call ( "dummy-contract" , "f" , & [ ] ) ;
12592
+
12593
+ let miner_1_block_n =
12594
+ wait_for_block_pushed_by_miner_key ( 30 , stacks_height_before + 1 , & miner_pk_1)
12595
+ . expect ( "Failed to get block N+1" ) ;
12596
+
12597
+ assert_eq ! ( last_block_contains_txid( & contract_call_txid) , false ) ;
12598
+
12599
+ // assure we have a successful sortition that miner 1 won
12600
+ verify_sortition_winner ( & sortdb, & miner_pkh_1) ;
12601
+
12602
+ info ! ( "------------------------- Miner 2 Submits a Block Commit -------------------------" ) ;
12603
+ miners. submit_commit_miner_2 ( & sortdb) ;
12604
+
12605
+ info ! ( "------------------------- Pause Miner 2's Block Mining -------------------------" ) ;
12606
+ TEST_MINE_STALL . set ( true ) ;
12607
+
12608
+ info ! ( "------------------------- Mine Tenure -------------------------" ) ;
12609
+ miners
12610
+ . mine_bitcoin_blocks_and_confirm ( & sortdb, 1 , 60 )
12611
+ . expect ( "Failed to mine BTC block" ) ;
12612
+
12613
+ info ! ( "------------------------- Miner 1 Submits a Block Commit -------------------------" ) ;
12614
+ miners. submit_commit_miner_1 ( & sortdb) ;
12615
+
12616
+ info ! ( "------------------------- Miner 2 Mines Block N+2 -------------------------" ) ;
12617
+
12618
+ TEST_MINE_STALL . set ( false ) ;
12619
+ let _ = wait_for_block_pushed_by_miner_key ( 30 , block_n_height + 1 , & miner_pk_2)
12620
+ . expect ( "Failed to get block N+1" ) ;
12621
+
12622
+ // assure we have a successful sortition that miner 2 won
12623
+ verify_sortition_winner ( & sortdb, & miner_pkh_2) ;
12624
+
12625
+ assert_eq ! (
12626
+ get_chain_info( & conf_1) . stacks_tip_height,
12627
+ block_n_height + 1
12628
+ ) ;
12629
+
12630
+ info ! ( "------------------------- Miner 2 Mines N+2 and N+3 -------------------------" ) ;
12631
+ miners
12632
+ . send_and_mine_transfer_tx ( 30 )
12633
+ . expect ( "Failed to send and mine transfer tx" ) ;
12634
+ miners
12635
+ . send_and_mine_transfer_tx ( 30 )
12636
+ . expect ( "Failed to send and mine transfer tx" ) ;
12637
+ assert_eq ! (
12638
+ get_chain_info( & conf_1) . stacks_tip_height,
12639
+ block_n_height + 3
12640
+ ) ;
12641
+
12642
+ info ! ( "------------------------- Miner 1 Wins the Next Tenure, Mines N+3 -------------------------" ) ;
12643
+ miners. btc_regtest_controller_mut ( ) . build_next_block ( 1 ) ;
12644
+
12645
+ let _ = wait_for_block_pushed_by_miner_key ( 30 , block_n_height + 1 , & miner_pk_2)
12646
+ . expect ( "Failed to get block N+3" ) ;
12647
+
12648
+ // check N+2 contains the contract call (previously rejected by miner 1)
12649
+ let miner1_blocks_after_boot_to_epoch3 = get_nakamoto_headers ( & conf_1)
12650
+ . into_iter ( )
12651
+ . filter ( |block| {
12652
+ // skip first nakamoto block
12653
+ if block. stacks_block_height == stacks_height_before {
12654
+ return false ;
12655
+ }
12656
+ let nakamoto_block_header = block. anchored_header . as_stacks_nakamoto ( ) . unwrap ( ) ;
12657
+ miner_pk_1
12658
+ . verify (
12659
+ nakamoto_block_header. miner_signature_hash ( ) . as_bytes ( ) ,
12660
+ & nakamoto_block_header. miner_signature ,
12661
+ )
12662
+ . unwrap ( )
12663
+ } )
12664
+ . count ( ) ;
12665
+
12666
+ assert_eq ! ( miner1_blocks_after_boot_to_epoch3, 1 ) ;
12667
+
12668
+ info ! ( "------------------------- Shutdown -------------------------" ) ;
12669
+ miners. shutdown ( ) ;
12670
+ }
0 commit comments