reth_e2e_test_utils/testsuite/actions/
reorg.rs

1//! Reorg actions for the e2e testing framework.
2
3use crate::testsuite::{
4    actions::{produce_blocks::BroadcastLatestForkchoice, Action, Sequence},
5    BlockInfo, Environment,
6};
7use alloy_primitives::B256;
8use alloy_rpc_types_engine::{ForkchoiceState, PayloadAttributes};
9use eyre::Result;
10use futures_util::future::BoxFuture;
11use reth_node_api::{EngineTypes, PayloadTypes};
12use std::marker::PhantomData;
13use tracing::debug;
14
15/// Target for reorg operation
16#[derive(Debug, Clone)]
17pub enum ReorgTarget {
18    /// Direct block hash
19    Hash(B256),
20    /// Tagged block reference
21    Tag(String),
22}
23
24/// Action that performs a reorg by setting a new head block as canonical
25#[derive(Debug)]
26pub struct ReorgTo<Engine> {
27    /// Target for the reorg operation
28    pub target: ReorgTarget,
29    /// Tracks engine type
30    _phantom: PhantomData<Engine>,
31}
32
33impl<Engine> ReorgTo<Engine> {
34    /// Create a new `ReorgTo` action with a direct block hash
35    pub const fn new(target_hash: B256) -> Self {
36        Self { target: ReorgTarget::Hash(target_hash), _phantom: PhantomData }
37    }
38
39    /// Create a new `ReorgTo` action with a tagged block reference
40    pub fn new_from_tag(tag: impl Into<String>) -> Self {
41        Self { target: ReorgTarget::Tag(tag.into()), _phantom: PhantomData }
42    }
43}
44
45impl<Engine> Action<Engine> for ReorgTo<Engine>
46where
47    Engine: EngineTypes + PayloadTypes,
48    Engine::PayloadAttributes: From<PayloadAttributes> + Clone,
49    Engine::ExecutionPayloadEnvelopeV3:
50        Into<alloy_rpc_types_engine::payload::ExecutionPayloadEnvelopeV3>,
51{
52    fn execute<'a>(&'a mut self, env: &'a mut Environment<Engine>) -> BoxFuture<'a, Result<()>> {
53        Box::pin(async move {
54            // resolve the target block info from either direct hash or tag
55            let target_block_info = match &self.target {
56                ReorgTarget::Hash(_hash) => {
57                    return Err(eyre::eyre!(
58                        "Direct hash reorgs are not supported. Use CaptureBlock to tag the target block first, then use ReorgTo::new_from_tag()"
59                    ));
60                }
61                ReorgTarget::Tag(tag) => {
62                    let (block_info, _node_idx) =
63                        env.block_registry.get(tag).copied().ok_or_else(|| {
64                            eyre::eyre!("Block tag '{}' not found in registry", tag)
65                        })?;
66                    block_info
67                }
68            };
69
70            let mut sequence = Sequence::new(vec![
71                Box::new(SetReorgTarget::new(target_block_info)),
72                Box::new(BroadcastLatestForkchoice::default()),
73            ]);
74
75            sequence.execute(env).await
76        })
77    }
78}
79
80/// Sub-action to set the reorg target block in the environment
81#[derive(Debug)]
82pub struct SetReorgTarget {
83    /// Complete block info for the reorg target
84    pub target_block_info: BlockInfo,
85}
86
87impl SetReorgTarget {
88    /// Create a new `SetReorgTarget` action
89    pub const fn new(target_block_info: BlockInfo) -> Self {
90        Self { target_block_info }
91    }
92}
93
94impl<Engine> Action<Engine> for SetReorgTarget
95where
96    Engine: EngineTypes,
97{
98    fn execute<'a>(&'a mut self, env: &'a mut Environment<Engine>) -> BoxFuture<'a, Result<()>> {
99        Box::pin(async move {
100            let block_info = self.target_block_info;
101
102            debug!(
103                "Setting reorg target to block {} (hash: {})",
104                block_info.number, block_info.hash
105            );
106
107            // update active node state to point to the target block
108            let active_node_state = env.active_node_state_mut()?;
109            active_node_state.current_block_info = Some(block_info);
110            active_node_state.latest_header_time = block_info.timestamp;
111
112            // update fork choice state to make the target block canonical
113            active_node_state.latest_fork_choice_state = ForkchoiceState {
114                head_block_hash: block_info.hash,
115                safe_block_hash: block_info.hash,
116                finalized_block_hash: block_info.hash,
117            };
118
119            debug!("Set reorg target to block {}", block_info.hash);
120            Ok(())
121        })
122    }
123}