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

            
59
pub struct MoonbeamEGA;
60

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

            
89
pub struct MoonbeamEthConfig<C, BE>(std::marker::PhantomData<(C, BE)>);
90

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

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

            
147
pub struct TracingConfig {
148
	pub tracing_requesters: crate::rpc::tracing::RpcRequesters,
149
	pub trace_filter_max_count: u32,
150
}
151

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

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

            
211
1848
	io.merge(System::new(Arc::clone(&client), Arc::clone(&pool)).into_rpc())?;
212
1848
	io.merge(TransactionPayment::new(Arc::clone(&client)).into_rpc())?;
213

            
214
	// TODO: are we supporting signing?
215
1848
	let signers = Vec::new();
216

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

            
228
1848
	// Need to clone it to avoid moving of `client` variable in closure below.
229
1848
	let client_for_cidp = client.clone();
230
1848

            
231
1848
	let pending_create_inherent_data_providers = move |block, _| {
232
8
		// Use timestamp in the future
233
8
		let timestamp = sp_timestamp::InherentDataProvider::new(
234
8
			Timestamp::current()
235
8
				.saturating_add(RELAY_CHAIN_SLOT_DURATION_MILLIS.saturating_mul(100))
236
8
				.into(),
237
8
		);
238
8

            
239
8
		let maybe_current_para_head = client_for_cidp.expect_header(block);
240
8
		async move {
241
8
			let current_para_block_head = Some(polkadot_primitives::HeadData(
242
8
				maybe_current_para_head?.encode(),
243
8
			));
244
8

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

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

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

            
278
8
			Ok((timestamp, parachain_inherent_data))
279
8
		}
280
8
	};
281

            
282
1848
	io.merge(
283
1848
		Eth::<_, _, _, _, _, _, MoonbeamEthConfig<_, _>>::new(
284
1848
			Arc::clone(&client.clone()),
285
1848
			Arc::clone(&pool),
286
1848
			graph.clone(),
287
1848
			convert_transaction,
288
1848
			Arc::clone(&sync),
289
1848
			signers,
290
1848
			Arc::clone(&overrides),
291
1848
			Arc::clone(&frontier_backend),
292
1848
			is_authority,
293
1848
			Arc::clone(&block_data_cache),
294
1848
			fee_history_cache,
295
1848
			fee_history_limit,
296
1848
			10,
297
1848
			forced_parent_hashes,
298
1848
			pending_create_inherent_data_providers,
299
1848
			Some(pending_consenus_data_provider),
300
1848
		)
301
1848
		.replace_config::<MoonbeamEthConfig<C, BE>>()
302
1848
		.into_rpc(),
303
1848
	)?;
304

            
305
1848
	if let Some(filter_pool) = filter_pool {
306
1848
		io.merge(
307
1848
			EthFilter::new(
308
1848
				client.clone(),
309
1848
				frontier_backend.clone(),
310
1848
				graph.clone(),
311
1848
				filter_pool,
312
1848
				500_usize, // max stored filters
313
1848
				max_past_logs,
314
1848
				max_block_range,
315
1848
				block_data_cache,
316
1848
			)
317
1848
			.into_rpc(),
318
1848
		)?;
319
	}
320

            
321
1848
	io.merge(
322
1848
		Net::new(
323
1848
			Arc::clone(&client),
324
1848
			network.clone(),
325
1848
			// Whether to format the `peer_count` response as Hex (default) or not.
326
1848
			true,
327
1848
		)
328
1848
		.into_rpc(),
329
1848
	)?;
330

            
331
1848
	io.merge(Web3::new(Arc::clone(&client)).into_rpc())?;
332
1848
	io.merge(
333
1848
		EthPubSub::new(
334
1848
			pool,
335
1848
			Arc::clone(&client),
336
1848
			sync.clone(),
337
1848
			subscription_task_executor,
338
1848
			overrides,
339
1848
			pubsub_notification_sinks.clone(),
340
1848
		)
341
1848
		.into_rpc(),
342
1848
	)?;
343

            
344
1848
	if ethapi_cmd.contains(&EthApiCmd::Txpool) {
345
1848
		io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
346
	}
347

            
348
1848
	io.merge(MoonbeamFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?;
349

            
350
1848
	if let Some(command_sink) = command_sink {
351
1848
		io.merge(
352
1848
			// We provide the rpc handler with the sending end of the channel to allow the rpc
353
1848
			// send EngineCommands to the background block authorship task.
354
1848
			ManualSeal::new(command_sink).into_rpc(),
355
1848
		)?;
356
	};
357

            
358
1848
	if let Some((downward_message_channel, hrmp_message_channel, additional_relay_offset)) =
359
1848
		dev_rpc_data
360
	{
361
1848
		io.merge(
362
1848
			DevRpc {
363
1848
				downward_message_channel,
364
1848
				hrmp_message_channel,
365
1848
				additional_relay_offset,
366
1848
			}
367
1848
			.into_rpc(),
368
1848
		)?;
369
	}
370

            
371
1848
	if let Some(tracing_config) = maybe_tracing_config {
372
		if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
373
			io.merge(
374
				Trace::new(
375
					client,
376
					trace_filter_requester,
377
					tracing_config.trace_filter_max_count,
378
				)
379
				.into_rpc(),
380
			)?;
381
		}
382

            
383
		if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
384
			io.merge(Debug::new(debug_requester).into_rpc())?;
385
		}
