@@ -34,6 +34,7 @@ use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader, NakamotoC
34
34
use stacks:: chainstate:: stacks:: address:: PoxAddress ;
35
35
use stacks:: chainstate:: stacks:: boot:: MINERS_NAME ;
36
36
use stacks:: chainstate:: stacks:: db:: { StacksBlockHeaderTypes , StacksChainState , StacksHeaderInfo } ;
37
+ use stacks:: chainstate:: stacks:: { StacksTransaction , TenureChangeCause , TransactionPayload } ;
37
38
use stacks:: codec:: StacksMessageCodec ;
38
39
use stacks:: core:: { StacksEpochId , CHAIN_ID_TESTNET } ;
39
40
use stacks:: libstackerdb:: StackerDBChunkData ;
@@ -42,7 +43,7 @@ use stacks::net::api::postblock_proposal::{ValidateRejectCode, TEST_VALIDATE_STA
42
43
use stacks:: net:: relay:: fault_injection:: set_ignore_block;
43
44
use stacks:: types:: chainstate:: { StacksAddress , StacksBlockId , StacksPrivateKey , StacksPublicKey } ;
44
45
use stacks:: types:: PublicKey ;
45
- use stacks:: util:: hash:: MerkleHashFunc ;
46
+ use stacks:: util:: hash:: { hex_bytes , MerkleHashFunc } ;
46
47
use stacks:: util:: secp256k1:: { Secp256k1PrivateKey , Secp256k1PublicKey } ;
47
48
use stacks:: util_lib:: boot:: boot_code_id;
48
49
use stacks:: util_lib:: signed_structured_data:: pox4:: {
@@ -4942,6 +4943,128 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
4942
4943
assert_ne ! ( block_n_2, block_n) ;
4943
4944
}
4944
4945
4946
+ #[ test]
4947
+ #[ ignore]
4948
+ /// Test that we can mine a tenure extend and then continue mining afterwards.
4949
+ fn continue_after_tenure_extend ( ) {
4950
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
4951
+ return ;
4952
+ }
4953
+
4954
+ tracing_subscriber:: registry ( )
4955
+ . with ( fmt:: layer ( ) )
4956
+ . with ( EnvFilter :: from_default_env ( ) )
4957
+ . init ( ) ;
4958
+
4959
+ info ! ( "------------------------- Test Setup -------------------------" ) ;
4960
+ let num_signers = 5 ;
4961
+ let sender_sk = Secp256k1PrivateKey :: new ( ) ;
4962
+ let sender_addr = tests:: to_addr ( & sender_sk) ;
4963
+ let recipient = PrincipalData :: from ( StacksAddress :: burn_address ( false ) ) ;
4964
+ let send_amt = 100 ;
4965
+ let send_fee = 180 ;
4966
+ let mut signer_test: SignerTest < SpawnedSigner > = SignerTest :: new (
4967
+ num_signers,
4968
+ vec ! [ ( sender_addr. clone( ) , ( send_amt + send_fee) * 5 ) ] ,
4969
+ ) ;
4970
+ let timeout = Duration :: from_secs ( 200 ) ;
4971
+ let coord_channel = signer_test. running_nodes . coord_channel . clone ( ) ;
4972
+ let http_origin = format ! ( "http://{}" , & signer_test. running_nodes. conf. node. rpc_bind) ;
4973
+
4974
+ signer_test. boot_to_epoch_3 ( ) ;
4975
+
4976
+ info ! ( "------------------------- Mine Normal Tenure -------------------------" ) ;
4977
+ signer_test. mine_and_verify_confirmed_naka_block ( timeout, num_signers) ;
4978
+
4979
+ info ! ( "------------------------- Extend Tenure -------------------------" ) ;
4980
+ signer_test
4981
+ . running_nodes
4982
+ . nakamoto_test_skip_commit_op
4983
+ . 0
4984
+ . lock ( )
4985
+ . unwrap ( )
4986
+ . replace ( true ) ;
4987
+
4988
+ // It's possible that we have a pending block commit already.
4989
+ // Mine two BTC blocks to "flush" this commit.
4990
+
4991
+ let mut blocks_processed_before = coord_channel
4992
+ . lock ( )
4993
+ . expect ( "Mutex poisoned" )
4994
+ . get_stacks_blocks_processed ( ) ;
4995
+
4996
+ for i in 0 ..2 {
4997
+ info ! (
4998
+ "------------- After pausing commits, triggering 2 BTC blocks: ({} of 2) -----------" ,
4999
+ i + 1
5000
+ ) ;
5001
+
5002
+ blocks_processed_before = coord_channel
5003
+ . lock ( )
5004
+ . expect ( "Mutex poisoned" )
5005
+ . get_stacks_blocks_processed ( ) ;
5006
+ signer_test
5007
+ . running_nodes
5008
+ . btc_regtest_controller
5009
+ . build_next_block ( 1 ) ;
5010
+
5011
+ wait_for ( 60 , || {
5012
+ let blocks_processed_after = coord_channel
5013
+ . lock ( )
5014
+ . expect ( "Mutex poisoned" )
5015
+ . get_stacks_blocks_processed ( ) ;
5016
+ Ok ( blocks_processed_after > blocks_processed_before)
5017
+ } )
5018
+ . expect ( "Timed out waiting for tenure extend block" ) ;
5019
+ }
5020
+
5021
+ // The last block should have a single instruction in it, the tenure extend
5022
+ let blocks = test_observer:: get_blocks ( ) ;
5023
+ let last_block = blocks. last ( ) . unwrap ( ) ;
5024
+ let transactions = last_block[ "transactions" ] . as_array ( ) . unwrap ( ) ;
5025
+ let tx = transactions. first ( ) . expect ( "No transactions in block" ) ;
5026
+ let raw_tx = tx[ "raw_tx" ] . as_str ( ) . unwrap ( ) ;
5027
+ let tx_bytes = hex_bytes ( & raw_tx[ 2 ..] ) . unwrap ( ) ;
5028
+ let parsed = StacksTransaction :: consensus_deserialize ( & mut & tx_bytes[ ..] ) . unwrap ( ) ;
5029
+ match & parsed. payload {
5030
+ TransactionPayload :: TenureChange ( payload)
5031
+ if payload. cause == TenureChangeCause :: Extended => { }
5032
+ _ => panic ! ( "Expected tenure extend transaction, got {:?}" , parsed) ,
5033
+ } ;
5034
+
5035
+ // Verify that the miner can continue mining in the tenure with the tenure extend
5036
+ info ! ( "------------------------- Mine After Tenure Extend -------------------------" ) ;
5037
+ let mut sender_nonce = 0 ;
5038
+ blocks_processed_before = coord_channel
5039
+ . lock ( )
5040
+ . expect ( "Mutex poisoned" )
5041
+ . get_stacks_blocks_processed ( ) ;
5042
+ for _ in 0 ..5 {
5043
+ // submit a tx so that the miner will mine an extra block
5044
+ let transfer_tx =
5045
+ make_stacks_transfer ( & sender_sk, sender_nonce, send_fee, & recipient, send_amt) ;
5046
+ sender_nonce += 1 ;
5047
+ submit_tx ( & http_origin, & transfer_tx) ;
5048
+
5049
+ info ! ( "Submitted transfer tx and waiting for block proposal" ) ;
5050
+ wait_for ( 30 , || {
5051
+ let blocks_processed_after = coord_channel
5052
+ . lock ( )
5053
+ . expect ( "Mutex poisoned" )
5054
+ . get_stacks_blocks_processed ( ) ;
5055
+ Ok ( blocks_processed_after > blocks_processed_before)
5056
+ } )
5057
+ . expect ( "Timed out waiting for block proposal" ) ;
5058
+ blocks_processed_before = coord_channel
5059
+ . lock ( )
5060
+ . expect ( "Mutex poisoned" )
5061
+ . get_stacks_blocks_processed ( ) ;
5062
+ info ! ( "Block {blocks_processed_before} processed, continuing" ) ;
5063
+ }
5064
+
5065
+ signer_test. shutdown ( ) ;
5066
+ }
5067
+
4945
5068
#[ test]
4946
5069
#[ ignore]
4947
5070
/// Test that signers can successfully sign a block proposal in the 0th tenure of a reward cycle
0 commit comments