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::Bytes;
11use alloy_rpc_types::engine::ExecutionData;
12use clap::Parser;
13use evm_config::{BbEvmConfig, BigBlockData};
14use reth_chainspec::{ChainSpec, EthereumHardforks};
15use reth_consensus::noop::NoopConsensus;
16use reth_ethereum_cli::{chainspec::EthereumChainSpecParser, interface::Cli};
17use reth_ethereum_primitives::{Block, EthPrimitives};
18use reth_evm_ethereum::EthEvmConfig;
19use reth_node_api::{
20    AddOnsContext, FullNodeComponents, NewPayloadError, NodeTypes, PayloadTypes, PayloadValidator,
21};
22use reth_node_builder::{
23    components::{
24        BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
25    },
26    node::FullNodeTypes,
27    rpc::{NoopEngineApiBuilder, PayloadValidatorBuilder, RpcAddOns},
28    BuilderContext, Node, NodeAdapter,
29};
30use reth_node_core::args::DefaultEngineValues;
31use reth_node_ethereum::{
32    EthPayloadTypes, EthereumEngineValidator, EthereumEthApiBuilder, EthereumNetworkBuilder,
33    EthereumNode, EthereumPayloadBuilder, EthereumPoolBuilder,
34};
35use reth_primitives_traits::SealedBlock;
36use reth_provider::EthStorage;
37use tracing::info;
38
39#[derive(Debug, Clone, Default)]
40pub struct BbPayloadTypes;
41
42impl PayloadTypes for BbPayloadTypes {
43    type ExecutionData = BigBlockData<ExecutionData>;
44    type BuiltPayload = <EthPayloadTypes as PayloadTypes>::BuiltPayload;
45    type PayloadAttributes = <EthPayloadTypes as PayloadTypes>::PayloadAttributes;
46
47    fn block_to_payload(
48        _block: SealedBlock<
49                <<Self::BuiltPayload as reth_node_api::BuiltPayload>::Primitives as reth_node_api::NodePrimitives>::Block,
50            >,
51        _bal: Option<Bytes>,
52    ) -> Self::ExecutionData {
53        unreachable!()
54    }
55}
56
57#[derive(Debug, Default, Clone)]
58pub struct BbEngineValidatorBuilder;
59
60impl<Node> PayloadValidatorBuilder<Node> for BbEngineValidatorBuilder
61where
62    Node: FullNodeComponents<Types = BbNode>,
63{
64    type Validator = BbEngineValidator;
65
66    async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
67        Ok(BbEngineValidator { inner: EthereumEngineValidator::new(ctx.config.chain.clone()) })
68    }
69}
70
71#[derive(Debug, Clone)]
72pub struct BbEngineValidator {
73    inner: EthereumEngineValidator,
74}
75
76impl PayloadValidator<BbPayloadTypes> for BbEngineValidator {
77    type Block = Block;
78
79    fn convert_payload_to_block(
80        &self,
81        payload: BigBlockData<ExecutionData>,
82    ) -> Result<SealedBlock<Block>, NewPayloadError> {
83        let mut blocks = payload
84            .env_switches
85            .into_iter()
86            .map(|data| {
87                PayloadValidator::<EthPayloadTypes>::convert_payload_to_block(&self.inner, data)
88            })
89            .collect::<Result<Vec<SealedBlock<Block>>, NewPayloadError>>()?;
90
91        let (mut block, hash) = blocks.pop().unwrap().split();
92
93        // Override the block number
94        block.header.number = payload.block_number;
95
96        // Set block's parent hash to the parent of the first block in this batch so that engine
97        // tree state is consistent.
98        if let Some(first) = blocks.first() {
99            block.header.parent_hash = first.parent_hash;
100        }
101
102        // Update block's gas usage to make sure metrics are correct
103        block.header.gas_used += blocks.iter().map(|b| b.gas_used).sum::<u64>();
104        block.header.gas_limit += blocks.iter().map(|b| b.gas_limit).sum::<u64>();
105
106        // Prepend transactions from previous blocks to make sure that persistence indices are
107        // correct.
108        block.body.transactions = blocks
109            .into_iter()
110            .flat_map(|b| b.into_body().transactions)
111            .chain(core::mem::take(&mut block.body.transactions))
112            .collect();
113
114        // Use `new_unchecked` to preserve the hash
115        Ok(SealedBlock::new_unchecked(block, hash))
116    }
117}
118
119// ---------------------------------------------------------------------------
120// Custom executor builder
121// ---------------------------------------------------------------------------
122
123/// Executor builder that creates a [`BbEvmConfig`].
124#[derive(Debug, Default)]
125#[non_exhaustive]
126pub struct BbExecutorBuilder;
127
128impl<Node> ExecutorBuilder<Node> for BbExecutorBuilder
129where
130    Node: FullNodeTypes<
131        Types: NodeTypes<
132            ChainSpec: reth_ethereum_forks::Hardforks
133                           + alloy_evm::eth::spec::EthExecutorSpec
134                           + EthereumHardforks,
135            Primitives = EthPrimitives,
136        >,
137    >,
138{
139    type EVM = BbEvmConfig<<Node::Types as NodeTypes>::ChainSpec>;
140
141    async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
142        Ok(BbEvmConfig::new(EthEvmConfig::new(ctx.chain_spec())))
143    }
144}
145
146// ---------------------------------------------------------------------------
147// Node type
148// ---------------------------------------------------------------------------
149
150/// Node type for big block execution.
151#[derive(Debug, Clone, Default)]
152#[non_exhaustive]
153pub struct BbNode;
154
155impl NodeTypes for BbNode {
156    type Primitives = EthPrimitives;
157    type ChainSpec = ChainSpec;
158    type Storage = EthStorage;
159    type Payload = BbPayloadTypes;
160}
161
162impl<N> Node<N> for BbNode
163where
164    N: FullNodeTypes<Types = Self>,
165{
166    type ComponentsBuilder = ComponentsBuilder<
167        N,
168        EthereumPoolBuilder,
169        BasicPayloadServiceBuilder<EthereumPayloadBuilder>,
170        EthereumNetworkBuilder,
171        BbExecutorBuilder,
172        BbConsensusBuilder,
173    >;
174
175    type AddOns = RpcAddOns<
176        NodeAdapter<N>,
177        EthereumEthApiBuilder,
178        BbEngineValidatorBuilder,
179        NoopEngineApiBuilder,
180    >;
181
182    fn components_builder(&self) -> Self::ComponentsBuilder {
183        EthereumNode::components()
184            .executor(BbExecutorBuilder::default())
185            .consensus(BbConsensusBuilder)
186    }
187
188    fn add_ons(&self) -> Self::AddOns {
189        Default::default()
190    }
191}
192
193// ---------------------------------------------------------------------------
194// Consensus builder
195// ---------------------------------------------------------------------------
196
197/// Consensus builder for big block execution.
198#[derive(Debug, Default, Clone, Copy)]
199pub struct BbConsensusBuilder;
200
201impl<Node> ConsensusBuilder<Node> for BbConsensusBuilder
202where
203    Node: FullNodeTypes<Types: NodeTypes<Primitives = EthPrimitives>>,
204{
205    type Consensus = NoopConsensus;
206
207    async fn build_consensus(self, _ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
208        Ok(NoopConsensus::default())
209    }
210}
211
212// ---------------------------------------------------------------------------
213// Main
214// ---------------------------------------------------------------------------
215
216fn main() {
217    reth_cli_util::sigsegv_handler::install();
218
219    if std::env::var_os("RUST_BACKTRACE").is_none() {
220        unsafe { std::env::set_var("RUST_BACKTRACE", "1") };
221    }
222
223    let _ = DefaultEngineValues::default().with_bal_parallel_execution_disabled(false).try_init();
224
225    if let Err(err) = Cli::<EthereumChainSpecParser>::parse().run(async move |builder, _| {
226        info!(target: "reth::cli", "Launching big block node");
227        let handle = builder.launch_node(BbNode::default()).await?;
228
229        handle.wait_for_node_exit().await
230    }) {
231        eprintln!("Error: {err:?}");
232        std::process::exit(1);
233    }
234}