@@ -3049,6 +3049,9 @@ fn bitcoind_forking_test() {
3049
3049
/// - Verify that the signer moves into tx replay state
3050
3050
/// - Verify that the signer correctly includes the stx transfer
3051
3051
/// in the tx replay set
3052
+ ///
3053
+ /// Then, a second fork scenario is tested, which
3054
+ /// includes multiple txs across multiple tenures.
3052
3055
fn tx_replay_forking_test() {
3053
3056
if env::var("BITCOIND_TEST") != Ok("1".into()) {
3054
3057
return;
@@ -3061,7 +3064,7 @@ fn tx_replay_forking_test() {
3061
3064
let send_fee = 180;
3062
3065
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
3063
3066
num_signers,
3064
- vec![(sender_addr, send_amt + send_fee)],
3067
+ vec![(sender_addr, ( send_amt + send_fee) * 10 )],
3065
3068
|_| {},
3066
3069
|node_config| {
3067
3070
node_config.miner.block_commit_delay = Duration::from_secs(1);
@@ -3079,9 +3082,10 @@ fn tx_replay_forking_test() {
3079
3082
for i in 0..pre_fork_tenures {
3080
3083
info!("Mining pre-fork tenure {} of {pre_fork_tenures}", i + 1);
3081
3084
signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3082
- signer_test.check_signer_states_normal();
3083
3085
}
3084
3086
3087
+ signer_test.check_signer_states_normal();
3088
+
3085
3089
let burn_blocks = test_observer::get_burn_blocks();
3086
3090
let forked_blocks = burn_blocks.iter().rev().take(2).collect::<Vec<_>>();
3087
3091
let last_forked_tenure: ConsensusHash = hex_bytes(
@@ -3220,6 +3224,8 @@ fn tx_replay_forking_test() {
3220
3224
3221
3225
TEST_MINE_STALL.set(false);
3222
3226
3227
+ info!("---- Mining post-fork block to clear tx replay set ----");
3228
+
3223
3229
// Now, make a new stacks block, which should clear the tx replay set
3224
3230
signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3225
3231
let (signer_states, _) = signer_test.get_burn_updated_states();
@@ -3230,6 +3236,146 @@ fn tx_replay_forking_test() {
3230
3236
);
3231
3237
}
3232
3238
3239
+ // Now, we'll trigger another fork, with more txs, across tenures
3240
+
3241
+ // The forked blocks are:
3242
+ // Tenure 1:
3243
+ // - Block with stx transfer
3244
+ // Tenure 2:
3245
+ // - Block with contract deploy
3246
+ // - Block with contract call
3247
+
3248
+ signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3249
+
3250
+ let pre_fork_2_tip = get_chain_info(&signer_test.running_nodes.conf);
3251
+
3252
+ let contract_code = "
3253
+ (define-public (call-fn)
3254
+ (ok true)
3255
+ )
3256
+ ";
3257
+ let contract_name = "test-contract";
3258
+
3259
+ let (transfer_txid, transfer_nonce) = signer_test
3260
+ .submit_transfer_tx(&sender_sk, send_fee, send_amt)
3261
+ .expect("Failed to submit transfer tx");
3262
+ signer_test
3263
+ .wait_for_nonce_increase(&sender_addr, transfer_nonce)
3264
+ .expect("Failed to wait for nonce increase");
3265
+ signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3266
+
3267
+ let (contract_deploy_txid, deploy_nonce) = signer_test
3268
+ .submit_contract_deploy(&sender_sk, contract_code, contract_name)
3269
+ .expect("Failed to submit contract deploy");
3270
+ signer_test
3271
+ .wait_for_nonce_increase(&sender_addr, deploy_nonce)
3272
+ .expect("Failed to wait for nonce increase");
3273
+
3274
+ let (contract_call_txid, contract_call_nonce) = signer_test
3275
+ .submit_contract_call(&sender_sk, contract_name, "call-fn", &[])
3276
+ .expect("Failed to submit contract call");
3277
+ signer_test
3278
+ .wait_for_nonce_increase(&sender_addr, contract_call_nonce)
3279
+ .expect("Failed to wait for nonce increase");
3280
+ signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3281
+
3282
+ TEST_MINE_STALL.set(true);
3283
+
3284
+ let burn_header_hash_to_fork = signer_test
3285
+ .running_nodes
3286
+ .btc_regtest_controller
3287
+ .get_block_hash(pre_fork_2_tip.burn_block_height);
3288
+ signer_test
3289
+ .running_nodes
3290
+ .btc_regtest_controller
3291
+ .invalidate_block(&burn_header_hash_to_fork);
3292
+ signer_test
3293
+ .running_nodes
3294
+ .btc_regtest_controller
3295
+ .build_next_block(3);
3296
+
3297
+ let burn_blocks = test_observer::get_burn_blocks();
3298
+ let forked_blocks = burn_blocks.iter().rev().take(2).collect::<Vec<_>>();
3299
+ let last_forked_tenure: ConsensusHash = hex_bytes(
3300
+ &forked_blocks[0]
3301
+ .get("consensus_hash")
3302
+ .unwrap()
3303
+ .as_str()
3304
+ .unwrap()[2..],
3305
+ )
3306
+ .unwrap()
3307
+ .as_slice()
3308
+ .into();
3309
+ let first_forked_tenure: ConsensusHash = hex_bytes(
3310
+ &forked_blocks[1]
3311
+ .get("consensus_hash")
3312
+ .unwrap()
3313
+ .as_str()
3314
+ .unwrap()[2..],
3315
+ )
3316
+ .unwrap()
3317
+ .as_slice()
3318
+ .into();
3319
+
3320
+ let fork_info = signer_test
3321
+ .stacks_client
3322
+ .get_tenure_forking_info(&first_forked_tenure, &last_forked_tenure)
3323
+ .unwrap();
3324
+
3325
+ info!("---- Fork info: {fork_info:?} ----");
3326
+
3327
+ for fork in fork_info {
3328
+ info!("---- Fork: {} ----", fork.consensus_hash);
3329
+ fork.nakamoto_blocks.inspect(|blocks| {
3330
+ for block in blocks {
3331
+ info!("---- Block: {} ----", block.header.chain_length);
3332
+ }
3333
+ });
3334
+ }
3335
+
3336
+ for i in 0..3 {
3337
+ let current_burn_height = get_chain_info(&signer_test.running_nodes.conf).burn_block_height;
3338
+ info!(
3339
+ "Mining block #{i} to be considered a frequent miner";
3340
+ "current_burn_height" => current_burn_height,
3341
+ );
3342
+ let commits_count = submitted_commits.load(Ordering::SeqCst);
3343
+ next_block_and_controller(
3344
+ &mut signer_test.running_nodes.btc_regtest_controller,
3345
+ 60,
3346
+ |_btc_controller| {
3347
+ let commits_submitted = submitted_commits.load(Ordering::SeqCst);
3348
+ Ok(commits_submitted > commits_count)
3349
+ },
3350
+ )
3351
+ .unwrap();
3352
+ }
3353
+
3354
+ let expected_tx_replay_txids = vec![transfer_txid, contract_deploy_txid, contract_call_txid];
3355
+
3356
+ let (signer_states, _) = signer_test.get_burn_updated_states();
3357
+ for state in signer_states {
3358
+ match state {
3359
+ LocalStateMachine::Initialized(signer_state_machine) => {
3360
+ let Some(tx_replay_set) = signer_state_machine.tx_replay_set else {
3361
+ panic!(
3362
+ "Signer state machine is in tx replay state, but tx replay set is not set"
3363
+ );
3364
+ };
3365
+ info!("---- Tx replay set: {:?} ----", tx_replay_set);
3366
+ assert_eq!(tx_replay_set.len(), expected_tx_replay_txids.len());
3367
+ let state_replay_txids = tx_replay_set
3368
+ .iter()
3369
+ .map(|tx| tx.txid().to_hex())
3370
+ .collect::<Vec<_>>();
3371
+ assert_eq!(state_replay_txids, expected_tx_replay_txids);
3372
+ }
3373
+ _ => {
3374
+ panic!("Signer state is not in the initialized state");
3375
+ }
3376
+ }
3377
+ }
3378
+
3233
3379
signer_test.shutdown();
3234
3380
}
3235
3381
0 commit comments