fc_rpc/eth/
pending.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program 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// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
18
19// Substrate
20use sc_client_api::backend::{Backend, StorageProvider};
21use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
22use sp_api::{ApiExt, ApiRef, Core, ProvideRuntimeApi};
23use sp_block_builder::BlockBuilder as BlockBuilderApi;
24use sp_blockchain::{ApplyExtrinsicFailed, HeaderBackend};
25use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider};
26use sp_runtime::{
27	generic::Digest,
28	traits::{Block as BlockT, Header as HeaderT, One},
29	TransactionOutcome,
30};
31
32use crate::eth::Eth;
33use fp_rpc::EthereumRuntimeRPCApi;
34
35const LOG_TARGET: &str = "eth-pending";
36
37/// The generated error type for creating pending runtime api.
38#[derive(Debug, thiserror::Error)]
39pub(crate) enum Error {
40	#[error("Failed to call runtime API, {0}")]
41	CallApi(#[from] sp_api::ApiError),
42	#[error("Failed to create pending inherent data, {0}")]
43	PendingInherentData(#[from] sp_inherents::Error),
44	#[error("Failed to create pending inherent data provider, {0}")]
45	PendingCreateInherentDataProvider(#[from] Box<dyn std::error::Error + Send + Sync>),
46	#[error(transparent)]
47	Backend(#[from] sp_blockchain::Error),
48	#[error(transparent)]
49	ApplyExtrinsicFailed(#[from] ApplyExtrinsicFailed),
50}
51
52impl<B, C, P, CT, BE, CIDP, EC> Eth<B, C, P, CT, BE, CIDP, EC>
53where
54	B: BlockT,
55	C: ProvideRuntimeApi<B>,
56	C::Api: BlockBuilderApi<B>,
57	C::Api: EthereumRuntimeRPCApi<B>,
58	C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
59	BE: Backend<B>,
60	CIDP: CreateInherentDataProviders<B, ()> + Send + 'static,
61	P: TransactionPool<Block = B, Hash = B::Hash> + 'static,
62{
63	/// Creates a pending runtime API.
64	pub(crate) async fn pending_runtime_api(&self) -> Result<(B::Hash, ApiRef<C::Api>), Error> {
65		let api = self.client.runtime_api();
66
67		let info = self.client.info();
68		let (best_number, best_hash) = (info.best_number, info.best_hash);
69
70		let inherent_data_provider = self
71			.pending_create_inherent_data_providers
72			.create_inherent_data_providers(best_hash, ())
73			.await?;
74		let inherent_data = inherent_data_provider.create_inherent_data().await?;
75
76		let digest = if let Some(digest_provider) = &self.pending_consensus_data_provider {
77			if let Some(header) = self.client.header(best_hash)? {
78				digest_provider.create_digest(&header, &inherent_data)?
79			} else {
80				Default::default()
81			}
82		} else {
83			Default::default()
84		};
85
86		log::debug!(target: LOG_TARGET, "Pending runtime API: header digest = {digest:?}");
87
88		let pending_header = <<B as BlockT>::Header as HeaderT>::new(
89			best_number + One::one(),
90			Default::default(),
91			Default::default(),
92			best_hash,
93			digest,
94		);
95
96		// Initialize the pending block header
97		if api
98			.initialize_pending_block(best_hash, &pending_header)
99			.is_err()
100		{
101			api.initialize_block(best_hash, &pending_header)?;
102		}
103
104		// Apply inherents to the pending block.
105		let inherents = api.execute_in_transaction(move |api| {
106			// `create_inherents` should not change any state, to ensure this we always rollback
107			// the transaction.
108			TransactionOutcome::Rollback(api.inherent_extrinsics(best_hash, inherent_data))
109		})?;
110		log::debug!(target: LOG_TARGET, "Pending runtime API: inherent len = {}", inherents.len());
111		// Apply the inherents to the best block's state.
112		for ext in inherents {
113			let _ = api.execute_in_transaction(|api| match api.apply_extrinsic(best_hash, ext) {
114				Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())),
115				Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
116					ApplyExtrinsicFailed::Validity(tx_validity).into(),
117				)),
118				Err(err) => TransactionOutcome::Rollback(Err(Error::from(err))),
119			});
120		}
121
122		// Get all extrinsics from the ready queue.
123		let extrinsics: Vec<<B as BlockT>::Extrinsic> = self
124			.graph
125			.ready()
126			.map(|in_pool_tx| in_pool_tx.data().as_ref().clone())
127			.collect::<Vec<<B as BlockT>::Extrinsic>>();
128		log::debug!(target: LOG_TARGET, "Pending runtime API: extrinsic len = {}", extrinsics.len());
129		// Apply the extrinsics from the ready queue to the pending block's state.
130		for ext in extrinsics {
131			let _ = api.execute_in_transaction(|api| match api.apply_extrinsic(best_hash, ext) {
132				Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())),
133				Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err(
134					ApplyExtrinsicFailed::Validity(tx_validity).into(),
135				)),
136				Err(err) => TransactionOutcome::Rollback(Err(Error::from(err))),
137			});
138		}
139
140		Ok((best_hash, api))
141	}
142}
143
144/// Consensus data provider, pending api uses this trait object for authoring blocks valid for any runtime.
145pub trait ConsensusDataProvider<B: BlockT>: Send + Sync {
146	/// Attempt to create a consensus digest.
147	fn create_digest(
148		&self,
149		parent: &B::Header,
150		data: &InherentData,
151	) -> Result<Digest, sp_inherents::Error>;
152}
153
154impl<B: BlockT> ConsensusDataProvider<B> for () {
155	fn create_digest(
156		&self,
157		_: &B::Header,
158		_: &InherentData,
159	) -> Result<Digest, sp_inherents::Error> {
160		Ok(Default::default())
161	}
162}
163
164#[cfg(feature = "aura")]
165pub use self::aura::AuraConsensusDataProvider;
166#[cfg(feature = "aura")]
167mod aura {
168	use super::*;
169	use sc_client_api::{AuxStore, UsageProvider};
170	use sp_consensus_aura::{
171		digests::CompatibleDigestItem,
172		sr25519::{AuthorityId, AuthoritySignature},
173		AuraApi, Slot, SlotDuration,
174	};
175	use sp_runtime::generic::DigestItem;
176	use sp_timestamp::TimestampInherentData;
177	use std::{marker::PhantomData, sync::Arc};
178
179	/// Consensus data provider for Aura.
180	pub struct AuraConsensusDataProvider<B, C> {
181		// slot duration
182		slot_duration: SlotDuration,
183		// phantom data for required generics
184		_phantom: PhantomData<(B, C)>,
185	}
186
187	impl<B, C> AuraConsensusDataProvider<B, C>
188	where
189		B: BlockT,
190		C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B>,
191		C::Api: AuraApi<B, AuthorityId>,
192	{
193		/// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client`
194		/// implements [`sp_consensus_aura::AuraApi`]
195		pub fn new(client: Arc<C>) -> Self {
196			let slot_duration = sc_consensus_aura::slot_duration(&*client)
197				.expect("slot_duration is always present; qed.");
198			Self {
199				slot_duration,
200				_phantom: PhantomData,
201			}
202		}
203	}
204
205	impl<B: BlockT, C: Send + Sync> ConsensusDataProvider<B> for AuraConsensusDataProvider<B, C> {
206		fn create_digest(
207			&self,
208			_parent: &B::Header,
209			data: &InherentData,
210		) -> Result<Digest, sp_inherents::Error> {
211			let timestamp = data
212				.timestamp_inherent_data()?
213				.expect("Timestamp is always present; qed");
214
215			let digest_item =
216				<DigestItem as CompatibleDigestItem<AuthoritySignature>>::aura_pre_digest(
217					Slot::from_timestamp(timestamp, self.slot_duration),
218				);
219
220			Ok(Digest {
221				logs: vec![digest_item],
222			})
223		}
224	}
225}