Skip to main content

reth_rpc/eth/
builder.rs

1//! `EthApiBuilder` implementation
2
3use crate::{eth::core::EthApiInner, EthApi};
4use alloy_network::Ethereum;
5use reth_chain_state::CanonStateSubscriptions;
6use reth_chainspec::ChainSpecProvider;
7use reth_evm::SenderRecoveryCache;
8use reth_primitives_traits::HeaderTy;
9use reth_rpc_convert::{RpcConvert, RpcConverter};
10use reth_rpc_eth_api::{
11    helpers::pending_block::PendingEnvBuilder, node::RpcNodeCoreAdapter, RpcNodeCore,
12};
13use reth_rpc_eth_types::{
14    builder::config::PendingBlockKind, fee_history::fee_history_cache_new_blocks_task,
15    receipt::EthReceiptConverter, EthApiSettings, EthStateCache, EthStateCacheConfig,
16    FeeHistoryCache, FeeHistoryCacheConfig, ForwardConfig, GasCap, GasPriceOracle,
17    GasPriceOracleConfig,
18};
19use reth_rpc_server_types::constants::{
20    DEFAULT_ETH_PROOF_WINDOW, DEFAULT_MAX_BLOCKING_IO_REQUEST, DEFAULT_MAX_SIMULATE_BLOCKS,
21    DEFAULT_PROOF_PERMITS,
22};
23use reth_tasks::{pool::BlockingTaskPool, Runtime};
24use std::{sync::Arc, time::Duration};
25
26/// A helper to build the `EthApi` handler instance.
27///
28/// This builder type contains all settings to create an [`EthApiInner`] or an [`EthApi`] instance
29/// directly.
30#[derive(Debug)]
31pub struct EthApiBuilder<N: RpcNodeCore, Rpc, NextEnv = ()> {
32    components: N,
33    rpc_converter: Rpc,
34    gas_cap: GasCap,
35    max_simulate_blocks: u64,
36    compute_state_root_for_eth_simulate: bool,
37    eth_proof_window: u64,
38    fee_history_cache_config: FeeHistoryCacheConfig,
39    proof_permits: usize,
40    eth_state_cache_config: EthStateCacheConfig,
41    eth_cache: Option<EthStateCache<N::Primitives>>,
42    gas_oracle_config: GasPriceOracleConfig,
43    gas_oracle: Option<GasPriceOracle<N::Provider>>,
44    blocking_task_pool: Option<BlockingTaskPool>,
45    /// Runtime used to spawn tasks. Defaults to [`Runtime::test`] when not configured.
46    task_spawner: Option<Runtime>,
47    next_env: NextEnv,
48    max_batch_size: usize,
49    max_blocking_io_requests: usize,
50    pending_block_kind: PendingBlockKind,
51    raw_tx_forwarder: ForwardConfig,
52    sender_recovery_cache: Option<SenderRecoveryCache>,
53    send_raw_transaction_sync_timeout: Duration,
54    evm_memory_limit: u64,
55    force_blob_sidecar_upcasting: bool,
56}
57
58impl<Provider, Pool, Network, EvmConfig, ChainSpec>
59    EthApiBuilder<
60        RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>,
61        RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>,
62    >
63where
64    RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>:
65        RpcNodeCore<Provider: ChainSpecProvider<ChainSpec = ChainSpec>, Evm = EvmConfig>,
66{
67    /// Creates a new `EthApiBuilder` instance.
68    pub fn new(provider: Provider, pool: Pool, network: Network, evm_config: EvmConfig) -> Self {
69        Self::new_with_components(RpcNodeCoreAdapter::new(provider, pool, network, evm_config))
70    }
71}
72
73impl<N: RpcNodeCore, Rpc, NextEnv> EthApiBuilder<N, Rpc, NextEnv> {
74    /// Apply a function to the builder
75    pub fn apply<F>(self, f: F) -> Self
76    where
77        F: FnOnce(Self) -> Self,
78    {
79        f(self)
80    }
81
82    /// Converts the RPC converter type of this builder
83    pub fn map_converter<F, R>(self, f: F) -> EthApiBuilder<N, R, NextEnv>
84    where
85        F: FnOnce(Rpc) -> R,
86    {
87        let Self {
88            components,
89            rpc_converter,
90            gas_cap,
91            max_simulate_blocks,
92            compute_state_root_for_eth_simulate,
93            eth_proof_window,
94            fee_history_cache_config,
95            proof_permits,
96            eth_state_cache_config,
97            eth_cache,
98            gas_oracle_config,
99            gas_oracle,
100            blocking_task_pool,
101            task_spawner,
102            next_env,
103            max_batch_size,
104            max_blocking_io_requests,
105            pending_block_kind,
106            raw_tx_forwarder,
107            sender_recovery_cache,
108            send_raw_transaction_sync_timeout,
109            evm_memory_limit,
110            force_blob_sidecar_upcasting,
111        } = self;
112        EthApiBuilder {
113            components,
114            rpc_converter: f(rpc_converter),
115            gas_cap,
116            max_simulate_blocks,
117            compute_state_root_for_eth_simulate,
118            eth_proof_window,
119            fee_history_cache_config,
120            proof_permits,
121            eth_state_cache_config,
122            eth_cache,
123            gas_oracle_config,
124            gas_oracle,
125            blocking_task_pool,
126            task_spawner,
127            next_env,
128            max_batch_size,
129            max_blocking_io_requests,
130            pending_block_kind,
131            raw_tx_forwarder,
132            sender_recovery_cache,
133            send_raw_transaction_sync_timeout,
134            evm_memory_limit,
135            force_blob_sidecar_upcasting,
136        }
137    }
138}
139
140impl<N, ChainSpec> EthApiBuilder<N, RpcConverter<Ethereum, N::Evm, EthReceiptConverter<ChainSpec>>>
141where
142    N: RpcNodeCore<Provider: ChainSpecProvider<ChainSpec = ChainSpec>>,
143{
144    /// Creates a new `EthApiBuilder` instance with the provided components.
145    pub fn new_with_components(components: N) -> Self {
146        let rpc_converter =
147            RpcConverter::new(EthReceiptConverter::new(components.provider().chain_spec()));
148        Self {
149            components,
150            rpc_converter,
151            eth_cache: None,
152            gas_oracle: None,
153            gas_cap: GasCap::default(),
154            max_simulate_blocks: DEFAULT_MAX_SIMULATE_BLOCKS,
155            compute_state_root_for_eth_simulate: false,
156            eth_proof_window: DEFAULT_ETH_PROOF_WINDOW,
157            blocking_task_pool: None,
158            fee_history_cache_config: FeeHistoryCacheConfig::default(),
159            proof_permits: DEFAULT_PROOF_PERMITS,
160            task_spawner: None,
161            gas_oracle_config: Default::default(),
162            eth_state_cache_config: Default::default(),
163            next_env: Default::default(),
164            max_batch_size: 1,
165            max_blocking_io_requests: DEFAULT_MAX_BLOCKING_IO_REQUEST,
166            pending_block_kind: PendingBlockKind::Full,
167            raw_tx_forwarder: ForwardConfig::default(),
168            sender_recovery_cache: None,
169            send_raw_transaction_sync_timeout: Duration::from_secs(30),
170            evm_memory_limit: (1 << 32) - 1,
171            force_blob_sidecar_upcasting: false,
172        }
173    }
174}
175
176impl<N, Rpc, NextEnv> EthApiBuilder<N, Rpc, NextEnv>
177where
178    N: RpcNodeCore,
179{
180    /// Configures the task spawner used to spawn additional tasks.
181    pub fn task_spawner(mut self, spawner: Runtime) -> Self {
182        self.task_spawner = Some(spawner);
183        self
184    }
185
186    /// Changes the configured converter.
187    pub fn with_rpc_converter<RpcNew>(
188        self,
189        rpc_converter: RpcNew,
190    ) -> EthApiBuilder<N, RpcNew, NextEnv> {
191        let Self {
192            components,
193            rpc_converter: _,
194            gas_cap,
195            max_simulate_blocks,
196            compute_state_root_for_eth_simulate,
197            eth_proof_window,
198            fee_history_cache_config,
199            proof_permits,
200            eth_state_cache_config,
201            eth_cache,
202            gas_oracle,
203            blocking_task_pool,
204            task_spawner,
205            gas_oracle_config,
206            next_env,
207            max_batch_size,
208            max_blocking_io_requests,
209            pending_block_kind,
210            raw_tx_forwarder,
211            sender_recovery_cache,
212            send_raw_transaction_sync_timeout,
213            evm_memory_limit,
214            force_blob_sidecar_upcasting,
215        } = self;
216        EthApiBuilder {
217            components,
218            rpc_converter,
219            gas_cap,
220            max_simulate_blocks,
221            compute_state_root_for_eth_simulate,
222            eth_proof_window,
223            fee_history_cache_config,
224            proof_permits,
225            eth_state_cache_config,
226            eth_cache,
227            gas_oracle,
228            blocking_task_pool,
229            task_spawner,
230            gas_oracle_config,
231            next_env,
232            max_batch_size,
233            max_blocking_io_requests,
234            pending_block_kind,
235            raw_tx_forwarder,
236            sender_recovery_cache,
237            send_raw_transaction_sync_timeout,
238            evm_memory_limit,
239            force_blob_sidecar_upcasting,
240        }
241    }
242
243    /// Changes the configured pending environment builder.
244    pub fn with_pending_env_builder<NextEnvNew>(
245        self,
246        next_env: NextEnvNew,
247    ) -> EthApiBuilder<N, Rpc, NextEnvNew> {
248        let Self {
249            components,
250            rpc_converter,
251            gas_cap,
252            max_simulate_blocks,
253            compute_state_root_for_eth_simulate,
254            eth_proof_window,
255            fee_history_cache_config,
256            proof_permits,
257            eth_state_cache_config,
258            eth_cache,
259            gas_oracle,
260            blocking_task_pool,
261            task_spawner,
262            gas_oracle_config,
263            next_env: _,
264            max_batch_size,
265            max_blocking_io_requests,
266            pending_block_kind,
267            raw_tx_forwarder,
268            sender_recovery_cache,
269            send_raw_transaction_sync_timeout,
270            evm_memory_limit,
271            force_blob_sidecar_upcasting,
272        } = self;
273        EthApiBuilder {
274            components,
275            rpc_converter,
276            gas_cap,
277            max_simulate_blocks,
278            compute_state_root_for_eth_simulate,
279            eth_proof_window,
280            fee_history_cache_config,
281            proof_permits,
282            eth_state_cache_config,
283            eth_cache,
284            gas_oracle,
285            blocking_task_pool,
286            task_spawner,
287            gas_oracle_config,
288            next_env,
289            max_batch_size,
290            max_blocking_io_requests,
291            pending_block_kind,
292            raw_tx_forwarder,
293            sender_recovery_cache,
294            send_raw_transaction_sync_timeout,
295            evm_memory_limit,
296            force_blob_sidecar_upcasting,
297        }
298    }
299
300    /// Sets `eth_cache` config for the cache that will be used if no [`EthStateCache`] is
301    /// configured.
302    pub const fn eth_state_cache_config(
303        mut self,
304        eth_state_cache_config: EthStateCacheConfig,
305    ) -> Self {
306        self.eth_state_cache_config = eth_state_cache_config;
307        self
308    }
309
310    /// Sets `eth_cache` instance
311    pub fn eth_cache(mut self, eth_cache: EthStateCache<N::Primitives>) -> Self {
312        self.eth_cache = Some(eth_cache);
313        self
314    }
315
316    /// Sets `gas_oracle` config for the gas oracle that will be used if no [`GasPriceOracle`] is
317    /// configured.
318    pub const fn gas_oracle_config(mut self, gas_oracle_config: GasPriceOracleConfig) -> Self {
319        self.gas_oracle_config = gas_oracle_config;
320        self
321    }
322
323    /// Sets `gas_oracle` instance
324    pub fn gas_oracle(mut self, gas_oracle: GasPriceOracle<N::Provider>) -> Self {
325        self.gas_oracle = Some(gas_oracle);
326        self
327    }
328
329    /// Sets the gas cap.
330    pub const fn gas_cap(mut self, gas_cap: GasCap) -> Self {
331        self.gas_cap = gas_cap;
332        self
333    }
334
335    /// Sets the maximum number of blocks for `eth_simulateV1`.
336    pub const fn max_simulate_blocks(mut self, max_simulate_blocks: u64) -> Self {
337        self.max_simulate_blocks = max_simulate_blocks;
338        self
339    }
340
341    /// Sets whether to compute state roots for `eth_simulateV1`.
342    pub const fn compute_state_root_for_eth_simulate(mut self, enabled: bool) -> Self {
343        self.compute_state_root_for_eth_simulate = enabled;
344        self
345    }
346
347    /// Sets the maximum number of blocks into the past for generating state proofs.
348    pub const fn eth_proof_window(mut self, eth_proof_window: u64) -> Self {
349        self.eth_proof_window = eth_proof_window;
350        self
351    }
352
353    /// Sets the blocking task pool.
354    pub fn blocking_task_pool(mut self, blocking_task_pool: BlockingTaskPool) -> Self {
355        self.blocking_task_pool = Some(blocking_task_pool);
356        self
357    }
358
359    /// Sets the fee history cache.
360    pub const fn fee_history_cache_config(
361        mut self,
362        fee_history_cache_config: FeeHistoryCacheConfig,
363    ) -> Self {
364        self.fee_history_cache_config = fee_history_cache_config;
365        self
366    }
367
368    /// Sets the proof permits.
369    pub const fn proof_permits(mut self, proof_permits: usize) -> Self {
370        self.proof_permits = proof_permits;
371        self
372    }
373
374    /// Sets the max batch size for batching transaction insertions.
375    pub const fn max_batch_size(mut self, max_batch_size: usize) -> Self {
376        self.max_batch_size = max_batch_size;
377        self
378    }
379
380    /// Sets the maximum number of concurrent blocking IO requests.
381    pub const fn max_blocking_io_requests(mut self, max_blocking_io_requests: usize) -> Self {
382        self.max_blocking_io_requests = max_blocking_io_requests;
383        self
384    }
385
386    /// Sets the pending block kind
387    pub const fn pending_block_kind(mut self, pending_block_kind: PendingBlockKind) -> Self {
388        self.pending_block_kind = pending_block_kind;
389        self
390    }
391
392    /// Sets the sender recovery cache shared with transaction ingress and execution.
393    pub fn sender_recovery_cache(mut self, cache: Option<SenderRecoveryCache>) -> Self {
394        self.sender_recovery_cache = cache;
395        self
396    }
397
398    /// Sets the raw transaction forwarder.
399    pub fn raw_tx_forwarder(mut self, tx_forwarder: ForwardConfig) -> Self {
400        self.raw_tx_forwarder = tx_forwarder;
401        self
402    }
403
404    /// Returns the gas cap.
405    pub const fn get_gas_cap(&self) -> &GasCap {
406        &self.gas_cap
407    }
408
409    /// Returns the maximum simulate blocks.
410    pub const fn get_max_simulate_blocks(&self) -> u64 {
411        self.max_simulate_blocks
412    }
413
414    /// Returns whether state roots are computed for `eth_simulateV1`.
415    pub const fn get_compute_state_root_for_eth_simulate(&self) -> bool {
416        self.compute_state_root_for_eth_simulate
417    }
418
419    /// Returns the ETH proof window.
420    pub const fn get_eth_proof_window(&self) -> u64 {
421        self.eth_proof_window
422    }
423
424    /// Returns a reference to the fee history cache config.
425    pub const fn get_fee_history_cache_config(&self) -> &FeeHistoryCacheConfig {
426        &self.fee_history_cache_config
427    }
428
429    /// Returns the proof permits.
430    pub const fn get_proof_permits(&self) -> usize {
431        self.proof_permits
432    }
433
434    /// Returns a reference to the ETH state cache config.
435    pub const fn get_eth_state_cache_config(&self) -> &EthStateCacheConfig {
436        &self.eth_state_cache_config
437    }
438
439    /// Returns a reference to the gas oracle config.
440    pub const fn get_gas_oracle_config(&self) -> &GasPriceOracleConfig {
441        &self.gas_oracle_config
442    }
443
444    /// Returns the max batch size.
445    pub const fn get_max_batch_size(&self) -> usize {
446        self.max_batch_size
447    }
448
449    /// Returns the pending block kind.
450    pub const fn get_pending_block_kind(&self) -> PendingBlockKind {
451        self.pending_block_kind
452    }
453
454    /// Returns a reference to the raw tx forwarder config.
455    pub const fn get_raw_tx_forwarder(&self) -> &ForwardConfig {
456        &self.raw_tx_forwarder
457    }
458
459    /// Returns a mutable reference to the fee history cache config.
460    pub const fn fee_history_cache_config_mut(&mut self) -> &mut FeeHistoryCacheConfig {
461        &mut self.fee_history_cache_config
462    }
463
464    /// Returns a mutable reference to the ETH state cache config.
465    pub const fn eth_state_cache_config_mut(&mut self) -> &mut EthStateCacheConfig {
466        &mut self.eth_state_cache_config
467    }
468
469    /// Returns a mutable reference to the gas oracle config.
470    pub const fn gas_oracle_config_mut(&mut self) -> &mut GasPriceOracleConfig {
471        &mut self.gas_oracle_config
472    }
473
474    /// Returns a mutable reference to the raw tx forwarder config.
475    pub const fn raw_tx_forwarder_mut(&mut self) -> &mut ForwardConfig {
476        &mut self.raw_tx_forwarder
477    }
478
479    /// Modifies the fee history cache configuration using a closure.
480    pub fn modify_fee_history_cache_config<F>(mut self, f: F) -> Self
481    where
482        F: FnOnce(&mut FeeHistoryCacheConfig),
483    {
484        f(&mut self.fee_history_cache_config);
485        self
486    }
487
488    /// Modifies the ETH state cache configuration using a closure.
489    pub fn modify_eth_state_cache_config<F>(mut self, f: F) -> Self
490    where
491        F: FnOnce(&mut EthStateCacheConfig),
492    {
493        f(&mut self.eth_state_cache_config);
494        self
495    }
496
497    /// Modifies the gas oracle configuration using a closure.
498    pub fn modify_gas_oracle_config<F>(mut self, f: F) -> Self
499    where
500        F: FnOnce(&mut GasPriceOracleConfig),
501    {
502        f(&mut self.gas_oracle_config);
503        self
504    }
505
506    /// Modifies the raw tx forwarder configuration using a closure.
507    pub fn modify_raw_tx_forwarder<F>(mut self, f: F) -> Self
508    where
509        F: FnOnce(&mut ForwardConfig),
510    {
511        f(&mut self.raw_tx_forwarder);
512        self
513    }
514
515    /// Builds the [`EthApiInner`] instance.
516    ///
517    /// If not configured, this will spawn the cache backend: [`EthStateCache::spawn_with`].
518    ///
519    /// # Panics
520    ///
521    /// This function panics if the blocking task pool cannot be built.
522    /// This will panic if called outside the context of a Tokio runtime.
523    pub fn build_inner(self) -> EthApiInner<N, Rpc>
524    where
525        Rpc: RpcConvert,
526        NextEnv: PendingEnvBuilder<N::Evm>,
527    {
528        let Self {
529            components,
530            rpc_converter,
531            eth_state_cache_config,
532            gas_oracle_config,
533            eth_cache,
534            gas_oracle,
535            gas_cap,
536            max_simulate_blocks,
537            compute_state_root_for_eth_simulate,
538            eth_proof_window,
539            blocking_task_pool,
540            fee_history_cache_config,
541            proof_permits,
542            task_spawner,
543            next_env,
544            max_batch_size,
545            max_blocking_io_requests,
546            pending_block_kind,
547            raw_tx_forwarder,
548            sender_recovery_cache,
549            send_raw_transaction_sync_timeout,
550            evm_memory_limit,
551            force_blob_sidecar_upcasting,
552        } = self;
553
554        let provider = components.provider().clone();
555        let task_spawner = task_spawner.unwrap_or_else(Runtime::test);
556
557        let eth_cache = eth_cache.unwrap_or_else(|| {
558            EthStateCache::spawn_with(
559                provider.clone(),
560                eth_state_cache_config,
561                task_spawner.clone(),
562            )
563        });
564        let gas_oracle = gas_oracle.unwrap_or_else(|| {
565            GasPriceOracle::new(provider.clone(), gas_oracle_config, eth_cache.clone())
566        });
567        let fee_history_cache =
568            FeeHistoryCache::<HeaderTy<N::Primitives>>::new(fee_history_cache_config);
569        let new_canonical_blocks = provider.canonical_state_stream();
570        let fhc = fee_history_cache.clone();
571        let cache = eth_cache.clone();
572        task_spawner.spawn_critical_task(
573            "cache canonical blocks for fee history task",
574            async move {
575                fee_history_cache_new_blocks_task(fhc, new_canonical_blocks, provider, cache).await;
576            },
577        );
578
579        let settings = EthApiSettings {
580            sender_recovery_cache,
581            proof_permits,
582            max_batch_size,
583            max_blocking_io_requests,
584            cache_computed_bals: eth_state_cache_config.cache_computed_bals ||
585                eth_state_cache_config.prewarm_bals.is_some(),
586            gas_cap: gas_cap.into(),
587            max_simulate_blocks,
588            compute_state_root_for_eth_simulate,
589            eth_proof_window,
590            pending_block_kind,
591            send_raw_transaction_sync_timeout,
592            evm_memory_limit,
593            force_blob_sidecar_upcasting,
594        };
595
596        EthApiInner::new(
597            components,
598            eth_cache,
599            gas_oracle,
600            settings,
601            blocking_task_pool.unwrap_or_else(|| {
602                BlockingTaskPool::builder()
603                    .thread_name(|i| format!("blocking-{i:02}"))
604                    .build()
605                    .map(BlockingTaskPool::new)
606                    .expect("failed to build blocking task pool")
607            }),
608            fee_history_cache,
609            task_spawner,
610            rpc_converter,
611            next_env,
612            raw_tx_forwarder.forwarder_client(),
613        )
614    }
615
616    /// Builds the [`EthApi`] instance.
617    ///
618    /// If not configured, this will spawn the cache backend: [`EthStateCache::spawn_with`].
619    ///
620    /// # Panics
621    ///
622    /// This function panics if the blocking task pool cannot be built.
623    /// This will panic if called outside the context of a Tokio runtime.
624    pub fn build(self) -> EthApi<N, Rpc>
625    where
626        Rpc: RpcConvert,
627        NextEnv: PendingEnvBuilder<N::Evm>,
628    {
629        EthApi { inner: Arc::new(self.build_inner()) }
630    }
631
632    /// Sets the timeout for `send_raw_transaction_sync` RPC method.
633    pub const fn send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
634        self.send_raw_transaction_sync_timeout = timeout;
635        self
636    }
637
638    /// Sets the maximum memory the EVM can allocate per RPC request.
639    pub const fn evm_memory_limit(mut self, memory_limit: u64) -> Self {
640        self.evm_memory_limit = memory_limit;
641        self
642    }
643
644    /// Sets whether to force upcasting EIP-4844 blob sidecars to EIP-7594 format.
645    pub const fn force_blob_sidecar_upcasting(mut self, force: bool) -> Self {
646        self.force_blob_sidecar_upcasting = force;
647        self
648    }
649}