astar_collator/
rpc.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19//! Astar RPCs implementation.
20
21use fc_rpc::{
22    Eth, EthApiServer, EthBlockDataCacheTask, EthFilter, EthFilterApiServer, EthPubSub,
23    EthPubSubApiServer, LogsJournal, Net, NetApiServer, TxPool, TxPoolApiServer, Web3,
24    Web3ApiServer,
25};
26use fc_rpc_core::types::{FeeHistoryCache, FilterPool};
27use fc_storage::{StorageOverride, StorageOverrideHandler};
28use jsonrpsee::RpcModule;
29use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
30use std::path::Path;
31
32use cumulus_primitives_core::{ParaId, RelayParentOffsetApi};
33use sc_client_api::{
34    AuxStore, Backend, BlockchainEvents, StateBackend, StorageProvider, UsageProvider,
35};
36use sc_consensus_manual_seal::rpc::ManualSealApiServer;
37use sc_network::service::traits::NetworkService;
38use sc_network_sync::SyncingService;
39use sc_rpc::dev::DevApiServer;
40pub use sc_rpc::SubscriptionTaskExecutor;
41use sc_transaction_pool_api::TransactionPool;
42use sp_api::{CallApiAt, ProvideRuntimeApi};
43use sp_block_builder::BlockBuilder;
44use sp_blockchain::{
45    Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata,
46};
47use sp_consensus_aura::{sr25519::AuthorityId as AuraId, AuraApi};
48use sp_inherents::CreateInherentDataProviders;
49use sp_runtime::traits::{BlakeTwo256, Block as BlockT};
50use std::sync::Arc;
51use substrate_frame_rpc_system::{System, SystemApiServer};
52
53use moonbeam_rpc_debug::{Debug, DebugServer};
54use moonbeam_rpc_trace::{Trace, TraceServer};
55
56use crate::evm_tracing_types::{FrontierBackendConfig, FrontierConfig};
57use astar_primitives::*;
58
59pub mod tracing;
60
61type HashFor<Block> = <Block as BlockT>::Hash;
62
63#[derive(Clone)]
64pub struct EvmTracingConfig {
65    pub tracing_requesters: tracing::RpcRequesters,
66    pub trace_filter_max_count: u32,
67    pub trace_filter_max_block_range: u32,
68    pub enable_txpool: bool,
69}
70
71/// Available frontier backend types.
72#[derive(Debug, Copy, Clone, Default, clap::ValueEnum)]
73pub enum FrontierBackendType {
74    /// RocksDb KV database.
75    #[default]
76    KeyValue,
77    /// SQL database with custom log indexing.
78    Sql,
79}
80
81// TODO This is copied from frontier. It should be imported instead after
82// https://github.com/paritytech/frontier/issues/333 is solved
83pub fn open_frontier_backend<C, BE>(
84    client: Arc<C>,
85    config: &sc_service::Configuration,
86    rpc_config: &FrontierConfig,
87) -> Result<fc_db::Backend<Block, C>, String>
88where
89    C: ProvideRuntimeApi<Block> + StorageProvider<Block, BE> + AuxStore,
90    C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError>,
91    C: Send + Sync + 'static,
92    C::Api: fp_rpc::EthereumRuntimeRPCApi<Block>,
93    BE: Backend<Block> + 'static,
94    BE::State: StateBackend<BlakeTwo256>,
95{
96    let config_dir = config.base_path.config_dir(config.chain_spec.id());
97    let path = config_dir.join("frontier").join("db");
98
99    let frontier_backend = match rpc_config.frontier_backend_config {
100        FrontierBackendConfig::KeyValue => {
101            fc_db::Backend::KeyValue(Arc::new(fc_db::kv::Backend::<Block, C>::new(
102                client,
103                &fc_db::kv::DatabaseSettings {
104                    source: fc_db::DatabaseSource::RocksDb {
105                        path,
106                        cache_size: 0,
107                    },
108                },
109            )?))
110        }
111        FrontierBackendConfig::Sql {
112            pool_size,
113            num_ops_timeout,
114            thread_count,
115            cache_size,
116        } => {
117            let overrides = Arc::new(StorageOverrideHandler::new(client.clone()));
118            std::fs::create_dir_all(&path).expect("failed creating sql db directory");
119            let backend = futures::executor::block_on(fc_db::sql::Backend::new(
120                fc_db::sql::BackendConfig::Sqlite(fc_db::sql::SqliteBackendConfig {
121                    path: Path::new("sqlite:///")
122                        .join(path)
123                        .join("frontier.db3")
124                        .to_str()
125                        .expect("frontier sql path error"),
126                    create_if_missing: true,
127                    thread_count: thread_count,
128                    cache_size: cache_size,
129                }),
130                pool_size,
131                std::num::NonZeroU32::new(num_ops_timeout),
132                overrides.clone(),
133            ))
134            .unwrap_or_else(|err| panic!("failed creating sql backend: {:?}", err));
135            fc_db::Backend::Sql(Arc::new(backend))
136        }
137    };
138
139    Ok(frontier_backend)
140}
141
142pub struct AstarEthConfig<C, BE>(std::marker::PhantomData<(C, BE)>);
143
144impl<C, BE> fc_rpc::EthConfig<Block, C> for AstarEthConfig<C, BE>
145where
146    C: sc_client_api::StorageProvider<Block, BE> + Sync + Send + 'static,
147    BE: Backend<Block> + 'static,
148{
149    // Use to override (adapt) evm call to precompiles for proper gas estimation.
150    // We are not aware of any of our precompile that require this.
151    type EstimateGasAdapter = ();
152    // This assumes the use of HashedMapping<BlakeTwo256> for address mapping
153    type RuntimeStorageOverride =
154        fc_rpc::frontier_backend_client::SystemAccountId32StorageOverride<Block, C, BE>;
155}
156
157/// Full client dependencies
158pub struct FullDeps<C, P> {
159    /// The client instance to use.
160    pub client: Arc<C>,
161    /// Transaction pool instance.
162    pub pool: Arc<P>,
163    /// Graph pool instance.
164    pub graph: Arc<P>,
165    /// Network service
166    pub network: Arc<dyn NetworkService>,
167    /// Chain syncing service
168    pub sync: Arc<SyncingService<Block>>,
169    /// The Node authority flag
170    pub is_authority: bool,
171    /// Frontier Backend.
172    pub frontier_backend: Arc<dyn fc_api::Backend<Block>>,
173    /// EthFilterApi pool.
174    pub filter_pool: FilterPool,
175    /// Maximum fee history cache size.
176    pub fee_history_limit: u64,
177    /// Fee history cache.
178    pub fee_history_cache: FeeHistoryCache,
179    /// Ethereum data access storage_override.
180    pub storage_override: Arc<dyn StorageOverride<Block>>,
181    /// Cache for Ethereum block data.
182    pub block_data_cache: Arc<EthBlockDataCacheTask<Block>>,
183    /// Enable EVM RPC servers
184    pub enable_evm_rpc: bool,
185    /// Command sink for manual sealing
186    pub command_sink:
187        Option<futures::channel::mpsc::Sender<sc_consensus_manual_seal::EngineCommand<Hash>>>,
188}
189
190/// Instantiate all RPC extensions and Tracing RPC.
191pub fn create_full<C, P, BE>(
192    deps: FullDeps<C, P>,
193    subscription_task_executor: SubscriptionTaskExecutor,
194    pubsub_notification_sinks: Arc<
195        fc_mapping_sync::EthereumBlockNotificationSinks<
196            fc_mapping_sync::EthereumBlockNotification<Block>,
197        >,
198    >,
199    tracing_config: EvmTracingConfig,
200) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
201where
202    C: ProvideRuntimeApi<Block>
203        + HeaderBackend<Block>
204        + UsageProvider<Block>
205        + CallApiAt<Block>
206        + AuxStore
207        + StorageProvider<Block, BE>
208        + HeaderMetadata<Block, Error = BlockChainError>
209        + BlockchainEvents<Block>
210        + Send
211        + Sync
212        + 'static,
213    C: sc_client_api::BlockBackend<Block>,
214    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
215        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
216        + fp_rpc::ConvertTransactionRuntimeApi<Block>
217        + fp_rpc::EthereumRuntimeRPCApi<Block>
218        + BlockBuilder<Block>
219        + AuraApi<Block, AuraId>
220        + moonbeam_rpc_primitives_debug::DebugRuntimeApi<Block>
221        + moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi<Block>
222        + RelayParentOffsetApi<Block>,
223    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
224    BE: Backend<Block> + 'static,
225    BE::State: StateBackend<BlakeTwo256>,
226    BE::Blockchain: BlockchainBackend<Block>,
227{
228    let client = Arc::clone(&deps.client);
229    let graph = Arc::clone(&deps.graph);
230
231    let mut io = create_full_rpc(deps, subscription_task_executor, pubsub_notification_sinks)?;
232
233    if tracing_config.enable_txpool {
234        io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
235    }
236
237    if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
238        io.merge(
239            Trace::new(
240                client,
241                trace_filter_requester,
242                tracing_config.trace_filter_max_count,
243                tracing_config.trace_filter_max_block_range,
244            )
245            .into_rpc(),
246        )?;
247    }
248
249    if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
250        io.merge(Debug::new(debug_requester).into_rpc())?;
251    }
252
253    Ok(io)
254}
255
256/// Instantiate all RPC extensions and Tracing RPC for local dev mode.
257///
258/// This uses local pending inherent providers to align ETH pending/runtime calls
259/// with local mocked parachain inherent behavior.
260pub fn create_full_local_dev<C, P, BE>(
261    deps: FullDeps<C, P>,
262    subscription_task_executor: SubscriptionTaskExecutor,
263    pubsub_notification_sinks: Arc<
264        fc_mapping_sync::EthereumBlockNotificationSinks<
265            fc_mapping_sync::EthereumBlockNotification<Block>,
266        >,
267    >,
268    local_para_id: ParaId,
269    tracing_config: EvmTracingConfig,
270) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
271where
272    C: ProvideRuntimeApi<Block>
273        + HeaderBackend<Block>
274        + UsageProvider<Block>
275        + CallApiAt<Block>
276        + AuxStore
277        + StorageProvider<Block, BE>
278        + HeaderMetadata<Block, Error = BlockChainError>
279        + BlockchainEvents<Block>
280        + Send
281        + Sync
282        + 'static,
283    C: sc_client_api::BlockBackend<Block>,
284    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
285        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
286        + fp_rpc::ConvertTransactionRuntimeApi<Block>
287        + fp_rpc::EthereumRuntimeRPCApi<Block>
288        + BlockBuilder<Block>
289        + AuraApi<Block, AuraId>
290        + moonbeam_rpc_primitives_debug::DebugRuntimeApi<Block>
291        + moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi<Block>
292        + RelayParentOffsetApi<Block>,
293    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
294    BE: Backend<Block> + 'static,
295    BE::State: StateBackend<BlakeTwo256>,
296    BE::Blockchain: BlockchainBackend<Block>,
297{
298    let client = Arc::clone(&deps.client);
299    let graph = Arc::clone(&deps.graph);
300
301    let mut io = create_full_rpc_local_dev(
302        deps,
303        subscription_task_executor,
304        pubsub_notification_sinks,
305        local_para_id,
306    )?;
307
308    if tracing_config.enable_txpool {
309        io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
310    }
311
312    if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
313        io.merge(
314            Trace::new(
315                client,
316                trace_filter_requester,
317                tracing_config.trace_filter_max_count,
318                tracing_config.trace_filter_max_block_range,
319            )
320            .into_rpc(),
321        )?;
322    }
323
324    if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
325        io.merge(Debug::new(debug_requester).into_rpc())?;
326    }
327
328    Ok(io)
329}
330
331fn create_full_rpc_local_dev<C, P, BE>(
332    deps: FullDeps<C, P>,
333    subscription_task_executor: SubscriptionTaskExecutor,
334    pubsub_notification_sinks: Arc<
335        fc_mapping_sync::EthereumBlockNotificationSinks<
336            fc_mapping_sync::EthereumBlockNotification<Block>,
337        >,
338    >,
339    local_para_id: ParaId,
340) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
341where
342    C: ProvideRuntimeApi<Block>
343        + UsageProvider<Block>
344        + HeaderBackend<Block>
345        + CallApiAt<Block>
346        + AuxStore
347        + StorageProvider<Block, BE>
348        + HeaderMetadata<Block, Error = BlockChainError>
349        + BlockchainEvents<Block>
350        + Send
351        + Sync
352        + 'static,
353    C: sc_client_api::BlockBackend<Block>,
354    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
355        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
356        + fp_rpc::ConvertTransactionRuntimeApi<Block>
357        + fp_rpc::EthereumRuntimeRPCApi<Block>
358        + BlockBuilder<Block>
359        + AuraApi<Block, AuraId>
360        + RelayParentOffsetApi<Block>,
361    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
362    BE: Backend<Block> + 'static,
363    BE::State: StateBackend<BlakeTwo256>,
364    BE::Blockchain: BlockchainBackend<Block>,
365{
366    create_full_rpc_with_pending_provider(
367        deps,
368        subscription_task_executor,
369        pubsub_notification_sinks,
370        |client| crate::local::LocalPendingInherentDataProvider::new(client, local_para_id),
371    )
372}
373
374fn create_full_rpc<C, P, BE>(
375    deps: FullDeps<C, P>,
376    subscription_task_executor: SubscriptionTaskExecutor,
377    pubsub_notification_sinks: Arc<
378        fc_mapping_sync::EthereumBlockNotificationSinks<
379            fc_mapping_sync::EthereumBlockNotification<Block>,
380        >,
381    >,
382) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
383where
384    C: ProvideRuntimeApi<Block>
385        + UsageProvider<Block>
386        + HeaderBackend<Block>
387        + CallApiAt<Block>
388        + AuxStore
389        + StorageProvider<Block, BE>
390        + HeaderMetadata<Block, Error = BlockChainError>
391        + BlockchainEvents<Block>
392        + Send
393        + Sync
394        + 'static,
395    C: sc_client_api::BlockBackend<Block>,
396    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
397        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
398        + fp_rpc::ConvertTransactionRuntimeApi<Block>
399        + fp_rpc::EthereumRuntimeRPCApi<Block>
400        + BlockBuilder<Block>
401        + AuraApi<Block, AuraId>
402        + RelayParentOffsetApi<Block>,
403    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
404    BE: Backend<Block> + 'static,
405    BE::State: StateBackend<BlakeTwo256>,
406    BE::Blockchain: BlockchainBackend<Block>,
407{
408    create_full_rpc_with_pending_provider(
409        deps,
410        subscription_task_executor,
411        pubsub_notification_sinks,
412        crate::parachain::PendingCrateInherentDataProvider::new,
413    )
414}
415
416fn create_full_rpc_with_pending_provider<C, P, BE, CIDP, F>(
417    deps: FullDeps<C, P>,
418    subscription_task_executor: SubscriptionTaskExecutor,
419    pubsub_notification_sinks: Arc<
420        fc_mapping_sync::EthereumBlockNotificationSinks<
421            fc_mapping_sync::EthereumBlockNotification<Block>,
422        >,
423    >,
424    make_pending_inherent_data_provider: F,
425) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
426where
427    C: ProvideRuntimeApi<Block>
428        + UsageProvider<Block>
429        + HeaderBackend<Block>
430        + CallApiAt<Block>
431        + AuxStore
432        + StorageProvider<Block, BE>
433        + HeaderMetadata<Block, Error = BlockChainError>
434        + BlockchainEvents<Block>
435        + Send
436        + Sync
437        + 'static,
438    C: sc_client_api::BlockBackend<Block>,
439    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
440        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
441        + fp_rpc::ConvertTransactionRuntimeApi<Block>
442        + fp_rpc::EthereumRuntimeRPCApi<Block>
443        + BlockBuilder<Block>
444        + AuraApi<Block, AuraId>
445        + RelayParentOffsetApi<Block>,
446    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
447    BE: Backend<Block> + 'static,
448    BE::State: StateBackend<BlakeTwo256>,
449    BE::Blockchain: BlockchainBackend<Block>,
450    CIDP: CreateInherentDataProviders<Block, ()> + Send + Sync + 'static,
451    F: FnOnce(Arc<C>) -> CIDP,
452{
453    let mut io = RpcModule::new(());
454    let FullDeps {
455        client,
456        pool,
457        graph,
458        network,
459        sync,
460        is_authority,
461        frontier_backend,
462        filter_pool,
463        fee_history_limit,
464        fee_history_cache,
465        storage_override,
466        block_data_cache,
467        enable_evm_rpc,
468        command_sink,
469    } = deps;
470
471    io.merge(System::new(client.clone(), pool.clone()).into_rpc())?;
472    io.merge(TransactionPayment::new(client.clone()).into_rpc())?;
473    io.merge(sc_rpc::dev::Dev::new(client.clone()).into_rpc())?;
474
475    if let Some(command_sink) = command_sink {
476        io.merge(sc_consensus_manual_seal::rpc::ManualSeal::new(command_sink).into_rpc())?;
477    }
478
479    if !enable_evm_rpc {
480        return Ok(io);
481    }
482
483    let no_tx_converter: Option<fp_rpc::NoTransactionConverter> = None;
484
485    io.merge(
486        Eth::<_, _, _, _, _, _, ()>::new(
487            client.clone(),
488            pool.clone(),
489            no_tx_converter,
490            sync.clone(),
491            Default::default(),
492            storage_override.clone(),
493            frontier_backend.clone(),
494            is_authority,
495            block_data_cache.clone(),
496            fee_history_cache,
497            fee_history_limit,
498            // Allow 10x max allowed weight for non-transactional calls
499            10,
500            false, // rpc_allow_unprotected_txs: reject pre-EIP-155 txs via RPC
501            None,
502            make_pending_inherent_data_provider(client.clone()),
503            Some(Box::new(
504                crate::parachain::AuraConsensusDataProviderFallback::new(client.clone()),
505            )),
506        )
507        .replace_config::<AstarEthConfig<C, BE>>()
508        .into_rpc(),
509    )?;
510
511    let max_past_logs: u32 = 10_000;
512    let max_block_range: u32 = 1024;
513    let max_stored_filters: usize = 500;
514
515    let logs_journal = Arc::new(LogsJournal::new::<Block>(
516        subscription_task_executor.clone(),
517        storage_override.clone(),
518        pubsub_notification_sinks.clone(),
519    ));
520
521    io.merge(
522        EthFilter::new(
523            client.clone(),
524            frontier_backend,
525            graph.clone(),
526            filter_pool,
527            max_stored_filters,
528            max_past_logs,
529            max_block_range,
530            block_data_cache,
531            logs_journal.clone(),
532        )
533        .into_rpc(),
534    )?;
535
536    io.merge(Net::new(client.clone(), network.clone(), true).into_rpc())?;
537
538    io.merge(Web3::new(client.clone()).into_rpc())?;
539
540    io.merge(
541        EthPubSub::new(
542            pool,
543            client.clone(),
544            sync,
545            subscription_task_executor,
546            storage_override,
547            pubsub_notification_sinks,
548            logs_journal,
549        )
550        .into_rpc(),
551    )?;
552
553    Ok(io)
554}