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_api::TransactionPool;
48
use sp_api::{CallApiAt, ProvideRuntimeApi};
49
use sp_blockchain::{
50
	Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata,
51
};
52
use sp_core::H256;
53
use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT};
54
use std::collections::BTreeMap;
55

            
56
pub struct MoonbeamEGA;
57

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
313
1868
	io.merge(MoonbeamFinality::new(client.clone(), frontier_backend.clone()).into_rpc())?;
314

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

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

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

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

            
353
1868
	Ok(io)
354
1868
}
355

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

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

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

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