// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Cumulus is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . use polkadot_primitives::{Hash as PHash, PersistedValidationData}; use sc_client_api::Backend; use sc_consensus::{shared_data::SharedData, BlockImport, ImportResult}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use std::sync::Arc; mod level_monitor; mod parachain_consensus; #[cfg(test)] mod tests; pub use parachain_consensus::run_parachain_consensus; use level_monitor::LevelMonitor; pub use level_monitor::{LevelLimit, MAX_LEAVES_PER_LEVEL_SENSIBLE_DEFAULT}; /// The result of [`ParachainConsensus::produce_candidate`]. pub struct ParachainCandidate { /// The block that was built for this candidate. pub block: B, /// The proof that was recorded while building the block. pub proof: sp_trie::StorageProof, } /// A specific parachain consensus implementation that can be used by a collator to produce candidates. /// /// The collator will call [`Self::produce_candidate`] every time there is a free core for the parachain /// this collator is collating for. It is the job of the consensus implementation to decide if this /// specific collator should build a candidate for the given relay chain block. The consensus /// implementation could, for example, check whether this specific collator is part of a staked set. #[async_trait::async_trait] pub trait ParachainConsensus: Send + Sync + dyn_clone::DynClone { /// Produce a new candidate at the given parent block and relay-parent blocks. /// /// Should return `None` if the consensus implementation decided that it shouldn't build a /// candidate or if there occurred any error. /// /// # NOTE /// /// It is expected that the block is already imported when the future resolves. async fn produce_candidate( &mut self, parent: &B::Header, relay_parent: PHash, validation_data: &PersistedValidationData, ) -> Option>; } dyn_clone::clone_trait_object!( ParachainConsensus where B: BlockT); #[async_trait::async_trait] impl ParachainConsensus for Box + Send + Sync> { async fn produce_candidate( &mut self, parent: &B::Header, relay_parent: PHash, validation_data: &PersistedValidationData, ) -> Option> { (*self).produce_candidate(parent, relay_parent, validation_data).await } } /// Parachain specific block import. /// /// This is used to set `block_import_params.fork_choice` to `false` as long as the block origin is /// not `NetworkInitialSync`. The best block for parachains is determined by the relay chain. Meaning /// we will update the best block, as it is included by the relay-chain. pub struct ParachainBlockImport { inner: BI, monitor: Option>>, } impl> ParachainBlockImport { /// Create a new instance. /// /// The number of leaves per level limit is set to `LevelLimit::Default`. pub fn new(inner: BI, backend: Arc) -> Self { Self::new_with_limit(inner, backend, LevelLimit::Default) } /// Create a new instance with an explicit limit to the number of leaves per level. /// /// This function alone doesn't enforce the limit on levels for old imported blocks, /// the limit is eventually enforced only when new blocks are imported. pub fn new_with_limit(inner: BI, backend: Arc, level_leaves_max: LevelLimit) -> Self { let level_limit = match level_leaves_max { LevelLimit::None => None, LevelLimit::Some(limit) => Some(limit), LevelLimit::Default => Some(MAX_LEAVES_PER_LEVEL_SENSIBLE_DEFAULT), }; let monitor = level_limit.map(|level_limit| SharedData::new(LevelMonitor::new(level_limit, backend))); Self { inner, monitor } } } impl Clone for ParachainBlockImport { fn clone(&self) -> Self { ParachainBlockImport { inner: self.inner.clone(), monitor: self.monitor.clone() } } } #[async_trait::async_trait] impl BlockImport for ParachainBlockImport where Block: BlockT, BI: BlockImport + Send, BE: Backend, { type Error = BI::Error; type Transaction = BI::Transaction; async fn check_block( &mut self, block: sc_consensus::BlockCheckParams, ) -> Result { self.inner.check_block(block).await } async fn import_block( &mut self, mut params: sc_consensus::BlockImportParams, ) -> Result { // Blocks are stored within the backend by using POST hash. let hash = params.post_hash(); let number = *params.header.number(); // Best block is determined by the relay chain, or if we are doing the initial sync // we import all blocks as new best. params.fork_choice = Some(sc_consensus::ForkChoiceStrategy::Custom( params.origin == sp_consensus::BlockOrigin::NetworkInitialSync, )); let maybe_lock = self.monitor.as_ref().map(|monitor_lock| { let mut monitor = monitor_lock.shared_data_locked(); monitor.enforce_limit(number); monitor.release_mutex() }); let res = self.inner.import_block(params).await?; if let (Some(mut monitor_lock), ImportResult::Imported(_)) = (maybe_lock, &res) { let mut monitor = monitor_lock.upgrade(); monitor.block_imported(number, hash); } Ok(res) } } /// Marker trait denoting a block import type that fits the parachain requirements. pub trait ParachainBlockImportMarker {} impl ParachainBlockImportMarker for ParachainBlockImport {}