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

            
62
pub struct MoonbeamEGA;
63

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

            
92
pub struct MoonbeamEthConfig<C, BE>(std::marker::PhantomData<(C, BE)>);
93

            
94
impl<C, BE> fc_rpc::EthConfig<Block, C> for MoonbeamEthConfig<C, BE>
95
where
96
	C: sc_client_api::StorageProvider<Block, BE> + Sync + Send + 'static,
97
	BE: Backend<Block> + 'static,
98
{
99
	type EstimateGasAdapter = MoonbeamEGA;
100
	type RuntimeStorageOverride =
101
		fc_rpc::frontier_backend_client::SystemAccountId20StorageOverride<Block, C, BE>;
102
}
103

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

            
150
pub struct TracingConfig {
151
	pub tracing_requesters: crate::rpc::tracing::RpcRequesters,
152
	pub trace_filter_max_count: u32,
153
	pub max_block_range: u32,
154
}
155

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

            
192
1856
	let mut io = RpcModule::new(());
193
	let FullDeps {
194
1856
		client,
195
1856
		pool,
196
1856
		graph,
197
1856
		is_authority,
198
1856
		network,
199
1856
		sync,
200
1856
		filter_pool,
201
1856
		ethapi_cmd,
202
1856
		command_sink,
203
1856
		frontier_backend,
204
		backend: _,
205
1856
		max_past_logs,
206
1856
		max_block_range,
207
1856
		fee_history_limit,
208
1856
		fee_history_cache,
209
1856
		dev_rpc_data,
210
1856
		overrides,
211
1856
		block_data_cache,
212
1856
		forced_parent_hashes,
213
1856
	} = deps;
214

            
215
1856
	io.merge(System::new(Arc::clone(&client), Arc::clone(&pool)).into_rpc())?;
216
1856
	io.merge(TransactionPayment::new(Arc::clone(&client)).into_rpc())?;
217

            
218
	// TODO: are we supporting signing?
219
1856
	let signers = Vec::new();
220

            
221
	enum Never {}
222
	impl<T> fp_rpc::ConvertTransaction<T> for Never {
223
		fn convert_transaction(&self, _transaction: pallet_ethereum::Transaction) -> T {
224
			// The Never type is not instantiable, but this method requires the type to be
225
			// instantiated to be called (`&self` parameter), so if the code compiles we have the
226
			// guarantee that this function will never be called.
227
			unreachable!()
228
		}
229
	}
230
1856
	let convert_transaction: Option<Never> = None;
231

            
232
	// Need to clone it to avoid moving of `client` variable in closure below.
233
1856
	let client_for_cidp = client.clone();
234

            
235
1856
	let pending_create_inherent_data_providers = move |block, _| {
236
		// Use timestamp in the future
237
8
		let timestamp = sp_timestamp::InherentDataProvider::new(
238
8
			Timestamp::current()
239
8
				.saturating_add(RELAY_CHAIN_SLOT_DURATION_MILLIS.saturating_mul(100))
240
8
				.into(),
241
		);
242

            
243
8
		let maybe_current_para_head = client_for_cidp.expect_header(block);
244
8
		async move {
245
8
			let current_para_block_head = Some(polkadot_primitives::HeadData(
246
8
				maybe_current_para_head?.encode(),
247
			));
248

            
249
8
			let builder = RelayStateSproofBuilder {
250
8
				para_id,
251
8
				// Use a future relay slot (We derive one from the timestamp)
252
8
				current_slot: polkadot_primitives::Slot::from(
253
8
					timestamp
254
8
						.timestamp()
255
8
						.as_millis()
256
8
						.saturating_div(RELAY_CHAIN_SLOT_DURATION_MILLIS),
257
8
				),
258
8
				included_para_head: current_para_block_head,
259
8
				..Default::default()
260
8
			};
261

            
262
			// Create a dummy parachain inherent data provider which is required to pass
263
			// the checks by the para chain system. We use dummy values because in the 'pending context'
264
			// neither do we have access to the real values nor do we need them.
265
8
			let (relay_parent_storage_root, relay_chain_state) =
266
8
				builder.into_state_root_and_proof();
267

            
268
8
			let vfp = PersistedValidationData {
269
8
				// This is a hack to make `cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases`
270
8
				// happy. Relay parent number can't be bigger than u32::MAX.
271
8
				relay_parent_number: u32::MAX,
272
8
				relay_parent_storage_root,
273
8
				..Default::default()
274
8
			};
275
8
			let parachain_inherent_data = ParachainInherentData {
276
8
				validation_data: vfp,
277
8
				relay_chain_state,
278
8
				downward_messages: Default::default(),
279
8
				horizontal_messages: Default::default(),
280
8
				relay_parent_descendants: Default::default(),
281
8
				collator_peer_id: None,
282
8
			};
283

            
284
8
			Ok((timestamp, parachain_inherent_data))
285
8
		}
286
8
	};
