Skip to content

Commit d422eae

Browse files
committed
feat: wait for +2 blocks after previous fork tip to reset
1 parent 4fa3499 commit d422eae

File tree

9 files changed

+117
-74
lines changed

9 files changed

+117
-74
lines changed

stacks-signer/src/chainstate.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ pub struct ProposalEvalConfig {
143143
pub reorg_attempts_activity_timeout: Duration,
144144
/// Time to wait before submitting a block proposal to the stacks-node
145145
pub proposal_wait_for_parent_time: Duration,
146+
/// How many blocks after a fork should we reset the replay set,
147+
/// as a failsafe mechanism?
148+
pub reset_replay_set_after_fork_blocks: u64,
146149
}
147150

148151
impl From<&SignerConfig> for ProposalEvalConfig {
@@ -155,6 +158,7 @@ impl From<&SignerConfig> for ProposalEvalConfig {
155158
reorg_attempts_activity_timeout: value.reorg_attempts_activity_timeout,
156159
tenure_idle_timeout_buffer: value.tenure_idle_timeout_buffer,
157160
proposal_wait_for_parent_time: value.proposal_wait_for_parent_time,
161+
reset_replay_set_after_fork_blocks: value.reset_replay_set_after_fork_blocks,
158162
}
159163
}
160164
}

stacks-signer/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ pub(crate) mod tests {
432432
reorg_attempts_activity_timeout: config.reorg_attempts_activity_timeout,
433433
proposal_wait_for_parent_time: config.proposal_wait_for_parent_time,
434434
validate_with_replay_tx: config.validate_with_replay_tx,
435+
reset_replay_set_after_fork_blocks: config.reset_replay_set_after_fork_blocks,
435436
}
436437
}
437438

stacks-signer/src/config.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ const DEFAULT_TENURE_IDLE_TIMEOUT_BUFFER_SECS: u64 = 2;
4949
/// cannot determine that our stacks-node has processed the parent
5050
/// block
5151
const DEFAULT_PROPOSAL_WAIT_TIME_FOR_PARENT_SECS: u64 = 15;
52+
/// Default number of blocks after a fork to reset the replay set,
53+
/// as a failsafe mechanism
54+
const DEFAULT_RESET_REPLAY_SET_AFTER_FORK_BLOCKS: u64 = 2;
5255

5356
#[derive(thiserror::Error, Debug)]
5457
/// An error occurred parsing the provided configuration
@@ -184,6 +187,9 @@ pub struct SignerConfig {
184187
pub proposal_wait_for_parent_time: Duration,
185188
/// Whether or not to validate blocks with replay transactions
186189
pub validate_with_replay_tx: bool,
190+
/// How many blocks after a fork should we reset the replay set,
191+
/// as a failsafe mechanism?
192+
pub reset_replay_set_after_fork_blocks: u64,
187193
}
188194

189195
/// The parsed configuration for the signer
@@ -237,6 +243,9 @@ pub struct GlobalConfig {
237243
pub dry_run: bool,
238244
/// Whether or not to validate blocks with replay transactions
239245
pub validate_with_replay_tx: bool,
246+
/// How many blocks after a fork should we reset the replay set,
247+
/// as a failsafe mechanism?
248+
pub reset_replay_set_after_fork_blocks: u64,
240249
}
241250

242251
/// Internal struct for loading up the config file
@@ -288,6 +297,9 @@ struct RawConfigFile {
288297
pub dry_run: Option<bool>,
289298
/// Whether or not to validate blocks with replay transactions
290299
pub validate_with_replay_tx: Option<bool>,
300+
/// How many blocks after a fork should we reset the replay set,
301+
/// as a failsafe mechanism?
302+
pub reset_replay_set_after_fork_blocks: Option<u64>,
291303
}
292304

