1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! A collection of node-specific RPC extensions and related background tasks.
18

            
19
pub mod tracing;
20

            
21
use std::{sync::Arc, time::Duration};
22

            
23
use fp_rpc::EthereumRuntimeRPCApi;
24
use sp_block_builder::BlockBuilder;
25

            
26
use crate::client::RuntimeApiCollection;
27
use cumulus_primitives_core::{ParaId, PersistedValidationData};
28
use cumulus_primitives_parachain_inherent::ParachainInherentData;
29
use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
30
use fc_mapping_sync::{kv::MappingSyncWorker, SyncStrategy};
31
use fc_rpc::{pending::ConsensusDataProvider, EthBlockDataCacheTask, EthTask, StorageOverride};
32
use fc_rpc_core::types::{FeeHistoryCache, FilterPool, TransactionRequest};
33
use futures::StreamExt;
34
use jsonrpsee::RpcModule;
35
use moonbeam_cli_opt::EthApi as EthApiCmd;
36
use moonbeam_core_primitives::{Block, Hash};
37
use sc_client_api::{
38
	backend::{AuxStore, Backend, StateBackend, StorageProvider},
39
	client::BlockchainEvents,
40
	BlockOf,
41
};
42
use sc_consensus_manual_seal::rpc::{EngineCommand, ManualSeal, ManualSealApiServer};
43
use sc_network::service::traits::NetworkService;
44
use sc_network_sync::SyncingService;
45
use sc_rpc::SubscriptionTaskExecutor;
46
use sc_service::TaskManager;
47
use sc_transaction_pool::{ChainApi, Pool};
48
use sc_transaction_pool_api::TransactionPool;
49
use sp_api::{CallApiAt, ProvideRuntimeApi};
50
use sp_blockchain::{
51
	Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata,
52
};
53
use sp_core::H256;
54
use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT};
55
use std::collections::BTreeMap;
56

            
57
pub struct MoonbeamEGA;
58

            
59
impl fc_rpc::EstimateGasAdapter for MoonbeamEGA {
60
8086
	fn adapt_request(mut request: TransactionRequest) -> TransactionRequest {
61
8086
		// Redirect any call to batch precompile:
62
8086
		// force usage of batchAll method for estimation
63
8086
		use sp_core::H160;
64
8086
		const BATCH_PRECOMPILE_ADDRESS: H160 = H160(hex_literal::hex!(
65
8086
			"0000000000000000000000000000000000000808"
66
8086
		));
67
8086
		const BATCH_PRECOMPILE_BATCH_ALL_SELECTOR: [u8; 4] = hex_literal::hex!("96e292b8");
68
8086
		if request.to == Some(BATCH_PRECOMPILE_ADDRESS) {
69
18
			match (&mut request.data.input, &mut request.data.data) {
70
				(Some(ref mut input), _) => {
71
					if input.0.len() >= 4 {
72
						input.0[..4].copy_from_slice(&BATCH_PRECOMPILE_BATCH_ALL_SELECTOR);
73
					}
74
				}
75
18
				(None, Some(ref mut data)) => {
76
18
					if data.0.len() >= 4 {
77
18
						data.0[..4].copy_from_slice(&BATCH_PRECOMPILE_BATCH_ALL_SELECTOR);
78
18
					}
79
				}
80
				(_, _) => {}
81
			};
82
8068
		}
83
8086
		request
84
8086
	}
85
}
86

            
87
pub struct MoonbeamEthConfig<C, BE>(std::marker::PhantomData<(C, BE)>);
88

            
89
impl<C, BE> fc_rpc::EthConfig<Block, C> for MoonbeamEthConfig<C, BE>
90
where
91
	C: sc_client_api::StorageProvider<Block, BE> + Sync + Send + 'static,
92
	BE: Backend<Block> + 'static,