287

            
288
1856
	io.merge(
289
1856
		Eth::<_, _, _, _, _, _, MoonbeamEthConfig<_, _>>::new(
290
1856
			Arc::clone(&client.clone()),
291
1856
			Arc::clone(&pool),
292
1856
			convert_transaction,
293
1856
			Arc::clone(&sync),
294
1856
			signers,
295
1856
			Arc::clone(&overrides),
296
1856
			Arc::clone(&frontier_backend),
297
1856
			is_authority,
298
1856
			Arc::clone(&block_data_cache),
299
1856
			fee_history_cache,
300
1856
			fee_history_limit,
301
1856
			10,
302
1856
			false,
303
1856
			forced_parent_hashes,
304
1856
			pending_create_inherent_data_providers,
305
1856
			Some(pending_consenus_data_provider),
306
1856
		)
307
1856
		.replace_config::<MoonbeamEthConfig<C, BE>>()
308
1856
		.into_rpc(),
309
1856
	)?;
310

            
311
1856
	let logs_journal = Arc::new(LogsJournal::new(
312
1856
		subscription_task_executor.clone(),
313
1856
		overrides.clone(),
314
1856
		pubsub_notification_sinks.clone(),
315
	));
316

            
317
1856
	if let Some(filter_pool) = filter_pool {
318
1856
		io.merge(
319
1856
			EthFilter::new(
320
1856
				client.clone(),
321
1856
				frontier_backend.clone(),
322
1856
				graph.clone(),
323
1856
				filter_pool,
324
1856
				500_usize, // max stored filters
325
1856
				max_past_logs,
326
1856
				max_block_range,
327
1856
				block_data_cache,
328
1856
				logs_journal.clone(),
329
1856
			)
330
1856
			.into_rpc(),
331
1856
		)?;
332
	}
333

            
334
1856
	io.merge(
335
1856
		Net::new(
336
1856
			Arc::clone(&client),
337
1856
			network.clone(),
338
1856
			// Whether to format the `peer_count` response as Hex (default) or not.
339
1856
			true,
340
1856
		)
341
1856
		.into_rpc(),
342
1856
	)?;
343

            
344
1856
	io.merge(Web3::new(Arc::clone(&client)).into_rpc())?;
345
1856
	io.merge(
346
1856
		EthPubSub::new(
347
1856
			pool,
348
1856
			Arc::clone(&client),
349
1856
			sync.clone(),
350
1856
			subscription_task_executor,
351
1856
			overrides,
352
1856
			pubsub_notification_sinks.clone(),
353
1856
			logs_journal,
354
1856
		)
355
1856
		.into_rpc(),
356
1856
	)?;
357

            
358
1856
	if ethapi_cmd.contains(&EthApiCmd::Txpool) {
359
1856
		io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
360
	}
361

            
362
1856
	io.merge(MoonbeamFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?;
363

            
364
1856
	if let Some(command_sink) = command_sink {
365
1856
		io.merge(
366
1856
			// We provide the rpc handler with the sending end of the channel to allow the rpc
367
1856
			// send EngineCommands to the background block authorship task.
368
1856
			ManualSeal::new(command_sink).into_rpc(),
369
1856
		)?;
370
	};
371

            
372
1856
	if let Some((downward_message_channel, hrmp_message_channel, additional_relay_offset)) =
373
1856
		dev_rpc_data
374
	{
375
1856
		io.merge(
376
1856
			DevRpc {
377
1856
				downward_message_channel,
378
1856
				hrmp_message_channel,
379
1856
				additional_relay_offset,
380
1856
			}
381
1856
			.into_rpc(),
382
1856
		)?;
383
	}
384

            
385
1856
	if let Some(tracing_config) = maybe_tracing_config {
386
		if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
387
			io.merge(
388
				Trace::new(
389
					client,
390
					trace_filter_requester,
391
					tracing_config.trace_filter_max_count,
392
					tracing_config.max_block_range,
393
				)
394
				.into_rpc(),
395
			)?;
396
		}
397

            
398
		if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
399
			io.merge(Debug::new(debug_requester).into_rpc())?;
400
		}
401
1856
	}
402

            
403
1856
	Ok(io)
404
1856
}
405

            
406
pub struct SpawnTasksParams<'a, B: BlockT, C, BE> {
407
	pub task_manager: &'a TaskManager,
408
	pub client: Arc<C>,
409
	pub substrate_backend: Arc<BE>,
410
	pub frontier_backend: Arc<fc_db::Backend<B, C>>,
