1use crate::{AuthorityId, LOG_TARGET, authorities};
23use log::{debug, info, trace};
24use parity_scale_codec::Codec;
25use sc_client_api::{BlockOf, UsageProvider, backend::AuxStore};
26use sc_consensus::{
27 block_import::{BlockImport, BlockImportParams, ForkChoiceStrategy},
28 import_queue::{BasicQueue, DefaultImportQueue, Verifier},
29};
30use sc_consensus_aura::{
31 CheckForEquivocation, CompatibilityMode, Error, ImportQueueParams,
32 standalone::SealVerificationError,
33};
34use sc_consensus_slots::{CheckedHeader, check_equivocation};
35use sc_telemetry::{CONSENSUS_DEBUG, CONSENSUS_TRACE, TelemetryHandle, telemetry};
36use sp_api::{ApiExt, ProvideRuntimeApi};
37use sp_block_builder::BlockBuilder as BlockBuilderApi;
38use sp_blockchain::HeaderBackend;
39use sp_consensus::Error as ConsensusError;
40use sp_consensus_aura::AuraApi;
41use sp_consensus_slots::Slot;
42use sp_core::crypto::Pair;
43use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider};
44use sp_partner_chains_consensus_aura::{CurrentSlotProvider, InherentDigest};
45use sp_runtime::{
46 DigestItem,
47 traits::{Block as BlockT, Header, NumberFor},
48};
49use std::{fmt::Debug, marker::PhantomData, sync::Arc};
50
51#[allow(clippy::type_complexity)]
57fn check_header<C, B: BlockT, P: Pair>(
58 client: &C,
59 slot_now: Slot,
60 header: B::Header,
61 hash: B::Hash,
62 authorities: &[AuthorityId<P>],
63 check_for_equivocation: CheckForEquivocation,
64) -> Result<CheckedHeader<B::Header, (Slot, DigestItem)>, Error<B>>
65where
66 P::Public: Codec,
67 P::Signature: Codec,
68 C: AuxStore,
69{
70 let check_result = sc_consensus_aura::standalone::check_header_slot_and_seal::<B, P>(
71 slot_now,
72 header,
73 authorities,
74 );
75
76 match check_result {
77 Ok((header, slot, seal)) => {
78 let expected_author =
79 sc_consensus_aura::standalone::slot_author::<P>(slot, authorities);
80 let should_equiv_check = matches!(check_for_equivocation, CheckForEquivocation::Yes);
81 if let (true, Some(expected)) = (should_equiv_check, expected_author) {
82 if let Some(equivocation_proof) =
83 check_equivocation(client, slot_now, slot, &header, expected)
84 .map_err(Error::Client)?
85 {
86 info!(
87 target: LOG_TARGET,
88 "Slot author is equivocating at slot {} with headers {:?} and {:?}",
89 slot,
90 equivocation_proof.first_header.hash(),
91 equivocation_proof.second_header.hash(),
92 );
93 }
94 }
95
96 Ok(CheckedHeader::Checked(header, (slot, seal)))
97 },
98 Err(SealVerificationError::Deferred(header, slot)) => {
99 Ok(CheckedHeader::Deferred(header, slot))
100 },
101 Err(SealVerificationError::Unsealed) => Err(Error::HeaderUnsealed(hash)),
102 Err(SealVerificationError::BadSeal) => Err(Error::HeaderBadSeal(hash)),
103 Err(SealVerificationError::BadSignature) => Err(Error::BadSignature(hash)),
104 Err(SealVerificationError::SlotAuthorNotFound) => Err(Error::SlotAuthorNotFound),
105 Err(SealVerificationError::InvalidPreDigest(e)) => Err(Error::from(e)),
106 }
107}
108
109pub struct AuraVerifier<C, P, CIDP, N, ID> {
111 client: Arc<C>,
112 create_inherent_data_providers: CIDP,
113 check_for_equivocation: CheckForEquivocation,
114 telemetry: Option<TelemetryHandle>,
115 compatibility_mode: CompatibilityMode<N>,
116 _phantom: PhantomData<(fn() -> P, ID)>,
117}
118
119impl<C, P, CIDP, N, ID> AuraVerifier<C, P, CIDP, N, ID> {
120 pub(crate) fn new(
121 client: Arc<C>,
122 create_inherent_data_providers: CIDP,
123 check_for_equivocation: CheckForEquivocation,
124 telemetry: Option<TelemetryHandle>,
125 compatibility_mode: CompatibilityMode<N>,
126 ) -> Self {
127 Self {
128 client,
129 create_inherent_data_providers,
130 check_for_equivocation,
131 telemetry,
132 compatibility_mode,
133 _phantom: PhantomData,
134 }
135 }
136}
137
138impl<C, P, CIDP, N, ID> AuraVerifier<C, P, CIDP, N, ID>
139where
140 CIDP: Send,
141{
142 async fn check_inherents<B: BlockT>(
143 &self,
144 block: B,
145 at_hash: B::Hash,
146 inherent_data_providers: CIDP::InherentDataProviders,
147 ) -> Result<(), Error<B>>
148 where
149 C: ProvideRuntimeApi<B>,
150 C::Api: BlockBuilderApi<B>,
151 CIDP: CreateInherentDataProviders<B, (Slot, <ID as InherentDigest>::Value)>,
152 ID: InherentDigest,
153 {
154 let inherent_data = create_inherent_data::<B>(&inherent_data_providers).await?;
155
156 let inherent_res = self
157 .client
158 .runtime_api()
159 .check_inherents(at_hash, block, inherent_data)
160 .map_err(|e| Error::Client(e.into()))?;
161
162 if !inherent_res.ok() {
163 for (i, e) in inherent_res.into_errors() {
164 match inherent_data_providers.try_handle_error(&i, &e).await {
165 Some(res) => res.map_err(Error::Inherent)?,
166 None => return Err(Error::UnknownInherentError(i)),
167 }
168 }
169 }
170
171 Ok(())
172 }
173}
174
175#[async_trait::async_trait]
176impl<B: BlockT, C, P, CIDP, ID> Verifier<B> for AuraVerifier<C, P, CIDP, NumberFor<B>, ID>
177where
178 C: ProvideRuntimeApi<B> + Send + Sync + AuxStore,
179 C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>> + ApiExt<B>,
180 P: Pair,
181 P::Public: Codec + Debug,
182 P::Signature: Codec,
183 CIDP: CurrentSlotProvider
184 + CreateInherentDataProviders<B, (Slot, <ID as InherentDigest>::Value)>
185 + Send
186 + Sync,
187 ID: InherentDigest + Send + Sync + 'static,
188{
189 async fn verify(
190 &self,
191 mut block: BlockImportParams<B>,
192 ) -> Result<BlockImportParams<B>, String> {
193 if block.with_state() || block.state_action.skip_execution_checks() {
199 block.fork_choice = Some(ForkChoiceStrategy::Custom(block.with_state()));
201
202 return Ok(block);
203 }
204
205 let hash = block.header.hash();
206 let parent_hash = *block.header.parent_hash();
207 let authorities = authorities(
208 self.client.as_ref(),
209 parent_hash,
210 *block.header.number(),
211 &self.compatibility_mode,
212 )
213 .map_err(|e| format!("Could not fetch authorities at {:?}: {}", parent_hash, e))?;
214
215 let slot_now = self.create_inherent_data_providers.slot();
216
217 let checked_header = check_header::<C, B, P>(
221 &self.client,
222 slot_now + 1,
223 block.header.clone(),
224 hash,
225 &authorities[..],
226 self.check_for_equivocation,
227 )
228 .map_err(|e| e.to_string())?;
229 let inherent_digest = <ID as InherentDigest>::value_from_digest(
230 block.header.digest().logs(),
231 )
232 .map_err(|e| {
233 format!("Failed to retrieve inherent digest from header at {:?}: {}", parent_hash, e)
234 })?;
235 match checked_header {
236 CheckedHeader::Checked(pre_header, (slot, seal)) => {
237 if let Some(inner_body) = block.body.take() {
241 let new_block = B::new(pre_header.clone(), inner_body);
242
243 let inherent_data_providers = create_inherent_data_provider(
244 &self.create_inherent_data_providers,
245 parent_hash,
246 (slot, inherent_digest),
247 )
248 .await?;
249
250 if self
253 .client
254 .runtime_api()
255 .has_api_with::<dyn BlockBuilderApi<B>, _>(parent_hash, |v| v >= 2)
256 .map_err(|e| e.to_string())?
257 {
258 self.check_inherents(
259 new_block.clone(),
260 parent_hash,
261 inherent_data_providers,
262 )
263 .await
264 .map_err(|e| e.to_string())?;
265 }
266
267 let (_, inner_body) = new_block.deconstruct();
268 block.body = Some(inner_body);
269 }
270
271 trace!(target: LOG_TARGET, "Checked {:?}; importing.", pre_header);
272 telemetry!(
273 self.telemetry;
274 CONSENSUS_TRACE;
275 "aura.checked_and_importing";
276 "pre_header" => ?pre_header,
277 );
278
279 block.header = pre_header;
280 block.post_digests.push(seal);
281 block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
282 block.post_hash = Some(hash);
283
284 Ok(block)
285 },
286 CheckedHeader::Deferred(a, b) => {
287 debug!(target: LOG_TARGET, "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
288 telemetry!(
289 self.telemetry;
290 CONSENSUS_DEBUG;
291 "aura.header_too_far_in_future";
292 "hash" => ?hash,
293 "a" => ?a,
294 "b" => ?b,
295 );
296 Err(format!("Header {:?} rejected: too far in the future", hash))
297 },
298 }
299 }
300}
301
302pub fn import_queue<P, Block, I, C, S, CIDP, ID>(
304 ImportQueueParams {
305 block_import,
306 justification_import,
307 client,
308 create_inherent_data_providers,
309 spawner,
310 registry,
311 check_for_equivocation,
312 telemetry,
313 compatibility_mode,
314 }: ImportQueueParams<Block, I, C, S, CIDP>,
315) -> Result<DefaultImportQueue<Block>, sp_consensus::Error>
316where
317 Block: BlockT,
318 C::Api: BlockBuilderApi<Block> + AuraApi<Block, AuthorityId<P>> + ApiExt<Block>,
319 C: 'static
320 + ProvideRuntimeApi<Block>
321 + BlockOf
322 + Send
323 + Sync
324 + AuxStore
325 + UsageProvider<Block>
326 + HeaderBackend<Block>,
327 I: BlockImport<Block, Error = ConsensusError> + Send + Sync + 'static,
328 P: Pair + 'static,
329 P::Public: Codec + Debug,
330 P::Signature: Codec,
331 S: sp_core::traits::SpawnEssentialNamed,
332 CIDP: CurrentSlotProvider
333 + CreateInherentDataProviders<Block, (Slot, <ID as InherentDigest>::Value)>
334 + Sync
335 + Send
336 + 'static,
337 ID: InherentDigest + Send + Sync + 'static,
338{
339 let verifier = AuraVerifier::<_, P, _, _, ID>::new(
340 client,
341 create_inherent_data_providers,
342 check_for_equivocation,
343 telemetry,
344 compatibility_mode,
345 );
346
347 Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry))
348}
349
350async fn create_inherent_data_provider<CIDP, B: BlockT, ExtraArg>(
351 cidp: &CIDP,
352 hash: B::Hash,
353 extra_arg: ExtraArg,
354) -> Result<CIDP::InherentDataProviders, String>
355where
356 CIDP: CreateInherentDataProviders<B, ExtraArg>,
357{
358 cidp.create_inherent_data_providers(hash, extra_arg)
359 .await
360 .map_err(|e| Error::<B>::Client(sp_blockchain::Error::Application(e)).into())
361}
362
363async fn create_inherent_data<B: BlockT>(
364 provider: &impl InherentDataProvider,
365) -> Result<InherentData, Error<B>> {
366 provider.create_inherent_data().await.map_err(Error::<B>::Inherent)
367}