293305
impl RawConfigFile {
@@ -413,6 +425,10 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
413425
// https://github.com/stacks-network/stacks-core/issues/6087
414426
let validate_with_replay_tx = raw_data.validate_with_replay_tx.unwrap_or(false);
415427

428+
let reset_replay_set_after_fork_blocks = raw_data
429+
.reset_replay_set_after_fork_blocks
430+
.unwrap_or(DEFAULT_RESET_REPLAY_SET_AFTER_FORK_BLOCKS);
431+
416432
Ok(Self {
417433
node_host: raw_data.node_host,
418434
endpoint,
@@ -435,6 +451,7 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
435451
tenure_idle_timeout_buffer,
436452
proposal_wait_for_parent_time,
437453
validate_with_replay_tx,
454+
reset_replay_set_after_fork_blocks,
438455
})
439456
}
440457
}
@@ -714,12 +731,14 @@ network = "mainnet"
714731
auth_password = "abcd"
715732
db_path = ":memory:"
716733
validate_with_replay_tx = true
734+
reset_replay_set_after_fork_blocks = 100
717735
"#
718736
);
719737
let config = GlobalConfig::load_from_str(&config_toml).unwrap();
720738
assert_eq!(config.stacks_address.to_string(), expected_addr);
721739
assert_eq!(config.to_chain_id(), CHAIN_ID_MAINNET);
722740
assert!(config.validate_with_replay_tx);
741+
assert_eq!(config.reset_replay_set_after_fork_blocks, 100);
723742
}
724743

725744
#[test]

stacks-signer/src/runloop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
329329
reorg_attempts_activity_timeout: self.config.reorg_attempts_activity_timeout,
330330
proposal_wait_for_parent_time: self.config.proposal_wait_for_parent_time,
331331
validate_with_replay_tx: self.config.validate_with_replay_tx,
332+
reset_replay_set_after_fork_blocks: self.config.reset_replay_set_after_fork_blocks,
332333
}))
333334
}
334335

stacks-signer/src/tests/chainstate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fn setup_test_environment(
9292
tenure_idle_timeout_buffer: Duration::from_secs(2),
9393
reorg_attempts_activity_timeout: Duration::from_secs(3),
9494
proposal_wait_for_parent_time: Duration::from_secs(0),
95+
reset_replay_set_after_fork_blocks: 2,
9596
},
9697
};
9798

stacks-signer/src/v0/signer.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ pub struct Signer {
127127
pub validate_with_replay_tx: bool,
128128
/// Scope of Tx Replay in terms of Burn block boundaries
129129
pub tx_replay_scope: ReplayScopeOpt,
130+
/// The number of blocks after the past tip to reset the replay set
131+
pub reset_replay_set_after_fork_blocks: u64,
130132
}
131133

132134
impl std::fmt::Display for SignerMode {
@@ -244,6 +246,7 @@ impl SignerTrait<SignerMessage> for Signer {
244246
global_state_evaluator,
245247
validate_with_replay_tx: signer_config.validate_with_replay_tx,
246248
tx_replay_scope: None,
249+
reset_replay_set_after_fork_blocks: signer_config.reset_replay_set_after_fork_blocks,
247250
}
248251
}
249252