93
{
94
	type EstimateGasAdapter = MoonbeamEGA;
95
	type RuntimeStorageOverride =
96
		fc_rpc::frontier_backend_client::SystemAccountId20StorageOverride<Block, C, BE>;
97
}
98

            
99
/// Full client dependencies.
100
pub struct FullDeps<C, P, A: ChainApi, BE> {
101
	/// The client instance to use.
102
	pub client: Arc<C>,
103
	/// Transaction pool instance.
104
	pub pool: Arc<P>,
105
	/// Graph pool instance.
106
	pub graph: Arc<Pool<A>>,
107
	/// The Node authority flag
108
	pub is_authority: bool,
109
	/// Network service
110
	pub network: Arc<dyn NetworkService>,
111
	/// Chain syncing service
112
	pub sync: Arc<SyncingService<Block>>,
113
	/// EthFilterApi pool.
114
	pub filter_pool: Option<FilterPool>,
115
	/// The list of optional RPC extensions.
116
	pub ethapi_cmd: Vec<EthApiCmd>,
117
	/// Frontier Backend.
118
	pub frontier_backend: Arc<dyn fc_api::Backend<Block>>,
119
	/// Backend.
120
	pub backend: Arc<BE>,
121
	/// Manual seal command sink
122
	pub command_sink: Option<futures::channel::mpsc::Sender<EngineCommand<Hash>>>,
123
	/// Maximum number of logs in a query.
124
	pub max_past_logs: u32,
125
	/// Maximum fee history cache size.
126
	pub fee_history_limit: u64,
127
	/// Fee history cache.
128
	pub fee_history_cache: FeeHistoryCache,
129
	/// Channels for manual xcm messages (downward, hrmp)
130
	pub dev_rpc_data: Option<(
131
		flume::Sender<Vec<u8>>,
132
		flume::Sender<(ParaId, Vec<u8>)>,
133
		Arc<std::sync::atomic::AtomicU32>,
134
	)>,
135
	/// Ethereum data access overrides.
136
	pub overrides: Arc<dyn StorageOverride<Block>>,
137
	/// Cache for Ethereum block data.
138
	pub block_data_cache: Arc<EthBlockDataCacheTask<Block>>,
139
	/// Mandated parent hashes for a given block hash.
140
	pub forced_parent_hashes: Option<BTreeMap<H256, H256>>,
141
}
142

            
143
pub struct TracingConfig {
144
	pub tracing_requesters: crate::rpc::tracing::RpcRequesters,
145
	pub trace_filter_max_count: u32,
146
}
147

            
148
/// Instantiate all Full RPC extensions.
149
1860
pub fn create_full<C, P, BE, A>(
150
1860
	deps: FullDeps<C, P, A, BE>,
151
1860
	subscription_task_executor: SubscriptionTaskExecutor,
152
1860
	maybe_tracing_config: Option<TracingConfig>,
153
1860
	pubsub_notification_sinks: Arc<
154
1860
		fc_mapping_sync::EthereumBlockNotificationSinks<
155
1860
			fc_mapping_sync::EthereumBlockNotification<Block>,
156
1860
		>,
157
1860
	>,
158
1860
	pending_consenus_data_provider: Box<dyn ConsensusDataProvider<Block>>,
159
1860
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
160
1860
where
161
1860
	BE: Backend<Block> + 'static,
162
1860
	BE::State: StateBackend<BlakeTwo256>,
163
1860
	BE::Blockchain: BlockchainBackend<Block>,
164
1860
	C: ProvideRuntimeApi<Block> + StorageProvider<Block, BE> + AuxStore,
165
1860
	C: BlockchainEvents<Block>,
166
1860
	C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static,
167
1860
	C: CallApiAt<Block>,
168
1860
	C: Send + Sync + 'static,
169
1860
	A: ChainApi<Block = Block> + 'static,
170
1860
	C::Api: RuntimeApiCollection,
171
1860
	P: TransactionPool<Block = Block> + 'static,
172
1860
{
173
1860
	use fc_rpc::{
174
1860
		Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net,
175
1860
		NetApiServer, TxPool, TxPoolApiServer, Web3, Web3ApiServer,
176
1860
	};
177
1860
	use moonbeam_dev_rpc::{DevApiServer, DevRpc};
178
1860
	use moonbeam_finality_rpc::{MoonbeamFinality, MoonbeamFinalityApiServer};
179
1860
	use moonbeam_rpc_debug::{Debug, DebugServer};
180
1860
	use moonbeam_rpc_trace::{Trace, TraceServer};
181
1860
	use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
182
1860
	use substrate_frame_rpc_system::{System, SystemApiServer};
183
1860

            
184
1860
	let mut io = RpcModule::new(());
185
1860
	let FullDeps {
186
1860
		client,
187
1860
		pool,
188
1860
		graph,
189
1860
		is_authority,
190
1860
		network,
191
1860
		sync,
192
1860
		filter_pool,
193
1860
		ethapi_cmd,
194
1860
		command_sink,
195
1860
		frontier_backend,
196
1860
		backend: _,
197
1860
		max_past_logs,
198
1860
		fee_history_limit,
199
1860
		fee_history_cache,
200
1860
		dev_rpc_data,
201
1860
		overrides,
202
1860
		block_data_cache,
203
1860
		forced_parent_hashes,
204
1860
	} = deps;
205
1860

            
206
1860
	io.merge(System::new(Arc::clone(&client), Arc::clone(&pool)).into_rpc())?;
207
1860
	io.merge(TransactionPayment::new(Arc::clone(&client)).into_rpc())?;
208

            
209
	// TODO: are we supporting signing?
210
1860
	let signers = Vec::new();
211
1860

            
212
1860
	enum Never {}
213
1860
	impl<T> fp_rpc::ConvertTransaction<T> for Never {
214
1860
		fn convert_transaction(&self, _transaction: pallet_ethereum::Transaction) -> T {
215
			// The Never type is not instantiable, but this method requires the type to be
216
			// instantiated to be called (`&self` parameter), so if the code compiles we have the
217
			// guarantee that this function will never be called.
218
			unreachable!()
219
1860
		}
220
1860
	}
221
1860
	let convert_transaction: Option<Never> = None;
222
1860

            
223
1860
	let pending_create_inherent_data_providers = move |_, _| async move {
224
8
		let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
225
8
		// Create a dummy parachain inherent data provider which is required to pass
226
8
		// the checks by the para chain system. We use dummy values because in the 'pending context'
227
8
		// neither do we have access to the real values nor do we need them.
228
8
		let (relay_parent_storage_root, relay_chain_state) =
229
8
			RelayStateSproofBuilder::default().into_state_root_and_proof();
230
8
		let vfp = PersistedValidationData {
231
8
			// This is a hack to make `cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases`
232
8
			// happy. Relay parent number can't be bigger than u32::MAX.
233
8
			relay_parent_number: u32::MAX,
234
8
			relay_parent_storage_root,
235
8
			..Default::default()
236
8
		};
237
8
		let parachain_inherent_data = ParachainInherentData {
238
8
			validation_data: vfp,
239
8
			relay_chain_state,
240
8
			downward_messages: Default::default(),
241
8
			horizontal_messages: Default::default(),
242
8
		};
243
8
		Ok((timestamp, parachain_inherent_data))
244
8
	};
245

            
246
1860
	io.merge(
247
1860
		Eth::<_, _, _, _, _, _, _, MoonbeamEthConfig<_, _>>::new(
248
1860
			Arc::clone(&client),
249
1860
			Arc::clone(&pool),
250
1860
			graph.clone(),
251
1860
			convert_transaction,
252
1860
			Arc::clone(&sync),
253
1860
			signers,
254
1860
			Arc::clone(&overrides),
255
1860
			Arc::clone(&frontier_backend),
256
1860
			is_authority,
257
1860
			Arc::clone(&block_data_cache),
258
1860
			fee_history_cache,
259
1860
			fee_history_limit,
260
1860
			10,
261
1860
			forced_parent_hashes,
262
1860
			pending_create_inherent_data_providers,
263
1860
			Some(pending_consenus_data_provider),
264
1860
		)
265
1860
		.replace_config::<MoonbeamEthConfig<C, BE>>()
266
1860
		.into_rpc(),
267
1860
	)?;
268

            
269
1860
	if let Some(filter_pool) = filter_pool {
270
1860
		io.merge(
271
1860
			EthFilter::new(
272
1860
				client.clone(),
273
1860
				frontier_backend.clone(),
274
1860
				graph.clone(),
275
1860
				filter_pool,
276
1860
				500_usize, // max stored filters
277
1860
				max_past_logs,
278
1860
				block_data_cache,
279
1860
			)
280
1860
			.into_rpc(),
281
1860
		)?;
282
	}
283

            
284
1860
	io.merge(
285
1860
		Net::new(
286
1860
			Arc::clone(&client),
287
1860
			network.clone(),
288
1860
			// Whether to format the `peer_count` response as Hex (default) or not.
289
1860
			true,
290
1860
		)
291
1860
		.into_rpc(),
292
1860
	)?;
293

            
294
1860
	io.merge(Web3::new(Arc::clone(&client)).into_rpc())?;
295
1860
	io.merge(
296
1860
		EthPubSub::new(
297
1860
			pool,
298
1860
			Arc::clone(&client),
299
1860
			sync.clone(),
300
1860
			subscription_task_executor,
301
1860
			overrides,
302
1860
			pubsub_notification_sinks.clone(),
303
1860
		)
304
1860
		.into_rpc(),
305
1860
	)?;
306

            
307
1860
	if ethapi_cmd.contains(&EthApiCmd::Txpool) {
308
1860
		io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
309
	}
310

            
311
1860
	io.merge(MoonbeamFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?;
312

            
313
1860
	if let Some(command_sink) = command_sink {
314
1860
		io.merge(
315
1860
			// We provide the rpc handler with the sending end of the channel to allow the rpc
316
1860
			// send EngineCommands to the background block authorship task.
317
1860
			ManualSeal::new(command_sink).into_rpc(),
318
1860
		)?;
319
	};
320

            
321
1860
	if let Some((downward_message_channel, hrmp_message_channel, additional_relay_offset)) =
322
1860
		dev_rpc_data
323
	{
324
1860
		io.merge(
325
1860
			DevRpc {
326
1860
				downward_message_channel,
327
1860
				hrmp_message_channel,
328
1860
				additional_relay_offset,
329
1860
			}
330
1860
			.into_rpc(),
331
1860
		)?;
332
	}
333

            
334
1860
	if let Some(tracing_config) = maybe_tracing_config {
335
		if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
336
			io.merge(
337
				Trace::new(
338
					client,
339
					trace_filter_requester,
340
					tracing_config.trace_filter_max_count,
341
				)
342
				.into_rpc(),
343
			)?;
344
		}
345

            
346
		if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
347
			io.merge(Debug::new(debug_requester).into_rpc())?;
348
		}
349
1860
	}
350

            
351
1860
	Ok(io)
352
1860
}
353

            
354
pub struct SpawnTasksParams<'a, B: BlockT, C, BE> {
355
	pub task_manager: &'a TaskManager,
