// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // This file is part of Pezcumulus. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // Pezcumulus 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. // Pezcumulus 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 Pezcumulus. If not, see . use codec::Decode; use pezkuwi_primitives::{Block as PBlock, Hash as PHash, Header as PHeader, ValidationCodeHash}; use pezcumulus_primitives_core::{relay_chain, AbridgedHostConfiguration}; use pezcumulus_relay_chain_interface::{RelayChainError, RelayChainInterface}; use pezsc_client_api::Backend; use pezsc_consensus::{shared_data::SharedData, BlockImport, ImportResult}; use pezsp_consensus_slots::Slot; use pezsp_runtime::traits::{Block as BlockT, Header as HeaderT}; use pezsp_timestamp::Timestamp; use std::{sync::Arc, time::Duration}; mod level_monitor; mod parent_search; #[cfg(test)] mod tests; mod teyrchain_consensus; pub use parent_search::*; pub use pezcumulus_relay_chain_streams::finalized_heads; pub use teyrchain_consensus::spawn_teyrchain_consensus_tasks; use level_monitor::LevelMonitor; pub use level_monitor::{LevelLimit, MAX_LEAVES_PER_LEVEL_SENSIBLE_DEFAULT}; pub mod import_queue; /// Provides the hash of validation code used for authoring/execution of blocks at a given /// hash. pub trait ValidationCodeHashProvider { fn code_hash_at(&self, at: Hash) -> Option; } impl ValidationCodeHashProvider for F where F: Fn(Hash) -> Option, { fn code_hash_at(&self, at: Hash) -> Option { (self)(at) } } /// The result from building a collation. pub struct TeyrchainCandidate { /// The block that was built for this candidate. pub block: B, /// The proof that was recorded while building the block. pub proof: pezsp_trie::StorageProof, } /// Teyrchain specific block import. /// /// Specialized block import for teyrchains. It supports to delay setting the best block until the /// relay chain has included a candidate in its best block. By default the delayed best block /// setting is disabled. The block import also monitors the imported blocks and prunes by default if /// there are too many blocks at the same height. Too many blocks at the same height can for example /// happen if the relay chain is rejecting the teyrchain blocks in the validation. pub struct TeyrchainBlockImport { inner: BI, monitor: Option>>, delayed_best_block: bool, } impl> TeyrchainBlockImport { /// 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, delayed_best_block: false } } /// Create a new instance which delays setting the best block. /// /// The number of leaves per level limit is set to `LevelLimit::Default`. pub fn new_with_delayed_best_block(inner: BI, backend: Arc) -> Self { Self { delayed_best_block: true, ..Self::new_with_limit(inner, backend, LevelLimit::Default) } } } impl Clone for TeyrchainBlockImport { fn clone(&self) -> Self { TeyrchainBlockImport { inner: self.inner.clone(), monitor: self.monitor.clone(), delayed_best_block: self.delayed_best_block, } } } #[async_trait::async_trait] impl BlockImport for TeyrchainBlockImport where Block: BlockT, BI: BlockImport + Send + Sync, BE: Backend, { type Error = BI::Error; async fn check_block( &self, block: pezsc_consensus::BlockCheckParams, ) -> Result { self.inner.check_block(block).await } async fn import_block( &self, mut params: pezsc_consensus::BlockImportParams, ) -> Result { // Blocks are stored within the backend by using POST hash. let hash = params.post_hash(); let number = *params.header.number(); if params.with_state() { // Force imported state finality. // Required for warp sync. We assume that preconditions have been // checked properly and we are importing a finalized block with state. params.finalized = true; } if self.delayed_best_block { // 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(pezsc_consensus::ForkChoiceStrategy::Custom( params.origin == pezsp_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 teyrchain requirements. pub trait TeyrchainBlockImportMarker {} impl TeyrchainBlockImportMarker for TeyrchainBlockImport {} /// Get the relay-parent slot and timestamp from a header. pub fn relay_slot_and_timestamp( relay_parent_header: &PHeader, relay_chain_slot_duration: Duration, ) -> Option<(Slot, Timestamp)> { pezsc_consensus_babe::find_pre_digest::(relay_parent_header) .map(|babe_pre_digest| { let slot = babe_pre_digest.slot(); let t = Timestamp::new(relay_chain_slot_duration.as_millis() as u64 * *slot); (slot, t) }) .ok() } /// Reads abridged host configuration from the relay chain storage at the given relay parent. pub async fn load_abridged_host_configuration( relay_parent: PHash, relay_client: &impl RelayChainInterface, ) -> Result, RelayChainError> { relay_client .get_storage_by_key(relay_parent, relay_chain::well_known_keys::ACTIVE_CONFIG) .await? .map(|bytes| { AbridgedHostConfiguration::decode(&mut &bytes[..]) .map_err(RelayChainError::DeserializationError) }) .transpose() }