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, HeaderMetadata};
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: Pair, CIDP, B: BlockT, ID> {
111 client: Arc<C>,
112 create_inherent_data_providers: CIDP,
113 check_for_equivocation: CheckForEquivocation,
114 telemetry: Option<TelemetryHandle>,
115 compatibility_mode: CompatibilityMode<NumberFor<B>>,
116 _phantom: PhantomData<(fn() -> P, ID)>,
117}
118
119impl<C, P: Pair, CIDP, B: BlockT, ID> AuraVerifier<C, P, CIDP, B, 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<NumberFor<B>>,
126 ) -> Self {
127 Self {
128 client: client.clone(),
129 create_inherent_data_providers,
130 check_for_equivocation,
131 telemetry,
132 compatibility_mode,
133 _phantom: PhantomData,
134 }
135 }
136}
137
138#[async_trait::async_trait]
139impl<B, C, P, CIDP, ID> Verifier<B> for AuraVerifier<C, P, CIDP, B, ID>
140where
141 B: BlockT,
142 C: HeaderBackend<B>
143 + HeaderMetadata<B, Error = sp_blockchain::Error>
144 + ProvideRuntimeApi<B>
145 + Send
146 + Sync
147 + sc_client_api::backend::AuxStore,
148 C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>> + ApiExt<B>,
149 P: Pair,
150 P::Public: Codec + Debug,
151 P::Signature: Codec,
152 CIDP: CurrentSlotProvider
153 + CreateInherentDataProviders<B, (Slot, <ID as InherentDigest>::Value)>
154 + Send
155 + Sync,
156 ID: InherentDigest + Send + Sync + 'static,
157{
158 async fn verify(
159 &self,
160 mut block: BlockImportParams<B>,
161 ) -> Result<BlockImportParams<B>, String> {
162 if block.with_state() || block.state_action.skip_execution_checks() {
168 block.fork_choice = Some(ForkChoiceStrategy::Custom(block.with_state()));
170
171 return Ok(block);
172 }
173
174 let hash = block.header.hash();
175 let parent_hash = *block.header.parent_hash();
176 let authorities = authorities(
177 self.client.as_ref(),
178 parent_hash,
179 *block.header.number(),
180 &self.compatibility_mode,
181 )
182 .map_err(|e| format!("Could not fetch authorities at {:?}: {}", parent_hash, e))?;
183
184 let slot_now = self.create_inherent_data_providers.slot();
185
186 let checked_header = check_header::<C, B, P>(
190 &self.client,
191 slot_now + 1,
192 block.header.clone(),
193 hash,
194 &authorities[..],
195 self.check_for_equivocation,
196 )
197 .map_err(|e| e.to_string())?;
198 let inherent_digest = <ID as InherentDigest>::value_from_digest(
199 block.header.digest().logs(),
200 )
201 .map_err(|e| {
202 format!("Failed to retrieve inherent digest from header at {:?}: {}", parent_hash, e)
203 })?;
204 match checked_header {
205 CheckedHeader::Checked(pre_header, (slot, seal)) => {
206 if let Some(inner_body) = block.body.take() {
210 let new_block = B::new(pre_header.clone(), inner_body);
211
212 let inherent_data_providers = create_inherent_data_provider(
213 &self.create_inherent_data_providers,
214 parent_hash,
215 (slot, inherent_digest),
216 )
217 .await?;
218
219 if self
222 .client
223 .runtime_api()
224 .has_api_with::<dyn BlockBuilderApi<B>, _>(parent_hash, |v| v >= 2)
225 .map_err(|e| e.to_string())?
226 {
227 let inherent_data =
228 create_inherent_data::<B>(&inherent_data_providers).await?;
229 sp_block_builder::check_inherents_with_data(
230 self.client.clone(),
231 parent_hash,
232 new_block.clone(),
233 &inherent_data_providers,
234 inherent_data,
235 )
236 .await
237 .map_err(|e| format!("Error checking block inherents {:?}", e))?;
238 }
239
240 let (_, inner_body) = new_block.deconstruct();
241 block.body = Some(inner_body);
242 }
243
244 trace!(target: LOG_TARGET, "Checked {:?}; importing.", pre_header);
245 telemetry!(
246 self.telemetry;
247 CONSENSUS_TRACE;
248 "aura.checked_and_importing";
249 "pre_header" => ?pre_header,
250 );
251
252 block.header = pre_header;
253 block.post_digests.push(seal);
254 block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
255 block.post_hash = Some(hash);
256
257 Ok(block)
258 },
259 CheckedHeader::Deferred(a, b) => {
260 debug!(target: LOG_TARGET, "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
261 telemetry!(
262 self.telemetry;
263 CONSENSUS_DEBUG;
264 "aura.header_too_far_in_future";
265 "hash" => ?hash,
266 "a" => ?a,
267 "b" => ?b,
268 );
269 Err(format!("Header {:?} rejected: too far in the future", hash))
270 },
271 }
272 }
273}
274
275pub fn import_queue<P, Block, I, C, S, CIDP, ID>(
277 ImportQueueParams {
278 block_import,
279 justification_import,
280 client,
281 create_inherent_data_providers,
282 spawner,
283 registry,
284 check_for_equivocation,
285 telemetry,
286 compatibility_mode,
287 }: ImportQueueParams<Block, I, C, S, CIDP>,
288) -> Result<DefaultImportQueue<Block>, sp_consensus::Error>
289where
290 Block: BlockT,
291 C::Api: BlockBuilderApi<Block> + AuraApi<Block, AuthorityId<P>> + ApiExt<Block>,
292 C: 'static
293 + ProvideRuntimeApi<Block>
294 + BlockOf
295 + Send
296 + Sync
297 + AuxStore
298 + UsageProvider<Block>
299 + HeaderBackend<Block>
300 + HeaderMetadata<Block, Error = sp_blockchain::Error>,
301 I: BlockImport<Block, Error = ConsensusError> + Send + Sync + 'static,
302 P: Pair + 'static,
303 P::Public: Codec + Debug,
304 P::Signature: Codec,
305 S: sp_core::traits::SpawnEssentialNamed,
306 CIDP: CurrentSlotProvider
307 + CreateInherentDataProviders<Block, (Slot, <ID as InherentDigest>::Value)>
308 + Sync
309 + Send
310 + 'static,
311 ID: InherentDigest + Send + Sync + 'static,
312{
313 let verifier = AuraVerifier::<_, P, _, _, ID>::new(
314 client,
315 create_inherent_data_providers,
316 check_for_equivocation,
317 telemetry,
318 compatibility_mode,
319 );
320
321 Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry))
322}
323
324async fn create_inherent_data_provider<CIDP, B: BlockT, ExtraArg>(
325 cidp: &CIDP,
326 hash: B::Hash,
327 extra_arg: ExtraArg,
328) -> Result<CIDP::InherentDataProviders, String>
329where
330 CIDP: CreateInherentDataProviders<B, ExtraArg>,
331{
332 cidp.create_inherent_data_providers(hash, extra_arg)
333 .await
334 .map_err(|e| Error::<B>::Client(sp_blockchain::Error::Application(e)).into())
335}
336
337async fn create_inherent_data<B: BlockT>(
338 provider: &impl InherentDataProvider,
339) -> Result<InherentData, Error<B>> {
340 provider.create_inherent_data().await.map_err(Error::<B>::Inherent)
341}