stacks-signer/src/v0/signer_state.rs

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ impl LocalStateMachine {
636636
} else if Self::handle_possible_replay_failsafe(
637637
&replay_state,
638638
&expected_burn_block,
639-
client,
639+
proposal_config.reset_replay_set_after_fork_blocks,
640640
)? {
641641
info!(
642642
"Signer state: replay set is stalled after 2 tenures. Clearing the replay set."
@@ -1226,60 +1226,16 @@ impl LocalStateMachine {
12261226
fn handle_possible_replay_failsafe(
12271227
replay_state: &ReplayState,
12281228
new_burn_block: &NewBurnBlock,
1229-
client: &StacksClient,
1229+
reset_replay_set_after_fork_blocks: u64,
12301230
) -> Result<bool, SignerChainstateError> {
12311231
let ReplayState::InProgress(_, replay_scope) = replay_state else {
12321232
// Not in replay - skip
12331233
return Ok(false);
12341234
};
12351235

1236-
// if replay_scope.fork_origin.burn_block_height + 2 >= new_burn_block.burn_block_height {
1237-
if new_burn_block.burn_block_height < replay_scope.fork_origin.burn_block_height + 2 {
1238-
// We havent' had two burn blocks yet - skip
1239-
return Ok(false);
1240-
}
1241-
1242-
info!("Signer state: checking for replay set failsafe";
1243-
"replay_scope.fork_origin.burn_block_height" => replay_scope.fork_origin.burn_block_height,
1244-
"new_burn_block.burn_block_height" => new_burn_block.burn_block_height,
1245-
);
1246-
let Ok(fork_info) = client.get_tenure_forking_info(
1247-
&replay_scope.fork_origin.consensus_hash,
1248-
&new_burn_block.consensus_hash,
1249-
) else {
1250-
warn!("Signer state: failed to get fork info");
1251-
return Ok(false);
1252-
};
1253-
1254-
let tenures_with_sortition = fork_info
1255-
.iter()
1256-
.filter(|fork_info| {
1257-
fork_info.was_sortition
1258-
&& fork_info
1259-
.nakamoto_blocks
1260-
.as_ref()
1261-
.map(|b| b.len())
1262-
.unwrap_or(0)
1263-
> 0
1264-
})
1265-
.count();
1266-
1267-
info!("Signer state: fork info in failsafe check";
1268-
"tenures_with_sortition" => tenures_with_sortition,
1269-
"fork_info" => ?fork_info,
1270-
);
1271-
1272-
if tenures_with_sortition < 2 {
1273-
// We might have had 2 burn blocks, but not 2 tenures.
1274-
return Ok(false);
1275-
}
1276-
1277-
let forked_txs = Self::get_forked_txs_from_fork_info(&fork_info);
1278-
1279-
info!("Signer state: forked txs in failsafe check";
1280-
"forked_txs_len" => forked_txs.len(),
1281-
);
1236+
let failsafe_height =
1237+
replay_scope.past_tip.burn_block_height + reset_replay_set_after_fork_blocks;
12821238

1283-
Ok(forked_txs.is_empty())
1239+
Ok(new_burn_block.burn_block_height > failsafe_height)
12841240
}
12851241
}

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6589,6 +6589,7 @@ fn signer_chainstate() {
65896589
tenure_idle_timeout: Duration::from_secs(300),
65906590
tenure_idle_timeout_buffer: Duration::from_secs(2),
65916591
reorg_attempts_activity_timeout: Duration::from_secs(30),
6592+
reset_replay_set_after_fork_blocks: 2,
65926593
};
65936594
let mut sortitions_view =
65946595
SortitionsView::fetch_view(proposal_conf, &signer_client).unwrap();
@@ -6716,6 +6717,7 @@ fn signer_chainstate() {
67166717
tenure_idle_timeout: Duration::from_secs(300),
67176718
tenure_idle_timeout_buffer: Duration::from_secs(2),
67186719
reorg_attempts_activity_timeout: Duration::from_secs(30),
6720+
reset_replay_set_after_fork_blocks: 2,
67196721
};
67206722
let burn_block_height = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
67216723
.unwrap()
@@ -6794,6 +6796,7 @@ fn signer_chainstate() {
67946796
tenure_idle_timeout: Duration::from_secs(300),
67956797
tenure_idle_timeout_buffer: Duration::from_secs(2),
67966798
reorg_attempts_activity_timeout: Duration::from_secs(30),
6799+
reset_replay_set_after_fork_blocks: 2,
67976800
};
67986801
let mut sortitions_view = SortitionsView::fetch_view(proposal_conf, &signer_client).unwrap();
67996802
sortitions_view

0 commit comments

Comments
 (0)