@@ -5023,7 +5023,9 @@ fn miner_recovers_when_broadcast_block_delay_across_tenures_occurs() {
5023
5023
/// Miner 1 wins the first Nakamoto tenure A. Miner 1 mines a regular stacks block N.
5024
5024
/// Miner 2 wins the second Nakamoto tenure B and proposes block N+1, but it is rejected by the signers.
5025
5025
/// An empty burn block is mined
5026
- /// Miner 2 wins the third Nakamoto tenure C. Miner 2 proposes a block N+1' which all signers accept.
5026
+ /// TODO: which behaviour do we want to enforce? Should we allow both? If so, we should force both explicitly
5027
+ /// Miner 1 should issue a tenure extend and propose block N+1' which is accepted by the signers OR
5028
+ /// Miner 2 should issue a new TenureChangePayload followed by a TenureExtend.
5027
5029
/// Asserts:
5028
5030
/// - The stacks tip advances to N+1'
5029
5031
#[ test]
@@ -5034,11 +5036,13 @@ fn continue_after_fast_block_no_sortition() {
5034
5036
}
5035
5037
5036
5038
let num_signers = 5 ;
5039
+ let recipient = PrincipalData :: from ( StacksAddress :: burn_address ( false ) ) ;
5037
5040
let sender_sk = Secp256k1PrivateKey :: new ( ) ;
5038
5041
let sender_addr = tests:: to_addr ( & sender_sk) ;
5039
5042
let send_amt = 100 ;
5040
5043
let send_fee = 180 ;
5041
- let num_txs = 5 ;
5044
+ let num_txs = 1 ;
5045
+ let sender_nonce = 0 ;
5042
5046
5043
5047
let btc_miner_1_seed = vec ! [ 1 , 1 , 1 , 1 ] ;
5044
5048
let btc_miner_2_seed = vec ! [ 2 , 2 , 2 , 2 ] ;
@@ -5129,6 +5133,7 @@ fn continue_after_fast_block_no_sortition() {
5129
5133
conf. burnchain . chain_id ,
5130
5134
conf. burnchain . peer_version ,
5131
5135
) ;
5136
+ let http_origin = format ! ( "http://{}" , & signer_test. running_nodes. conf. node. rpc_bind) ;
5132
5137
5133
5138
let mut run_loop_2 = boot_nakamoto:: BootRunLoop :: new ( conf_node_2. clone ( ) ) . unwrap ( ) ;
5134
5139
let run_loop_stopper_2 = run_loop_2. get_termination_switch ( ) ;
@@ -5145,6 +5150,7 @@ fn continue_after_fast_block_no_sortition() {
5145
5150
5146
5151
info ! ( "------------------------- Pause Miner 2's Block Commits -------------------------" ) ;
5147
5152
5153
+ // Make sure Miner 2 cannot win a sortition at first.
5148
5154
rl2_skip_commit_op. set ( true ) ;
5149
5155
let run_loop_2_thread = thread:: Builder :: new ( )
5150
5156
. name ( "run_loop_2" . into ( ) )
@@ -5185,16 +5191,23 @@ fn continue_after_fast_block_no_sortition() {
5185
5191
5186
5192
info ! ( "------------------------- Miner 1 Mines a Normal Tenure A -------------------------" ) ;
5187
5193
let blocks_processed_before_1 = blocks_mined1. load ( Ordering :: SeqCst ) ;
5188
- let commits_before_1 = rl1_commits. load ( Ordering :: SeqCst ) ;
5194
+ info ! ( "------------------------- Pause Miner 1's Block Commit -------------------------" ) ;
5195
+ // Make sure miner 1 doesn't submit a block commit for the next tenure BEFORE mining the bitcoin block
5196
+ signer_test
5197
+ . running_nodes
5198
+ . nakamoto_test_skip_commit_op
5199
+ . set ( true ) ;
5189
5200
5190
- next_block_and (
5191
- & mut signer_test. running_nodes . btc_regtest_controller ,
5192
- 60 ,
5193
- || Ok ( rl1_commits. load ( Ordering :: SeqCst ) > commits_before_1) ,
5194
- )
5195
- . unwrap ( ) ;
5201
+ signer_test
5202
+ . running_nodes
5203
+ . btc_regtest_controller
5204
+ . build_next_block ( 1 ) ;
5196
5205
btc_blocks_mined += 1 ;
5197
5206
5207
+ // assure we have a sortition
5208
+ let tip = SortitionDB :: get_canonical_burn_chain_tip ( sortdb. conn ( ) ) . unwrap ( ) ;
5209
+ assert ! ( tip. sortition) ;
5210
+
5198
5211
// wait for the new block to be processed
5199
5212
wait_for ( 60 , || {
5200
5213
Ok ( blocks_mined1. load ( Ordering :: SeqCst ) > blocks_processed_before_1)
@@ -5212,28 +5225,35 @@ fn continue_after_fast_block_no_sortition() {
5212
5225
. expect ( "Failed to get peer info" )
5213
5226
. stacks_tip_height ;
5214
5227
5228
+ info ! ( "------------------------- Make Signers Reject All Subsequent Proposals -------------------------" ) ;
5215
5229
// Make all signers ignore block proposals
5216
5230
let ignoring_signers = all_signers. to_vec ( ) ;
5217
5231
TEST_REJECT_ALL_BLOCK_PROPOSAL
5218
5232
. lock ( )
5219
5233
. unwrap ( )
5220
5234
. replace ( ignoring_signers. clone ( ) ) ;
5221
5235
5222
- // Make sure miner 1 doesn't submit a block commit for the next tenure
5223
- signer_test
5224
- . running_nodes
5225
- . nakamoto_test_skip_commit_op
5226
- . set ( true ) ;
5227
-
5236
+ info ! ( "------------------------- Unpause Miner 2's Block Commits -------------------------" ) ;
5228
5237
let rejections_before = signer_test
5229
5238
. running_nodes
5230
5239
. nakamoto_blocks_rejected
5231
5240
. load ( Ordering :: SeqCst ) ;
5232
5241
5242
+ let rl2_commits_before = rl2_commits. load ( Ordering :: SeqCst ) ;
5233
5243
// Unpause miner 2's block commits
5234
5244
rl2_skip_commit_op. set ( false ) ;
5235
5245
5236
- // Mine a new burn block
5246
+ info ! ( "------------------------- Wait for Miner 2's Block Commit Submission -------------------------" ) ;
5247
+ // Ensure the miner 2 submits a block commit before mining the bitcoin block
5248
+ wait_for ( 30 , || {
5249
+ Ok ( rl2_commits. load ( Ordering :: SeqCst ) > rl2_commits_before)
5250
+ } )
5251
+ . unwrap ( ) ;
5252
+
5253
+ // Make miner 2 also fail to submit any FURTHER block commits
5254
+ info ! ( "------------------------- Pause Miner 2's Block Commits -------------------------" ) ;
5255
+ rl2_skip_commit_op. set ( true ) ;
5256
+
5237
5257
let burn_height_before = get_burn_height ( ) ;
5238
5258
info ! ( "------------------------- Miner 2 Mines an Empty Tenure B -------------------------" ;
5239
5259
"burn_height_before" => burn_height_before,
@@ -5253,20 +5273,29 @@ fn continue_after_fast_block_no_sortition() {
5253
5273
assert ! ( tip. sortition) ;
5254
5274
5255
5275
info ! ( "----- Waiting for block rejections -----" ) ;
5256
- let min_rejections = ( num_signers as u64 ) * 4 / 10 ;
5276
+ let min_rejections = num_signers * 4 / 10 ;
5257
5277
// Wait until we have some block rejections
5258
5278
wait_for ( 30 , || {
5259
- let rejections = signer_test
5260
- . running_nodes
5261
- . nakamoto_blocks_rejected
5262
- . load ( Ordering :: SeqCst ) ;
5263
- let rejections_diff = rejections - rejections_before;
5264
- Ok ( rejections_diff >= min_rejections)
5279
+ std:: thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
5280
+ let chunks = test_observer:: get_stackerdb_chunks ( ) ;
5281
+ let rejections: Vec < _ > = chunks
5282
+ . into_iter ( )
5283
+ . flat_map ( |chunk| chunk. modified_slots )
5284
+ . filter ( |chunk| {
5285
+ let Ok ( message) = SignerMessage :: consensus_deserialize ( & mut chunk. data . as_slice ( ) )
5286
+ else {
5287
+ return false ;
5288
+ } ;
5289
+ matches ! (
5290
+ message,
5291
+ SignerMessage :: BlockResponse ( BlockResponse :: Rejected ( _) )
5292
+ )
5293
+ } )
5294
+ . collect ( ) ;
5295
+ Ok ( rejections. len ( ) >= min_rejections)
5265
5296
} )
5266
5297
. expect ( "Timed out waiting for block rejections" ) ;
5267
5298
5268
- // Make miner 2 also fail to submit commits
5269
- rl2_skip_commit_op. set ( true ) ;
5270
5299
// Miner another block and ensure there is _no_ sortition
5271
5300
info ! ( "------------------------- Mine Burn Block with No Sortition -------------------------" ) ;
5272
5301
let blocks_processed_before_1 = blocks_mined1. load ( Ordering :: SeqCst ) ;
@@ -5294,6 +5323,10 @@ fn continue_after_fast_block_no_sortition() {
5294
5323
blocks_processed_before_2
5295
5324
) ;
5296
5325
5326
+ // assure we have NO sortition
5327
+ let tip = SortitionDB :: get_canonical_burn_chain_tip ( sortdb. conn ( ) ) . unwrap ( ) ;
5328
+ assert ! ( !tip. sortition) ;
5329
+
5297
5330
// Verify that no Stacks blocks have been mined (signers are ignoring) and no commits have been submitted by either miner
5298
5331
let stacks_height = signer_test
5299
5332
. stacks_client
@@ -5303,56 +5336,32 @@ fn continue_after_fast_block_no_sortition() {
5303
5336
assert_eq ! ( stacks_height, stacks_height_before) ;
5304
5337
let stacks_height_before = stacks_height;
5305
5338
5306
- info ! (
5307
- "------------------------- Miner 2 Attempts to Mine a Tenure C -------------------------"
5308
- ) ;
5309
-
5310
5339
let blocks_processed_before_2 = blocks_mined2. load ( Ordering :: SeqCst ) ;
5311
- let burn_height_before = get_burn_height ( ) ;
5312
- let commits_before_2 = rl2_commits. load ( Ordering :: SeqCst ) ;
5313
-
5314
5340
info ! ( "----- Enabling signers to approve proposals -----" ;
5315
5341
"stacks_height" => stacks_height_before,
5316
5342
) ;
5343
+
5344
+ let nmb_old_blocks = test_observer:: get_blocks ( ) . len ( ) ;
5317
5345
// Allow signers to respond to proposals again
5318
5346
TEST_REJECT_ALL_BLOCK_PROPOSAL
5319
5347
. lock ( )
5320
5348
. unwrap ( )
5321
5349
. replace ( Vec :: new ( ) ) ;
5322
5350
5323
- // Unpause miner 2's block commits
5324
- rl2_skip_commit_op. set ( false ) ;
5325
-
5326
- // TODO: can combine the following three wait_for and also the next_block_and once fixed
5327
- next_block_and (
5328
- & mut signer_test. running_nodes . btc_regtest_controller ,
5329
- 60 ,
5330
- || Ok ( rl2_commits. load ( Ordering :: SeqCst ) > commits_before_2) ,
5331
- )
5332
- . unwrap ( ) ;
5333
- btc_blocks_mined += 1 ;
5334
-
5335
- wait_for ( 30 , || Ok ( get_burn_height ( ) > burn_height_before) ) . unwrap ( ) ;
5336
-
5337
- // TODO DELETE THIS
5338
- let blocks = test_observer:: get_blocks ( ) ;
5339
- // Debug the last 4 blocks
5340
- let blocks = blocks. iter ( ) . rev ( ) . take ( 4 ) . rev ( ) . collect :: < Vec < _ > > ( ) ;
5341
- for block in blocks {
5342
- println ! ( "\n \n " ) ;
5343
- info ! ( "Block: {}" , serde_json:: to_string_pretty( & block) . unwrap( ) ) ;
5344
- let transactions = block. get ( "transactions" ) . unwrap ( ) . as_array ( ) . unwrap ( ) ;
5345
- for tx in transactions. iter ( ) . rev ( ) {
5346
- let raw_tx = tx. get ( "raw_tx" ) . unwrap ( ) . as_str ( ) . unwrap ( ) ;
5347
- if raw_tx != "0x00" {
5348
- let tx_bytes = hex_bytes ( & raw_tx[ 2 ..] ) . unwrap ( ) ;
5349
- let parsed =
5350
- StacksTransaction :: consensus_deserialize ( & mut tx_bytes. as_slice ( ) ) . unwrap ( ) ;
5351
- info ! ( "Tx: {}" , serde_json:: to_string_pretty( & parsed) . unwrap( ) ) ;
5352
- }
5353
- }
5354
- }
5351
+ // submit a tx so that the miner will mine an extra block just in case due to timing constraints, the first block with the tenure extend was
5352
+ // rejected already by the signers
5353
+ let transfer_tx = make_stacks_transfer (
5354
+ & sender_sk,
5355
+ sender_nonce,
5356
+ send_fee,
5357
+ signer_test. running_nodes . conf . burnchain . chain_id ,
5358
+ & recipient,
5359
+ send_amt,
5360
+ ) ;
5361
+ submit_tx ( & http_origin, & transfer_tx) ;
5355
5362
5363
+ // TODO: combine these wait fors once fixed code
5364
+ // wait for the new block to be processed
5356
5365
wait_for ( 30 , || {
5357
5366
Ok ( blocks_mined2. load ( Ordering :: SeqCst ) > blocks_processed_before_2)
5358
5367
} )
@@ -5368,14 +5377,37 @@ fn continue_after_fast_block_no_sortition() {
5368
5377
} )
5369
5378
. expect ( "Expected a new Stacks block to be mined" ) ;
5370
5379
5380
+ wait_for (
5381
+ 30 ,
5382
+ || Ok ( test_observer:: get_blocks ( ) . len ( ) > nmb_old_blocks) ,
5383
+ )
5384
+ . expect ( "Timed out waiting for test observer to see new block" ) ;
5385
+
5386
+ let blocks = test_observer:: get_blocks ( ) ;
5387
+ let tenure_extend_block = if nmb_old_blocks + 1 == test_observer:: get_blocks ( ) . len ( ) {
5388
+ blocks. last ( ) . unwrap ( )
5389
+ } else {
5390
+ & blocks[ blocks. len ( ) - 2 ]
5391
+ } ;
5392
+ let transactions = tenure_extend_block[ "transactions" ] . as_array ( ) . unwrap ( ) ;
5393
+ let tx = transactions. first ( ) . expect ( "No transactions in block" ) ;
5394
+ let raw_tx = tx[ "raw_tx" ] . as_str ( ) . unwrap ( ) ;
5395
+ let tx_bytes = hex_bytes ( & raw_tx[ 2 ..] ) . unwrap ( ) ;
5396
+ let parsed = StacksTransaction :: consensus_deserialize ( & mut & tx_bytes[ ..] ) . unwrap ( ) ;
5397
+ match & parsed. payload {
5398
+ TransactionPayload :: TenureChange ( payload)
5399
+ if payload. cause == TenureChangeCause :: Extended => { }
5400
+ _ => panic ! ( "Expected tenure extend transaction, got {parsed:?}" ) ,
5401
+ } ;
5402
+
5371
5403
let peer_info = signer_test
5372
5404
. stacks_client
5373
5405
. get_peer_info ( )
5374
5406
. expect ( "Failed to get peer info" ) ;
5375
5407
5376
5408
assert_eq ! ( get_burn_height( ) , starting_burn_height + btc_blocks_mined) ;
5377
- // We only successfully mine 2 stacks block in this test
5378
- assert_eq ! ( peer_info. stacks_tip_height, starting_peer_height + 2 ) ;
5409
+ // We successfully mine at least 2 stacks block in this test
5410
+ assert ! ( peer_info. stacks_tip_height >= starting_peer_height + 2 ) ;
5379
5411
rl2_coord_channels
5380
5412
. lock ( )
5381
5413
. expect ( "Mutex poisoned" )
0 commit comments