domain_eth_service/
service.rs

1use fc_mapping_sync::kv::MappingSyncWorker;
2use fc_mapping_sync::SyncStrategy;
3use fc_rpc::EthTask;
4pub use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool};
5use fc_storage::StorageOverride;
6use futures::{future, StreamExt};
7use sc_client_api::{BlockchainEvents, StorageProvider};
8use sc_network_sync::SyncingService;
9use sc_service::error::Error as ServiceError;
10use sp_api::ProvideRuntimeApi;
11use sp_blockchain::HeaderBackend;
12use sp_core::traits::SpawnEssentialNamed;
13use sp_runtime::traits::{Block as BlockT, NumberFor, Zero};
14use std::collections::BTreeMap;
15use std::sync::{Arc, Mutex};
16use std::time::Duration;
17
18/// The ethereum-compatibility configuration used to run a node.
19#[derive(Clone, Debug, clap::Parser)]
20pub struct EthConfiguration {
21    /// Maximum number of logs in a query.
22    #[arg(long, default_value = "10000")]
23    pub max_past_logs: u32,
24
25    /// Maximum fee history cache size.
26    #[arg(long, default_value = "2048")]
27    pub fee_history_limit: u64,
28
29    #[arg(long)]
30    pub enable_dev_signer: bool,
31
32    /// The dynamic-fee pallet target gas price set by block author
33    #[arg(long, default_value = "1")]
34    pub target_gas_price: u64,
35
36    /// Maximum allowed gas limit will be `block.gas_limit * execute_gas_limit_multiplier`
37    /// when using eth_call/eth_estimateGas.
38    #[arg(long, default_value = "10")]
39    pub execute_gas_limit_multiplier: u64,
40
41    /// Size in bytes of the LRU cache for block data.
42    #[arg(long, default_value = "50")]
43    pub eth_log_block_cache: usize,
44
45    /// Size in bytes of the LRU cache for transactions statuses data.
46    #[arg(long, default_value = "50")]
47    pub eth_statuses_cache: usize,
48}
49
50pub(crate) struct FrontierPartialComponents {
51    pub(crate) filter_pool: Option<FilterPool>,
52    pub(crate) fee_history_cache: FeeHistoryCache,
53    pub(crate) fee_history_cache_limit: FeeHistoryCacheLimit,
54}
55
56#[expect(clippy::result_large_err, reason = "Comes from Substrate")]
57pub(crate) fn new_frontier_partial(
58    fee_history_cache_limit: u64,
59) -> Result<FrontierPartialComponents, ServiceError> {
60    Ok(FrontierPartialComponents {
61        filter_pool: Some(Arc::new(Mutex::new(BTreeMap::new()))),
62        fee_history_cache: Arc::new(Mutex::new(BTreeMap::new())),
63        fee_history_cache_limit,
64    })
65}
66
67#[allow(clippy::too_many_arguments)]
68pub(crate) fn spawn_frontier_tasks<Block, Client, Backend, SE>(
69    essential_task_spawner: SE,
70    client: Arc<Client>,
71    backend: Arc<Backend>,
72    frontier_backend: Arc<fc_db::kv::Backend<Block, Client>>,
73    storage_override: Arc<dyn StorageOverride<Block>>,
74    frontier_partial_components: FrontierPartialComponents,
75    sync: Arc<SyncingService<Block>>,
76    pubsub_notification_sinks: Arc<
77        fc_mapping_sync::EthereumBlockNotificationSinks<
78            fc_mapping_sync::EthereumBlockNotification<Block>,
79        >,
80    >,
81) where
82    Block: BlockT,
83    Backend: sc_client_api::Backend<Block> + 'static,
84    Client: ProvideRuntimeApi<Block>
85        + BlockchainEvents<Block>
86        + HeaderBackend<Block>
87        + StorageProvider<Block, Backend>
88        + Send
89        + Sync
90        + 'static,
91    Client::Api: sp_api::ApiExt<Block>
92        + fp_rpc::EthereumRuntimeRPCApi<Block>
93        + fp_rpc::ConvertTransactionRuntimeApi<Block>,
94    SE: SpawnEssentialNamed,
95{
96    essential_task_spawner.spawn_essential(
97        "frontier-mapping-sync-worker",
98        Some("frontier"),
99        Box::pin(
100            MappingSyncWorker::new(
101                client.import_notification_stream(),
102                Duration::new(6, 0),
103                client.clone(),
104                backend,
105                storage_override.clone(),
106                frontier_backend,
107                3,
108                NumberFor::<Block>::zero(),
109                SyncStrategy::Normal,
110                sync,
111                pubsub_notification_sinks,
112            )
113            .for_each(|()| future::ready(())),
114        ),
115    );
116
117    let FrontierPartialComponents {
118        filter_pool,
119        fee_history_cache,
120        fee_history_cache_limit,
121    } = frontier_partial_components;
122
123    // Spawn Frontier EthFilterApi maintenance task.
124    if let Some(filter_pool) = filter_pool {
125        // Each filter is allowed to stay in the pool for 100 blocks.
126        const FILTER_RETAIN_THRESHOLD: u64 = 100;
127        essential_task_spawner.spawn_essential(
128            "frontier-filter-pool",
129            Some("frontier"),
130            Box::pin(EthTask::filter_pool_task(
131                client.clone(),
132                filter_pool,
133                FILTER_RETAIN_THRESHOLD,
134            )),
135        );
136    }
137
138    // Spawn Frontier FeeHistory cache maintenance task.
139    essential_task_spawner.spawn_essential(
140        "frontier-fee-history",
141        Some("frontier"),
142        Box::pin(EthTask::fee_history_task(
143            client,
144            storage_override,
145            fee_history_cache,
146            fee_history_cache_limit,
147        )),
148    );
149}