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 sc_client_api::{
32    AuxStore, Backend, BlockchainEvents, StateBackend, StorageProvider, UsageProvider,
33};
34use sc_network::service::traits::NetworkService;
35use sc_network_sync::SyncingService;
36use sc_rpc::dev::DevApiServer;
37pub use sc_rpc::SubscriptionTaskExecutor;
38use sc_transaction_pool_api::TransactionPool;
39use sp_api::{CallApiAt, ProvideRuntimeApi};
40use sp_block_builder::BlockBuilder;
41use sp_blockchain::{
42    Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata,
43};
44use sp_consensus_aura::{sr25519::AuthorityId as AuraId, AuraApi};
45use sp_runtime::traits::{BlakeTwo256, Block as BlockT};
46use std::sync::Arc;
47use substrate_frame_rpc_system::{System, SystemApiServer};
48
49use moonbeam_rpc_debug::{Debug, DebugServer};
50use moonbeam_rpc_trace::{Trace, TraceServer};
51
52use crate::evm_tracing_types::{FrontierBackendConfig, FrontierConfig};
53use astar_primitives::*;
54
55pub mod tracing;
56
57type HashFor<Block> = <Block as BlockT>::Hash;
58
59#[derive(Clone)]
60pub struct EvmTracingConfig {
61    pub tracing_requesters: tracing::RpcRequesters,
62    pub trace_filter_max_count: u32,
63    pub enable_txpool: bool,
64}
65
66/// Available frontier backend types.
67#[derive(Debug, Copy, Clone, Default, clap::ValueEnum)]
68pub enum FrontierBackendType {
69    /// RocksDb KV database.
70    #[default]
71    KeyValue,
72    /// SQL database with custom log indexing.
73    Sql,
74}
75
76// TODO This is copied from frontier. It should be imported instead after
77// https://github.com/paritytech/frontier/issues/333 is solved
78pub fn open_frontier_backend<C, BE>(
79    client: Arc<C>,
80    config: &sc_service::Configuration,
81    rpc_config: &FrontierConfig,
82) -> Result<fc_db::Backend<Block, C>, String>
83where
84    C: ProvideRuntimeApi<Block> + StorageProvider<Block, BE> + AuxStore,
85    C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError>,
86    C: Send + Sync + 'static,
87    C::Api: fp_rpc::EthereumRuntimeRPCApi<Block>,
88    BE: Backend<Block> + 'static,
89    BE::State: StateBackend<BlakeTwo256>,
90{
91    let config_dir = config.base_path.config_dir(config.chain_spec.id());
92    let path = config_dir.join("frontier").join("db");
93
94    let frontier_backend = match rpc_config.frontier_backend_config {
95        FrontierBackendConfig::KeyValue => {
96            fc_db::Backend::KeyValue(Arc::new(fc_db::kv::Backend::<Block, C>::new(
97                client,
98                &fc_db::kv::DatabaseSettings {
99                    source: fc_db::DatabaseSource::RocksDb {
100                        path,
101                        cache_size: 0,
102                    },
103                },
104            )?))
105        }
106        FrontierBackendConfig::Sql {
107            pool_size,
108            num_ops_timeout,
109            thread_count,
110            cache_size,
111        } => {
112            let overrides = Arc::new(StorageOverrideHandler::new(client.clone()));
113            std::fs::create_dir_all(&path).expect("failed creating sql db directory");
114            let backend = futures::executor::block_on(fc_db::sql::Backend::new(
115                fc_db::sql::BackendConfig::Sqlite(fc_db::sql::SqliteBackendConfig {
116                    path: Path::new("sqlite:///")
117                        .join(path)
118                        .join("frontier.db3")
119                        .to_str()
120                        .expect("frontier sql path error"),
121                    create_if_missing: true,
122                    thread_count: thread_count,
123                    cache_size: cache_size,
124                }),
125                pool_size,
126                std::num::NonZeroU32::new(num_ops_timeout),
127                overrides.clone(),
128            ))
129            .unwrap_or_else(|err| panic!("failed creating sql backend: {:?}", err));
130            fc_db::Backend::Sql(Arc::new(backend))
131        }
132    };
133
134    Ok(frontier_backend)
135}
136
137pub struct AstarEthConfig<C, BE>(std::marker::PhantomData<(C, BE)>);
138
139impl<C, BE> fc_rpc::EthConfig<Block, C> for AstarEthConfig<C, BE>
140where
141    C: sc_client_api::StorageProvider<Block, BE> + Sync + Send + 'static,
142    BE: Backend<Block> + 'static,
143{
144    // Use to override (adapt) evm call to precompiles for proper gas estimation.
145    // We are not aware of any of our precompile that require this.
146    type EstimateGasAdapter = ();
147    // This assumes the use of HashedMapping<BlakeTwo256> for address mapping
148    type RuntimeStorageOverride =
149        fc_rpc::frontier_backend_client::SystemAccountId32StorageOverride<Block, C, BE>;
150}
151
152/// Full client dependencies
153pub struct FullDeps<C, P> {
154    /// The client instance to use.
155    pub client: Arc<C>,
156    /// Transaction pool instance.
157    pub pool: Arc<P>,
158    /// Graph pool instance.
159    pub graph: Arc<P>,
160    /// Network service
161    pub network: Arc<dyn NetworkService>,
162    /// Chain syncing service
163    pub sync: Arc<SyncingService<Block>>,
164    /// The Node authority flag
165    pub is_authority: bool,
166    /// Frontier Backend.
167    pub frontier_backend: Arc<dyn fc_api::Backend<Block>>,
168    /// EthFilterApi pool.
169    pub filter_pool: FilterPool,
170    /// Maximum fee history cache size.
171    pub fee_history_limit: u64,
172    /// Fee history cache.
173    pub fee_history_cache: FeeHistoryCache,
174    /// Ethereum data access storage_override.
175    pub storage_override: Arc<dyn StorageOverride<Block>>,
176    /// Cache for Ethereum block data.
177    pub block_data_cache: Arc<EthBlockDataCacheTask<Block>>,
178    /// Enable EVM RPC servers
179    pub enable_evm_rpc: bool,
180    /// Command sink for manual sealing
181    #[cfg(feature = "manual-seal")]
182    pub command_sink:
183        Option<futures::channel::mpsc::Sender<sc_consensus_manual_seal::EngineCommand<Hash>>>,
184}
185
186/// Instantiate all RPC extensions and Tracing RPC.
187pub fn create_full<C, P, BE>(
188    deps: FullDeps<C, P>,
189    subscription_task_executor: SubscriptionTaskExecutor,
190    pubsub_notification_sinks: Arc<
191        fc_mapping_sync::EthereumBlockNotificationSinks<
192            fc_mapping_sync::EthereumBlockNotification<Block>,
193        >,
194    >,
195    tracing_config: EvmTracingConfig,
196) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
197where
198    C: ProvideRuntimeApi<Block>
199        + HeaderBackend<Block>
200        + UsageProvider<Block>
201        + CallApiAt<Block>
202        + AuxStore
203        + StorageProvider<Block, BE>
204        + HeaderMetadata<Block, Error = BlockChainError>
205        + BlockchainEvents<Block>
206        + Send
207        + Sync
208        + 'static,
209    C: sc_client_api::BlockBackend<Block>,
210    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
211        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
212        + fp_rpc::ConvertTransactionRuntimeApi<Block>
213        + fp_rpc::EthereumRuntimeRPCApi<Block>
214        + BlockBuilder<Block>
215        + AuraApi<Block, AuraId>
216        + moonbeam_rpc_primitives_debug::DebugRuntimeApi<Block>
217        + moonbeam_rpc_primitives_txpool::TxPoolRuntimeApi<Block>,
218    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
219    BE: Backend<Block> + 'static,
220    BE::State: StateBackend<BlakeTwo256>,
221    BE::Blockchain: BlockchainBackend<Block>,
222{
223    let client = Arc::clone(&deps.client);
224    let graph = Arc::clone(&deps.graph);
225
226    let mut io = create_full_rpc(deps, subscription_task_executor, pubsub_notification_sinks)?;
227
228    if tracing_config.enable_txpool {
229        io.merge(TxPool::new(Arc::clone(&client), graph).into_rpc())?;
230    }
231
232    if let Some(trace_filter_requester) = tracing_config.tracing_requesters.trace {
233        io.merge(
234            Trace::new(
235                client,
236                trace_filter_requester,
237                tracing_config.trace_filter_max_count,
238            )
239            .into_rpc(),
240        )?;
241    }
242
243    if let Some(debug_requester) = tracing_config.tracing_requesters.debug {
244        io.merge(Debug::new(debug_requester).into_rpc())?;
245    }
246
247    Ok(io)
248}
249
250fn create_full_rpc<C, P, BE>(
251    deps: FullDeps<C, P>,
252    subscription_task_executor: SubscriptionTaskExecutor,
253    pubsub_notification_sinks: Arc<
254        fc_mapping_sync::EthereumBlockNotificationSinks<
255            fc_mapping_sync::EthereumBlockNotification<Block>,
256        >,
257    >,
258) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
259where
260    C: ProvideRuntimeApi<Block>
261        + UsageProvider<Block>
262        + HeaderBackend<Block>
263        + CallApiAt<Block>
264        + AuxStore
265        + StorageProvider<Block, BE>
266        + HeaderMetadata<Block, Error = BlockChainError>
267        + BlockchainEvents<Block>
268        + Send
269        + Sync
270        + 'static,
271    C: sc_client_api::BlockBackend<Block>,
272    C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
273        + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
274        + fp_rpc::ConvertTransactionRuntimeApi<Block>
275        + fp_rpc::EthereumRuntimeRPCApi<Block>
276        + BlockBuilder<Block>
277        + AuraApi<Block, AuraId>,
278    P: TransactionPool<Block = Block, Hash = HashFor<Block>> + Sync + Send + 'static,
279    BE: Backend<Block> + 'static,
280    BE::State: StateBackend<BlakeTwo256>,
281    BE::Blockchain: BlockchainBackend<Block>,
282{
283    let mut io = RpcModule::new(());
284    let FullDeps {
285        client,
286        pool,
287        graph,
288        network,
289        sync,
290        is_authority,
291        frontier_backend,
292        filter_pool,
293        fee_history_limit,
294        fee_history_cache,
295        storage_override,
296        block_data_cache,
297        enable_evm_rpc,
298        #[cfg(feature = "manual-seal")]
299        command_sink,
300    } = deps;
301
302    io.merge(System::new(client.clone(), pool.clone()).into_rpc())?;
303    io.merge(TransactionPayment::new(client.clone()).into_rpc())?;
304    io.merge(sc_rpc::dev::Dev::new(client.clone()).into_rpc())?;
305
306    #[cfg(feature = "manual-seal")]
307    if let Some(command_sink) = command_sink {
308        use sc_consensus_manual_seal::rpc::ManualSealApiServer;
309        io.merge(sc_consensus_manual_seal::rpc::ManualSeal::new(command_sink).into_rpc())?;
310    }
311
312    if !enable_evm_rpc {
313        return Ok(io);
314    }
315
316    let no_tx_converter: Option<fp_rpc::NoTransactionConverter> = None;
317
318    io.merge(
319        Eth::<_, _, _, _, _, _, ()>::new(
320            client.clone(),
321            pool.clone(),
322            graph.clone(),
323            no_tx_converter,
324            sync.clone(),
325            Default::default(),
326            storage_override.clone(),
327            frontier_backend.clone(),
328            is_authority,
329            block_data_cache.clone(),
330            fee_history_cache,
331            fee_history_limit,
332            // Allow 10x max allowed weight for non-transactional calls
333            10,
334            None,
335            crate::parachain::PendingCrateInherentDataProvider::new(client.clone()),
336            Some(Box::new(
337                crate::parachain::AuraConsensusDataProviderFallback::new(client.clone()),
338            )),
339        )
340        .replace_config::<AstarEthConfig<C, BE>>()
341        .into_rpc(),
342    )?;
343
344    let max_past_logs: u32 = 10_000;
345    let max_block_range: u32 = 1024;
346    let max_stored_filters: usize = 500;
347    io.merge(
348        EthFilter::new(
349            client.clone(),
350            frontier_backend,
351            graph.clone(),
352            filter_pool,
353            max_stored_filters,
354            max_past_logs,
355            max_block_range,
356            block_data_cache,
357        )
358        .into_rpc(),
359    )?;
360
361    io.merge(Net::new(client.clone(), network.clone(), true).into_rpc())?;
362
363    io.merge(Web3::new(client.clone()).into_rpc())?;
364
365    io.merge(
366        EthPubSub::new(
367            pool,
368            client.clone(),
369            sync,
370            subscription_task_executor,
371            storage_override,
372            pubsub_notification_sinks,
373        )
374        .into_rpc(),
375    )?;
376
377    Ok(io)
378}