reth_rpc/eth/
core.rs

1//! Implementation of the [`jsonrpsee`] generated [`EthApiServer`](crate::EthApi) trait
2//! Handles RPC requests for the `eth_` namespace.
3
4use std::{sync::Arc, time::Duration};
5
6use crate::{eth::helpers::types::EthRpcConverter, EthApiBuilder};
7use alloy_consensus::BlockHeader;
8use alloy_eips::BlockNumberOrTag;
9use alloy_network::Ethereum;
10use alloy_primitives::{Bytes, U256};
11use alloy_rpc_client::RpcClient;
12use derive_more::Deref;
13use reth_chainspec::{ChainSpec, ChainSpecProvider};
14use reth_evm_ethereum::EthEvmConfig;
15use reth_network_api::noop::NoopNetwork;
16use reth_node_api::{FullNodeComponents, FullNodeTypes};
17use reth_rpc_convert::{RpcConvert, RpcConverter};
18use reth_rpc_eth_api::{
19    helpers::{pending_block::PendingEnvBuilder, spec::SignersForRpc, SpawnBlocking},
20    node::{RpcNodeCoreAdapter, RpcNodeCoreExt},
21    EthApiTypes, RpcNodeCore,
22};
23use reth_rpc_eth_types::{
24    builder::config::PendingBlockKind, receipt::EthReceiptConverter, tx_forward::ForwardConfig,
25    EthApiError, EthStateCache, FeeHistoryCache, GasCap, GasPriceOracle, PendingBlock,
26};
27use reth_storage_api::{noop::NoopProvider, BlockReaderIdExt, ProviderHeader};
28use reth_tasks::{
29    pool::{BlockingTaskGuard, BlockingTaskPool},
30    TaskSpawner, TokioTaskExecutor,
31};
32use reth_transaction_pool::{
33    blobstore::BlobSidecarConverter, noop::NoopTransactionPool, AddedTransactionOutcome,
34    BatchTxProcessor, BatchTxRequest, TransactionPool,
35};
36use tokio::sync::{broadcast, mpsc, Mutex, Semaphore};
37
38const DEFAULT_BROADCAST_CAPACITY: usize = 2000;
39
40/// Helper type alias for [`RpcConverter`] with components from the given [`FullNodeComponents`].
41pub type EthRpcConverterFor<N, NetworkT = Ethereum> = RpcConverter<
42    NetworkT,
43    <N as FullNodeComponents>::Evm,
44    EthReceiptConverter<<<N as FullNodeTypes>::Provider as ChainSpecProvider>::ChainSpec>,
45>;
46
47/// Helper type alias for [`EthApi`] with components from the given [`FullNodeComponents`].
48pub type EthApiFor<N, NetworkT = Ethereum> = EthApi<N, EthRpcConverterFor<N, NetworkT>>;
49
50/// Helper type alias for [`EthApi`] with components from the given [`FullNodeComponents`].
51pub type EthApiBuilderFor<N, NetworkT = Ethereum> =
52    EthApiBuilder<N, EthRpcConverterFor<N, NetworkT>>;
53
54/// `Eth` API implementation.
55///
56/// This type provides the functionality for handling `eth_` related requests.
57/// These are implemented two-fold: Core functionality is implemented as
58/// [`EthApiSpec`](reth_rpc_eth_api::helpers::EthApiSpec) trait. Additionally, the required server
59/// implementations (e.g. [`EthApiServer`](reth_rpc_eth_api::EthApiServer)) are implemented
60/// separately in submodules. The rpc handler implementation can then delegate to the main impls.
61/// This way [`EthApi`] is not limited to [`jsonrpsee`] and can be used standalone or in other
62/// network handlers (for example ipc).
63///
64/// ## Trait requirements
65///
66/// While this type requires various unrestricted generic components, trait bounds are enforced when
67/// additional traits are implemented for this type.
68#[derive(Deref)]
69pub struct EthApi<N: RpcNodeCore, Rpc: RpcConvert> {
70    /// All nested fields bundled together.
71    #[deref]
72    pub(super) inner: Arc<EthApiInner<N, Rpc>>,
73}
74
75impl<N, Rpc> Clone for EthApi<N, Rpc>
76where
77    N: RpcNodeCore,
78    Rpc: RpcConvert,
79{
80    fn clone(&self) -> Self {
81        Self { inner: self.inner.clone() }
82    }
83}
84
85impl
86    EthApi<
87        RpcNodeCoreAdapter<NoopProvider, NoopTransactionPool, NoopNetwork, EthEvmConfig>,
88        EthRpcConverter<ChainSpec>,
89    >
90{
91    /// Convenience fn to obtain a new [`EthApiBuilder`] instance with mandatory components.
92    ///
93    /// Creating an [`EthApi`] requires a few mandatory components:
94    ///  - provider: The type responsible for fetching requested data from disk.
95    ///  - transaction pool: To interact with the pool, submitting new transactions (e.g.
96    ///    `eth_sendRawTransactions`).
97    ///  - network: required to handle requests related to network state (e.g. `eth_syncing`).
98    ///  - evm config: Knows how create a new EVM instance to transact,estimate,call,trace.
99    ///
100    /// # Create an instance with noop ethereum implementations
101    ///
102    /// ```no_run
103    /// use alloy_network::Ethereum;
104    /// use reth_evm_ethereum::EthEvmConfig;
105    /// use reth_network_api::noop::NoopNetwork;
106    /// use reth_provider::noop::NoopProvider;
107    /// use reth_rpc::EthApi;
108    /// use reth_transaction_pool::noop::NoopTransactionPool;
109    /// let eth_api = EthApi::builder(
110    ///     NoopProvider::default(),
111    ///     NoopTransactionPool::default(),
112    ///     NoopNetwork::default(),
113    ///     EthEvmConfig::mainnet(),
114    /// )
115    /// .build();
116    /// ```
117    #[expect(clippy::type_complexity)]
118    pub fn builder<Provider, Pool, Network, EvmConfig, ChainSpec>(
119        provider: Provider,
120        pool: Pool,
121        network: Network,
122        evm_config: EvmConfig,
123    ) -> EthApiBuilder<
124        RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>,
125        RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>,
126    >
127    where
128        RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>:
129            RpcNodeCore<Provider: ChainSpecProvider<ChainSpec = ChainSpec>, Evm = EvmConfig>,
130    {
131        EthApiBuilder::new(provider, pool, network, evm_config)
132    }
133}
134
135impl<N, Rpc> EthApi<N, Rpc>
136where
137    N: RpcNodeCore,
138    Rpc: RpcConvert,
139    (): PendingEnvBuilder<N::Evm>,
140{
141    /// Creates a new, shareable instance using the default tokio task spawner.
142    #[expect(clippy::too_many_arguments)]
143    pub fn new(
144        components: N,
145        eth_cache: EthStateCache<N::Primitives>,
146        gas_oracle: GasPriceOracle<N::Provider>,
147        gas_cap: impl Into<GasCap>,
148        max_simulate_blocks: u64,
149        eth_proof_window: u64,
150        blocking_task_pool: BlockingTaskPool,
151        fee_history_cache: FeeHistoryCache<ProviderHeader<N::Provider>>,
152        proof_permits: usize,
153        rpc_converter: Rpc,
154        max_batch_size: usize,
155        max_blocking_io_requests: usize,
156        pending_block_kind: PendingBlockKind,
157        raw_tx_forwarder: ForwardConfig,
158        send_raw_transaction_sync_timeout: Duration,
159        evm_memory_limit: u64,
160    ) -> Self {
161        let inner = EthApiInner::new(
162            components,
163            eth_cache,
164            gas_oracle,
165            gas_cap,
166            max_simulate_blocks,
167            eth_proof_window,
168            blocking_task_pool,
169            fee_history_cache,
170            TokioTaskExecutor::default().boxed(),
171            proof_permits,
172            rpc_converter,
173            (),
174            max_batch_size,
175            max_blocking_io_requests,
176            pending_block_kind,
177            raw_tx_forwarder.forwarder_client(),
178            send_raw_transaction_sync_timeout,
179            evm_memory_limit,
180        );
181
182        Self { inner: Arc::new(inner) }
183    }
184}
185
186impl<N, Rpc> EthApiTypes for EthApi<N, Rpc>
187where
188    N: RpcNodeCore,
189    Rpc: RpcConvert<Error = EthApiError>,
190{
191    type Error = EthApiError;
192    type NetworkTypes = Rpc::Network;
193    type RpcConvert = Rpc;
194
195    fn converter(&self) -> &Self::RpcConvert {
196        &self.converter
197    }
198}
199
200impl<N, Rpc> RpcNodeCore for EthApi<N, Rpc>
201where
202    N: RpcNodeCore,
203    Rpc: RpcConvert,
204{
205    type Primitives = N::Primitives;
206    type Provider = N::Provider;
207    type Pool = N::Pool;
208    type Evm = N::Evm;
209    type Network = N::Network;
210
211    fn pool(&self) -> &Self::Pool {
212        self.inner.pool()
213    }
214
215    fn evm_config(&self) -> &Self::Evm {
216        self.inner.evm_config()
217    }
218
219    fn network(&self) -> &Self::Network {
220        self.inner.network()
221    }
222
223    fn provider(&self) -> &Self::Provider {
224        self.inner.provider()
225    }
226}
227
228impl<N, Rpc> RpcNodeCoreExt for EthApi<N, Rpc>
229where
230    N: RpcNodeCore,
231    Rpc: RpcConvert,
232{
233    #[inline]
234    fn cache(&self) -> &EthStateCache<N::Primitives> {
235        self.inner.cache()
236    }
237}
238
239impl<N, Rpc> std::fmt::Debug for EthApi<N, Rpc>
240where
241    N: RpcNodeCore,
242    Rpc: RpcConvert,
243{
244    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245        f.debug_struct("EthApi").finish_non_exhaustive()
246    }
247}
248
249impl<N, Rpc> SpawnBlocking for EthApi<N, Rpc>
250where
251    N: RpcNodeCore,
252    Rpc: RpcConvert<Error = EthApiError>,
253{
254    #[inline]
255    fn io_task_spawner(&self) -> impl TaskSpawner {
256        self.inner.task_spawner()
257    }
258
259    #[inline]
260    fn tracing_task_pool(&self) -> &BlockingTaskPool {
261        self.inner.blocking_task_pool()
262    }
263
264    #[inline]
265    fn tracing_task_guard(&self) -> &BlockingTaskGuard {
266        self.inner.blocking_task_guard()
267    }
268
269    #[inline]
270    fn blocking_io_task_guard(&self) -> &std::sync::Arc<tokio::sync::Semaphore> {
271        self.inner.blocking_io_request_semaphore()
272    }
273}
274
275/// Container type `EthApi`
276#[expect(missing_debug_implementations)]
277pub struct EthApiInner<N: RpcNodeCore, Rpc: RpcConvert> {
278    /// The components of the node.
279    components: N,
280    /// All configured Signers
281    signers: SignersForRpc<N::Provider, Rpc::Network>,
282    /// The async cache frontend for eth related data
283    eth_cache: EthStateCache<N::Primitives>,
284    /// The async gas oracle frontend for gas price suggestions
285    gas_oracle: GasPriceOracle<N::Provider>,
286    /// Maximum gas limit for `eth_call` and call tracing RPC methods.
287    gas_cap: u64,
288    /// Maximum number of blocks for `eth_simulateV1`.
289    max_simulate_blocks: u64,
290    /// The maximum number of blocks into the past for generating state proofs.
291    eth_proof_window: u64,
292    /// The block number at which the node started
293    starting_block: U256,
294    /// The type that can spawn tasks which would otherwise block.
295    task_spawner: Box<dyn TaskSpawner>,
296    /// Cached pending block if any
297    pending_block: Mutex<Option<PendingBlock<N::Primitives>>>,
298    /// A pool dedicated to CPU heavy blocking tasks.
299    blocking_task_pool: BlockingTaskPool,
300    /// Cache for block fees history
301    fee_history_cache: FeeHistoryCache<ProviderHeader<N::Provider>>,
302
303    /// Guard for getproof calls
304    blocking_task_guard: BlockingTaskGuard,
305
306    /// Semaphore to limit concurrent blocking IO requests (`eth_call`, `eth_estimateGas`, etc.)
307    blocking_io_request_semaphore: Arc<Semaphore>,
308
309    /// Transaction broadcast channel
310    raw_tx_sender: broadcast::Sender<Bytes>,
311
312    /// Raw transaction forwarder
313    raw_tx_forwarder: Option<RpcClient>,
314
315    /// Converter for RPC types.
316    converter: Rpc,
317
318    /// Builder for pending block environment.
319    next_env_builder: Box<dyn PendingEnvBuilder<N::Evm>>,
320
321    /// Transaction batch sender for batching tx insertions
322    tx_batch_sender:
323        mpsc::UnboundedSender<BatchTxRequest<<N::Pool as TransactionPool>::Transaction>>,
324
325    /// Configuration for pending block construction.
326    pending_block_kind: PendingBlockKind,
327
328    /// Timeout duration for `send_raw_transaction_sync` RPC method.
329    send_raw_transaction_sync_timeout: Duration,
330
331    /// Blob sidecar converter
332    blob_sidecar_converter: BlobSidecarConverter,
333
334    /// Maximum memory the EVM can allocate per RPC request.
335    evm_memory_limit: u64,
336}
337
338impl<N, Rpc> EthApiInner<N, Rpc>
339where
340    N: RpcNodeCore,
341    Rpc: RpcConvert,
342{
343    /// Creates a new, shareable instance using the default tokio task spawner.
344    #[expect(clippy::too_many_arguments)]
345    pub fn new(
346        components: N,
347        eth_cache: EthStateCache<N::Primitives>,
348        gas_oracle: GasPriceOracle<N::Provider>,
349        gas_cap: impl Into<GasCap>,
350        max_simulate_blocks: u64,
351        eth_proof_window: u64,
352        blocking_task_pool: BlockingTaskPool,
353        fee_history_cache: FeeHistoryCache<ProviderHeader<N::Provider>>,
354        task_spawner: Box<dyn TaskSpawner + 'static>,
355        proof_permits: usize,
356        converter: Rpc,
357        next_env: impl PendingEnvBuilder<N::Evm>,
358        max_batch_size: usize,
359        max_blocking_io_requests: usize,
360        pending_block_kind: PendingBlockKind,
361        raw_tx_forwarder: Option<RpcClient>,
362        send_raw_transaction_sync_timeout: Duration,
363        evm_memory_limit: u64,
364    ) -> Self {
365        let signers = parking_lot::RwLock::new(Default::default());
366        // get the block number of the latest block
367        let starting_block = U256::from(
368            components
369                .provider()
370                .header_by_number_or_tag(BlockNumberOrTag::Latest)
371                .ok()
372                .flatten()
373                .map(|header| header.number())
374                .unwrap_or_default(),
375        );
376
377        let (raw_tx_sender, _) = broadcast::channel(DEFAULT_BROADCAST_CAPACITY);
378
379        // Create tx pool insertion batcher
380        let (processor, tx_batch_sender) =
381            BatchTxProcessor::new(components.pool().clone(), max_batch_size);
382        task_spawner.spawn_critical("tx-batcher", Box::pin(processor));
383
384        Self {
385            components,
386            signers,
387            eth_cache,
388            gas_oracle,
389            gas_cap: gas_cap.into().into(),
390            max_simulate_blocks,
391            eth_proof_window,
392            starting_block,
393            task_spawner,
394            pending_block: Default::default(),
395            blocking_task_pool,
396            fee_history_cache,
397            blocking_task_guard: BlockingTaskGuard::new(proof_permits),
398            blocking_io_request_semaphore: Arc::new(Semaphore::new(max_blocking_io_requests)),
399            raw_tx_sender,
400            raw_tx_forwarder,
401            converter,
402            next_env_builder: Box::new(next_env),
403            tx_batch_sender,
404            pending_block_kind,
405            send_raw_transaction_sync_timeout,
406            blob_sidecar_converter: BlobSidecarConverter::new(),
407            evm_memory_limit,
408        }
409    }
410}
411
412impl<N, Rpc> EthApiInner<N, Rpc>
413where
414    N: RpcNodeCore,
415    Rpc: RpcConvert,
416{
417    /// Returns a handle to data on disk.
418    #[inline]
419    pub fn provider(&self) -> &N::Provider {
420        self.components.provider()
421    }
422
423    /// Returns a handle to the transaction response builder.
424    #[inline]
425    pub const fn converter(&self) -> &Rpc {
426        &self.converter
427    }
428
429    /// Returns a handle to data in memory.
430    #[inline]
431    pub const fn cache(&self) -> &EthStateCache<N::Primitives> {
432        &self.eth_cache
433    }
434
435    /// Returns a handle to the pending block.
436    #[inline]
437    pub const fn pending_block(&self) -> &Mutex<Option<PendingBlock<N::Primitives>>> {
438        &self.pending_block
439    }
440
441    /// Returns a type that knows how to build a [`reth_evm::ConfigureEvm::NextBlockEnvCtx`] for a
442    /// pending block.
443    #[inline]
444    pub const fn pending_env_builder(&self) -> &dyn PendingEnvBuilder<N::Evm> {
445        &*self.next_env_builder
446    }
447
448    /// Returns a handle to the task spawner.
449    #[inline]
450    pub const fn task_spawner(&self) -> &dyn TaskSpawner {
451        &*self.task_spawner
452    }
453
454    /// Returns a handle to the blocking thread pool.
455    ///
456    /// This is intended for tasks that are CPU bound.
457    #[inline]
458    pub const fn blocking_task_pool(&self) -> &BlockingTaskPool {
459        &self.blocking_task_pool
460    }
461
462    /// Returns a handle to the EVM config.
463    #[inline]
464    pub fn evm_config(&self) -> &N::Evm {
465        self.components.evm_config()
466    }
467
468    /// Returns a handle to the transaction pool.
469    #[inline]
470    pub fn pool(&self) -> &N::Pool {
471        self.components.pool()
472    }
473
474    /// Returns the gas cap.
475    #[inline]
476    pub const fn gas_cap(&self) -> u64 {
477        self.gas_cap
478    }
479
480    /// Returns the `max_simulate_blocks`.
481    #[inline]
482    pub const fn max_simulate_blocks(&self) -> u64 {
483        self.max_simulate_blocks
484    }
485
486    /// Returns a handle to the gas oracle.
487    #[inline]
488    pub const fn gas_oracle(&self) -> &GasPriceOracle<N::Provider> {
489        &self.gas_oracle
490    }
491
492    /// Returns a handle to the fee history cache.
493    #[inline]
494    pub const fn fee_history_cache(&self) -> &FeeHistoryCache<ProviderHeader<N::Provider>> {
495        &self.fee_history_cache
496    }
497
498    /// Returns a handle to the signers.
499    #[inline]
500    pub const fn signers(&self) -> &SignersForRpc<N::Provider, Rpc::Network> {
501        &self.signers
502    }
503
504    /// Returns the starting block.
505    #[inline]
506    pub const fn starting_block(&self) -> U256 {
507        self.starting_block
508    }
509
510    /// Returns the inner `Network`
511    #[inline]
512    pub fn network(&self) -> &N::Network {
513        self.components.network()
514    }
515
516    /// The maximum number of blocks into the past for generating state proofs.
517    #[inline]
518    pub const fn eth_proof_window(&self) -> u64 {
519        self.eth_proof_window
520    }
521
522    /// Returns reference to [`BlockingTaskGuard`].
523    #[inline]
524    pub const fn blocking_task_guard(&self) -> &BlockingTaskGuard {
525        &self.blocking_task_guard
526    }
527
528    /// Returns [`broadcast::Receiver`] of new raw transactions
529    #[inline]
530    pub fn subscribe_to_raw_transactions(&self) -> broadcast::Receiver<Bytes> {
531        self.raw_tx_sender.subscribe()
532    }
533
534    /// Broadcasts raw transaction if there are active subscribers.
535    #[inline]
536    pub fn broadcast_raw_transaction(&self, raw_tx: Bytes) {
537        let _ = self.raw_tx_sender.send(raw_tx);
538    }
539
540    /// Returns the transaction batch sender
541    #[inline]
542    pub const fn tx_batch_sender(
543        &self,
544    ) -> &mpsc::UnboundedSender<BatchTxRequest<<N::Pool as TransactionPool>::Transaction>> {
545        &self.tx_batch_sender
546    }
547
548    /// Adds an _unvalidated_ transaction into the pool via the transaction batch sender.
549    #[inline]
550    pub async fn add_pool_transaction(
551        &self,
552        transaction: <N::Pool as TransactionPool>::Transaction,
553    ) -> Result<AddedTransactionOutcome, EthApiError> {
554        let (response_tx, response_rx) = tokio::sync::oneshot::channel();
555        let request = reth_transaction_pool::BatchTxRequest::new(transaction, response_tx);
556
557        self.tx_batch_sender()
558            .send(request)
559            .map_err(|_| reth_rpc_eth_types::EthApiError::BatchTxSendError)?;
560
561        Ok(response_rx.await??)
562    }
563
564    /// Returns the pending block kind
565    #[inline]
566    pub const fn pending_block_kind(&self) -> PendingBlockKind {
567        self.pending_block_kind
568    }
569
570    /// Returns a handle to the raw transaction forwarder.
571    #[inline]
572    pub const fn raw_tx_forwarder(&self) -> Option<&RpcClient> {
573        self.raw_tx_forwarder.as_ref()
574    }
575
576    /// Returns the timeout duration for `send_raw_transaction_sync` RPC method.
577    #[inline]
578    pub const fn send_raw_transaction_sync_timeout(&self) -> Duration {
579        self.send_raw_transaction_sync_timeout
580    }
581
582    /// Returns a handle to the blob sidecar converter.
583    #[inline]
584    pub const fn blob_sidecar_converter(&self) -> &BlobSidecarConverter {
585        &self.blob_sidecar_converter
586    }
587
588    /// Returns the EVM memory limit.
589    #[inline]
590    pub const fn evm_memory_limit(&self) -> u64 {
591        self.evm_memory_limit
592    }
593
594    /// Returns a reference to the blocking IO request semaphore.
595    #[inline]
596    pub const fn blocking_io_request_semaphore(&self) -> &Arc<Semaphore> {
597        &self.blocking_io_request_semaphore
598    }
599}
600
601#[cfg(test)]
602mod tests {
603    use crate::{eth::helpers::types::EthRpcConverter, EthApi, EthApiBuilder};
604    use alloy_consensus::{Block, BlockBody, Header};
605    use alloy_eips::BlockNumberOrTag;
606    use alloy_primitives::{Signature, B256, U64};
607    use alloy_rpc_types::FeeHistory;
608    use jsonrpsee_types::error::INVALID_PARAMS_CODE;
609    use rand::Rng;
610    use reth_chain_state::CanonStateSubscriptions;
611    use reth_chainspec::{ChainSpec, ChainSpecProvider, EthChainSpec};
612    use reth_ethereum_primitives::TransactionSigned;
613    use reth_evm_ethereum::EthEvmConfig;
614    use reth_network_api::noop::NoopNetwork;
615    use reth_provider::{
616        test_utils::{MockEthProvider, NoopProvider},
617        StageCheckpointReader,
618    };
619    use reth_rpc_eth_api::{node::RpcNodeCoreAdapter, EthApiServer};
620    use reth_storage_api::{BlockReader, BlockReaderIdExt, StateProviderFactory};
621    use reth_testing_utils::generators;
622    use reth_transaction_pool::test_utils::{testing_pool, TestPool};
623
624    type FakeEthApi<P = MockEthProvider> = EthApi<
625        RpcNodeCoreAdapter<P, TestPool, NoopNetwork, EthEvmConfig>,
626        EthRpcConverter<ChainSpec>,
627    >;
628
629    fn build_test_eth_api<
630        P: BlockReaderIdExt<
631                Block = reth_ethereum_primitives::Block,
632                Receipt = reth_ethereum_primitives::Receipt,
633                Header = alloy_consensus::Header,
634                Transaction = reth_ethereum_primitives::TransactionSigned,
635            > + BlockReader
636            + ChainSpecProvider<ChainSpec = ChainSpec>
637            + StateProviderFactory
638            + CanonStateSubscriptions<Primitives = reth_ethereum_primitives::EthPrimitives>
639            + StageCheckpointReader
640            + Unpin
641            + Clone
642            + 'static,
643    >(
644        provider: P,
645    ) -> FakeEthApi<P> {
646        EthApiBuilder::new(
647            provider.clone(),
648            testing_pool(),
649            NoopNetwork::default(),
650            EthEvmConfig::new(provider.chain_spec()),
651        )
652        .build()
653    }
654
655    // Function to prepare the EthApi with mock data
656    fn prepare_eth_api(
657        newest_block: u64,
658        mut oldest_block: Option<B256>,
659        block_count: u64,
660        mock_provider: MockEthProvider,
661    ) -> (FakeEthApi, Vec<u128>, Vec<f64>) {
662        let mut rng = generators::rng();
663
664        // Build mock data
665        let mut gas_used_ratios = Vec::with_capacity(block_count as usize);
666        let mut base_fees_per_gas = Vec::with_capacity(block_count as usize);
667        let mut last_header = None;
668        let mut parent_hash = B256::default();
669
670        for i in (0..block_count).rev() {
671            let hash = rng.random();
672            // Note: Generates saner values to avoid invalid overflows later
673            let gas_limit = rng.random::<u32>() as u64;
674            let base_fee_per_gas: Option<u64> =
675                rng.random::<bool>().then(|| rng.random::<u32>() as u64);
676            let gas_used = rng.random::<u32>() as u64;
677
678            let header = Header {
679                number: newest_block - i,
680                gas_limit,
681                gas_used,
682                base_fee_per_gas,
683                parent_hash,
684                ..Default::default()
685            };
686            last_header = Some(header.clone());
687            parent_hash = hash;
688
689            const TOTAL_TRANSACTIONS: usize = 100;
690            let mut transactions = Vec::with_capacity(TOTAL_TRANSACTIONS);
691            for _ in 0..TOTAL_TRANSACTIONS {
692                let random_fee: u128 = rng.random();
693
694                if let Some(base_fee_per_gas) = header.base_fee_per_gas {
695                    let transaction = TransactionSigned::new_unhashed(
696                        reth_ethereum_primitives::Transaction::Eip1559(
697                            alloy_consensus::TxEip1559 {
698                                max_priority_fee_per_gas: random_fee,
699                                max_fee_per_gas: random_fee + base_fee_per_gas as u128,
700                                ..Default::default()
701                            },
702                        ),
703                        Signature::test_signature(),
704                    );
705
706                    transactions.push(transaction);
707                } else {
708                    let transaction = TransactionSigned::new_unhashed(
709                        reth_ethereum_primitives::Transaction::Legacy(Default::default()),
710                        Signature::test_signature(),
711                    );
712
713                    transactions.push(transaction);
714                }
715            }
716
717            mock_provider.add_block(
718                hash,
719                Block {
720                    header: header.clone(),
721                    body: BlockBody { transactions, ..Default::default() },
722                },
723            );
724            mock_provider.add_header(hash, header);
725
726            oldest_block.get_or_insert(hash);
727            gas_used_ratios.push(gas_used as f64 / gas_limit as f64);
728            base_fees_per_gas.push(base_fee_per_gas.map(|fee| fee as u128).unwrap_or_default());
729        }
730
731        // Add final base fee (for the next block outside of the request)
732        let last_header = last_header.unwrap();
733        let spec = mock_provider.chain_spec();
734        base_fees_per_gas.push(
735            spec.next_block_base_fee(&last_header, last_header.timestamp).unwrap_or_default()
736                as u128,
737        );
738
739        let eth_api = build_test_eth_api(mock_provider);
740
741        (eth_api, base_fees_per_gas, gas_used_ratios)
742    }
743
744    /// Invalid block range
745    #[tokio::test]
746    async fn test_fee_history_empty() {
747        let response = <EthApi<_, _> as EthApiServer<_, _, _, _, _, _>>::fee_history(
748            &build_test_eth_api(NoopProvider::default()),
749            U64::from(1),
750            BlockNumberOrTag::Latest,
751            None,
752        )
753        .await;
754        assert!(response.is_err());
755        let error_object = response.unwrap_err();
756        assert_eq!(error_object.code(), INVALID_PARAMS_CODE);
757    }
758
759    #[tokio::test]
760    /// Invalid block range (request is before genesis)
761    async fn test_fee_history_invalid_block_range_before_genesis() {
762        let block_count = 10;
763        let newest_block = 1337;
764        let oldest_block = None;
765
766        let (eth_api, _, _) =
767            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
768
769        let response = <EthApi<_, _> as EthApiServer<_, _, _, _, _, _>>::fee_history(
770            &eth_api,
771            U64::from(newest_block + 1),
772            newest_block.into(),
773            Some(vec![10.0]),
774        )
775        .await;
776
777        assert!(response.is_err());
778        let error_object = response.unwrap_err();
779        assert_eq!(error_object.code(), INVALID_PARAMS_CODE);
780    }
781
782    #[tokio::test]
783    /// Invalid block range (request is in the future)
784    async fn test_fee_history_invalid_block_range_in_future() {
785        let block_count = 10;
786        let newest_block = 1337;
787        let oldest_block = None;
788
789        let (eth_api, _, _) =
790            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
791
792        let response = <EthApi<_, _> as EthApiServer<_, _, _, _, _, _>>::fee_history(
793            &eth_api,
794            U64::from(1),
795            (newest_block + 1000).into(),
796            Some(vec![10.0]),
797        )
798        .await;
799
800        assert!(response.is_err());
801        let error_object = response.unwrap_err();
802        assert_eq!(error_object.code(), INVALID_PARAMS_CODE);
803    }
804
805    #[tokio::test]
806    /// Requesting no block should result in a default response
807    async fn test_fee_history_no_block_requested() {
808        let block_count = 10;
809        let newest_block = 1337;
810        let oldest_block = None;
811
812        let (eth_api, _, _) =
813            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
814
815        let response = <EthApi<_, _> as EthApiServer<_, _, _, _, _, _>>::fee_history(
816            &eth_api,
817            U64::from(0),
818            newest_block.into(),
819            None,
820        )
821        .await
822        .unwrap();
823        assert_eq!(
824            response,
825            FeeHistory::default(),
826            "none: requesting no block should yield a default response"
827        );
828    }
829
830    #[tokio::test]
831    /// Requesting a single block should return 1 block (+ base fee for the next block over)
832    async fn test_fee_history_single_block() {
833        let block_count = 10;
834        let newest_block = 1337;
835        let oldest_block = None;
836
837        let (eth_api, base_fees_per_gas, gas_used_ratios) =
838            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
839
840        let fee_history =
841            eth_api.fee_history(U64::from(1), newest_block.into(), None).await.unwrap();
842        assert_eq!(
843            fee_history.base_fee_per_gas,
844            &base_fees_per_gas[base_fees_per_gas.len() - 2..],
845            "one: base fee per gas is incorrect"
846        );
847        assert_eq!(
848            fee_history.base_fee_per_gas.len(),
849            2,
850            "one: should return base fee of the next block as well"
851        );
852        assert_eq!(
853            &fee_history.gas_used_ratio,
854            &gas_used_ratios[gas_used_ratios.len() - 1..],
855            "one: gas used ratio is incorrect"
856        );
857        assert_eq!(fee_history.oldest_block, newest_block, "one: oldest block is incorrect");
858        assert!(
859            fee_history.reward.is_none(),
860            "one: no percentiles were requested, so there should be no rewards result"
861        );
862    }
863
864    /// Requesting all blocks should be ok
865    #[tokio::test]
866    async fn test_fee_history_all_blocks() {
867        let block_count = 10;
868        let newest_block = 1337;
869        let oldest_block = None;
870
871        let (eth_api, base_fees_per_gas, gas_used_ratios) =
872            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
873
874        let fee_history =
875            eth_api.fee_history(U64::from(block_count), newest_block.into(), None).await.unwrap();
876
877        assert_eq!(
878            &fee_history.base_fee_per_gas, &base_fees_per_gas,
879            "all: base fee per gas is incorrect"
880        );
881        assert_eq!(
882            fee_history.base_fee_per_gas.len() as u64,
883            block_count + 1,
884            "all: should return base fee of the next block as well"
885        );
886        assert_eq!(
887            &fee_history.gas_used_ratio, &gas_used_ratios,
888            "all: gas used ratio is incorrect"
889        );
890        assert_eq!(
891            fee_history.oldest_block,
892            newest_block - block_count + 1,
893            "all: oldest block is incorrect"
894        );
895        assert!(
896            fee_history.reward.is_none(),
897            "all: no percentiles were requested, so there should be no rewards result"
898        );
899    }
900}