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