domain_eth_service/
provider.rs

1use crate::rpc::{EthDeps, create_eth_rpc};
2use crate::service::{
3    EthConfiguration, FrontierPartialComponents, new_frontier_partial, spawn_frontier_tasks,
4};
5use clap::Parser;
6use domain_runtime_primitives::{Balance, Nonce};
7use domain_service::FullClient;
8use domain_service::providers::{BlockImportProvider, DefaultProvider, RpcProvider};
9use domain_service::rpc::FullDeps;
10use fc_consensus::FrontierBlockImport;
11use fc_rpc::EthConfig;
12use fc_storage::StorageOverrideHandler;
13use fp_rpc::{ConvertTransaction, ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi};
14use jsonrpsee::RpcModule;
15use parity_scale_codec::{Decode, Encode};
16use sc_client_api::{AuxStore, Backend, BlockBackend, BlockchainEvents, StorageProvider};
17use sc_rpc::SubscriptionTaskExecutor;
18use sc_rpc_server::SubscriptionIdProvider;
19use sc_service::BasePath;
20use sc_transaction_pool_api::TransactionPool;
21use serde::de::DeserializeOwned;
22use sp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Core, ProvideRuntimeApi};
23use sp_block_builder::BlockBuilder;
24use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
25use sp_core::H256;
26use sp_core::traits::SpawnEssentialNamed;
27use sp_inherents::CreateInherentDataProviders;
28use sp_runtime::traits::Block as BlockT;
29use std::env;
30use std::error::Error;
31use std::fmt::{Debug, Display};
32use std::marker::PhantomData;
33use std::path::Path;
34use std::sync::Arc;
35use substrate_frame_rpc_system::AccountNonceApi;
36
37/// Ethereum specific providers.
38pub struct EthProvider<CT, EC> {
39    eth_config: EthConfiguration,
40    base_path: Option<BasePath>,
41    marker: PhantomData<(CT, EC)>,
42}
43
44impl<CT, EC> EthProvider<CT, EC> {
45    pub fn new(base_path: Option<&Path>, eth_cli: impl Iterator<Item = String>) -> Self {
46        let eth_config = EthConfiguration::parse_from(env::args().take(1).chain(eth_cli));
47        Self::with_configuration(base_path, eth_config)
48    }
49
50    pub fn with_configuration(base_path: Option<&Path>, eth_config: EthConfiguration) -> Self {
51        Self {
52            eth_config,
53            base_path: base_path.map(|base_path| BasePath::new(base_path.join("evm"))),
54            marker: Default::default(),
55        }
56    }
57}
58
59impl<Block, RuntimeApi, CT, EC> BlockImportProvider<Block, FullClient<Block, RuntimeApi>>
60    for EthProvider<CT, EC>
61where
62    Block: BlockT,
63    RuntimeApi: ConstructRuntimeApi<Block, FullClient<Block, RuntimeApi>> + Send + Sync + 'static,
64    RuntimeApi::RuntimeApi:
65        ApiExt<Block> + Core<Block> + BlockBuilder<Block> + EthereumRuntimeRPCApi<Block>,
66{
67    type BI = FrontierBlockImport<
68        Block,
69        Arc<FullClient<Block, RuntimeApi>>,
70        FullClient<Block, RuntimeApi>,
71    >;
72
73    fn block_import(&self, client: Arc<FullClient<Block, RuntimeApi>>) -> Self::BI {
74        FrontierBlockImport::new(client.clone(), client)
75    }
76}
77
78impl<Block, Client, BE, TxPool, AccountId, CT, EC, CIDP>
79    RpcProvider<Block, Client, TxPool, BE, AccountId, CIDP> for EthProvider<CT, EC>
80where
81    Block: BlockT<Hash = H256>,
82    BE: Backend<Block> + 'static,
83    Client: ProvideRuntimeApi<Block>
84        + BlockchainEvents<Block>
85        + StorageProvider<Block, BE>
86        + HeaderBackend<Block>
87        + CallApiAt<Block>
88        + HeaderMetadata<Block, Error = BlockChainError>
89        + BlockBackend<Block>
90        + AuxStore
91        + Send
92        + Sync
93        + 'static,
94    Client::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
95        + EthereumRuntimeRPCApi<Block>
96        + AccountNonceApi<Block, AccountId, Nonce>
97        + ConvertTransactionRuntimeApi<Block>,
98    Client::Api: BlockBuilder<Block>,
99    Client::Api: EthereumRuntimeRPCApi<Block>,
100    CT: ConvertTransaction<Block::Extrinsic> + Clone + Default + Send + Sync + 'static,
101    EC: EthConfig<Block, Client>,
102    TxPool: TransactionPool<Block = Block, Hash = H256> + Sync + Send + 'static,
103    AccountId: DeserializeOwned + Encode + Debug + Decode + Display + Clone + Sync + Send + 'static,
104    CIDP: CreateInherentDataProviders<Block, ()> + Send + Clone + 'static,
105{
106    type Deps = EthDeps<Client, TxPool, CT, Block, BE, CIDP>;
107
108    fn deps(
109        &self,
110        full_deps: FullDeps<Block, Client, TxPool, BE, CIDP>,
111    ) -> Result<Self::Deps, sc_service::Error> {
112        let client = full_deps.client.clone();
113        let storage_override = Arc::new(StorageOverrideHandler::new(client.clone()));
114        let base_path = match &self.base_path {
115            None => BasePath::new_temp_dir()?,
116            Some(base_path) => BasePath::new(base_path.path()),
117        };
118
119        let frontier_backend = Arc::new(fc_db::kv::Backend::open(
120            client,
121            &full_deps.database_source,
122            base_path.path(),
123        )?);
124
125        let FrontierPartialComponents {
126            filter_pool,
127            fee_history_cache,
128            fee_history_cache_limit,
129        } = new_frontier_partial(self.eth_config.fee_history_limit)?;
130
131        Ok(EthDeps {
132            full_deps: full_deps.clone(),
133            converter: Some(CT::default()),
134            enable_dev_signer: self.eth_config.enable_dev_signer,
135            sync: full_deps.sync.clone(),
136            frontier_backend,
137            storage_override: storage_override.clone(),
138            block_data_cache: Arc::new(fc_rpc::EthBlockDataCacheTask::new(
139                full_deps.task_spawner.clone(),
140                storage_override,
141                self.eth_config.eth_log_block_cache,
142                self.eth_config.eth_statuses_cache,
143                full_deps.prometheus_registry,
144            )),
145            filter_pool,
146            max_past_logs: self.eth_config.max_past_logs,
147            fee_history_cache,
148            fee_history_cache_limit,
149            execute_gas_limit_multiplier: self.eth_config.execute_gas_limit_multiplier,
150            forced_parent_hashes: None,
151            pending_inherent_data_provider: full_deps.create_inherent_data_provider,
152        })
153    }
154
155    fn rpc_id(&self) -> Option<Box<dyn SubscriptionIdProvider>> {
156        Some(Box::new(fc_rpc::EthereumSubIdProvider))
157    }
158
159    fn rpc_builder<SE>(
160        &self,
161        deps: Self::Deps,
162        subscription_task_executor: SubscriptionTaskExecutor,
163        essential_task_spawner: SE,
164    ) -> Result<RpcModule<()>, Box<dyn Error + Send + Sync>>
165    where
166        SE: SpawnEssentialNamed + Clone,
167    {
168        let default_provider = DefaultProvider;
169        let io = default_provider.rpc_builder(
170            deps.full_deps.clone(),
171            subscription_task_executor.clone(),
172            essential_task_spawner.clone(),
173        )?;
174
175        // Sinks for pubsub notifications.
176        // Everytime a new subscription is created, a new mpsc channel is added to the sink pool.
177        // The MappingSyncWorker sends through the channel on block import and the subscription emits a notification to the subscriber on receiving a message through this channel.
178        // This way we avoid race conditions when using native substrate block import notification stream.
179        let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks<
180            fc_mapping_sync::EthereumBlockNotification<Block>,
181        > = Default::default();
182        let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks);
183
184        let io = create_eth_rpc::<Client, BE, TxPool, CT, Block, EC, CIDP>(
185            io,
186            deps.clone(),
187            subscription_task_executor,
188            pubsub_notification_sinks.clone(),
189        )?;
190
191        // spawn essential rpc tasks
192        spawn_frontier_tasks(
193            essential_task_spawner,
194            deps.full_deps.client,
195            deps.full_deps.backend,
196            deps.frontier_backend,
197            deps.storage_override,
198            FrontierPartialComponents {
199                filter_pool: deps.filter_pool,
200                fee_history_cache: deps.fee_history_cache,
201                fee_history_cache_limit: deps.fee_history_cache_limit,
202            },
203            deps.sync,
204            pubsub_notification_sinks,
205        );
206        Ok(io)
207    }
208}