Skip to main content

reth_bb/
main.rs

1//! reth-bb: a modified reth node for benchmarking big block execution.
2#![allow(missing_docs)]
3
4#[global_allocator]
5static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
6
7mod evm;
8mod evm_config;
9
10use alloy_primitives::B256;
11
12use alloy_rpc_types::engine::{ExecutionData, ForkchoiceState, ForkchoiceUpdated};
13use async_trait::async_trait;
14use clap::Parser;
15use evm_config::{BbEvmConfig, BigBlockData};
16use jsonrpsee::core::RpcResult;
17use reth_chainspec::{ChainSpec, EthereumHardforks, Hardforks};
18use reth_consensus::noop::NoopConsensus;
19use reth_engine_primitives::ConsensusEngineHandle;
20use reth_ethereum_cli::{chainspec::EthereumChainSpecParser, interface::Cli};
21use reth_ethereum_primitives::EthPrimitives;
22use reth_evm_ethereum::EthEvmConfig;
23use reth_node_api::{AddOnsContext, FullNodeComponents, NodeTypes, PayloadTypes};
24use reth_node_builder::{
25    components::{
26        BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
27    },
28    node::FullNodeTypes,
29    rpc::{
30        BasicEngineApiBuilder, BasicEngineValidatorBuilder, EngineApiBuilder, EngineValidatorAddOn,
31        EngineValidatorBuilder, PayloadValidatorBuilder, RethRpcAddOns, RpcAddOns, RpcHandle,
32        RpcHooks,
33    },
34    BuilderContext, Node,
35};
36use reth_node_ethereum::{
37    EthEngineTypes, EthereumEngineValidatorBuilder, EthereumEthApiBuilder, EthereumNetworkBuilder,
38    EthereumNode, EthereumPayloadBuilder, EthereumPoolBuilder,
39};
40use reth_payload_primitives::ExecutionPayload;
41use reth_primitives_traits::SealedBlock;
42use reth_provider::EthStorage;
43use reth_rpc_api::{RethNewPayloadInput, RethPayloadStatus};
44use reth_rpc_engine_api::EngineApiError;
45use std::{
46    collections::HashMap,
47    sync::{Arc, Mutex},
48};
49use tracing::{info, trace};
50
51/// Shared map for big block data, keyed by payload hash.
52pub type BigBlockMap = Arc<Mutex<HashMap<B256, BigBlockData<ExecutionData>>>>;
53
54// ---------------------------------------------------------------------------
55// Custom RPC trait for big-block payloads
56// ---------------------------------------------------------------------------
57
58/// Big-block extension of the `reth_` engine API.
59#[jsonrpsee::proc_macros::rpc(server, namespace = "reth")]
60pub trait BbRethEngineApi {
61    /// `reth_newPayload` with optional big-block data.
62    #[method(name = "newPayload")]
63    async fn reth_new_payload(
64        &self,
65        payload: RethNewPayloadInput<ExecutionData>,
66        wait_for_persistence: Option<bool>,
67        wait_for_caches: Option<bool>,
68        big_block_data: Option<BigBlockData<ExecutionData>>,
69    ) -> RpcResult<RethPayloadStatus>;
70
71    /// `reth_forkchoiceUpdated` – pass-through.
72    #[method(name = "forkchoiceUpdated")]
73    async fn reth_forkchoice_updated(
74        &self,
75        forkchoice_state: ForkchoiceState,
76    ) -> RpcResult<ForkchoiceUpdated>;
77}
78
79/// Server-side implementation of `BbRethEngineApi`.
80#[derive(Debug)]
81struct BbRethEngineApiHandler {
82    pending: BigBlockMap,
83    engine: ConsensusEngineHandle<EthEngineTypes>,
84}
85
86#[async_trait]
87impl BbRethEngineApiServer for BbRethEngineApiHandler {
88    async fn reth_new_payload(
89        &self,
90        input: RethNewPayloadInput<ExecutionData>,
91        wait_for_persistence: Option<bool>,
92        wait_for_caches: Option<bool>,
93        big_block_data: Option<BigBlockData<ExecutionData>>,
94    ) -> RpcResult<RethPayloadStatus> {
95        let wait_for_persistence = wait_for_persistence.unwrap_or(true);
96        let wait_for_caches = wait_for_caches.unwrap_or(true);
97        trace!(
98            target: "rpc::engine",
99            wait_for_persistence,
100            wait_for_caches,
101            has_big_block_data = big_block_data.is_some(),
102            "Serving bb reth_newPayload"
103        );
104
105        let payload = match input {
106            RethNewPayloadInput::ExecutionData(data) => data,
107            RethNewPayloadInput::BlockRlp(rlp) => {
108                let block = alloy_rlp::Decodable::decode(&mut rlp.as_ref())
109                    .map_err(|err| EngineApiError::Internal(Box::new(err)))?;
110                <EthEngineTypes as PayloadTypes>::block_to_payload(SealedBlock::new_unhashed(block))
111            }
112        };
113
114        if let Some(data) = big_block_data {
115            let hash = ExecutionPayload::block_hash(&payload);
116            self.pending.lock().unwrap().insert(hash, data);
117        }
118
119        let (status, timings) = self
120            .engine
121            .reth_new_payload(payload, wait_for_persistence, wait_for_caches)
122            .await
123            .map_err(EngineApiError::from)?;
124
125        Ok(RethPayloadStatus {
126            status,
127            latency_us: timings.latency.as_micros() as u64,
128            persistence_wait_us: timings.persistence_wait.as_micros() as u64,
129            execution_cache_wait_us: timings.execution_cache_wait.map(|d| d.as_micros() as u64),
130            sparse_trie_wait_us: timings.sparse_trie_wait.map(|d| d.as_micros() as u64),
131        })
132    }
133
134    async fn reth_forkchoice_updated(
135        &self,
136        forkchoice_state: ForkchoiceState,
137    ) -> RpcResult<ForkchoiceUpdated> {
138        trace!(target: "rpc::engine", "Serving reth_forkchoiceUpdated");
139        self.engine
140            .fork_choice_updated(forkchoice_state, None)
141            .await
142            .map_err(|e| EngineApiError::from(e).into())
143    }
144}
145
146// ---------------------------------------------------------------------------
147// Node add-ons wrapper
148// ---------------------------------------------------------------------------
149
150/// Add-ons for the big-block node.
151#[derive(Debug)]
152pub struct BbAddOns {
153    pending: BigBlockMap,
154}
155
156impl BbAddOns {
157    const fn new(pending: BigBlockMap) -> Self {
158        Self { pending }
159    }
160
161    fn make_rpc_add_ons<N: FullNodeComponents>(
162        &self,
163    ) -> RpcAddOns<
164        N,
165        EthereumEthApiBuilder,
166        EthereumEngineValidatorBuilder,
167        BasicEngineApiBuilder<EthereumEngineValidatorBuilder>,
168        BasicEngineValidatorBuilder<EthereumEngineValidatorBuilder>,
169    >
170    where
171        EthereumEthApiBuilder: reth_node_builder::rpc::EthApiBuilder<N>,
172    {
173        RpcAddOns::new(
174            EthereumEthApiBuilder::default(),
175            EthereumEngineValidatorBuilder::default(),
176            BasicEngineApiBuilder::default(),
177            BasicEngineValidatorBuilder::default(),
178            Default::default(),
179        )
180    }
181}
182
183impl<N> reth_node_api::NodeAddOns<N> for BbAddOns
184where
185    N: FullNodeComponents<
186        Types: NodeTypes<
187            ChainSpec: EthereumHardforks + Hardforks + Clone + 'static,
188            Payload = EthEngineTypes,
189            Primitives = EthPrimitives,
190        >,
191    >,
192    EthereumEthApiBuilder: reth_node_builder::rpc::EthApiBuilder<N>,
193    EthereumEngineValidatorBuilder: PayloadValidatorBuilder<N>,
194    BasicEngineApiBuilder<EthereumEngineValidatorBuilder>: EngineApiBuilder<N>,
195    BasicEngineValidatorBuilder<EthereumEngineValidatorBuilder>: EngineValidatorBuilder<N>,
196{
197    type Handle =
198        RpcHandle<N, <EthereumEthApiBuilder as reth_node_builder::rpc::EthApiBuilder<N>>::EthApi>;
199
200    async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
201        let engine_handle = ctx.beacon_engine_handle.clone();
202        let pending = self.pending.clone();
203        let rpc_add_ons = self.make_rpc_add_ons::<N>();
204
205        rpc_add_ons
206            .launch_add_ons_with(ctx, move |container| {
207                let handler = BbRethEngineApiHandler { pending, engine: engine_handle };
208                let bb_module = BbRethEngineApiServer::into_rpc(handler);
209                container.auth_module.replace_auth_methods(bb_module.remove_context())?;
210                Ok(())
211            })
212            .await
213    }
214}
215
216impl<N> RethRpcAddOns<N> for BbAddOns
217where
218    N: FullNodeComponents<
219        Types: NodeTypes<
220            ChainSpec: EthereumHardforks + Hardforks + Clone + 'static,
221            Payload = EthEngineTypes,
222            Primitives = EthPrimitives,
223        >,
224    >,
225    EthereumEthApiBuilder: reth_node_builder::rpc::EthApiBuilder<N>,
226    EthereumEngineValidatorBuilder: PayloadValidatorBuilder<N>,
227    BasicEngineApiBuilder<EthereumEngineValidatorBuilder>: EngineApiBuilder<N>,
228    BasicEngineValidatorBuilder<EthereumEngineValidatorBuilder>: EngineValidatorBuilder<N>,
229{
230    type EthApi = <EthereumEthApiBuilder as reth_node_builder::rpc::EthApiBuilder<N>>::EthApi;
231
232    fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi> {
233        unimplemented!("BbAddOns does not support dynamic hook mutation")
234    }
235}
236
237impl<N> EngineValidatorAddOn<N> for BbAddOns
238where
239    N: FullNodeComponents,
240    BasicEngineValidatorBuilder<EthereumEngineValidatorBuilder>: EngineValidatorBuilder<N>,
241{
242    type ValidatorBuilder = BasicEngineValidatorBuilder<EthereumEngineValidatorBuilder>;
243
244    fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
245        BasicEngineValidatorBuilder::default()
246    }
247}
248
249// ---------------------------------------------------------------------------
250// Custom executor builder
251// ---------------------------------------------------------------------------
252
253/// Executor builder that creates a [`BbEvmConfig`].
254#[derive(Debug)]
255pub struct BbExecutorBuilder {
256    pending: BigBlockMap,
257}
258
259impl<Node> ExecutorBuilder<Node> for BbExecutorBuilder
260where
261    Node: FullNodeTypes<
262        Types: NodeTypes<
263            ChainSpec: reth_ethereum_forks::Hardforks
264                           + alloy_evm::eth::spec::EthExecutorSpec
265                           + EthereumHardforks,
266            Primitives = EthPrimitives,
267        >,
268    >,
269{
270    type EVM = BbEvmConfig<<Node::Types as NodeTypes>::ChainSpec>;
271
272    async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
273        Ok(BbEvmConfig::new(EthEvmConfig::new(ctx.chain_spec()), self.pending))
274    }
275}
276
277// ---------------------------------------------------------------------------
278// Node type
279// ---------------------------------------------------------------------------
280
281/// Node type for big block execution.
282#[derive(Debug, Clone)]
283pub struct BbNode {
284    pending: BigBlockMap,
285}
286
287impl BbNode {
288    const fn new(pending: BigBlockMap) -> Self {
289        Self { pending }
290    }
291}
292
293impl NodeTypes for BbNode {
294    type Primitives = EthPrimitives;
295    type ChainSpec = ChainSpec;
296    type Storage = EthStorage;
297    type Payload = EthEngineTypes;
298}
299
300impl<N> Node<N> for BbNode
301where
302    N: FullNodeTypes<Types = Self>,
303{
304    type ComponentsBuilder = ComponentsBuilder<
305        N,
306        EthereumPoolBuilder,
307        BasicPayloadServiceBuilder<EthereumPayloadBuilder>,
308        EthereumNetworkBuilder,
309        BbExecutorBuilder,
310        BbConsensusBuilder,
311    >;
312
313    type AddOns = BbAddOns;
314
315    fn components_builder(&self) -> Self::ComponentsBuilder {
316        EthereumNode::components()
317            .executor(BbExecutorBuilder { pending: self.pending.clone() })
318            .consensus(BbConsensusBuilder)
319    }
320
321    fn add_ons(&self) -> Self::AddOns {
322        BbAddOns::new(self.pending.clone())
323    }
324}
325
326// ---------------------------------------------------------------------------
327// Consensus builder
328// ---------------------------------------------------------------------------
329
330/// Consensus builder for big block execution.
331#[derive(Debug, Default, Clone, Copy)]
332pub struct BbConsensusBuilder;
333
334impl<Node> ConsensusBuilder<Node> for BbConsensusBuilder
335where
336    Node: FullNodeTypes<Types: NodeTypes<Primitives = EthPrimitives>>,
337{
338    type Consensus = NoopConsensus;
339
340    async fn build_consensus(self, _ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
341        Ok(NoopConsensus::default())
342    }
343}
344
345// ---------------------------------------------------------------------------
346// Main
347// ---------------------------------------------------------------------------
348
349fn main() {
350    reth_cli_util::sigsegv_handler::install();
351
352    if std::env::var_os("RUST_BACKTRACE").is_none() {
353        unsafe { std::env::set_var("RUST_BACKTRACE", "1") };
354    }
355
356    let pending: BigBlockMap = Arc::new(Mutex::new(HashMap::new()));
357
358    if let Err(err) = Cli::<EthereumChainSpecParser>::parse().run(async move |builder, _| {
359        info!(target: "reth::cli", "Launching big block node");
360        let handle = builder.launch_node(BbNode::new(pending.clone())).await?;
361
362        handle.wait_for_node_exit().await
363    }) {
364        eprintln!("Error: {err:?}");
365        std::process::exit(1);
366    }
367}