386
1848
	}
387

            
388
1848
	Ok(io)
389
1848
}
390

            
391
pub struct SpawnTasksParams<'a, B: BlockT, C, BE> {
392
	pub task_manager: &'a TaskManager,
393
	pub client: Arc<C>,
394
	pub substrate_backend: Arc<BE>,
395
	pub frontier_backend: Arc<fc_db::Backend<B, C>>,
396
	pub filter_pool: Option<FilterPool>,
397
	pub overrides: Arc<dyn StorageOverride<B>>,
398
	pub fee_history_limit: u64,
399
	pub fee_history_cache: FeeHistoryCache,
400
}
401

            
402
/// Spawn the tasks that are required to run Moonbeam.
403
924
pub fn spawn_essential_tasks<B, C, BE>(
404
924
	params: SpawnTasksParams<B, C, BE>,
405
924
	sync: Arc<SyncingService<B>>,
406
924
	pubsub_notification_sinks: Arc<
407
924
		fc_mapping_sync::EthereumBlockNotificationSinks<
408
924
			fc_mapping_sync::EthereumBlockNotification<B>,
409
924
		>,
410
924
	>,
411
924
) where
412
924
	C: ProvideRuntimeApi<B> + BlockOf,
413
924
	C: HeaderBackend<B> + HeaderMetadata<B, Error = BlockChainError> + 'static,
414
924
	C: BlockchainEvents<B> + StorageProvider<B, BE>,
415
924
	C: Send + Sync + 'static,
416
924
	C::Api: EthereumRuntimeRPCApi<B>,
417
924
	C::Api: BlockBuilder<B>,
418
924
	B: BlockT<Hash = H256> + Send + Sync + 'static,
419
924
	B::Header: HeaderT<Number = u32>,
420
924
	BE: Backend<B> + 'static,
421
924
	BE::State: StateBackend<BlakeTwo256>,
422
924
{
423
924
	// Frontier offchain DB task. Essential.
424
924
	// Maps emulated ethereum data to substrate native data.
425
924
	match *params.frontier_backend {
426
924
		fc_db::Backend::KeyValue(ref b) => {
427
924
			params.task_manager.spawn_essential_handle().spawn(
428
924
				"frontier-mapping-sync-worker",
429
924
				Some("frontier"),
430
924
				MappingSyncWorker::new(
431
924
					params.client.import_notification_stream(),
432
924
					Duration::new(6, 0),
433
924
					params.client.clone(),
434
924
					params.substrate_backend.clone(),
435
924
					params.overrides.clone(),
436
924
					b.clone(),
437
924
					3,
438
924
					0,
439
924
					SyncStrategy::Parachain,
440
924
					sync.clone(),
441
924
					pubsub_notification_sinks.clone(),
442
924
				)
443
59606
				.for_each(|()| futures::future::ready(())),
444
924
			);
445
924
		}
446
		fc_db::Backend::Sql(ref b) => {
447
			params.task_manager.spawn_essential_handle().spawn_blocking(
448
				"frontier-mapping-sync-worker",
449
				Some("frontier"),
450
				fc_mapping_sync::sql::SyncWorker::run(
451
					params.client.clone(),
452
					params.substrate_backend.clone(),
453
					b.clone(),
454
					params.client.import_notification_stream(),
455
					fc_mapping_sync::sql::SyncWorkerConfig {
456
						read_notification_timeout: Duration::from_secs(10),
457
						check_indexed_blocks_interval: Duration::from_secs(60),
458
					},
459
					fc_mapping_sync::SyncStrategy::Parachain,
460
					sync.clone(),
461
					pubsub_notification_sinks.clone(),
462
				),
463
			);
464
		}
465
	}
466

            
467
	// Frontier `EthFilterApi` maintenance.
468
	// Manages the pool of user-created Filters.
469
924
	if let Some(filter_pool) = params.filter_pool {
470
924
		// Each filter is allowed to stay in the pool for 100 blocks.
471
924
		const FILTER_RETAIN_THRESHOLD: u64 = 100;
472
924
		params.task_manager.spawn_essential_handle().spawn(
473
924
			"frontier-filter-pool",
474
924
			Some("frontier"),
475
924
			EthTask::filter_pool_task(
476
924
				Arc::clone(&params.client),
477
924
				filter_pool,
478
924
				FILTER_RETAIN_THRESHOLD,
479
924
			),
480
924
		);
481
924
	}
482

            
483
	// Spawn Frontier FeeHistory cache maintenance task.
484
924
	params.task_manager.spawn_essential_handle().spawn(
485
924
		"frontier-fee-history",
486
924
		Some("frontier"),
487
924
		EthTask::fee_history_task(
488
924
			Arc::clone(&params.client),
489
924
			Arc::clone(&params.overrides),
490
924
			params.fee_history_cache,
491
924
			params.fee_history_limit,
492
924
		),
493
924
	);
494
924
}