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