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, 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/// 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: 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		// Skip checks that include execution, if being told so or when importing only state.
163		//
164		// This is done for example when gap syncing and it is expected that the block after the gap
165		// was checked/chosen properly, e.g. by warp syncing to this block using a finality proof.
166		// Or when we are importing state only and can not verify the seal.
167		if block.with_state() || block.state_action.skip_execution_checks() {
168			// When we are importing only the state of a block, it will be the best block.
169			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		// we add one to allow for some small drift.
187		// FIXME #1019 in the future, alter this queue to allow deferring of
188		// headers
189		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 the body is passed through, we need to use the runtime
207				// to check that the internally-set timestamp in the inherents
208				// actually matches the slot set in the seal.
209				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					// skip the inherents verification if the runtime API is old or not expected to
220					// exist.
221					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
275/// Start an import queue for the Aura consensus algorithm.
276pub 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}