domain_eth_service/
provider.rs

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