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