Skip to content

Commit a2ff29c

Browse files
authored
Merge pull request #6212 from hstove/feat/tx-replay-failsafe
feat: add failsafe to transaction replay
2 parents 0cec1cd + 5d0c27e commit a2ff29c

File tree

14 files changed

+941
-771
lines changed

14 files changed

+941
-771
lines changed

stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PrivateKey, Secp
111111
use stacks_common::util::{get_epoch_time_secs, sleep_ms};
112112
use stacks_signer::chainstate::v1::SortitionsView;
113113
use stacks_signer::chainstate::ProposalEvalConfig;
114+
use stacks_signer::config::DEFAULT_RESET_REPLAY_SET_AFTER_FORK_BLOCKS;
114115
use stacks_signer::signerdb::{BlockInfo, BlockState, ExtraBlockInfo, SignerDb};
115116
use stacks_signer::v0::SpawnedSigner;
116117

@@ -6708,6 +6709,7 @@ fn signer_chainstate() {
67086709
tenure_idle_timeout: Duration::from_secs(300),
67096710
tenure_idle_timeout_buffer: Duration::from_secs(2),
67106711
reorg_attempts_activity_timeout: Duration::from_secs(30),
6712+
reset_replay_set_after_fork_blocks: DEFAULT_RESET_REPLAY_SET_AFTER_FORK_BLOCKS,
67116713
};
67126714
let mut sortitions_view =
67136715
SortitionsView::fetch_view(proposal_conf, &signer_client).unwrap();
@@ -6818,6 +6820,7 @@ fn signer_chainstate() {
68186820
tenure_idle_timeout: Duration::from_secs(300),
68196821
tenure_idle_timeout_buffer: Duration::from_secs(2),
68206822
reorg_attempts_activity_timeout: Duration::from_secs(30),
6823+
reset_replay_set_after_fork_blocks: DEFAULT_RESET_REPLAY_SET_AFTER_FORK_BLOCKS,
68216824
};
68226825
let burn_block_height = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
68236826
.unwrap()
@@ -6890,6 +6893,7 @@ fn signer_chainstate() {
68906893
tenure_idle_timeout: Duration::from_secs(300),
68916894
tenure_idle_timeout_buffer: Duration::from_secs(2),
68926895
reorg_attempts_activity_timeout: Duration::from_secs(30),
6896+
reset_replay_set_after_fork_blocks: DEFAULT_RESET_REPLAY_SET_AFTER_FORK_BLOCKS,
68936897
};
68946898
let mut sortitions_view = SortitionsView::fetch_view(proposal_conf, &signer_client).unwrap();
68956899
sortitions_view

stacks-node/src/tests/signer/mod.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use libsigner::v0::messages::{
3535
use libsigner::v0::signer_state::MinerState;
3636
use libsigner::{BlockProposal, SignerEntries, SignerEventTrait};
3737
use serde::{Deserialize, Serialize};
38+
use stacks::burnchains::Txid;
3839
use stacks::chainstate::coordinator::comm::CoordinatorChannels;
3940
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
4041
use stacks::chainstate::nakamoto::NakamotoBlock;
@@ -239,6 +240,9 @@ impl<Z: SpawnedSignerTrait> SignerTest<Z> {
239240
let (mut naka_conf, _miner_account) =
240241
naka_neon_integration_conf(snapshot_name.map(|n| n.as_bytes()));
241242

243+
naka_conf.miner.activated_vrf_key_path =
244+
Some(format!("{}/vrf_key", naka_conf.node.working_dir));
245+
242246
node_config_modifier(&mut naka_conf);
243247

244248
// Add initial balances to the config
@@ -365,7 +369,11 @@ impl<Z: SpawnedSignerTrait> SignerTest<Z> {
365369
let metadata_path = snapshot_path.join("metadata.json");
366370
if !metadata_path.clone().exists() {
367371
warn!("Snapshot metadata file does not exist, not restoring snapshot");
368-
return SetupSnapshotResult::NoSnapshot;
372+
std::fs::remove_dir_all(snapshot_path.clone()).unwrap();
373+
return SetupSnapshotResult::WithSnapshot(SnapshotSetupInfo {
374+
snapshot_path: snapshot_path.clone(),
375+
snapshot_exists: false,
376+
});
369377
}
370378
let Ok(metadata) = serde_json::from_reader::<_, SnapshotMetadata>(
371379
File::open(metadata_path.clone()).unwrap(),
@@ -1067,6 +1075,20 @@ impl<Z: SpawnedSignerTrait> SignerTest<Z> {
10671075
})
10681076
}
10691077

1078+
pub fn wait_for_replay_set_eq(&self, timeout: u64, expected_txids: Vec<String>) {
1079+
self.wait_for_signer_state_check(timeout, |state| {
1080+
let Some(replay_set) = state.get_tx_replay_set() else {
1081+
return Ok(false);
1082+
};
1083+
let txids = replay_set
1084+
.iter()
1085+
.map(|tx| tx.txid().to_hex())
1086+
.collect::<Vec<_>>();
1087+
Ok(txids == expected_txids)
1088+
})
1089+
.expect("Timed out waiting for replay set to be equal to expected txids");
1090+
}
1091+
10701092
/// Replace the test's configured signer st
10711093
pub fn replace_signers(
10721094
&mut self,
@@ -1585,6 +1607,31 @@ impl<Z: SpawnedSignerTrait> SignerTest<Z> {
15851607
.send_message_with_retry::<SignerMessage>(accepted.into())
15861608
.expect("Failed to send accept signature");
15871609
}
1610+
1611+
/// Get the txid of the parent block commit transaction for the given miner
1612+
pub fn get_parent_block_commit_txid(&self, miner_pk: &StacksPublicKey) -> Option<Txid> {
1613+
let Some(confirmed_utxo) = self
1614+
.running_nodes
1615+
.btc_regtest_controller
1616+
.get_all_utxos(&miner_pk)
1617+
.into_iter()
1618+
.find(|utxo| utxo.confirmations == 0)
1619+
else {
1620+
return None;
1621+
};
1622+
let unconfirmed_txid = Txid::from_bitcoin_tx_hash(&confirmed_utxo.txid);
1623+
let unconfirmed_tx = self
1624+
.running_nodes
1625+
.btc_regtest_controller
1626+
.get_raw_transaction(&unconfirmed_txid);
1627+
let parent_txid = unconfirmed_tx
1628+
.input
1629+
.get(0)
1630+
.expect("First input should exist")
1631+
.previous_output
1632+
.txid;
1633+
Some(Txid::from_bitcoin_tx_hash(&parent_txid))
1634+
}
15881635
}
15891636

15901637
fn setup_stx_btc_node<G: FnMut(&mut NeonConfig)>(

0 commit comments

Comments
 (0)