1use crate::{
22 fetch_authorities_from_runtime, standalone::SealVerificationError, AuthoritiesTracker,
23 AuthorityId, CompatibilityMode, Error, LOG_TARGET,
24};
25use codec::Codec;
26use log::{debug, info, trace};
27use prometheus_endpoint::Registry;
28use sc_client_api::{backend::AuxStore, BlockOf, UsageProvider};
29use sc_consensus::{
30 block_import::{BlockImport, BlockImportParams, ForkChoiceStrategy},
31 import_queue::{BasicQueue, BoxJustificationImport, DefaultImportQueue, Verifier},
32};
33use sc_consensus_slots::{check_equivocation, CheckedHeader, InherentDataProviderExt};
34use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_TRACE};
35use sp_api::{ApiExt, ProvideRuntimeApi};
36use sp_block_builder::BlockBuilder as BlockBuilderApi;
37use sp_blockchain::{HeaderBackend, HeaderMetadata};
38use sp_consensus::Error as ConsensusError;
39use sp_consensus_aura::{inherents::AuraInherentData, AuraApi};
40use sp_consensus_slots::Slot;
41use sp_core::crypto::Pair;
42use sp_inherents::{CreateInherentDataProviders, InherentDataProvider as _};
43use sp_runtime::{
44 traits::{Block as BlockT, Header, NumberFor},
45 DigestItem,
46};
47use std::{fmt::Debug, sync::Arc};
48
49fn check_header<C, B: BlockT, P: Pair>(
55 client: &C,
56 slot_now: Slot,
57 header: B::Header,
58 hash: B::Hash,
59 authorities: &[AuthorityId<P>],
60 check_for_equivocation: CheckForEquivocation,
61) -> Result<CheckedHeader<B::Header, (Slot, DigestItem)>, Error<B>>
62where
63 P::Public: Codec,
64 P::Signature: Codec,
65 C: sc_client_api::backend::AuxStore,
66{
67 let check_result =
68 crate::standalone::check_header_slot_and_seal::<B, P>(slot_now, header, authorities);
69
70 match check_result {
71 Ok((header, slot, seal)) => {
72 let expected_author = crate::standalone::slot_author::<P>(slot, &authorities);
73 let should_equiv_check = check_for_equivocation.check_for_equivocation();
74 if let (true, Some(expected)) = (should_equiv_check, expected_author) {
75 if let Some(equivocation_proof) =
76 check_equivocation(client, slot_now, slot, &header, expected)
77 .map_err(Error::Client)?
78 {
79 info!(
80 target: LOG_TARGET,
81 "Slot author is equivocating at slot {} with headers {:?} and {:?}",
82 slot,
83 equivocation_proof.first_header.hash(),
84 equivocation_proof.second_header.hash(),
85 );
86 }
87 }
88
89 Ok(CheckedHeader::Checked(header, (slot, seal)))
90 },
91 Err(SealVerificationError::Deferred(header, slot)) => {
92 Ok(CheckedHeader::Deferred(header, slot))
93 },
94 Err(SealVerificationError::Unsealed) => Err(Error::HeaderUnsealed(hash)),
95 Err(SealVerificationError::BadSeal) => Err(Error::HeaderBadSeal(hash)),
96 Err(SealVerificationError::BadSignature) => Err(Error::BadSignature(hash)),
97 Err(SealVerificationError::SlotAuthorNotFound) => Err(Error::SlotAuthorNotFound),
98 Err(SealVerificationError::InvalidPreDigest(e)) => Err(Error::from(e)),
99 }
100}
101
102pub struct AuraVerifier<C, P: Pair, CIDP, B: BlockT> {
104 client: Arc<C>,
105 create_inherent_data_providers: CIDP,
106 check_for_equivocation: CheckForEquivocation,
107 telemetry: Option<TelemetryHandle>,
108 compatibility_mode: CompatibilityMode<NumberFor<B>>,
109 _authorities_tracker: AuthoritiesTracker<P, B, C>,
110}
111
112impl<C, P: Pair, CIDP, B: BlockT> AuraVerifier<C, P, CIDP, B> {
113 pub(crate) fn new(
114 client: Arc<C>,
115 create_inherent_data_providers: CIDP,
116 check_for_equivocation: CheckForEquivocation,
117 telemetry: Option<TelemetryHandle>,
118 compatibility_mode: CompatibilityMode<NumberFor<B>>,
119 ) -> Self {
120 Self {
121 client: client.clone(),
122 create_inherent_data_providers,
123 check_for_equivocation,
124 telemetry,
125 compatibility_mode,
126 _authorities_tracker: AuthoritiesTracker::new(client),
127 }
128 }
129}
130
131#[async_trait::async_trait]
132impl<B, C, P, CIDP> Verifier<B> for AuraVerifier<C, P, CIDP, B>
133where
134 B: BlockT,
135 C: HeaderBackend<B>
136 + HeaderMetadata<B, Error = sp_blockchain::Error>
137 + ProvideRuntimeApi<B>
138 + Send
139 + Sync
140 + sc_client_api::backend::AuxStore,
141 C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>> + ApiExt<B>,
142 P: Pair,
143 P::Public: Codec + Debug,
144 P::Signature: Codec,
145 CIDP: CreateInherentDataProviders<B, ()> + Send + Sync,
146 CIDP::InherentDataProviders: InherentDataProviderExt + Send + Sync,
147{
148 async fn verify(
149 &self,
150 mut block: BlockImportParams<B>,
151 ) -> Result<BlockImportParams<B>, String> {
152 if block.with_state() || block.state_action.skip_execution_checks() {
158 block.fork_choice = Some(ForkChoiceStrategy::Custom(block.with_state()));
160
161 return Ok(block);
162 }
163
164 let hash = block.header.hash();
165 let parent_hash = *block.header.parent_hash();
166 let authorities = fetch_authorities_from_runtime(
167 self.client.as_ref(),
168 parent_hash,
169 *block.header.number(),
170 &self.compatibility_mode,
171 )
172 .map_err(|e| format!("Could not fetch authorities at {:?}: {}", parent_hash, e))?;
173
174 let create_inherent_data_providers = self
175 .create_inherent_data_providers
176 .create_inherent_data_providers(parent_hash, ())
177 .await
178 .map_err(|e| Error::<B>::Client(sp_blockchain::Error::Application(e)))?;
179
180 let mut inherent_data = create_inherent_data_providers
181 .create_inherent_data()
182 .await
183 .map_err(Error::<B>::Inherent)?;
184
185 let slot_now = create_inherent_data_providers.slot();
186
187 let checked_header = check_header::<C, B, P>(
191 &self.client,
192 slot_now + 1,
193 block.header,
194 hash,
195 &authorities[..],
196 self.check_for_equivocation,
197 )
198 .map_err(|e| e.to_string())?;
199 match checked_header {
200 CheckedHeader::Checked(pre_header, (slot, seal)) => {
201 if let Some(inner_body) = block.body.take() {
205 let new_block = B::new(pre_header.clone(), inner_body);
206
207 inherent_data.aura_replace_inherent_data(slot);
208
209 if self
212 .client
213 .runtime_api()
214 .has_api_with::<dyn BlockBuilderApi<B>, _>(parent_hash, |v| v >= 2)
215 .map_err(|e| e.to_string())?
216 {
217 sp_block_builder::check_inherents_with_data(
218 self.client.clone(),
219 parent_hash,
220 new_block.clone(),
221 &create_inherent_data_providers,
222 inherent_data,
223 )
224 .await
225 .map_err(|e| format!("Error checking block inherents {:?}", e))?;
226 }
227
228 let (_, inner_body) = new_block.deconstruct();
229 block.body = Some(inner_body);
230 }
231
232 trace!(target: LOG_TARGET, "Checked {:?}; importing.", pre_header);
233 telemetry!(
234 self.telemetry;
235 CONSENSUS_TRACE;
236 "aura.checked_and_importing";
237 "pre_header" => ?pre_header,
238 );
239
240 block.header = pre_header;
241 block.post_digests.push(seal);
242 block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
243 block.post_hash = Some(hash);
244
245 Ok(block)
246 },
247 CheckedHeader::Deferred(a, b) => {
248 debug!(target: LOG_TARGET, "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
249 telemetry!(
250 self.telemetry;
251 CONSENSUS_DEBUG;
252 "aura.header_too_far_in_future";
253 "hash" => ?hash,
254 "a" => ?a,
255 "b" => ?b,
256 );
257 Err(format!("Header {:?} rejected: too far in the future", hash))
258 },
259 }
260 }
261}
262
263#[derive(Debug, Clone, Copy)]
265pub enum CheckForEquivocation {
266 Yes,
270 No,
272}
273
274impl CheckForEquivocation {
275 fn check_for_equivocation(self) -> bool {
277 matches!(self, Self::Yes)
278 }
279}
280
281impl Default for CheckForEquivocation {
282 fn default() -> Self {
283 Self::Yes
284 }
285}
286
287pub struct ImportQueueParams<'a, Block: BlockT, I, C, S, CIDP> {
289 pub block_import: I,
291 pub justification_import: Option<BoxJustificationImport<Block>>,
293 pub client: Arc<C>,
295 pub create_inherent_data_providers: CIDP,
297 pub spawner: &'a S,
299 pub registry: Option<&'a Registry>,
301 pub check_for_equivocation: CheckForEquivocation,
303 pub telemetry: Option<TelemetryHandle>,
305 pub compatibility_mode: CompatibilityMode<NumberFor<Block>>,
309}
310
311pub fn import_queue<P, Block, I, C, S, CIDP>(
313 ImportQueueParams {
314 block_import,
315 justification_import,
316 client,
317 create_inherent_data_providers,
318 spawner,
319 registry,
320 check_for_equivocation,
321 telemetry,
322 compatibility_mode,
323 }: ImportQueueParams<Block, I, C, S, CIDP>,
324) -> Result<DefaultImportQueue<Block>, sp_consensus::Error>
325where
326 Block: BlockT,
327 C::Api: BlockBuilderApi<Block> + AuraApi<Block, AuthorityId<P>> + ApiExt<Block>,
328 C: 'static
329 + ProvideRuntimeApi<Block>
330 + BlockOf
331 + Send
332 + Sync
333 + AuxStore
334 + UsageProvider<Block>
335 + HeaderBackend<Block>
336 + HeaderMetadata<Block, Error = sp_blockchain::Error>,
337 I: BlockImport<Block, Error = ConsensusError> + Send + Sync + 'static,
338 P: Pair + 'static,
339 P::Public: Codec + Debug,
340 P::Signature: Codec,
341 S: sp_core::traits::SpawnEssentialNamed,
342 CIDP: CreateInherentDataProviders<Block, ()> + Sync + Send + 'static,
343 CIDP::InherentDataProviders: InherentDataProviderExt + Send + Sync,
344{
345 let verifier = build_verifier::<P, _, _, _>(BuildVerifierParams {
346 client,
347 create_inherent_data_providers,
348 check_for_equivocation,
349 telemetry,
350 compatibility_mode,
351 });
352
353 Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry))
354}
355
356pub struct BuildVerifierParams<C, CIDP, N> {
358 pub client: Arc<C>,
360 pub create_inherent_data_providers: CIDP,
362 pub check_for_equivocation: CheckForEquivocation,
364 pub telemetry: Option<TelemetryHandle>,
366 pub compatibility_mode: CompatibilityMode<N>,
370}
371
372pub fn build_verifier<P: Pair, C, CIDP, B: BlockT>(
374 BuildVerifierParams {
375 client,
376 create_inherent_data_providers,
377 check_for_equivocation,
378 telemetry,
379 compatibility_mode,
380 }: BuildVerifierParams<C, CIDP, NumberFor<B>>,
381) -> AuraVerifier<C, P, CIDP, B> {
382 AuraVerifier::<_, P, _, _>::new(
383 client,
384 create_inherent_data_providers,
385 check_for_equivocation,
386 telemetry,
387 compatibility_mode,
388 )
389}