referrerpolicy=no-referrer-when-downgrade

cumulus_client_consensus_common/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5// Cumulus is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// Cumulus is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with Cumulus. If not, see <https://www.gnu.org/licenses/>.
17
18use codec::Decode;
19use polkadot_primitives::{Block as PBlock, Hash as PHash, Header as PHeader, ValidationCodeHash};
20
21use cumulus_primitives_core::{relay_chain, AbridgedHostConfiguration};
22use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface};
23
24use sc_client_api::Backend;
25use sc_consensus::{shared_data::SharedData, BlockImport, ImportResult};
26use sp_consensus_slots::Slot;
27
28use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
29use sp_timestamp::Timestamp;
30
31use std::{sync::Arc, time::Duration};
32
33mod finality;
34mod level_monitor;
35mod parachain_consensus;
36mod parent_search;
37#[cfg(test)]
38mod tests;
39
40pub use finality::old_finalized_hash;
41pub use parent_search::*;
42
43pub use cumulus_relay_chain_streams::finalized_heads;
44pub use parachain_consensus::spawn_parachain_consensus_tasks;
45
46use level_monitor::LevelMonitor;
47pub use level_monitor::{LevelLimit, MAX_LEAVES_PER_LEVEL_SENSIBLE_DEFAULT};
48
49pub mod import_queue;
50
51const LOG_TARGET: &str = "consensus::common";
52
53/// Provides the hash of validation code used for authoring/execution of blocks at a given
54/// hash.
55pub trait ValidationCodeHashProvider<Hash> {
56	fn code_hash_at(&self, at: Hash) -> Option<ValidationCodeHash>;
57}
58
59impl<F, Hash> ValidationCodeHashProvider<Hash> for F
60where
61	F: Fn(Hash) -> Option<ValidationCodeHash>,
62{
63	fn code_hash_at(&self, at: Hash) -> Option<ValidationCodeHash> {
64		(self)(at)
65	}
66}
67
68/// The result from building a collation.
69pub struct ParachainCandidate<B> {
70	/// The block that was built for this candidate.
71	pub block: B,
72	/// The proof that was recorded while building the block.
73	pub proof: sp_trie::StorageProof,
74}
75
76/// Parachain specific block import.
77///
78/// Specialized block import for parachains. It supports to delay setting the best block until the
79/// relay chain has included a candidate in its best block. By default the delayed best block
80/// setting is disabled. The block import also monitors the imported blocks and prunes by default if
81/// there are too many blocks at the same height. Too many blocks at the same height can for example
82/// happen if the relay chain is rejecting the parachain blocks in the validation.
83pub struct ParachainBlockImport<Block: BlockT, BI, BE> {
84	inner: BI,
85	monitor: Option<SharedData<LevelMonitor<Block, BE>>>,
86	delayed_best_block: bool,
87}
88
89impl<Block: BlockT, BI, BE: Backend<Block>> ParachainBlockImport<Block, BI, BE> {
90	/// Create a new instance.
91	///
92	/// The number of leaves per level limit is set to `LevelLimit::Default`.
93	pub fn new(inner: BI, backend: Arc<BE>) -> Self {
94		Self::new_with_limit(inner, backend, LevelLimit::Default)
95	}
96
97	/// Create a new instance with an explicit limit to the number of leaves per level.
98	///
99	/// This function alone doesn't enforce the limit on levels for old imported blocks,
100	/// the limit is eventually enforced only when new blocks are imported.
101	pub fn new_with_limit(inner: BI, backend: Arc<BE>, level_leaves_max: LevelLimit) -> Self {
102		let level_limit = match level_leaves_max {
103			LevelLimit::None => None,
104			LevelLimit::Some(limit) => Some(limit),
105			LevelLimit::Default => Some(MAX_LEAVES_PER_LEVEL_SENSIBLE_DEFAULT),
106		};
107
108		let monitor =
109			level_limit.map(|level_limit| SharedData::new(LevelMonitor::new(level_limit, backend)));
110
111		Self { inner, monitor, delayed_best_block: false }
112	}
113
114	/// Create a new instance which delays setting the best block.
115	///
116	/// The number of leaves per level limit is set to `LevelLimit::Default`.
117	pub fn new_with_delayed_best_block(inner: BI, backend: Arc<BE>) -> Self {
118		Self {
119			delayed_best_block: true,
120			..Self::new_with_limit(inner, backend, LevelLimit::Default)
121		}
122	}
123}
124
125impl<Block: BlockT, I: Clone, BE> Clone for ParachainBlockImport<Block, I, BE> {
126	fn clone(&self) -> Self {
127		ParachainBlockImport {
128			inner: self.inner.clone(),
129			monitor: self.monitor.clone(),
130			delayed_best_block: self.delayed_best_block,
131		}
132	}
133}
134
135#[async_trait::async_trait]
136impl<Block, BI, BE> BlockImport<Block> for ParachainBlockImport<Block, BI, BE>
137where
138	Block: BlockT,
139	BI: BlockImport<Block> + Send + Sync,
140	BE: Backend<Block>,
141{
142	type Error = BI::Error;
143
144	async fn check_block(
145		&self,
146		block: sc_consensus::BlockCheckParams<Block>,
147	) -> Result<sc_consensus::ImportResult, Self::Error> {
148		self.inner.check_block(block).await
149	}
150
151	async fn import_block(
152		&self,
153		mut params: sc_consensus::BlockImportParams<Block>,
154	) -> Result<sc_consensus::ImportResult, Self::Error> {
155		// Blocks are stored within the backend by using POST hash.
156		let hash = params.post_hash();
157		let number = *params.header.number();
158
159		if params.with_state() {
160			// Force imported state finality.
161			// Required for warp sync. We assume that preconditions have been
162			// checked properly and we are importing a finalized block with state.
163			params.finalized = true;
164		}
165
166		if self.delayed_best_block {
167			// Best block is determined by the relay chain, or if we are doing the initial sync
168			// we import all blocks as new best.
169			params.fork_choice = Some(sc_consensus::ForkChoiceStrategy::Custom(
170				params.origin == sp_consensus::BlockOrigin::NetworkInitialSync,
171			));
172		}
173
174		let maybe_lock = self.monitor.as_ref().map(|monitor_lock| {
175			let mut monitor = monitor_lock.shared_data_locked();
176			monitor.enforce_limit(number);
177			monitor.release_mutex()
178		});
179
180		let res = self.inner.import_block(params).await?;
181
182		if let (Some(mut monitor_lock), ImportResult::Imported(_)) = (maybe_lock, &res) {
183			let mut monitor = monitor_lock.upgrade();
184			monitor.block_imported(number, hash);
185		}
186
187		Ok(res)
188	}
189}
190
191/// Marker trait denoting a block import type that fits the parachain requirements.
192pub trait ParachainBlockImportMarker {}
193
194impl<B: BlockT, BI, BE> ParachainBlockImportMarker for ParachainBlockImport<B, BI, BE> {}
195
196/// Get the relay slot from a header.
197pub fn get_relay_slot(relay_header: &PHeader) -> Option<Slot> {
198	match sc_consensus_babe::find_pre_digest::<PBlock>(relay_header) {
199		Ok(pre_digest) => Some(pre_digest.slot()),
200		Err(err) => {
201			tracing::error!(
202				target: LOG_TARGET,
203				hash = %relay_header.hash(),
204				?err,
205				"Relay chain block does not contain a BABE pre-digest. This should never happen.",
206			);
207			None
208		},
209	}
210}
211
212/// Get the relay slot and timestamp from a header.
213pub fn get_relay_slot_and_timestamp(
214	relay_header: &PHeader,
215	relay_slot_duration: Duration,
216) -> Option<(Slot, Timestamp)> {
217	get_relay_slot(relay_header).map(|slot| {
218		let t = Timestamp::new(relay_slot_duration.as_millis() as u64 * *slot);
219		(slot, t)
220	})
221}
222
223/// Reads abridged host configuration from the relay chain storage at the given relay parent.
224pub async fn load_abridged_host_configuration(
225	relay_parent: PHash,
226	relay_client: &impl RelayChainInterface,
227) -> Result<Option<AbridgedHostConfiguration>, RelayChainError> {
228	relay_client
229		.get_storage_by_key(relay_parent, relay_chain::well_known_keys::ACTIVE_CONFIG)
230		.await?
231		.map(|bytes| {
232			AbridgedHostConfiguration::decode(&mut &bytes[..])
233				.map_err(RelayChainError::DeserializationError)
234		})
235		.transpose()
236}