Skip to content

Commit 9f000bf

Browse files
committed
refactor: extracted method for computing txs set in a fork, #5971
1 parent 8a345d4 commit 9f000bf

File tree

1 file changed

+60
-83
lines changed

1 file changed

+60
-83
lines changed

stacks-signer/src/v0/signer_state.rs

Lines changed: 60 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -954,9 +954,12 @@ impl LocalStateMachine {
954954

955955
let updated_replay_set;
956956
let updated_scope_opt;
957-
if let Some(replay_set) =
958-
self.compute_forked_txs_set(db, client, expected_burn_block, past_tip)?
959-
{
957+
if let Some(replay_set) = self.compute_forked_txs_set_in_same_cycle(
958+
db,
959+
client,
960+
expected_burn_block,
961+
past_tip,
962+
)? {
960963
let scope = (expected_burn_block.clone(), past_tip.clone());
961964

962965
info!("Tx Replay: replay set updated with {} tx(s)", replay_set.len();
@@ -990,96 +993,71 @@ impl LocalStateMachine {
990993
}
991994
}
992995

993-
// Determine the tenures that were forked
994-
let mut parent_burn_block_info =
995-
db.get_burn_block_by_ch(&prior_state_machine.burn_block)?;
996-
997996
let potential_replay_tip = NewBurnBlock {
998-
burn_block_height: parent_burn_block_info.block_height,
999-
consensus_hash: parent_burn_block_info.consensus_hash,
997+
burn_block_height: prior_state_machine.burn_block_height,
998+
consensus_hash: prior_state_machine.burn_block,
1000999
};
10011000

1002-
let last_forked_tenure = prior_state_machine.burn_block;
1003-
let mut first_forked_tenure = prior_state_machine.burn_block;
1004-
1005-
let mut forked_tenures = vec![(
1006-
prior_state_machine.burn_block,
1007-
prior_state_machine.burn_block_height,
1008-
)];
1009-
while parent_burn_block_info.block_height > expected_burn_block.burn_block_height {
1010-
parent_burn_block_info =
1011-
db.get_burn_block_by_hash(&parent_burn_block_info.parent_burn_block_hash)?;
1012-
first_forked_tenure = parent_burn_block_info.consensus_hash;
1013-
forked_tenures.push((
1014-
parent_burn_block_info.consensus_hash,
1015-
parent_burn_block_info.block_height,
1016-
));
1017-
}
1018-
let fork_info =
1019-
client.get_tenure_forking_info(&first_forked_tenure, &last_forked_tenure)?;
1020-
1021-
// Check if fork occurred within current reward cycle. Reject tx replay otherwise.
1022-
let reward_cycle_info = client.get_current_reward_cycle_info()?;
1023-
let current_reward_cycle = reward_cycle_info.reward_cycle;
1024-
let is_fork_in_current_reward_cycle = fork_info.iter().all(|fork_info| {
1025-
let block_height = fork_info.burn_block_height;
1026-
let block_rc = reward_cycle_info.get_reward_cycle(block_height);
1027-
block_rc == current_reward_cycle
1028-
});
1029-
if !is_fork_in_current_reward_cycle {
1030-
info!("Detected bitcoin fork occurred in previous reward cycle. Tx replay won't be executed");
1031-
return Ok(None);
1001+
match self.compute_forked_txs_set_in_same_cycle(
1002+
db,
1003+
client,
1004+
expected_burn_block,
1005+
&potential_replay_tip,
1006+
)? {
1007+
None => {
1008+
info!("Detected bitcoin fork occurred in previous reward cycle. Tx replay won't be executed");
1009+
Ok(None)
1010+
}
1011+
Some(replay_set) => {
1012+
let scope_opt = if !replay_set.is_empty() {
1013+
let scope = (expected_burn_block.clone(), potential_replay_tip);
1014+
info!("Tx Replay: replay set updated with {} tx(s)", replay_set.len();
1015+
"tx_replay_set" => ?replay_set,
1016+
"tx_replay_scope" => ?scope);
1017+
Some(scope)
1018+
} else {
1019+
info!("Tx Replay: no transactions to be replayed.");
1020+
None
1021+
};
1022+
Ok(Some((replay_set, scope_opt)))
1023+
}
10321024
}
1033-
1034-
// Collect transactions to be replayed across the forked blocks
1035-
let mut forked_blocks = fork_info
1036-
.iter()
1037-
.flat_map(|fork_info| fork_info.nakamoto_blocks.iter().flatten())
1038-
.collect::<Vec<_>>();
1039-
forked_blocks.sort_by_key(|block| block.header.chain_length);
1040-
let forked_txs = forked_blocks
1041-
.iter()
1042-
.flat_map(|block| block.txs.iter())
1043-
.filter(|tx|
1044-
// Don't include Coinbase, TenureChange, or PoisonMicroblock transactions
1045-
!matches!(
1046-
tx.payload,
1047-
TransactionPayload::TenureChange(..)
1048-
| TransactionPayload::Coinbase(..)
1049-
| TransactionPayload::PoisonMicroblock(..)
1050-
))
1051-
.cloned()
1052-
.collect::<Vec<_>>();
1053-
let scope_opt = if !forked_txs.is_empty() {
1054-
let scope = (expected_burn_block.clone(), potential_replay_tip);
1055-
info!("Tx Replay: replay set updated with {} tx(s)", forked_txs.len();
1056-
"tx_replay_set" => ?forked_txs,
1057-
"tx_replay_scope" => ?scope);
1058-
Some(scope)
1059-
} else {
1060-
info!("Tx Replay: no transactions to be replayed.");
1061-
None
1062-
};
1063-
Ok(Some((forked_txs, scope_opt)))
10641025
}
10651026

1066-
///TODO: This method can be used to remove dublication in 'handle_possible_bitcoin_fork'
1067-
/// Just waiting to avoid potential merge conflict with PR #6109
1068-
/// Retrieve all the transactions that are involved by the fork
1069-
/// from the start block (highest height) back to the end block (lowest height)
1070-
fn compute_forked_txs_set(
1027+
/// Retrieves the set of transactions that were part of a Bitcoin fork within the same reward cycle.
1028+
///
1029+
/// This method identifies the range of Tenures affected by a fork, from the `tip` down to the `fork_origin`
1030+
///
1031+
/// It then verifies whether the fork occurred entirely within the current reward cycle. If so,
1032+
/// collect the relevant transactions (skipping TenureChange, Coinbase, and Microblocks).
1033+
/// Otherwise, if fork involve a different reward cycle cancel the search.
1034+
///
1035+
/// # Arguments
1036+
///
1037+
/// * `db` - A reference to the SignerDb, used to fetch burn block information.
1038+
/// * `client` - A reference to a `StacksClient`, used to query chain state and fork information.
1039+
/// * `fork_origin` - The burn block that originated the fork.
1040+
/// * `tip` - The burn block tip in the fork sequence.
1041+
///
1042+
/// # Returns
1043+
///
1044+
/// Returns a `Result` containing either:
1045+
/// * `Ok(Some(Vec<StacksTransaction>))` — A list of transactions to be considered for replay, or
1046+
/// * `Ok(None)` — If the fork occurred outside the current reward cycle, or
1047+
/// * `Err(SignerChainstateError)` — If there was an error accessing chain state.
1048+
fn compute_forked_txs_set_in_same_cycle(
10711049
&self,
10721050
db: &SignerDb,
10731051
client: &StacksClient,
1074-
end_block: &NewBurnBlock,
1075-
start_block: &NewBurnBlock,
1052+
fork_origin: &NewBurnBlock,
1053+
tip: &NewBurnBlock,
10761054
) -> Result<Option<Vec<StacksTransaction>>, SignerChainstateError> {
10771055
// Determine the tenures that were forked
1078-
let mut parent_burn_block_info = db.get_burn_block_by_ch(&start_block.consensus_hash)?;
1079-
let last_forked_tenure = start_block.consensus_hash;
1080-
let mut first_forked_tenure = start_block.consensus_hash;
1081-
let mut forked_tenures = vec![(start_block.consensus_hash, start_block.burn_block_height)];
1082-
while parent_burn_block_info.block_height > end_block.burn_block_height {
1056+
let mut parent_burn_block_info = db.get_burn_block_by_ch(&tip.consensus_hash)?;
1057+
let last_forked_tenure = tip.consensus_hash;
1058+
let mut first_forked_tenure = tip.consensus_hash;
1059+
let mut forked_tenures = vec![(tip.consensus_hash, tip.burn_block_height)];
1060+
while parent_burn_block_info.block_height > fork_origin.burn_block_height {
10831061
parent_burn_block_info =
10841062
db.get_burn_block_by_hash(&parent_burn_block_info.parent_burn_block_hash)?;
10851063
first_forked_tenure = parent_burn_block_info.consensus_hash;
@@ -1102,7 +1080,6 @@ impl LocalStateMachine {
11021080
});
11031081

11041082
if !is_fork_in_current_reward_cycle {
1105-
info!("Detected bitcoin fork occurred in previous reward cycle. Tx replay won't be executed");
11061083
return Ok(None);
11071084
}
11081085

0 commit comments

Comments
 (0)