// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. If not, see .
//! Generic utilities for epoch-based consensus engines.
pub mod migration;
use codec::{Decode, Encode};
use fork_tree::{FilterAction, ForkTree};
use sc_client_api::utils::is_descendent_of;
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata};
use sp_runtime::traits::{Block as BlockT, NumberFor, One, Zero};
use std::{
borrow::{Borrow, BorrowMut},
collections::BTreeMap,
ops::{Add, Sub},
};
/// A builder for `is_descendent_of` functions.
pub trait IsDescendentOfBuilder {
/// The error returned by the function.
type Error: std::error::Error;
/// A function that can tell you if the second parameter is a descendent of
/// the first.
type IsDescendentOf: Fn(&Hash, &Hash) -> Result;
/// Build an `is_descendent_of` function.
///
/// The `current` parameter can be `Some` with the details a fresh block whose
/// details aren't yet stored, but its parent is.
///
/// The format of `current` when `Some` is `(current, current_parent)`.
fn build_is_descendent_of(&self, current: Option<(Hash, Hash)>) -> Self::IsDescendentOf;
}
/// Produce a descendent query object given the client.
pub fn descendent_query(client: &H) -> HeaderBackendDescendentBuilder<&H, Block> {
HeaderBackendDescendentBuilder(client, std::marker::PhantomData)
}
/// Wrapper to get around unconstrained type errors when implementing
/// `IsDescendentOfBuilder` for header backends.
pub struct HeaderBackendDescendentBuilder(H, std::marker::PhantomData);
impl<'a, H, Block> IsDescendentOfBuilder
for HeaderBackendDescendentBuilder<&'a H, Block>
where
H: HeaderBackend + HeaderMetadata,
Block: BlockT,
{
type Error = ClientError;
type IsDescendentOf = Box Result + 'a>;
fn build_is_descendent_of(
&self,
current: Option<(Block::Hash, Block::Hash)>,
) -> Self::IsDescendentOf {
Box::new(is_descendent_of(self.0, current))
}
}
/// Epoch data, distinguish whether it is genesis or not.
///
/// Once an epoch is created, it must have a known `start_slot` and `end_slot`, which cannot be
/// changed. Consensus engine may modify any other data in the epoch, if needed.
pub trait Epoch: std::fmt::Debug {
/// Descriptor for the next epoch.
type NextEpochDescriptor;
/// Type of the slot number.
type Slot: Ord + Copy + std::fmt::Debug;
/// The starting slot of the epoch.
fn start_slot(&self) -> Self::Slot;
/// Produce the "end slot" of the epoch. This is NOT inclusive to the epoch,
/// i.e. the slots covered by the epoch are `self.start_slot() .. self.end_slot()`.
fn end_slot(&self) -> Self::Slot;
/// Increment the epoch data, using the next epoch descriptor.
fn increment(&self, descriptor: Self::NextEpochDescriptor) -> Self;
}
impl<'a, E: Epoch> From<&'a E> for EpochHeader {
fn from(epoch: &'a E) -> EpochHeader {
Self { start_slot: epoch.start_slot(), end_slot: epoch.end_slot() }
}
}
/// Header of epoch data, consisting of start and end slot.
#[derive(Eq, PartialEq, Encode, Decode, Debug)]
pub struct EpochHeader {
/// The starting slot of the epoch.
pub start_slot: E::Slot,
/// The end slot of the epoch. This is NOT inclusive to the epoch,
/// i.e. the slots covered by the epoch are `self.start_slot() .. self.end_slot()`.
pub end_slot: E::Slot,
}
impl Clone for EpochHeader {
fn clone(&self) -> Self {
Self { start_slot: self.start_slot, end_slot: self.end_slot }
}
}
/// Position of the epoch identifier.
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
pub enum EpochIdentifierPosition {
/// The identifier points to a genesis epoch `epoch_0`.
Genesis0,
/// The identifier points to a genesis epoch `epoch_1`.
Genesis1,
/// The identifier points to a regular epoch.
Regular,
}
/// Epoch identifier.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
pub struct EpochIdentifier {
/// Location of the epoch.
pub position: EpochIdentifierPosition,
/// Hash of the block when the epoch is signaled.
pub hash: Hash,
/// Number of the block when the epoch is signaled.
pub number: Number,
}
/// The viable epoch under which a block can be verified.
///
/// If this is the first non-genesis block in the chain, then it will
/// hold an `UnimportedGenesis` epoch.
pub enum ViableEpoch {
/// Unimported genesis viable epoch data.
UnimportedGenesis(E),
/// Regular viable epoch data.
Signaled(ERef),
}
impl AsRef for ViableEpoch
where
ERef: Borrow,
{
fn as_ref(&self) -> &E {
match *self {
ViableEpoch::UnimportedGenesis(ref e) => e,
ViableEpoch::Signaled(ref e) => e.borrow(),
}
}
}
impl AsMut for ViableEpoch
where
ERef: BorrowMut,
{
fn as_mut(&mut self) -> &mut E {
match *self {
ViableEpoch::UnimportedGenesis(ref mut e) => e,
ViableEpoch::Signaled(ref mut e) => e.borrow_mut(),
}
}
}
impl ViableEpoch
where
E: Epoch + Clone,
ERef: Borrow,
{
/// Extract the underlying epoch, disregarding the fact that a genesis
/// epoch may be unimported.
pub fn into_cloned_inner(self) -> E {
match self {
ViableEpoch::UnimportedGenesis(e) => e,
ViableEpoch::Signaled(e) => e.borrow().clone(),
}
}
/// Get cloned value for the viable epoch.
pub fn into_cloned(self) -> ViableEpoch {
match self {
ViableEpoch::UnimportedGenesis(e) => ViableEpoch::UnimportedGenesis(e),
ViableEpoch::Signaled(e) => ViableEpoch::Signaled(e.borrow().clone()),
}
}
/// Increment the epoch, yielding an `IncrementedEpoch` to be imported
/// into the fork-tree.
pub fn increment(&self, next_descriptor: E::NextEpochDescriptor) -> IncrementedEpoch {
let next = self.as_ref().increment(next_descriptor);
let to_persist = match *self {
ViableEpoch::UnimportedGenesis(ref epoch_0) =>
PersistedEpoch::Genesis(epoch_0.clone(), next),
ViableEpoch::Signaled(_) => PersistedEpoch::Regular(next),
};
IncrementedEpoch(to_persist)
}
}
/// Descriptor for a viable epoch.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum ViableEpochDescriptor {
/// The epoch is an unimported genesis, with given start slot number.
UnimportedGenesis(E::Slot),
/// The epoch is signaled and has been imported, with given identifier and header.
Signaled(EpochIdentifier, EpochHeader),
}
impl ViableEpochDescriptor {
/// Start slot of the descriptor.
pub fn start_slot(&self) -> E::Slot {
match self {
Self::UnimportedGenesis(start_slot) => *start_slot,
Self::Signaled(_, header) => header.start_slot,
}
}
}
/// Persisted epoch stored in EpochChanges.
#[derive(Clone, Encode, Decode, Debug)]
pub enum PersistedEpoch {
/// Genesis persisted epoch data. epoch_0, epoch_1.
Genesis(E, E),
/// Regular persisted epoch data. epoch_n.
Regular(E),
}
impl PersistedEpoch {
/// Returns if this is a genesis epoch.
pub fn is_genesis(&self) -> bool {
matches!(self, Self::Genesis(_, _))
}
}
impl<'a, E: Epoch> From<&'a PersistedEpoch> for PersistedEpochHeader {
fn from(epoch: &'a PersistedEpoch) -> Self {
match epoch {
PersistedEpoch::Genesis(ref epoch_0, ref epoch_1) =>
PersistedEpochHeader::Genesis(epoch_0.into(), epoch_1.into()),
PersistedEpoch::Regular(ref epoch_n) => PersistedEpochHeader::Regular(epoch_n.into()),
}
}
}
impl PersistedEpoch {
/// Map the epoch to a different type using a conversion function.
pub fn map(self, h: &Hash, n: &Number, f: &mut F) -> PersistedEpoch
where
B: Epoch,
F: FnMut(&Hash, &Number, E) -> B,
{
match self {
PersistedEpoch::Genesis(epoch_0, epoch_1) =>
PersistedEpoch::Genesis(f(h, n, epoch_0), f(h, n, epoch_1)),
PersistedEpoch::Regular(epoch_n) => PersistedEpoch::Regular(f(h, n, epoch_n)),
}
}
}
/// Persisted epoch header stored in ForkTree.
#[derive(Encode, Decode, PartialEq, Eq, Debug)]
pub enum PersistedEpochHeader {
/// Genesis persisted epoch header. epoch_0, epoch_1.
Genesis(EpochHeader, EpochHeader),
/// Regular persisted epoch header. epoch_n.
Regular(EpochHeader),
}
impl Clone for PersistedEpochHeader {
fn clone(&self) -> Self {
match self {
Self::Genesis(epoch_0, epoch_1) => Self::Genesis(epoch_0.clone(), epoch_1.clone()),
Self::Regular(epoch_n) => Self::Regular(epoch_n.clone()),
}
}
}
impl PersistedEpochHeader {
/// Map the epoch header to a different type.
pub fn map(self) -> PersistedEpochHeader
where
B: Epoch,
{
match self {
PersistedEpochHeader::Genesis(epoch_0, epoch_1) => PersistedEpochHeader::Genesis(
EpochHeader { start_slot: epoch_0.start_slot, end_slot: epoch_0.end_slot },
EpochHeader { start_slot: epoch_1.start_slot, end_slot: epoch_1.end_slot },
),
PersistedEpochHeader::Regular(epoch_n) => PersistedEpochHeader::Regular(EpochHeader {
start_slot: epoch_n.start_slot,
end_slot: epoch_n.end_slot,
}),
}
}
}
/// A fresh, incremented epoch to import into the underlying fork-tree.
///
/// Create this with `ViableEpoch::increment`.
#[must_use = "Freshly-incremented epoch must be imported with `EpochChanges::import`"]
pub struct IncrementedEpoch(PersistedEpoch);
impl AsRef for IncrementedEpoch {
fn as_ref(&self) -> &E {
match self.0 {
PersistedEpoch::Genesis(_, ref epoch_1) => epoch_1,
PersistedEpoch::Regular(ref epoch_n) => epoch_n,
}
}
}
/// Tree of all epoch changes across all *seen* forks. Data stored in tree is
/// the hash and block number of the block signaling the epoch change, and the
/// epoch that was signalled at that block.
///
/// The first epoch, epoch_0, is special cased by saying that it starts at
/// slot number of the first block in the chain. When bootstrapping a chain,
/// there can be multiple competing block #1s, so we have to ensure that the overlaid
/// DAG doesn't get confused.
///
/// The first block of every epoch should be producing a descriptor for the next
/// epoch - this is checked in higher-level code. So the first block of epoch_0 contains
/// a descriptor for epoch_1. We special-case these and bundle them together in the
/// same DAG entry, pinned to a specific block #1.
///
/// Further epochs (epoch_2, ..., epoch_n) each get their own entry.
#[derive(Clone, Encode, Decode, Debug)]
pub struct EpochChanges {
inner: ForkTree>,
epochs: BTreeMap<(Hash, Number), PersistedEpoch>,
}
// create a fake header hash which hasn't been included in the chain.
fn fake_head_hash + AsMut<[u8]> + Clone>(parent_hash: &H) -> H {
let mut h = parent_hash.clone();
// dirty trick: flip the first bit of the parent hash to create a hash
// which has not been in the chain before (assuming a strong hash function).
h.as_mut()[0] ^= 0b10000000;
h
}
impl Default for EpochChanges
where
Hash: PartialEq + Ord,
Number: Ord,
{
fn default() -> Self {
EpochChanges { inner: ForkTree::new(), epochs: BTreeMap::new() }
}
}
impl EpochChanges
where
Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy + std::fmt::Debug,
Number: Ord + One + Zero + Add