sc_partner_chains_consensus_aura/
import_queue.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17// Additional modifications by Input Output Global, Inc.
18// Copyright (C) 2024, Input Output Global, Inc.
19
20//! Module implementing the logic for verifying and importing AuRa blocks.
21
22use 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/// check a header has been signed by the right key. If the slot is too far in the future, an error
52/// will be returned. If it's successful, returns the pre-header and the digest item
53/// containing the seal.
54///
55/// This digest item will always return `Some` when used with `as_aura_seal`.
56#[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
109/// A verifier for Aura blocks, with added ID phantom type.
110pub 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		// Skip checks that include execution, if being told so or when importing only state.
194		//
195		// This is done for example when gap syncing and it is expected that the block after the gap
196		// was checked/chosen properly, e.g. by warp syncing to this block using a finality proof.
197		// Or when we are importing state only and can not verify the seal.
198		if block.with_state() || block.state_action.skip_execution_checks() {
199			// When we are importing only the state of a block, it will be the best block.
200			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		// we add one to allow for some small drift.
218		// FIXME #1019 in the future, alter this queue to allow deferring of
219		// headers
220		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 the body is passed through, we need to use the runtime
238				// to check that the internally-set timestamp in the inherents
239				// actually matches the slot set in the seal.
240				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					// skip the inherents verification if the runtime API is old or not expected to
251					// exist.
252					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
302/// Start an import queue for the Aura consensus algorithm.
303pub 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}