frontier_template_node/
eth.rs

1use std::{
2	collections::BTreeMap,
3	path::PathBuf,
4	sync::{Arc, Mutex},
5	time::Duration,
6};
7
8use futures::{future, prelude::*};
9// Substrate
10use sc_client_api::BlockchainEvents;
11use sc_executor::HostFunctions;
12use sc_network_sync::SyncingService;
13use sc_service::{error::Error as ServiceError, Configuration, TaskManager};
14use sp_api::ConstructRuntimeApi;
15use sp_core::H256;
16use sp_runtime::traits::Block as BlockT;
17// Frontier
18pub use fc_consensus::FrontierBlockImport;
19use fc_rpc::EthTask;
20pub use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool};
21pub use fc_storage::{StorageOverride, StorageOverrideHandler};
22
23use crate::client::{FullBackend, FullClient};
24
25/// Frontier DB backend type.
26pub type FrontierBackend<B, C> = fc_db::Backend<B, C>;
27
28pub fn db_config_dir(config: &Configuration) -> PathBuf {
29	config.base_path.config_dir(config.chain_spec.id())
30}
31
32/// Available frontier backend types.
33#[derive(Debug, Copy, Clone, Default, clap::ValueEnum)]
34pub enum BackendType {
35	/// Either RocksDb or ParityDb as per inherited from the global backend settings.
36	#[default]
37	KeyValue,
38	/// Sql database with custom log indexing.
39	Sql,
40}
41
42/// The ethereum-compatibility configuration used to run a node.
43#[derive(Clone, Debug, clap::Parser)]
44pub struct EthConfiguration {
45	/// Maximum number of logs in a query.
46	#[arg(long, default_value = "10000")]
47	pub max_past_logs: u32,
48
49	/// Maximum fee history cache size.
50	#[arg(long, default_value = "2048")]
51	pub fee_history_limit: u64,
52
53	#[arg(long)]
54	pub enable_dev_signer: bool,
55
56	/// The dynamic-fee pallet target gas price set by block author
57	#[arg(long, default_value = "1")]
58	pub target_gas_price: u64,
59
60	/// Maximum allowed gas limit will be `block.gas_limit * execute_gas_limit_multiplier`
61	/// when using eth_call/eth_estimateGas.
62	#[arg(long, default_value = "10")]
63	pub execute_gas_limit_multiplier: u64,
64
65	/// Size in bytes of the LRU cache for block data.
66	#[arg(long, default_value = "50")]
67	pub eth_log_block_cache: usize,
68
69	/// Size in bytes of the LRU cache for transactions statuses data.
70	#[arg(long, default_value = "50")]
71	pub eth_statuses_cache: usize,
72
73	/// Sets the frontier backend type (KeyValue or Sql)
74	#[arg(long, value_enum, ignore_case = true, default_value_t = BackendType::default())]
75	pub frontier_backend_type: BackendType,
76
77	// Sets the SQL backend's pool size.
78	#[arg(long, default_value = "100")]
79	pub frontier_sql_backend_pool_size: u32,
80
81	/// Sets the SQL backend's query timeout in number of VM ops.
82	#[arg(long, default_value = "10000000")]
83	pub frontier_sql_backend_num_ops_timeout: u32,
84
85	/// Sets the SQL backend's auxiliary thread limit.
86	#[arg(long, default_value = "4")]
87	pub frontier_sql_backend_thread_count: u32,
88
89	/// Sets the SQL backend's query timeout in number of VM ops.
90	/// Default value is 200MB.
91	#[arg(long, default_value = "209715200")]
92	pub frontier_sql_backend_cache_size: u64,
93}
94
95pub struct FrontierPartialComponents {
96	pub filter_pool: Option<FilterPool>,
97	pub fee_history_cache: FeeHistoryCache,
98	pub fee_history_cache_limit: FeeHistoryCacheLimit,
99}
100
101pub fn new_frontier_partial(
102	config: &EthConfiguration,
103) -> Result<FrontierPartialComponents, ServiceError> {
104	Ok(FrontierPartialComponents {
105		filter_pool: Some(Arc::new(Mutex::new(BTreeMap::new()))),
106		fee_history_cache: Arc::new(Mutex::new(BTreeMap::new())),
107		fee_history_cache_limit: config.fee_history_limit,
108	})
109}
110
111/// A set of APIs that ethereum-compatible runtimes must implement.
112pub trait EthCompatRuntimeApiCollection<Block: BlockT>:
113	sp_api::ApiExt<Block>
114	+ fp_rpc::ConvertTransactionRuntimeApi<Block>
115	+ fp_rpc::EthereumRuntimeRPCApi<Block>
116{
117}
118
119impl<Block, Api> EthCompatRuntimeApiCollection<Block> for Api
120where
121	Block: BlockT,
122	Api: sp_api::ApiExt<Block>
123		+ fp_rpc::ConvertTransactionRuntimeApi<Block>
124		+ fp_rpc::EthereumRuntimeRPCApi<Block>,
125{
126}
127
128pub async fn spawn_frontier_tasks<B, RA, HF>(
129	task_manager: &TaskManager,
130	client: Arc<FullClient<B, RA, HF>>,
131	backend: Arc<FullBackend<B>>,
132	frontier_backend: Arc<FrontierBackend<B, FullClient<B, RA, HF>>>,
133	filter_pool: Option<FilterPool>,
134	storage_override: Arc<dyn StorageOverride<B>>,
135	fee_history_cache: FeeHistoryCache,
136	fee_history_cache_limit: FeeHistoryCacheLimit,
137	sync: Arc<SyncingService<B>>,
138	pubsub_notification_sinks: Arc<
139		fc_mapping_sync::EthereumBlockNotificationSinks<
140			fc_mapping_sync::EthereumBlockNotification<B>,
141		>,
142	>,
143) where
144	B: BlockT<Hash = H256>,
145	RA: ConstructRuntimeApi<B, FullClient<B, RA, HF>>,
146	RA: Send + Sync + 'static,
147	RA::RuntimeApi: EthCompatRuntimeApiCollection<B>,
148	HF: HostFunctions + 'static,
149{
150	// Spawn main mapping sync worker background task.
151	match &*frontier_backend {
152		fc_db::Backend::KeyValue(b) => {
153			task_manager.spawn_essential_handle().spawn(
154				"frontier-mapping-sync-worker",
155				Some("frontier"),
156				fc_mapping_sync::kv::MappingSyncWorker::new(
157					client.import_notification_stream(),
158					Duration::new(6, 0),
159					client.clone(),
160					backend,
161					storage_override.clone(),
162					b.clone(),
163					3,
164					0u32.into(),
165					fc_mapping_sync::SyncStrategy::Normal,
166					sync,
167					pubsub_notification_sinks,
168				)
169				.for_each(|()| future::ready(())),
170			);
171		}
172		fc_db::Backend::Sql(b) => {
173			task_manager.spawn_essential_handle().spawn_blocking(
174				"frontier-mapping-sync-worker",
175				Some("frontier"),
176				fc_mapping_sync::sql::SyncWorker::run(
177					client.clone(),
178					backend,
179					b.clone(),
180					client.import_notification_stream(),
181					fc_mapping_sync::sql::SyncWorkerConfig {
182						read_notification_timeout: Duration::from_secs(30),
183						check_indexed_blocks_interval: Duration::from_secs(60),
184					},
185					fc_mapping_sync::SyncStrategy::Parachain,
186					sync,
187					pubsub_notification_sinks,
188				),
189			);
190		}
191	}
192
193	// Spawn Frontier EthFilterApi maintenance task.
194	if let Some(filter_pool) = filter_pool {
195		// Each filter is allowed to stay in the pool for 100 blocks.
196		const FILTER_RETAIN_THRESHOLD: u64 = 100;
197		task_manager.spawn_essential_handle().spawn(
198			"frontier-filter-pool",
199			Some("frontier"),
200			EthTask::filter_pool_task(client.clone(), filter_pool, FILTER_RETAIN_THRESHOLD),
201		);
202	}
203
204	// Spawn Frontier FeeHistory cache maintenance task.
205	task_manager.spawn_essential_handle().spawn(
206		"frontier-fee-history",
207		Some("frontier"),
208		EthTask::fee_history_task(
209			client,
210			storage_override,
211			fee_history_cache,
212			fee_history_cache_limit,
213		),
214	);
215}