1use 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#[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 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 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 let inherents = api.execute_in_transaction(move |api| {
106 TransactionOutcome::Rollback(api.inherent_extrinsics(best_hash, inherent_data))
109 })?;
110 log::debug!(target: LOG_TARGET, "Pending runtime API: inherent len = {}", inherents.len());
111 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 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 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
144pub trait ConsensusDataProvider<B: BlockT>: Send + Sync {
146 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 pub struct AuraConsensusDataProvider<B, C> {
181 slot_duration: SlotDuration,
183 _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 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}