@@ -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:: {
@@ -5077,6 +5078,143 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
5077
5078
assert_ne ! ( block_n_2, block_n) ;
5078
5079
}
5079
5080
5081
+ #[ test]
5082
+ #[ ignore]
5083
+ /// Test that we can mine a tenure extend and then continue mining afterwards.
5084
+ fn continue_after_tenure_extend ( ) {
5085
+ if env:: var ( "BITCOIND_TEST" ) != Ok ( "1" . into ( ) ) {
5086
+ return ;
5087
+ }
5088
+
5089
+ tracing_subscriber:: registry ( )
5090
+ . with ( fmt:: layer ( ) )
5091
+ . with ( EnvFilter :: from_default_env ( ) )
5092
+ . init ( ) ;
5093
+
5094
+ info ! ( "------------------------- Test Setup -------------------------" ) ;
5095
+ let num_signers = 5 ;
5096
+ let sender_sk = Secp256k1PrivateKey :: new ( ) ;
5097
+ let sender_addr = tests:: to_addr ( & sender_sk) ;
5098
+ let recipient = PrincipalData :: from ( StacksAddress :: burn_address ( false ) ) ;
5099
+ let send_amt = 100 ;
5100
+ let send_fee = 180 ;
5101
+ let mut signer_test: SignerTest < SpawnedSigner > = SignerTest :: new (
5102
+ num_signers,
5103
+ vec ! [ ( sender_addr. clone( ) , ( send_amt + send_fee) * 5 ) ] ,
5104
+ ) ;
5105
+ let timeout = Duration :: from_secs ( 200 ) ;
5106
+ let coord_channel = signer_test. running_nodes . coord_channel . clone ( ) ;
5107
+ let http_origin = format ! ( "http://{}" , & signer_test. running_nodes. conf. node. rpc_bind) ;
5108
+
5109
+ signer_test. boot_to_epoch_3 ( ) ;
5110
+
5111
+ info ! ( "------------------------- Mine Normal Tenure -------------------------" ) ;
5112
+ signer_test. mine_and_verify_confirmed_naka_block ( timeout, num_signers) ;
5113
+
5114
+ info ! ( "------------------------- Extend Tenure -------------------------" ) ;
5115
+ signer_test
5116
+ . running_nodes
5117
+ . nakamoto_test_skip_commit_op
5118
+ . 0
5119
+ . lock ( )
5120
+ . unwrap ( )
5121
+ . replace ( true ) ;
5122
+
5123
+ // It's possible that we have a pending block commit already.
5124
+ // Mine two BTC blocks to "flush" this commit.
5125
+ let burn_height = signer_test
5126
+ . stacks_client
5127
+ . get_peer_info ( )
5128
+ . expect ( "Failed to get peer info" )
5129
+ . burn_block_height ;
5130
+ for i in 0 ..2 {
5131
+ info ! (
5132
+ "------------- After pausing commits, triggering 2 BTC blocks: ({} of 2) -----------" ,
5133
+ i + 1
5134
+ ) ;
5135
+
5136
+ let blocks_processed_before = coord_channel
5137
+ . lock ( )
5138
+ . expect ( "Mutex poisoned" )
5139
+ . get_stacks_blocks_processed ( ) ;
5140
+ signer_test
5141
+ . running_nodes
5142
+ . btc_regtest_controller
5143
+ . build_next_block ( 1 ) ;
5144
+
5145
+ wait_for ( 60 , || {
5146
+ let blocks_processed_after = coord_channel
5147
+ . lock ( )
5148
+ . expect ( "Mutex poisoned" )
5149
+ . get_stacks_blocks_processed ( ) ;
5150
+ Ok ( blocks_processed_after > blocks_processed_before)
5151
+ } )
5152
+ . expect ( "Timed out waiting for tenure extend block" ) ;
5153
+ }
5154
+
5155
+ wait_for ( 30 , || {
5156
+ let new_burn_height = signer_test
5157
+ . stacks_client
5158
+ . get_peer_info ( )
5159
+ . expect ( "Failed to get peer info" )
5160
+ . burn_block_height ;
5161
+ Ok ( new_burn_height == burn_height + 2 )
5162
+ } )
5163
+ . expect ( "Timed out waiting for burnchain to advance" ) ;
5164
+
5165
+ // The last block should have a single instruction in it, the tenure extend
5166
+ let blocks = test_observer:: get_blocks ( ) ;
5167
+ let last_block = blocks. last ( ) . unwrap ( ) ;
5168
+ let transactions = last_block[ "transactions" ] . as_array ( ) . unwrap ( ) ;
5169
+ let tx = transactions. first ( ) . expect ( "No transactions in block" ) ;
5170
+ let raw_tx = tx[ "raw_tx" ] . as_str ( ) . unwrap ( ) ;
5171
+ let tx_bytes = hex_bytes ( & raw_tx[ 2 ..] ) . unwrap ( ) ;
5172
+ let parsed = StacksTransaction :: consensus_deserialize ( & mut & tx_bytes[ ..] ) . unwrap ( ) ;
5173
+ match & parsed. payload {
5174
+ TransactionPayload :: TenureChange ( payload)
5175
+ if payload. cause == TenureChangeCause :: Extended => { }
5176
+ _ => panic ! ( "Expected tenure extend transaction, got {:?}" , parsed) ,
5177
+ } ;
5178
+
5179
+ // Verify that the miner can continue mining in the tenure with the tenure extend
5180
+ info ! ( "------------------------- Mine After Tenure Extend -------------------------" ) ;
5181
+ let mut sender_nonce = 0 ;
5182
+ let mut blocks_processed_before = coord_channel
5183
+ . lock ( )
5184
+ . expect ( "Mutex poisoned" )
5185
+ . get_stacks_blocks_processed ( ) ;
5186
+ for _ in 0 ..5 {
5187
+ // submit a tx so that the miner will mine an extra block
5188
+ let transfer_tx = make_stacks_transfer (
5189
+ & sender_sk,
5190
+ sender_nonce,
5191
+ send_fee,
5192
+ signer_test. running_nodes . conf . burnchain . chain_id ,
5193
+ & recipient,
5194
+ send_amt,
5195
+ ) ;
5196
+ sender_nonce += 1 ;
5197
+ submit_tx ( & http_origin, & transfer_tx) ;
5198
+
5199
+ info ! ( "Submitted transfer tx and waiting for block proposal" ) ;
5200
+ wait_for ( 30 , || {
5201
+ let blocks_processed_after = coord_channel
5202
+ . lock ( )
5203
+ . expect ( "Mutex poisoned" )
5204
+ . get_stacks_blocks_processed ( ) ;
5205
+ Ok ( blocks_processed_after > blocks_processed_before)
5206
+ } )
5207
+ . expect ( "Timed out waiting for block proposal" ) ;
5208
+ blocks_processed_before = coord_channel
5209
+ . lock ( )
5210
+ . expect ( "Mutex poisoned" )
5211
+ . get_stacks_blocks_processed ( ) ;
5212
+ info ! ( "Block {blocks_processed_before} processed, continuing" ) ;
5213
+ }
5214
+
5215
+ signer_test. shutdown ( ) ;
5216
+ }
5217
+
5080
5218
#[ test]
5081
5219
#[ ignore]
5082
5220
/// Test that signers can successfully sign a block proposal in the 0th tenure of a reward cycle
0 commit comments