411
	pub filter_pool: Option<FilterPool>,
412
	pub overrides: Arc<dyn StorageOverride<B>>,
413
	pub fee_history_limit: u64,
414
	pub fee_history_cache: FeeHistoryCache,
415
	pub state_pruning: Option<PruningMode>,
416
}
417

            
418
/// Spawn the tasks that are required to run Moonbeam.
419
928
pub fn spawn_essential_tasks<B, C, BE>(
420
928
	params: SpawnTasksParams<B, C, BE>,
421
928
	sync: Arc<SyncingService<B>>,
422
928
	pubsub_notification_sinks: Arc<
423
928
		fc_mapping_sync::EthereumBlockNotificationSinks<
424
928
			fc_mapping_sync::EthereumBlockNotification<B>,
425
928
		>,
426
928
	>,
427
928
) where
428
928
	C: ProvideRuntimeApi<B> + BlockOf,
429
928
	C: HeaderBackend<B> + HeaderMetadata<B, Error = BlockChainError> + 'static,
430
928
	C: BlockchainEvents<B> + StorageProvider<B, BE>,
431
928
	C: Send + Sync + 'static,
432
928
	C::Api: EthereumRuntimeRPCApi<B>,
433
928
	C::Api: BlockBuilder<B>,
434
928
	B: BlockT<Hash = H256> + Send + Sync + 'static,
435
928
	B::Header: HeaderT<Number = u32>,
436
928
	BE: Backend<B> + 'static,
437
928
	BE::State: StateBackend<BlakeTwo256>,
438
{
439
	// Frontier offchain DB task. Essential.
440
	// Maps emulated ethereum data to substrate native data.
441
928
	match *params.frontier_backend {
442
928
		fc_db::Backend::KeyValue(ref b) => {
443
928
			params.task_manager.spawn_essential_handle().spawn(
444
				"frontier-mapping-sync-worker",
445
928
				Some("frontier"),
446
928
				MappingSyncWorker::new(
447
928
					params.client.import_notification_stream(),
448
928
					Duration::new(6, 0),
449
928
					params.client.clone(),
450
928
					params.substrate_backend.clone(),
451
928
					params.overrides.clone(),
452
928
					b.clone(),
453
					3,
454
					0,
455
928
					params.state_pruning.and_then(|mode| {
456
						if let PruningMode::Constrained(c) = mode {
457
							c.max_blocks.map(u64::from)
458
						} else {
459
							None
460
						}
461
					}),
462
928
					SyncStrategy::Parachain,
463
928
					sync.clone(),
464
928
					pubsub_notification_sinks.clone(),
465
				)
466
60286
				.for_each(|()| futures::future::ready(())),
467
			);
468
		}
469
		fc_db::Backend::Sql(ref b) => {
470
			params.task_manager.spawn_essential_handle().spawn_blocking(
471
				"frontier-mapping-sync-worker",
472
				Some("frontier"),
473
				fc_mapping_sync::sql::SyncWorker::run(
474
					params.client.clone(),
475
					params.substrate_backend.clone(),
476
					b.clone(),
477
					params.client.import_notification_stream(),
478
					fc_mapping_sync::sql::SyncWorkerConfig {
479
						read_notification_timeout: Duration::from_secs(10),
480
						check_indexed_blocks_interval: Duration::from_secs(60),
481
					},
482
					fc_mapping_sync::SyncStrategy::Parachain,
483
					sync.clone(),
484
					pubsub_notification_sinks.clone(),
485
				),
486
			);
487
		}
488
	}
489

            
490
	// Frontier `EthFilterApi` maintenance.
491
	// Manages the pool of user-created Filters.
492
928
	if let Some(filter_pool) = params.filter_pool {
493
		// Each filter is allowed to stay in the pool for 100 blocks.
494
		const FILTER_RETAIN_THRESHOLD: u64 = 100;
495
928
		params.task_manager.spawn_essential_handle().spawn(
496
			"frontier-filter-pool",
497
928
			Some("frontier"),
498
928
			EthTask::filter_pool_task(
499
928
				Arc::clone(&params.client),
500
928
				filter_pool,
501
				FILTER_RETAIN_THRESHOLD,
502
			),
503
		);
504
	}
505

            
506
	// Spawn Frontier FeeHistory cache maintenance task.
507
928
	params.task_manager.spawn_essential_handle().spawn(
508
		"frontier-fee-history",
509
928
		Some("frontier"),
510
928
		EthTask::fee_history_task(
511
928
			Arc::clone(&params.client),
512
928
			Arc::clone(&params.overrides),
513
928
			params.fee_history_cache,
514
928
			params.fee_history_limit,
515
		),
516
	);
517
928
}