Refactor epoch changes to a separate crate (#4785)

* Init epoch changes module

* Initial integration of new epoch changes module for BABE

* Fix all initial compile errors

* rename: digest -> digests

* Fix babe tests

* Bump impl_version

* Fix more test issues

* Remove test flag for tree

It unfortunately won't work for multiple crates.

* Update cargo lock

* Fix duplicate parking_lot version

* Add missing license header
This commit is contained in:
Wei Tang
2020-02-06 16:48:38 +01:00
committed by GitHub
parent c7a7197f97
commit 4df27e760e
15 changed files with 265 additions and 218 deletions
@@ -22,6 +22,7 @@ sc-telemetry = { version = "2.0.0", path = "../../telemetry" }
sc-keystore = { version = "2.0.0", path = "../../keystore" }
sc-client-api = { version = "2.0.0", path = "../../api" }
sc-client = { version = "0.8", path = "../../" }
sc-consensus-epochs = { version = "0.8", path = "../epochs" }
sp-api = { version = "2.0.0", path = "../../../primitives/api" }
sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" }
sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" }
@@ -17,13 +17,17 @@
//! BABE authority selection and slot claiming.
use merlin::Transcript;
use sp_consensus_babe::{AuthorityId, BabeAuthorityWeight, BABE_ENGINE_ID, BABE_VRF_PREFIX};
use sp_consensus_babe::{Epoch, SlotNumber, AuthorityPair, BabePreDigest, BabeConfiguration};
use sp_consensus_babe::{
AuthorityId, BabeAuthorityWeight, BABE_ENGINE_ID, BABE_VRF_PREFIX,
SlotNumber, AuthorityPair, BabeConfiguration
};
use sp_consensus_babe::digests::PreDigest;
use sp_core::{U256, blake2_256};
use codec::Encode;
use schnorrkel::vrf::VRFInOut;
use sp_core::Pair;
use sc_keystore::KeyStorePtr;
use super::Epoch;
/// Calculates the primary selection threshold for a given authority, taking
/// into account `c` (`1 - c` represents the probability of a slot being empty).
@@ -104,7 +108,7 @@ fn claim_secondary_slot(
authorities: &[(AuthorityId, BabeAuthorityWeight)],
keystore: &KeyStorePtr,
randomness: [u8; 32],
) -> Option<(BabePreDigest, AuthorityPair)> {
) -> Option<(PreDigest, AuthorityPair)> {
if authorities.is_empty() {
return None;
}
@@ -124,7 +128,7 @@ fn claim_secondary_slot(
})
{
if pair.public() == *expected_author {
let pre_digest = BabePreDigest::Secondary {
let pre_digest = PreDigest::Secondary {
slot_number,
authority_index: authority_index as u32,
};
@@ -145,7 +149,7 @@ pub(super) fn claim_slot(
epoch: &Epoch,
config: &BabeConfiguration,
keystore: &KeyStorePtr,
) -> Option<(BabePreDigest, AuthorityPair)> {
) -> Option<(PreDigest, AuthorityPair)> {
claim_primary_slot(slot_number, epoch, config.c, keystore)
.or_else(|| {
if config.secondary_slots {
@@ -175,7 +179,7 @@ fn claim_primary_slot(
epoch: &Epoch,
c: (u64, u64),
keystore: &KeyStorePtr,
) -> Option<(BabePreDigest, AuthorityPair)> {
) -> Option<(PreDigest, AuthorityPair)> {
let Epoch { authorities, randomness, epoch_index, .. } = epoch;
let keystore = keystore.read();
@@ -196,7 +200,7 @@ fn claim_primary_slot(
let pre_digest = get_keypair(&pair)
.vrf_sign_after_check(transcript, |inout| super::authorship::check_primary_threshold(inout, threshold))
.map(|s| {
BabePreDigest::Primary {
PreDigest::Primary {
slot_number,
vrf_output: s.0.to_output(),
vrf_proof: s.1,
@@ -16,6 +16,8 @@
//! Schema for BABE epoch changes in the aux-db.
use std::sync::Arc;
use parking_lot::Mutex;
use log::info;
use codec::{Decode, Encode};
@@ -23,8 +25,8 @@ use sc_client_api::backend::AuxStore;
use sp_blockchain::{Result as ClientResult, Error as ClientError};
use sp_runtime::traits::Block as BlockT;
use sp_consensus_babe::BabeBlockWeight;
use super::{epoch_changes::EpochChangesFor, SharedEpochChanges};
use sc_consensus_epochs::{EpochChangesFor, SharedEpochChanges};
use crate::Epoch;
const BABE_EPOCH_CHANGES: &[u8] = b"babe_epoch_changes";
@@ -49,14 +51,14 @@ fn load_decode<B, T>(backend: &B, key: &[u8]) -> ClientResult<Option<T>>
/// Load or initialize persistent epoch change data from backend.
pub(crate) fn load_epoch_changes<Block: BlockT, B: AuxStore>(
backend: &B,
) -> ClientResult<SharedEpochChanges<Block>> {
let epoch_changes = load_decode::<_, EpochChangesFor<Block>>(backend, BABE_EPOCH_CHANGES)?
.map(Into::into)
) -> ClientResult<SharedEpochChanges<Block, Epoch>> {
let epoch_changes = load_decode::<_, EpochChangesFor<Block, Epoch>>(backend, BABE_EPOCH_CHANGES)?
.map(|v| Arc::new(Mutex::new(v)))
.unwrap_or_else(|| {
info!(target: "babe",
"Creating empty BABE epoch changes on what appears to be first startup."
);
SharedEpochChanges::new()
SharedEpochChanges::<Block, Epoch>::default()
});
// rebalance the tree after deserialization. this isn't strictly necessary
@@ -70,7 +72,7 @@ pub(crate) fn load_epoch_changes<Block: BlockT, B: AuxStore>(
/// Update the epoch changes on disk after a change.
pub(crate) fn write_epoch_changes<Block: BlockT, F, R>(
epoch_changes: &EpochChangesFor<Block>,
epoch_changes: &EpochChangesFor<Block, Epoch>,
write_aux: F,
) -> R where
F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
@@ -1,657 +0,0 @@
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Handling epoch changes in BABE.
//!
//! This exposes the `SharedEpochChanges`, which is a wrapper around a
//! persistent DAG superimposed over the forks of the blockchain.
use std::sync::Arc;
use sp_consensus_babe::{Epoch, SlotNumber, NextEpochDescriptor};
use fork_tree::ForkTree;
use parking_lot::{Mutex, MutexGuard};
use sp_runtime::traits::{Block as BlockT, NumberFor, One, Zero};
use codec::{Encode, Decode};
use sc_client_api::utils::is_descendent_of;
use sp_blockchain::{HeaderMetadata, HeaderBackend, Error as ClientError};
use std::ops::Add;
/// A builder for `is_descendent_of` functions.
pub trait IsDescendentOfBuilder<Hash> {
/// 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<bool, Self::Error>;
/// 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(crate) fn descendent_query<H, Block>(client: &H) -> HeaderBackendDescendentBuilder<&H, Block> {
HeaderBackendDescendentBuilder(client, std::marker::PhantomData)
}
/// Wrapper to get around unconstrained type errors when implementing
/// `IsDescendentOfBuilder` for header backends.
pub(crate) struct HeaderBackendDescendentBuilder<H, Block>(H, std::marker::PhantomData<Block>);
impl<'a, H, Block> IsDescendentOfBuilder<Block::Hash>
for HeaderBackendDescendentBuilder<&'a H, Block> where
H: HeaderBackend<Block> + HeaderMetadata<Block, Error=ClientError>,
Block: BlockT,
{
type Error = ClientError;
type IsDescendentOf = Box<dyn Fn(&Block::Hash, &Block::Hash) -> Result<bool, ClientError> + 'a>;
fn build_is_descendent_of(&self, current: Option<(Block::Hash, Block::Hash)>)
-> Self::IsDescendentOf
{
Box::new(is_descendent_of(self.0, current))
}
}
/// An unimported genesis epoch.
pub struct UnimportedGenesis(Epoch);
/// 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 {
Genesis(UnimportedGenesis),
Regular(Epoch),
}
impl From<Epoch> for ViableEpoch {
fn from(epoch: Epoch) -> ViableEpoch {
ViableEpoch::Regular(epoch)
}
}
impl AsRef<Epoch> for ViableEpoch {
fn as_ref(&self) -> &Epoch {
match *self {
ViableEpoch::Genesis(UnimportedGenesis(ref e)) => e,
ViableEpoch::Regular(ref e) => e,
}
}
}
impl ViableEpoch {
/// Extract the underlying epoch, disregarding the fact that a genesis
/// epoch may be unimported.
pub fn into_inner(self) -> Epoch {
match self {
ViableEpoch::Genesis(UnimportedGenesis(e)) => e,
ViableEpoch::Regular(e) => e,
}
}
/// Increment the epoch, yielding an `IncrementedEpoch` to be imported
/// into the fork-tree.
pub fn increment(&self, next_descriptor: NextEpochDescriptor) -> IncrementedEpoch {
let next = self.as_ref().increment(next_descriptor);
let to_persist = match *self {
ViableEpoch::Genesis(UnimportedGenesis(ref epoch_0)) =>
PersistedEpoch::Genesis(epoch_0.clone(), next),
ViableEpoch::Regular(_) => PersistedEpoch::Regular(next),
};
IncrementedEpoch(to_persist)
}
}
/// The datatype encoded on disk.
// This really shouldn't be public, but the encode/decode derives force it to be.
#[derive(Clone, Encode, Decode)]
pub enum PersistedEpoch {
// epoch_0, epoch_1,
Genesis(Epoch, Epoch),
// epoch_n
Regular(Epoch),
}
/// 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<Epoch> for IncrementedEpoch {
fn as_ref(&self) -> &Epoch {
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.
///
/// BABE special-cases the first epoch, epoch_0, 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 overlayed
/// 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)]
pub struct EpochChanges<Hash, Number> {
inner: ForkTree<Hash, Number, PersistedEpoch>,
}
// create a fake header hash which hasn't been included in the chain.
fn fake_head_hash<H: AsRef<[u8]> + 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<Hash, Number> EpochChanges<Hash, Number> where
Hash: PartialEq + AsRef<[u8]> + AsMut<[u8]> + Copy,
Number: Ord + One + Zero + Add<Output=Number> + Copy,
{
/// Create a new epoch-change tracker.
fn new() -> Self {
EpochChanges { inner: ForkTree::new() }
}
/// Rebalances the tree of epoch changes so that it is sorted by length of
/// fork (longest fork first).
pub fn rebalance(&mut self) {
self.inner.rebalance()
}
/// Prune out finalized epochs, except for the ancestor of the finalized
/// block. The given slot should be the slot number at which the finalized
/// block was authored.
pub fn prune_finalized<D: IsDescendentOfBuilder<Hash>>(
&mut self,
descendent_of_builder: D,
hash: &Hash,
number: Number,
slot: SlotNumber,
) -> Result<(), fork_tree::Error<D::Error>> {
let is_descendent_of = descendent_of_builder
.build_is_descendent_of(None);
let predicate = |epoch: &PersistedEpoch| match *epoch {
PersistedEpoch::Genesis(_, ref epoch_1) =>
slot >= epoch_1.end_slot(),
PersistedEpoch::Regular(ref epoch_n) =>
slot >= epoch_n.end_slot(),
};
// prune any epochs which could not be _live_ as of the children of the
// finalized block, i.e. re-root the fork tree to the oldest ancestor of
// (hash, number) where epoch.end_slot() >= finalized_slot
self.inner.prune(
hash,
&number,
&is_descendent_of,
&predicate,
)?;
Ok(())
}
/// Finds the epoch for a child of the given block, assuming the given slot number.
///
/// If the returned epoch is an `UnimportedGenesis` epoch, it should be imported into the
/// tree.
pub fn epoch_for_child_of<D: IsDescendentOfBuilder<Hash>, G>(
&self,
descendent_of_builder: D,
parent_hash: &Hash,
parent_number: Number,
slot_number: SlotNumber,
make_genesis: G,
) -> Result<Option<ViableEpoch>, fork_tree::Error<D::Error>>
where G: FnOnce(SlotNumber) -> Epoch
{
// find_node_where will give you the node in the fork-tree which is an ancestor
// of the `parent_hash` by default. if the last epoch was signalled at the parent_hash,
// then it won't be returned. we need to create a new fake chain head hash which
// "descends" from our parent-hash.
let fake_head_hash = fake_head_hash(parent_hash);
let is_descendent_of = descendent_of_builder
.build_is_descendent_of(Some((fake_head_hash, *parent_hash)));
if parent_number == Zero::zero() {
// need to insert the genesis epoch.
let genesis_epoch = make_genesis(slot_number);
return Ok(Some(ViableEpoch::Genesis(UnimportedGenesis(genesis_epoch))));
}
// We want to find the deepest node in the tree which is an ancestor
// of our block and where the start slot of the epoch was before the
// slot of our block. The genesis special-case doesn't need to look
// at epoch_1 -- all we're doing here is figuring out which node
// we need.
let predicate = |epoch: &PersistedEpoch| match *epoch {
PersistedEpoch::Genesis(ref epoch_0, _) =>
epoch_0.start_slot <= slot_number,
PersistedEpoch::Regular(ref epoch_n) =>
epoch_n.start_slot <= slot_number,
};
self.inner.find_node_where(
&fake_head_hash,
&(parent_number + One::one()),
&is_descendent_of,
&predicate,
)
.map(|n| n.map(|node| ViableEpoch::Regular(match node.data {
// Ok, we found our node.
// and here we figure out which of the internal epochs
// of a genesis node to use based on their start slot.
PersistedEpoch::Genesis(ref epoch_0, ref epoch_1) =>
if epoch_1.start_slot <= slot_number {
epoch_1.clone()
} else {
epoch_0.clone()
},
PersistedEpoch::Regular(ref epoch_n) => epoch_n.clone(),
})))
}
/// Import a new epoch-change, signalled at the given block.
///
/// This assumes that the given block is prospective (i.e. has not been
/// imported yet), but its parent has. This is why the parent hash needs
/// to be provided.
pub fn import<D: IsDescendentOfBuilder<Hash>>(
&mut self,
descendent_of_builder: D,
hash: Hash,
number: Number,
parent_hash: Hash,
epoch: IncrementedEpoch,
) -> Result<(), fork_tree::Error<D::Error>> {
let is_descendent_of = descendent_of_builder
.build_is_descendent_of(Some((hash, parent_hash)));
let res = self.inner.import(
hash,
number,
epoch.0,
&is_descendent_of,
);
match res {
Ok(_) | Err(fork_tree::Error::Duplicate) => Ok(()),
Err(e) => Err(e),
}
}
/// Return the inner fork tree, useful for testing purposes.
#[cfg(test)]
pub fn tree(&self) -> &ForkTree<Hash, Number, PersistedEpoch> {
&self.inner
}
}
/// Type alias to produce the epoch-changes tree from a block type.
pub type EpochChangesFor<Block> = EpochChanges<<Block as BlockT>::Hash, NumberFor<Block>>;
/// A shared epoch changes tree.
#[derive(Clone)]
pub struct SharedEpochChanges<Block: BlockT> {
inner: Arc<Mutex<EpochChangesFor<Block>>>,
}
impl<Block: BlockT> SharedEpochChanges<Block> {
/// Create a new instance of the `SharedEpochChanges`.
pub fn new() -> Self {
SharedEpochChanges {
inner: Arc::new(Mutex::new(EpochChanges::<_, _>::new()))
}
}
/// Lock the shared epoch changes,
pub fn lock(&self) -> MutexGuard<EpochChangesFor<Block>> {
self.inner.lock()
}
}
impl<Block: BlockT> From<EpochChangesFor<Block>> for SharedEpochChanges<Block> {
fn from(epoch_changes: EpochChangesFor<Block>) -> Self {
SharedEpochChanges {
inner: Arc::new(Mutex::new(epoch_changes))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
pub struct TestError;
impl std::fmt::Display for TestError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "TestError")
}
}
impl std::error::Error for TestError {}
impl<'a, F: 'a , H: 'a + PartialEq + std::fmt::Debug> IsDescendentOfBuilder<H> for &'a F
where F: Fn(&H, &H) -> Result<bool, TestError>
{
type Error = TestError;
type IsDescendentOf = Box<dyn Fn(&H, &H) -> Result<bool, TestError> + 'a>;
fn build_is_descendent_of(&self, current: Option<(H, H)>)
-> Self::IsDescendentOf
{
let f = *self;
Box::new(move |base, head| {
let mut head = head;
if let Some((ref c_head, ref c_parent)) = current {
if head == c_head {
if base == c_parent {
return Ok(true);
} else {
head = c_parent;
}
}
}
f(base, head)
})
}
}
type Hash = [u8; 1];
#[test]
fn genesis_epoch_is_created_but_not_imported() {
//
// A - B
// \
// — C
//
let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
match (base, *block) {
(b"A", b) => Ok(b == *b"B" || b == *b"C" || b == *b"D"),
(b"B", b) | (b"C", b) => Ok(b == *b"D"),
(b"0", _) => Ok(true),
_ => Ok(false),
}
};
let make_genesis = |slot| Epoch {
epoch_index: 0,
start_slot: slot,
duration: 100,
authorities: Vec::new(),
randomness: [0; 32],
};
let epoch_changes = EpochChanges::new();
let genesis_epoch = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"0",
0,
10101,
&make_genesis,
).unwrap().unwrap();
match genesis_epoch {
ViableEpoch::Genesis(_) => {},
_ => panic!("should be unimported genesis"),
};
assert_eq!(genesis_epoch.as_ref(), &make_genesis(10101));
let genesis_epoch_2 = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"0",
0,
10102,
&make_genesis,
).unwrap().unwrap();
match genesis_epoch_2 {
ViableEpoch::Genesis(_) => {},
_ => panic!("should be unimported genesis"),
};
assert_eq!(genesis_epoch_2.as_ref(), &make_genesis(10102));
}
#[test]
fn epoch_changes_between_blocks() {
//
// A - B
// \
// — C
//
let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
match (base, *block) {
(b"A", b) => Ok(b == *b"B" || b == *b"C" || b == *b"D"),
(b"B", b) | (b"C", b) => Ok(b == *b"D"),
(b"0", _) => Ok(true),
_ => Ok(false),
}
};
let make_genesis = |slot| Epoch {
epoch_index: 0,
start_slot: slot,
duration: 100,
authorities: Vec::new(),
randomness: [0; 32],
};
let mut epoch_changes = EpochChanges::new();
let genesis_epoch = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"0",
0,
100,
&make_genesis,
).unwrap().unwrap();
assert_eq!(genesis_epoch.as_ref(), &make_genesis(100));
let import_epoch_1 = genesis_epoch.increment(NextEpochDescriptor {
authorities: Vec::new(),
randomness: [1; 32],
});
let epoch_1 = import_epoch_1.as_ref().clone();
epoch_changes.import(
&is_descendent_of,
*b"A",
1,
*b"0",
import_epoch_1,
).unwrap();
let genesis_epoch = genesis_epoch.into_inner();
assert!(is_descendent_of(b"0", b"A").unwrap());
let end_slot = genesis_epoch.end_slot();
assert_eq!(end_slot, epoch_1.start_slot);
{
// x is still within the genesis epoch.
let x = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"A",
1,
end_slot - 1,
&make_genesis,
).unwrap().unwrap().into_inner();
assert_eq!(x, genesis_epoch);
}
{
// x is now at the next epoch, because the block is now at the
// start slot of epoch 1.
let x = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"A",
1,
end_slot,
&make_genesis,
).unwrap().unwrap().into_inner();
assert_eq!(x, epoch_1);
}
{
// x is now at the next epoch, because the block is now after
// start slot of epoch 1.
let x = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"A",
1,
epoch_1.end_slot() - 1,
&make_genesis,
).unwrap().unwrap().into_inner();
assert_eq!(x, epoch_1);
}
}
#[test]
fn two_block_ones_dont_conflict() {
// X - Y
// /
// 0 - A - B
//
let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
match (base, *block) {
(b"A", b) => Ok(b == *b"B"),
(b"X", b) => Ok(b == *b"Y"),
(b"0", _) => Ok(true),
_ => Ok(false),
}
};
let duration = 100;
let make_genesis = |slot| Epoch {
epoch_index: 0,
start_slot: slot,
duration,
authorities: Vec::new(),
randomness: [0; 32],
};
let mut epoch_changes = EpochChanges::new();
let next_descriptor = NextEpochDescriptor {
authorities: Vec::new(),
randomness: [0; 32],
};
// insert genesis epoch for A
{
let genesis_epoch_a = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"0",
0,
100,
&make_genesis,
).unwrap().unwrap();
epoch_changes.import(
&is_descendent_of,
*b"A",
1,
*b"0",
genesis_epoch_a.increment(next_descriptor.clone()),
).unwrap();
}
// insert genesis epoch for X
{
let genesis_epoch_x = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"0",
0,
1000,
&make_genesis,
).unwrap().unwrap();
epoch_changes.import(
&is_descendent_of,
*b"X",
1,
*b"0",
genesis_epoch_x.increment(next_descriptor.clone()),
).unwrap();
}
// now check that the genesis epochs for our respective block 1s
// respect the chain structure.
{
let epoch_for_a_child = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"A",
1,
101,
&make_genesis,
).unwrap().unwrap();
assert_eq!(epoch_for_a_child.into_inner(), make_genesis(100));
let epoch_for_x_child = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"X",
1,
1001,
&make_genesis,
).unwrap().unwrap();
assert_eq!(epoch_for_x_child.into_inner(), make_genesis(1000));
let epoch_for_x_child_before_genesis = epoch_changes.epoch_for_child_of(
&is_descendent_of,
b"X",
1,
101,
&make_genesis,
).unwrap();
// even though there is a genesis epoch at that slot, it's not in
// this chain.
assert!(epoch_for_x_child_before_genesis.is_none());
}
}
}
+55 -21
View File
@@ -59,8 +59,10 @@
#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub use sp_consensus_babe::{
BabeApi, ConsensusLog, BABE_ENGINE_ID, BabePreDigest, SlotNumber, BabeConfiguration,
CompatibleDigestItem,
BabeApi, ConsensusLog, BABE_ENGINE_ID, SlotNumber, BabeConfiguration,
AuthorityId, AuthorityPair, AuthoritySignature,
BabeAuthorityWeight, VRF_OUTPUT_LENGTH,
digests::{PreDigest, CompatibleDigestItem, NextEpochDescriptor},
};
pub use sp_consensus::SyncOracle;
use std::{collections::HashMap, sync::Arc, u64, pin::Pin, time::{Instant, Duration}};
@@ -101,26 +103,58 @@ use log::{warn, debug, info, trace};
use sc_consensus_slots::{
SlotWorker, SlotInfo, SlotCompatible, StorageChanges, CheckedHeader, check_equivocation,
};
use epoch_changes::descendent_query;
use sc_consensus_epochs::{descendent_query, SharedEpochChanges, EpochChangesFor, Epoch as EpochT};
use sp_blockchain::{
Result as ClientResult, Error as ClientError,
HeaderBackend, ProvideCache, HeaderMetadata
};
use schnorrkel::SignatureError;
use codec::{Encode, Decode};
use sp_api::ApiExt;
mod aux_schema;
mod verification;
mod epoch_changes;
mod authorship;
#[cfg(test)]
mod tests;
pub use sp_consensus_babe::{
AuthorityId, AuthorityPair, AuthoritySignature, Epoch, NextEpochDescriptor,
};
pub use epoch_changes::{EpochChanges, EpochChangesFor, SharedEpochChanges};
/// BABE epoch information
#[derive(Decode, Encode, Default, PartialEq, Eq, Clone, Debug)]
pub struct Epoch {
/// The epoch index
pub epoch_index: u64,
/// The starting slot of the epoch,
pub start_slot: SlotNumber,
/// The duration of this epoch
pub duration: SlotNumber,
/// The authorities and their weights
pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
/// Randomness for this epoch
pub randomness: [u8; VRF_OUTPUT_LENGTH],
}
impl EpochT for Epoch {
type NextEpochDescriptor = NextEpochDescriptor;
type SlotNumber = SlotNumber;
fn increment(&self, descriptor: NextEpochDescriptor) -> Epoch {
Epoch {
epoch_index: self.epoch_index + 1,
start_slot: self.start_slot + self.duration,
duration: self.duration,
authorities: descriptor.authorities,
randomness: descriptor.randomness,
}
}
fn start_slot(&self) -> SlotNumber {
self.start_slot
}
fn end_slot(&self) -> SlotNumber {
self.start_slot + self.duration
}
}
#[derive(derive_more::Display, Debug)]
enum Error<B: BlockT> {
@@ -343,7 +377,7 @@ struct BabeWorker<B: BlockT, C, E, I, SO> {
sync_oracle: SO,
force_authoring: bool,
keystore: KeyStorePtr,
epoch_changes: SharedEpochChanges<B>,
epoch_changes: SharedEpochChanges<B, Epoch>,
config: Config,
}
@@ -361,7 +395,7 @@ impl<B, C, E, I, Error, SO> sc_consensus_slots::SimpleSlotWorker<B> for BabeWork
Error: std::error::Error + Send + From<ConsensusError> + From<I::Error> + 'static,
{
type EpochData = Epoch;
type Claim = (BabePreDigest, AuthorityPair);
type Claim = (PreDigest, AuthorityPair);
type SyncOracle = SO;
type CreateProposer = Pin<Box<
dyn Future<Output = Result<E::Proposer, sp_consensus::Error>> + Send + 'static
@@ -533,12 +567,12 @@ impl<B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<B, C, E, I, SO> where
/// Extract the BABE pre digest from the given header. Pre-runtime digests are
/// mandatory, the function will return `Err` if none is found.
fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<BabePreDigest, Error<B>>
fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<PreDigest, Error<B>>
{
// genesis block doesn't contain a pre digest so let's generate a
// dummy one to not break any invariants in the rest of the code
if header.number().is_zero() {
return Ok(BabePreDigest::Secondary {
return Ok(PreDigest::Secondary {
slot_number: 0,
authority_index: 0,
});
@@ -597,7 +631,7 @@ impl SlotCompatible for TimeSource {
#[derive(Clone)]
pub struct BabeLink<Block: BlockT> {
time_source: TimeSource,
epoch_changes: SharedEpochChanges<Block>,
epoch_changes: SharedEpochChanges<Block, Epoch>,
config: Config,
}
/// A verifier for Babe blocks.
@@ -606,7 +640,7 @@ pub struct BabeVerifier<B, E, Block: BlockT, RA, PRA> {
api: Arc<PRA>,
inherent_data_providers: sp_inherents::InherentDataProviders,
config: Config,
epoch_changes: SharedEpochChanges<Block>,
epoch_changes: SharedEpochChanges<Block, Epoch>,
time_source: TimeSource,
}
@@ -711,7 +745,7 @@ impl<B, E, Block, RA, PRA> Verifier<Block> for BabeVerifier<B, E, Block, RA, PRA
let mut inherent_data = self
.inherent_data_providers
.create_inherent_data()
.map_err( Error::<Block>::Runtime)?;
.map_err(Error::<Block>::Runtime)?;
let (_, slot_now, _) = self.time_source.extract_timestamp_and_slot(&inherent_data)
.map_err(Error::<Block>::Extraction)?;
@@ -855,7 +889,7 @@ pub struct BabeBlockImport<B, E, Block: BlockT, I, RA, PRA> {
inner: I,
client: Arc<Client<B, E, Block, RA>>,
api: Arc<PRA>,
epoch_changes: SharedEpochChanges<Block>,
epoch_changes: SharedEpochChanges<Block, Epoch>,
config: Config,
}
@@ -875,7 +909,7 @@ impl<B, E, Block: BlockT, I, RA, PRA> BabeBlockImport<B, E, Block, I, RA, PRA> {
fn new(
client: Arc<Client<B, E, Block, RA>>,
api: Arc<PRA>,
epoch_changes: SharedEpochChanges<Block>,
epoch_changes: SharedEpochChanges<Block, Epoch>,
block_import: I,
config: Config,
) -> Self {
@@ -1114,7 +1148,7 @@ impl<B, E, Block, I, RA, PRA> BlockImport<Block> for BabeBlockImport<B, E, Block
/// Gets the best finalized block and its slot, and prunes the given epoch tree.
fn prune_finalized<B, E, Block, RA>(
client: &Client<B, E, Block, RA>,
epoch_changes: &mut EpochChangesFor<Block>,
epoch_changes: &mut EpochChangesFor<Block, Epoch>,
) -> Result<(), ConsensusError> where
Block: BlockT,
E: CallExecutor<Block> + Send + Sync,
@@ -1161,7 +1195,7 @@ pub fn block_import<B, E, Block: BlockT, I, RA, PRA>(
RA: Send + Sync,
Client<B, E, Block, RA>: AuxStore,
{
let epoch_changes = aux_schema::load_epoch_changes(&*client)?;
let epoch_changes = aux_schema::load_epoch_changes::<Block, _>(&*client)?;
let link = BabeLink {
epoch_changes: epoch_changes.clone(),
time_source: Default::default(),
@@ -1245,7 +1279,7 @@ pub mod test_helpers {
client: &C,
keystore: &KeyStorePtr,
link: &BabeLink<B>,
) -> Option<BabePreDigest> where
) -> Option<PreDigest> where
B: BlockT,
C: ProvideRuntimeApi<B> +
ProvideCache<B> +
+2 -3
View File
@@ -59,7 +59,7 @@ type Mutator = Arc<dyn Fn(&mut TestHeader, Stage) + Send + Sync>;
#[derive(Clone)]
struct DummyFactory {
client: Arc<TestClient>,
epoch_changes: crate::SharedEpochChanges<TestBlock>,
epoch_changes: SharedEpochChanges<TestBlock, Epoch>,
config: Config,
mutator: Mutator,
}
@@ -105,7 +105,6 @@ impl DummyProposer {
>
>
{
use codec::Encode;
let block_builder = self.factory.client.new_block_at(
&BlockId::Hash(self.parent_hash),
pre_digests,
@@ -558,7 +557,7 @@ fn propose_and_import_block<Transaction>(
let pre_digest = sp_runtime::generic::Digest {
logs: vec![
Item::babe_pre_digest(
BabePreDigest::Secondary {
PreDigest::Secondary {
authority_index: 0,
slot_number,
},
@@ -18,11 +18,11 @@
use schnorrkel::vrf::{VRFOutput, VRFProof};
use sp_runtime::{traits::Header, traits::DigestItemFor};
use sp_core::{Pair, Public};
use sp_consensus_babe::{Epoch, BabePreDigest, CompatibleDigestItem, AuthorityId};
use sp_consensus_babe::{AuthoritySignature, SlotNumber, AuthorityIndex, AuthorityPair};
use sp_consensus_babe::{AuthoritySignature, SlotNumber, AuthorityIndex, AuthorityPair, AuthorityId};
use sp_consensus_babe::digests::{PreDigest, CompatibleDigestItem};
use sc_consensus_slots::CheckedHeader;
use log::{debug, trace};
use super::{find_pre_digest, babe_err, BlockT, Error};
use super::{find_pre_digest, babe_err, Epoch, BlockT, Error};
use super::authorship::{make_transcript, calculate_primary_threshold, check_primary_threshold, secondary_slot_author};
/// BABE verification parameters
@@ -32,7 +32,7 @@ pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
/// the pre-digest of the header being verified. this is optional - if prior
/// verification code had to read it, it can be included here to avoid duplicate
/// work.
pub(super) pre_digest: Option<BabePreDigest>,
pub(super) pre_digest: Option<PreDigest>,
/// the slot number of the current time.
pub(super) slot_now: SlotNumber,
/// epoch descriptor of the epoch this block _should_ be under, if it's valid.
@@ -93,7 +93,7 @@ pub(super) fn check_header<B: BlockT + Sized>(
};
match &pre_digest {
BabePreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number } => {
PreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number } => {
debug!(target: "babe", "Verifying Primary block");
let digest = (vrf_output, vrf_proof, *authority_index, *slot_number);
@@ -106,7 +106,7 @@ pub(super) fn check_header<B: BlockT + Sized>(
config.c,
)?;
},
BabePreDigest::Secondary { authority_index, slot_number } if config.secondary_slots => {
PreDigest::Secondary { authority_index, slot_number } if config.secondary_slots => {
debug!(target: "babe", "Verifying Secondary block");
let digest = (*authority_index, *slot_number);