1#![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
51pub type BigBlockMap = Arc<Mutex<HashMap<B256, BigBlockData<ExecutionData>>>>;
53
54#[jsonrpsee::proc_macros::rpc(server, namespace = "reth")]
60pub trait BbRethEngineApi {
61 #[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 #[method(name = "forkchoiceUpdated")]
73 async fn reth_forkchoice_updated(
74 &self,
75 forkchoice_state: ForkchoiceState,
76 ) -> RpcResult<ForkchoiceUpdated>;
77}
78
79#[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#[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#[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#[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#[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
345fn 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}