reth_exex/
context.rs

1use crate::{ExExContextDyn, ExExEvent, ExExNotifications, ExExNotificationsStream};
2use alloy_eips::BlockNumHash;
3use reth_exex_types::ExExHead;
4use reth_node_api::{FullNodeComponents, NodePrimitives, NodeTypes, PrimitivesTy};
5use reth_node_core::node_config::NodeConfig;
6use reth_payload_builder::PayloadBuilderHandle;
7use reth_provider::BlockReader;
8use reth_tasks::TaskExecutor;
9use std::fmt::Debug;
10use tokio::sync::mpsc::{error::SendError, UnboundedSender};
11
12/// Captures the context that an `ExEx` has access to.
13///
14/// This type wraps various node components that the `ExEx` has access to.
15pub struct ExExContext<Node: FullNodeComponents> {
16    /// The current head of the blockchain at launch.
17    pub head: BlockNumHash,
18    /// The config of the node
19    pub config: NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
20    /// The loaded node config
21    pub reth_config: reth_config::Config,
22    /// Channel used to send [`ExExEvent`]s to the rest of the node.
23    ///
24    /// # Important
25    ///
26    /// The exex should emit a `FinishedHeight` whenever a processed block is safe to prune.
27    /// Additionally, the exex can preemptively emit a `FinishedHeight` event to specify what
28    /// blocks to receive notifications for.
29    pub events: UnboundedSender<ExExEvent>,
30    /// Channel to receive [`ExExNotification`](crate::ExExNotification)s.
31    ///
32    /// # Important
33    ///
34    /// Once an [`ExExNotification`](crate::ExExNotification) is sent over the channel, it is
35    /// considered delivered by the node.
36    pub notifications: ExExNotifications<Node::Provider, Node::Executor>,
37
38    /// Node components
39    pub components: Node,
40}
41
42impl<Node> Debug for ExExContext<Node>
43where
44    Node: FullNodeComponents,
45    Node::Provider: Debug,
46    Node::Executor: Debug,
47{
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        f.debug_struct("ExExContext")
50            .field("head", &self.head)
51            .field("config", &self.config)
52            .field("reth_config", &self.reth_config)
53            .field("events", &self.events)
54            .field("notifications", &self.notifications)
55            .field("components", &"...")
56            .finish()
57    }
58}
59
60impl<Node> ExExContext<Node>
61where
62    Node: FullNodeComponents,
63    Node::Provider: Debug + BlockReader,
64    Node::Executor: Debug,
65    Node::Types: NodeTypes<Primitives: NodePrimitives>,
66{
67    /// Returns dynamic version of the context
68    pub fn into_dyn(self) -> ExExContextDyn<PrimitivesTy<Node::Types>> {
69        ExExContextDyn::from(self)
70    }
71}
72
73impl<Node> ExExContext<Node>
74where
75    Node: FullNodeComponents,
76    Node::Types: NodeTypes<Primitives: NodePrimitives>,
77{
78    /// Returns the transaction pool of the node.
79    pub fn pool(&self) -> &Node::Pool {
80        self.components.pool()
81    }
82
83    /// Returns the node's evm config.
84    pub fn evm_config(&self) -> &Node::Evm {
85        self.components.evm_config()
86    }
87
88    /// Returns the node's executor type.
89    pub fn block_executor(&self) -> &Node::Executor {
90        self.components.block_executor()
91    }
92
93    /// Returns the provider of the node.
94    pub fn provider(&self) -> &Node::Provider {
95        self.components.provider()
96    }
97
98    /// Returns the handle to the network
99    pub fn network(&self) -> &Node::Network {
100        self.components.network()
101    }
102
103    /// Returns the handle to the payload builder service.
104    pub fn payload_builder_handle(
105        &self,
106    ) -> &PayloadBuilderHandle<<Node::Types as NodeTypes>::Payload> {
107        self.components.payload_builder_handle()
108    }
109
110    /// Returns the task executor.
111    ///
112    /// This type should be used to spawn (critical) tasks.
113    pub fn task_executor(&self) -> &TaskExecutor {
114        self.components.task_executor()
115    }
116
117    /// Sets notifications stream to [`crate::ExExNotificationsWithoutHead`], a stream of
118    /// notifications without a head.
119    pub fn set_notifications_without_head(&mut self) {
120        self.notifications.set_without_head();
121    }
122
123    /// Sets notifications stream to [`crate::ExExNotificationsWithHead`], a stream of notifications
124    /// with the provided head.
125    pub fn set_notifications_with_head(&mut self, head: ExExHead) {
126        self.notifications.set_with_head(head);
127    }
128
129    /// Sends an [`ExExEvent::FinishedHeight`] to the ExEx task manager letting it know that this
130    /// ExEx has processed the corresponding block.
131    ///
132    /// Returns an error if the channel was closed (ExEx task manager panicked).
133    pub fn send_finished_height(
134        &self,
135        height: BlockNumHash,
136    ) -> Result<(), SendError<BlockNumHash>> {
137        self.events.send(ExExEvent::FinishedHeight(height)).map_err(|_| SendError(height))
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use crate::ExExContext;
144    use reth_exex_types::ExExHead;
145    use reth_node_api::FullNodeComponents;
146    use reth_provider::BlockReader;
147
148    /// <https://github.com/paradigmxyz/reth/issues/12054>
149    #[test]
150    const fn issue_12054() {
151        #[expect(dead_code)]
152        struct ExEx<Node: FullNodeComponents> {
153            ctx: ExExContext<Node>,
154        }
155
156        impl<Node: FullNodeComponents> ExEx<Node>
157        where
158            Node::Provider: BlockReader,
159        {
160            async fn _test_bounds(mut self) -> eyre::Result<()> {
161                self.ctx.pool();
162                self.ctx.block_executor();
163                self.ctx.provider();
164                self.ctx.network();
165                self.ctx.payload_builder_handle();
166                self.ctx.task_executor();
167                self.ctx.set_notifications_without_head();
168                self.ctx.set_notifications_with_head(ExExHead { block: Default::default() });
169                Ok(())
170            }
171        }
172    }
173}