@@ -954,9 +954,12 @@ impl LocalStateMachine {
954
954
955
955
let updated_replay_set;
956
956
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
+ ) ? {
960
963
let scope = ( expected_burn_block. clone ( ) , past_tip. clone ( ) ) ;
961
964
962
965
info ! ( "Tx Replay: replay set updated with {} tx(s)" , replay_set. len( ) ;
@@ -990,96 +993,71 @@ impl LocalStateMachine {
990
993
}
991
994
}
992
995
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
-
997
996
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 ,
1000
999
} ;
1001
1000
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
+ }
1032
1024
}
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) ) )
1064
1025
}
1065
1026
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 (
1071
1049
& self ,
1072
1050
db : & SignerDb ,
1073
1051
client : & StacksClient ,
1074
- end_block : & NewBurnBlock ,
1075
- start_block : & NewBurnBlock ,
1052
+ fork_origin : & NewBurnBlock ,
1053
+ tip : & NewBurnBlock ,
1076
1054
) -> Result < Option < Vec < StacksTransaction > > , SignerChainstateError > {
1077
1055
// 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 {
1083
1061
parent_burn_block_info =
1084
1062
db. get_burn_block_by_hash ( & parent_burn_block_info. parent_burn_block_hash ) ?;
1085
1063
first_forked_tenure = parent_burn_block_info. consensus_hash ;
@@ -1102,7 +1080,6 @@ impl LocalStateMachine {
1102
1080
} ) ;
1103
1081
1104
1082
if !is_fork_in_current_reward_cycle {
1105
- info ! ( "Detected bitcoin fork occurred in previous reward cycle. Tx replay won't be executed" ) ;
1106
1083
return Ok ( None ) ;
1107
1084
}
1108
1085
0 commit comments