// 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