356
	pub client: Arc<C>,
357
	pub substrate_backend: Arc<BE>,
358
	pub frontier_backend: Arc<fc_db::Backend<B, C>>,
359
	pub filter_pool: Option<FilterPool>,
360
	pub overrides: Arc<dyn StorageOverride<B>>,
361
	pub fee_history_limit: u64,
362
	pub fee_history_cache: FeeHistoryCache,
363
}
364

            
365
/// Spawn the tasks that are required to run Moonbeam.
366
930
pub fn spawn_essential_tasks<B, C, BE>(
367
930
	params: SpawnTasksParams<B, C, BE>,
368
930
	sync: Arc<SyncingService<B>>,
369
930
	pubsub_notification_sinks: Arc<
370
930
		fc_mapping_sync::EthereumBlockNotificationSinks<
371
930
			fc_mapping_sync::EthereumBlockNotification<B>,
372
930
		>,
373
930
	>,
374
930
) where
375
930
	C: ProvideRuntimeApi<B> + BlockOf,
376
930
	C: HeaderBackend<B> + HeaderMetadata<B, Error = BlockChainError> + 'static,
377
930
	C: BlockchainEvents<B> + StorageProvider<B, BE>,
378
930
	C: Send + Sync + 'static,
379
930
	C::Api: EthereumRuntimeRPCApi<B>,
