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