@@ -644,6 +644,8 @@ impl LocalStateMachine {
644
644
} ;
645
645
646
646
// No matter what, if we're in tx replay mode, remove the tx replay set
647
+ // TODO: in later versions, we will only clear the tx replay
648
+ // set when replay is completed.
647
649
prior_state_machine. tx_replay_set = None ;
648
650
649
651
let MinerState :: ActiveMiner {
@@ -780,59 +782,12 @@ impl LocalStateMachine {
780
782
} ;
781
783
return Err ( ClientError :: InvalidResponse ( err_msg) . into ( ) ) ;
782
784
}
783
- if expected_burn_block. burn_block_height <= prior_state_machine. burn_block_height
784
- && expected_burn_block. consensus_hash != prior_state_machine. burn_block
785
- // TODO: handle fork while still in replay
786
- && tx_replay_set. is_none ( )
787
- {
788
- info ! ( "Signer State: fork detected" ;
789
- "expected_burn_block.height" => expected_burn_block. burn_block_height,
790
- "expected_burn_block.hash" => %expected_burn_block. consensus_hash,
791
- "next_burn_block_height" => next_burn_block_height,
792
- "next_burn_block_hash" => %next_burn_block_hash,
793
- "prior_state_machine.burn_block_height" => prior_state_machine. burn_block_height,
794
- "prior_state_machine.burn_block" => %prior_state_machine. burn_block,
795
- ) ;
796
- // Determine the tenures that were forked
797
- let mut parent_burn_block_info =
798
- db. get_burn_block_by_ch ( & prior_state_machine. burn_block ) ?;
799
- let last_forked_tenure = prior_state_machine. burn_block ;
800
- let mut first_forked_tenure = prior_state_machine. burn_block ;
801
- let mut forked_tenures = vec ! [ (
802
- prior_state_machine. burn_block,
803
- prior_state_machine. burn_block_height,
804
- ) ] ;
805
- while parent_burn_block_info. block_height > expected_burn_block. burn_block_height {
806
- parent_burn_block_info =
807
- db. get_burn_block_by_hash ( & parent_burn_block_info. parent_burn_block_hash ) ?;
808
- first_forked_tenure = parent_burn_block_info. consensus_hash ;
809
- forked_tenures. push ( (
810
- parent_burn_block_info. consensus_hash ,
811
- parent_burn_block_info. block_height ,
812
- ) ) ;
813
- }
814
- let fork_info =
815
- client. get_tenure_forking_info ( & first_forked_tenure, & last_forked_tenure) ?;
816
- let forked_txs = fork_info
817
- . iter ( )
818
- . flat_map ( |fork_info| {
819
- fork_info
820
- . nakamoto_blocks
821
- . iter ( )
822
- . flat_map ( |blocks| blocks. iter ( ) )
823
- . flat_map ( |block| block. txs . iter ( ) )
824
- } )
825
- . filter ( |tx| match tx. payload {
826
- // Don't include Coinbase, TenureChange, or PoisonMicroblock transactions
827
- TransactionPayload :: TenureChange ( ..)
828
- | TransactionPayload :: Coinbase ( ..)
829
- | TransactionPayload :: PoisonMicroblock ( ..) => false ,
830
- _ => true ,
831
- } )
832
- . cloned ( )
833
- . collect :: < Vec < _ > > ( ) ;
834
- tx_replay_set = Some ( forked_txs) ;
835
- }
785
+ tx_replay_set = self . handle_possible_bitcoin_fork (
786
+ db,
787
+ client,
788
+ & expected_burn_block,
789
+ & prior_state_machine,
790
+ ) ?;
836
791
}
837
792
838
793
let CurrentAndLastSortition {
@@ -993,4 +948,68 @@ impl LocalStateMachine {
993
948
} ;
994
949
state. tx_replay_set . clone ( )
995
950
}
951
+
952
+ /// Handle a possible bitcoin fork. If a fork is detetected,
953
+ /// return the transactions that should be replayed.
954
+ pub fn handle_possible_bitcoin_fork (
955
+ & self ,
956
+ db : & SignerDb ,
957
+ client : & StacksClient ,
958
+ expected_burn_block : & NewBurnBlock ,
959
+ prior_state_machine : & SignerStateMachine ,
960
+ ) -> Result < Option < Vec < StacksTransaction > > , SignerChainstateError > {
961
+ if expected_burn_block. burn_block_height <= prior_state_machine. burn_block_height
962
+ && expected_burn_block. consensus_hash != prior_state_machine. burn_block
963
+ // TODO: handle fork while still in replay
964
+ && prior_state_machine. tx_replay_set . is_none ( )
965
+ {
966
+ info ! ( "Signer State: fork detected" ;
967
+ "expected_burn_block.height" => expected_burn_block. burn_block_height,
968
+ "expected_burn_block.hash" => %expected_burn_block. consensus_hash,
969
+ "prior_state_machine.burn_block_height" => prior_state_machine. burn_block_height,
970
+ "prior_state_machine.burn_block" => %prior_state_machine. burn_block,
971
+ ) ;
972
+ // Determine the tenures that were forked
973
+ let mut parent_burn_block_info =
974
+ db. get_burn_block_by_ch ( & prior_state_machine. burn_block ) ?;
975
+ let last_forked_tenure = prior_state_machine. burn_block ;
976
+ let mut first_forked_tenure = prior_state_machine. burn_block ;
977
+ let mut forked_tenures = vec ! [ (
978
+ prior_state_machine. burn_block,
979
+ prior_state_machine. burn_block_height,
980
+ ) ] ;
981
+ while parent_burn_block_info. block_height > expected_burn_block. burn_block_height {
982
+ parent_burn_block_info =
983
+ db. get_burn_block_by_hash ( & parent_burn_block_info. parent_burn_block_hash ) ?;
984
+ first_forked_tenure = parent_burn_block_info. consensus_hash ;
985
+ forked_tenures. push ( (
986
+ parent_burn_block_info. consensus_hash ,
987
+ parent_burn_block_info. block_height ,
988
+ ) ) ;
989
+ }
990
+ let fork_info =
991
+ client. get_tenure_forking_info ( & first_forked_tenure, & last_forked_tenure) ?;
992
+ let forked_txs = fork_info
993
+ . iter ( )
994
+ . flat_map ( |fork_info| {
995
+ fork_info
996
+ . nakamoto_blocks
997
+ . iter ( )
998
+ . flat_map ( |blocks| blocks. iter ( ) )
999
+ . flat_map ( |block| block. txs . iter ( ) )
1000
+ } )
1001
+ . filter ( |tx| match tx. payload {
1002
+ // Don't include Coinbase, TenureChange, or PoisonMicroblock transactions
1003
+ TransactionPayload :: TenureChange ( ..)
1004
+ | TransactionPayload :: Coinbase ( ..)
1005
+ | TransactionPayload :: PoisonMicroblock ( ..) => false ,
1006
+ _ => true ,
1007
+ } )
1008
+ . cloned ( )
1009
+ . collect :: < Vec < _ > > ( ) ;
1010
+ Ok ( Some ( forked_txs) )
1011
+ } else {
1012
+ Ok ( None )
1013
+ }
1014
+ }
996
1015
}
0 commit comments