@@ -49,7 +49,6 @@ use stacks::chainstate::stacks::boot::MINERS_NAME;
49
49
use stacks::chainstate::stacks::db::{StacksBlockHeaderTypes, StacksChainState, StacksHeaderInfo};
50
50
use stacks::chainstate::stacks::miner::{
51
51
TransactionEvent, TransactionSuccessEvent, TEST_EXCLUDE_REPLAY_TXS,
52
- TEST_MINE_ALLOWED_REPLAY_TXS,
53
52
};
54
53
use stacks::chainstate::stacks::{
55
54
StacksTransaction, TenureChangeCause, TenureChangePayload, TransactionPayload,
@@ -65,8 +64,8 @@ use stacks::core::{StacksEpochId, CHAIN_ID_TESTNET, HELIUM_BLOCK_LIMIT_20};
65
64
use stacks::libstackerdb::StackerDBChunkData;
66
65
use stacks::net::api::getsigner::GetSignerResponse;
67
66
use stacks::net::api::postblock_proposal::{
68
- BlockValidateResponse, ValidateRejectCode, TEST_VALIDATE_DELAY_DURATION_SECS ,
69
- TEST_VALIDATE_STALL,
67
+ BlockValidateResponse, ValidateRejectCode, TEST_REJECT_REPLAY_TXS ,
68
+ TEST_VALIDATE_DELAY_DURATION_SECS, TEST_VALIDATE_STALL,
70
69
};
71
70
use stacks::net::relay::fault_injection::{clear_ignore_block, set_ignore_block};
72
71
use stacks::types::chainstate::{
@@ -3802,6 +3801,195 @@ fn tx_replay_btc_on_stx_invalidation() {
3802
3801
signer_test.shutdown();
3803
3802
}
3804
3803
3804
+ /// Test scenario to ensure that the replay set is cleared
3805
+ /// if there have been multiple tenures with a stalled replay set.
3806
+ ///
3807
+ /// This test is executed by triggering a fork, and then using
3808
+ /// a test flag to reject any transaction replay blocks.
3809
+ ///
3810
+ /// The test mines a number of burn blocks during replay before
3811
+ /// validating that the replay set is eventually cleared.
3812
+ #[ignore]
3813
+ #[test]
3814
+ fn tx_replay_failsafe() {
3815
+ if env::var("BITCOIND_TEST") != Ok("1".into()) {
3816
+ return;
3817
+ }
3818
+
3819
+ let num_signers = 5;
3820
+ let sender_sk = Secp256k1PrivateKey::from_seed("sender_1".as_bytes());
3821
+ let sender_addr = tests::to_addr(&sender_sk);
3822
+ let send_amt = 100;
3823
+ let send_fee = 180;
3824
+ let signer_test: SignerTest<SpawnedSigner> =
3825
+ SignerTest::new_with_config_modifications_and_snapshot(
3826
+ num_signers,
3827
+ vec![(sender_addr, (send_amt + send_fee) * 10)],
3828
+ |c| {
3829
+ c.validate_with_replay_tx = true;
3830
+ },
3831
+ |node_config| {
3832
+ node_config.miner.block_commit_delay = Duration::from_secs(1);
3833
+ node_config.miner.replay_transactions = true;
3834
+ node_config.miner.activated_vrf_key_path =
3835
+ Some(format!("{}/vrf_key", node_config.node.working_dir));
3836
+ },
3837
+ None,
3838
+ None,
3839
+ Some(function_name!()),
3840
+ );
3841
+
3842
+ let conf = &signer_test.running_nodes.conf;
3843
+ let _http_origin = format!("http://{}", &conf.node.rpc_bind);
3844
+ let btc_controller = &signer_test.running_nodes.btc_regtest_controller;
3845
+
3846
+ if signer_test.bootstrap_snapshot() {
3847
+ signer_test.shutdown_and_snapshot();
3848
+ return;
3849
+ }
3850
+
3851
+ info!("------------------------- Beginning test -------------------------");
3852
+
3853
+ let burnchain = conf.get_burnchain();
3854
+
3855
+ let tip = signer_test.get_peer_info();
3856
+ let pox_info = signer_test.get_pox_data();
3857
+
3858
+ info!("---- Burnchain ----";
3859
+ // "burnchain" => ?conf.burnchain,
3860
+ "pox_constants" => ?burnchain.pox_constants,
3861
+ "cycle" => burnchain.pox_constants.reward_cycle_index(0, tip.burn_block_height),
3862
+ "pox_info" => ?pox_info,
3863
+ );
3864
+
3865
+ let pre_fork_tenures = 11;
3866
+ for i in 0..pre_fork_tenures {
3867
+ info!("Mining pre-fork tenure {} of {pre_fork_tenures}", i + 1);
3868
+ signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3869
+ }
3870
+
3871
+ info!("---- Submitting STX transfer ----");
3872
+
3873
+ let tip = get_chain_info(&conf);
3874
+ // Make a transfer tx (this will get forked)
3875
+ let (txid, nonce) = signer_test
3876
+ .submit_transfer_tx(&sender_sk, send_fee, send_amt)
3877
+ .unwrap();
3878
+
3879
+ // Ensure we got a new block with this tx
3880
+ signer_test
3881
+ .wait_for_nonce_increase(&sender_addr, nonce)
3882
+ .expect("Timed out waiting for transfer tx to be mined");
3883
+
3884
+ wait_for(30, || {
3885
+ let new_tip = get_chain_info(&conf);
3886
+ Ok(new_tip.stacks_tip_height > tip.stacks_tip_height)
3887
+ })
3888
+ .expect("Timed out waiting for transfer tx to be mined");
3889
+
3890
+ let tip = get_chain_info(&conf);
3891
+
3892
+ info!("---- Triggering Bitcoin fork ----";
3893
+ "tip.stacks_tip_height" => tip.stacks_tip_height,
3894
+ "tip.burn_block_height" => tip.burn_block_height,
3895
+ );
3896
+
3897
+ let burn_header_hash_to_fork = btc_controller.get_block_hash(tip.burn_block_height - 2);
3898
+ btc_controller.invalidate_block(&burn_header_hash_to_fork);
3899
+ btc_controller.build_next_block(3);
3900
+
3901
+ TEST_MINE_STALL.set(true);
3902
+
3903
+ let submitted_commits = signer_test
3904
+ .running_nodes
3905
+ .counters
3906
+ .naka_submitted_commits
3907
+ .clone();
3908
+
3909
+ // we need to mine some blocks to get back to being considered a frequent miner
3910
+ for i in 0..3 {
3911
+ let current_burn_height = get_chain_info(&conf).burn_block_height;
3912
+ info!(
3913
+ "Mining block #{i} to be considered a frequent miner";
3914
+ "current_burn_height" => current_burn_height,
3915
+ );
3916
+ let commits_count = submitted_commits.load(Ordering::SeqCst);
3917
+ next_block_and(&btc_controller, 60, || {
3918
+ Ok(submitted_commits.load(Ordering::SeqCst) > commits_count)
3919
+ })
3920
+ .unwrap();
3921
+ }
3922
+
3923
+ info!("---- Wait for tx replay set to be updated ----");
3924
+
3925
+ signer_test
3926
+ .wait_for_signer_state_check(30, |state| {
3927
+ let Some(tx_replay_set) = state.get_tx_replay_set() else {
3928
+ return Ok(false);
3929
+ };
3930
+ let len_ok = tx_replay_set.len() == 1;
3931
+ let txid_ok = tx_replay_set[0].txid().to_hex() == txid;
3932
+ info!("---- Signer state check ----";
3933
+ "tx_replay_set" => ?tx_replay_set,
3934
+ "len_ok" => len_ok,
3935
+ "txid_ok" => txid_ok,
3936
+ );
3937
+ Ok(len_ok && txid_ok)
3938
+ })
3939
+ .expect("Timed out waiting for tx replay set to be updated");
3940
+
3941
+ let tip_after_fork = get_chain_info(&conf);
3942
+
3943
+ info!("---- Waiting for two tenures, without replay set cleared ----";
3944
+ "tip_after_fork.stacks_tip_height" => tip_after_fork.stacks_tip_height,
3945
+ "tip_after_fork.burn_block_height" => tip_after_fork.burn_block_height
3946
+ );
3947
+
3948
+ TEST_REJECT_REPLAY_TXS.set(true);
3949
+ TEST_MINE_STALL.set(false);
3950
+
3951
+ wait_for(30, || {
3952
+ let tip = get_chain_info(&conf);
3953
+ Ok(tip.stacks_tip_height > tip_after_fork.stacks_tip_height)
3954
+ })
3955
+ .expect("Timed out waiting for one TenureChange block to be mined");
3956
+
3957
+ signer_test
3958
+ .wait_for_signer_state_check(30, |state| Ok(state.get_tx_replay_set().is_some()))
3959
+ .expect("Expected replay set to still be set");
3960
+
3961
+ info!("---- Mining a second tenure ----");
3962
+
3963
+ signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3964
+
3965
+ signer_test
3966
+ .wait_for_signer_state_check(30, |state| Ok(state.get_tx_replay_set().is_some()))
3967
+ .expect("Expected replay set to still be set");
3968
+
3969
+ wait_for(30, || {
3970
+ let tip = get_chain_info(&conf);
3971
+ Ok(tip.stacks_tip_height > tip_after_fork.stacks_tip_height + 1)
3972
+ })
3973
+ .expect("Timed out waiting for a TenureChange block to be mined");
3974
+
3975
+ info!("---- Mining a third tenure ----");
3976
+ signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
3977
+
3978
+ wait_for(30, || {
3979
+ let tip = get_chain_info(&conf);
3980
+ Ok(tip.stacks_tip_height > tip_after_fork.stacks_tip_height + 1)
3981
+ })
3982
+ .expect("Timed out waiting for a TenureChange block to be mined");
3983
+
3984
+ info!("---- Waiting for tx replay set to be cleared ----");
3985
+
3986
+ signer_test
3987
+ .wait_for_signer_state_check(30, |state| Ok(state.get_tx_replay_set().is_none()))
3988
+ .expect("Expected replay set to be cleared");
3989
+
3990
+ signer_test.shutdown();
3991
+ }
3992
+
3805
3993
/// Test scenario where two signers disagree on the tx replay set,
3806
3994
/// which means there is no consensus on the tx replay set.
3807
3995
#[test]
0 commit comments