380
930
	C::Api: BlockBuilder<B>,
381
930
	B: BlockT<Hash = H256> + Send + Sync + 'static,
382
930
	B::Header: HeaderT<Number = u32>,
383
930
	BE: Backend<B> + 'static,
384
930
	BE::State: StateBackend<BlakeTwo256>,
385
930
{
386
930
	// Frontier offchain DB task. Essential.
387
930
	// Maps emulated ethereum data to substrate native data.
388
930
	match *params.frontier_backend {
389
930
		fc_db::Backend::KeyValue(ref b) => {
390
930
			params.task_manager.spawn_essential_handle().spawn(
391
930
				"frontier-mapping-sync-worker",
392
930
				Some("frontier"),
393
930
				MappingSyncWorker::new(
394
930
					params.client.import_notification_stream(),
395
930
					Duration::new(6, 0),
396
930
					params.client.clone(),
397
930
					params.substrate_backend.clone(),
398
930
					params.overrides.clone(),
399
930
					b.clone(),
400
930
					3,
401
930
					0,
402
930
					SyncStrategy::Parachain,
403
930
					sync.clone(),
404
930
					pubsub_notification_sinks.clone(),
405
930
				)
406
55534
				.for_each(|()| futures::future::ready(())),
407
930
			);
408
930
		}
409
		fc_db::Backend::Sql(ref b) => {
410
			params.task_manager.spawn_essential_handle().spawn_blocking(
411
				"frontier-mapping-sync-worker",
412
				Some("frontier"),
413
				fc_mapping_sync::sql::SyncWorker::run(
414
					params.client.clone(),
415
					params.substrate_backend.clone(),
416
					b.clone(),
417
					params.client.import_notification_stream(),
418
					fc_mapping_sync::sql::SyncWorkerConfig {
419
						read_notification_timeout: Duration::from_secs(10),
420
						check_indexed_blocks_interval: Duration::from_secs(60),
421
					},
422
					fc_mapping_sync::SyncStrategy::Parachain,
423
					sync.clone(),
424
					pubsub_notification_sinks.clone(),
425
				),
426
			);
427
		}
428
	}
429

            
430
	// Frontier `EthFilterApi` maintenance.
431
	// Manages the pool of user-created Filters.
432
930
	if let Some(filter_pool) = params.filter_pool {
433
930
		// Each filter is allowed to stay in the pool for 100 blocks.
434
930
		const FILTER_RETAIN_THRESHOLD: u64 = 100;
435
930
		params.task_manager.spawn_essential_handle().spawn(
436
930
			"frontier-filter-pool",
437
930
			Some("frontier"),
438
930
			EthTask::filter_pool_task(
439
930
				Arc::clone(&params.client),
440
930
				filter_pool,
441
930
				FILTER_RETAIN_THRESHOLD,
442
930
			),
443
930
		);
444
930
	}
445

            
446
	// Spawn Frontier FeeHistory cache maintenance task.
447
930
	params.task_manager.spawn_essential_handle().spawn(
448
930
		"frontier-fee-history",
449
930
		Some("frontier"),
450
930
		EthTask::fee_history_task(
451
930
			Arc::clone(&params.client),
452
930
			Arc::clone(&params.overrides),
453
930
			params.fee_history_cache,
454
930
			params.fee_history_limit,
455
930
		),
456
930
	);
457
930
}