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;
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    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
221    BE: Backend<Block> + 'static,
222    BE::State: StateBackend<BlakeTwo256>,
223    BE::Blockchain: BlockchainBackend<Block>,
224{
225    let client = Arc::clone(&deps.client);
226    let graph = Arc::clone(&deps.graph);
227
228    let mut io = create_full_rpc(deps, subscription_task_executor, pubsub_notification_sinks)?;
229
230    if tracing_config.enable_txpool {
231        io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
232    }
233
234    if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
235        io.merge(
236            Trace::new(
237                client,
238                trace_filter_requester,
239                tracing_config.trace_filter_max_count,
240            )
241            .into_rpc(),
242        )?;
243    }
244
245    if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
246        io.merge(Debug::new(debug_requester).into_rpc())?;
247    }
248
249    Ok(io)
250}
251
252/// Instantiate all RPC extensions and Tracing RPC for local dev mode.
253///
254/// This uses local pending inherent providers to align ETH pending/runtime calls
255/// with local mocked parachain inherent behavior.
256pub fn create_full_local_dev<C, P, BE>(
257    deps: FullDeps<C, P>,
258    subscription_task_executor: SubscriptionTaskExecutor,
259    pubsub_notification_sinks: Arc<
260        fc_mapping_sync::EthereumBlockNotificationSinks<
261            fc_mapping_sync::EthereumBlockNotification<Block>,
262        >,
263    >,
264    local_para_id: ParaId,
265    tracing_config: EvmTracingConfig,
266) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
267where
268    C: ProvideRuntimeApi<Block>
269        + HeaderBackend<Block>
270        + UsageProvider<Block>
271        + CallApiAt<Block>
272        + AuxStore
273        + StorageProvider<Block, BE>
274        + HeaderMetadata<Block, Error = BlockChainError>
275        + BlockchainEvents<Block>
276        + Send
277        + Sync
278        + 'static,
279    C: sc_client_api::BlockBackend<Block>,
280    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
281        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
282        + fp_rpc::ConvertTransactionRuntimeApi<Block>
283        + fp_rpc::EthereumRuntimeRPCApi<Block>
284        + BlockBuilder<Block>
285        + AuraApi<Block, AuraId>
286        + moonbeam_rpc_primitives_debug::DebugRuntimeApi<Block>
287        + moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi<Block>,
288    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
289    BE: Backend<Block> + 'static,
290    BE::State: StateBackend<BlakeTwo256>,
291    BE::Blockchain: BlockchainBackend<Block>,
292{
293    let client = Arc::clone(&deps.client);
294    let graph = Arc::clone(&deps.graph);
295
296    let mut io = create_full_rpc_local_dev(
297        deps,
298        subscription_task_executor,
299        pubsub_notification_sinks,
300        local_para_id,
301    )?;
302
303    if tracing_config.enable_txpool {
304        io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
305    }
306
307    if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
308        io.merge(
309            Trace::new(
310                client,
311                trace_filter_requester,
312                tracing_config.trace_filter_max_count,
313            )
314            .into_rpc(),
315        )?;
316    }
317
318    if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
319        io.merge(Debug::new(debug_requester).into_rpc())?;
320    }
321
322    Ok(io)
323}
324
325fn create_full_rpc_local_dev<C, P, BE>(
326    deps: FullDeps<C, P>,
327    subscription_task_executor: SubscriptionTaskExecutor,
328    pubsub_notification_sinks: Arc<
329        fc_mapping_sync::EthereumBlockNotificationSinks<
330            fc_mapping_sync::EthereumBlockNotification<Block>,
331        >,
332    >,
333    local_para_id: ParaId,
334) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
335where
336    C: ProvideRuntimeApi<Block>
337        + UsageProvider<Block>
338        + HeaderBackend<Block>
339        + CallApiAt<Block>
340        + AuxStore
341        + StorageProvider<Block, BE>
342        + HeaderMetadata<Block, Error = BlockChainError>
343        + BlockchainEvents<Block>
344        + Send
345        + Sync
346        + 'static,
347    C: sc_client_api::BlockBackend<Block>,
348    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
349        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
350        + fp_rpc::ConvertTransactionRuntimeApi<Block>
351        + fp_rpc::EthereumRuntimeRPCApi<Block>
352        + BlockBuilder<Block>
353        + AuraApi<Block, AuraId>,
354    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
355    BE: Backend<Block> + 'static,
356    BE::State: StateBackend<BlakeTwo256>,
357    BE::Blockchain: BlockchainBackend<Block>,
358{
359    create_full_rpc_with_pending_provider(
360        deps,
361        subscription_task_executor,
362        pubsub_notification_sinks,
363        |client| crate::local::LocalPendingInherentDataProvider::new(client, local_para_id),
364    )
365}
366
367fn create_full_rpc<C, P, BE>(
368    deps: FullDeps<C, P>,
369    subscription_task_executor: SubscriptionTaskExecutor,
370    pubsub_notification_sinks: Arc<
371        fc_mapping_sync::EthereumBlockNotificationSinks<
372            fc_mapping_sync::EthereumBlockNotification<Block>,
373        >,
374    >,
375) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
376where
377    C: ProvideRuntimeApi<Block>
378        + UsageProvider<Block>
379        + HeaderBackend<Block>
380        + CallApiAt<Block>
381        + AuxStore
382        + StorageProvider<Block, BE>
383        + HeaderMetadata<Block, Error = BlockChainError>
384        + BlockchainEvents<Block>
385        + Send
386        + Sync
387        + 'static,
388    C: sc_client_api::BlockBackend<Block>,
389    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
390        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
391        + fp_rpc::ConvertTransactionRuntimeApi<Block>
392        + fp_rpc::EthereumRuntimeRPCApi<Block>
393        + BlockBuilder<Block>
394        + AuraApi<Block, AuraId>,
395    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
396    BE: Backend<Block> + 'static,
397    BE::State: StateBackend<BlakeTwo256>,
398    BE::Blockchain: BlockchainBackend<Block>,
399{
400    create_full_rpc_with_pending_provider(
401        deps,
402        subscription_task_executor,
403        pubsub_notification_sinks,
404        crate::parachain::PendingCrateInherentDataProvider::new,
405    )
406}
407
408fn create_full_rpc_with_pending_provider<C, P, BE, CIDP, F>(
409    deps: FullDeps<C, P>,
410    subscription_task_executor: SubscriptionTaskExecutor,
411    pubsub_notification_sinks: Arc<
412        fc_mapping_sync::EthereumBlockNotificationSinks<
413            fc_mapping_sync::EthereumBlockNotification<Block>,
414        >,
415    >,
416    make_pending_inherent_data_provider: F,
417) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
418where
419    C: ProvideRuntimeApi<Block>
420        + UsageProvider<Block>
421        + HeaderBackend<Block>
422        + CallApiAt<Block>
423        + AuxStore
424        + StorageProvider<Block, BE>
425        + HeaderMetadata<Block, Error = BlockChainError>
426        + BlockchainEvents<Block>
427        + Send
428        + Sync
429        + 'static,
430    C: sc_client_api::BlockBackend<Block>,
431    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
432        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
433        + fp_rpc::ConvertTransactionRuntimeApi<Block>
434        + fp_rpc::EthereumRuntimeRPCApi<Block>
435        + BlockBuilder<Block>
436        + AuraApi<Block, AuraId>,
437    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
438    BE: Backend<Block> + 'static,
439    BE::State: StateBackend<BlakeTwo256>,
440    BE::Blockchain: BlockchainBackend<Block>,
441    CIDP: CreateInherentDataProviders<Block, ()> + Send + Sync + 'static,
442    F: FnOnce(Arc<C>) -> CIDP,
443{
444    let mut io = RpcModule::new(());
445    let FullDeps {
446        client,
447        pool,
448        graph,
449        network,
450        sync,
451        is_authority,
452        frontier_backend,
453        filter_pool,
454        fee_history_limit,
455        fee_history_cache,
456        storage_override,
457        block_data_cache,
458        enable_evm_rpc,
459        command_sink,
460    } = deps;
461
462    io.merge(System::new(client.clone(), pool.clone()).into_rpc())?;
463    io.merge(TransactionPayment::new(client.clone()).into_rpc())?;
464    io.merge(sc_rpc::dev::Dev::new(client.clone()).into_rpc())?;
465
466    if let Some(command_sink) = command_sink {
467        io.merge(sc_consensus_manual_seal::rpc::ManualSeal::new(command_sink).into_rpc())?;
468    }
469
470    if !enable_evm_rpc {
471        return Ok(io);
472    }
473
474    let no_tx_converter: Option<fp_rpc::NoTransactionConverter> = None;
475
476    io.merge(
477        Eth::<_, _, _, _, _, _, ()>::new(
478            client.clone(),
479            pool.clone(),
480            graph.clone(),
481            no_tx_converter,
482            sync.clone(),
483            Default::default(),
484            storage_override.clone(),
485            frontier_backend.clone(),
486            is_authority,
487            block_data_cache.clone(),
488            fee_history_cache,
489            fee_history_limit,
490            // Allow 10x max allowed weight for non-transactional calls
491            10,
492            None,
493            make_pending_inherent_data_provider(client.clone()),
494            Some(Box::new(
495                crate::parachain::AuraConsensusDataProviderFallback::new(client.clone()),
496            )),
497        )
498        .replace_config::<AstarEthConfig<C, BE>>()
499        .into_rpc(),
500    )?;
501
502    let max_past_logs: u32 = 10_000;
503    let max_block_range: u32 = 1024;
504    let max_stored_filters: usize = 500;
505    io.merge(
506        EthFilter::new(
507            client.clone(),
508            frontier_backend,
509            graph.clone(),
510            filter_pool,
511            max_stored_filters,
512            max_past_logs,
513            max_block_range,
514            block_data_cache,
515        )
516        .into_rpc(),
517    )?;
518
519    io.merge(Net::new(client.clone(), network.clone(), true).into_rpc())?;
520
521    io.merge(Web3::new(client.clone()).into_rpc())?;
522
523    io.merge(
524        EthPubSub::new(
525            pool,
526            client.clone(),
527            sync,
528            subscription_task_executor,
529            storage_override,
530            pubsub_notification_sinks,
531        )
532        .into_rpc(),
533    )?;
534
535    Ok(io)
536}