BABE Epochs (#3028)

* Add `epoch` field to `SlotInfo`

* Add slot calculations

* More work on epochs in BABE

* Apply suggestions from code review

Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com>

* Typo: `/` not `%` for division

* Delete useless `LastSlotInEpoch::put(false)`

* Bump `spec_version`

* Make test suite pass again

* Implement BABE epoch randomness signing

* Try to fix compilation

Currently causes a stack overflow in the compiler

* Fix rustc stack overflow

* Add missing `PartialEq` and `Eq` implementations

* Fix compile errors in test suite

* Another silly compile error

* Clone `epoch`

* Fix compile error in benchmarks

* Implement `clone` for `Epoch`

* Merge master

* AUTHORING TEST PASSES!!!

* Fix compilation

* Bump `spec_version`

* Fix compilation

* Fix compilation (again)

* Remove an outdated FIXME

* Fix run.sh and move it to scripts/

* Delete commented-out code

* Fix documentation

Co-Authored-By: André Silva <andre.beat@gmail.com>

* Fix BABE initialization and refactor

* Respond to review

* typo

* Remove useless data in `CheckedHeader::Deferred`

* Remove `slot_number` from Epoch

It is not needed, and only served to waste space and cause confusion.

* Remove epoch from BABE digests

* Move digest.rs to primitives

* Fix incorrect warning names

* Fix compile error

* Consistent field naming for BABE digests

* More compiler error fixex

* Unbound variable

* more compile errors

* another compile error

* Fix compile errors in runtime

* another compile error

* Another compile error

* Fix wasm build

* missing import

* Fix more compile errors

* yet another compile error

* compile fix in test runtime

* Fix and simplify the BABE runtime

The BABE runtime was massively overcomplicated and also wrong.  It
assumed it needed to:

1. delay new authorities taking effect until the next epoch
2. not delay emitting `Consensus` digests to mark epoch changes

However, the first is handled by the `srml_session` crate, and the
second is flat-out incorrect: `Consensus` digests take effect
immediately.  Furthermore, `srml_babe` tried to duplicate the
functionality of `srml_session::PeriodicSession`, but did it both
clumsily and incorrectly.  Fortunately, the new code is simpler and far
more likely to be correct.

* Use `system` to get the test authorities

The genesis block used by tests defines no authorities.  Only the test
suite is affected.

* Fix test runtime impl for BabeApi::epoch() with std

* Fix compilation

* Cached authorities are in the form of an epoch

not a `Vec<AuthorityId>`.

* `slots_per_epoch` is not fixed in general

The BABE code previously assumed `slots_per_epoch` to be a constant,
but that assumption is false in general.  Furthermore, removing this
assumption also allows a lot of code to go away.

* fix compile error

* Implement epoch checker

* Fix runtime compilation

* fork-tree: add method for finding a node in the tree

* babe: register epoch transitions in fork tree and validate them

* fork-tree: add method for arbitrary pruning

* Expose the queued validator set to SRML modules

BABE needs to know not only what the current validator set is, but also
what the next validator set will be.  Expose this to clients of the
session module.

* Bump hex-literal

Hopefully this will fix the panic

* babe: prune epoch change fork tree on finality

* babe: validate epoch index on transition

* babe: persist epoch changes tree

* Fix compile error in tests

* Fix compile error in tests

* Another compile error in tests

* Fix compilation of tests

* core: move grandpa::is_descendent_of to client utils

* babe: use is_descendent_of from client utils

* babe: extract slot_number from pre_digest in import_block

* Move BABE testsuite to its own file

* Initial part of test code

* Missing `WeightMultiplierUpdate` in test-runtime

* bump `spec_version`

* Add a test that a very bogus is rejected

* Run the tests again

* Fix compiler diagnostics

* Bump `spec_version`

* Initial infrastructure for mutation testing

* Mutation testing of block import

* babe: revert epoch changes in case of block import error

* babe: fix logging target

* babe: BabeBlockImport doesn't box inner BlockImport

* babe: fix epoch check in block import

* babe: populate authorities cache on block authorship

* babe: remove unused functions

* babe: use RANDOMNESS_LENGTH const

* babe: remove unneeded config parameters

* core: revert change to hex dependency version

* cleanup gitignore

* babe: add docs to aux_schema

* babe: remove useless drops in tests

* babe: remove annoying macos smart quotes

* fork-tree: docs

* fork-tree: add tests

* babe: style

* babe: rename randomness config variable

* babe: remove randomness helper function

* babe: style fixes

* babe: add docs

* babe: fix tests

* node: bump spec_version

* babe: fix tests
This commit is contained in:
DemiMarie-parity
2019-07-23 04:36:16 -04:00
committed by Robert Habermeier
parent 78bc5edc14
commit f78a780790
30 changed files with 1368 additions and 571 deletions
@@ -0,0 +1,138 @@
// Copyright 2019 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/>.
//! Private implementation details of BABE digests.
#[cfg(feature = "std")]
use substrate_primitives::sr25519::Signature;
#[cfg(feature = "std")]
use super::{BABE_ENGINE_ID, Epoch};
#[cfg(not(feature = "std"))]
use super::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH};
use super::SlotNumber;
#[cfg(feature = "std")]
use runtime_primitives::{DigestItem, generic::OpaqueDigestItemId};
#[cfg(feature = "std")]
use std::fmt::Debug;
use parity_codec::{Decode, Encode};
#[cfg(feature = "std")]
use parity_codec::{Codec, Input};
#[cfg(feature = "std")]
use schnorrkel::vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH};
/// A BABE pre-digest
#[cfg(feature = "std")]
#[derive(Clone, Debug)]
pub struct BabePreDigest {
/// VRF output
pub vrf_output: VRFOutput,
/// VRF proof
pub vrf_proof: VRFProof,
/// Authority index
pub authority_index: super::AuthorityIndex,
/// Slot number
pub slot_number: SlotNumber,
}
/// The prefix used by BABE for its VRF keys.
pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf";
/// A raw version of `BabePreDigest`, usable on `no_std`.
#[derive(Copy, Clone, Encode, Decode)]
pub struct RawBabePreDigest {
/// Slot number
pub slot_number: SlotNumber,
/// Authority index
pub authority_index: super::AuthorityIndex,
/// VRF output
pub vrf_output: [u8; VRF_OUTPUT_LENGTH],
/// VRF proof
pub vrf_proof: [u8; VRF_PROOF_LENGTH],
}
#[cfg(feature = "std")]
impl Encode for BabePreDigest {
fn encode(&self) -> Vec<u8> {
let tmp = RawBabePreDigest {
vrf_output: *self.vrf_output.as_bytes(),
vrf_proof: self.vrf_proof.to_bytes(),
authority_index: self.authority_index,
slot_number: self.slot_number,
};
parity_codec::Encode::encode(&tmp)
}
}
#[cfg(feature = "std")]
impl Decode for BabePreDigest {
fn decode<R: Input>(i: &mut R) -> Option<Self> {
let RawBabePreDigest { vrf_output, vrf_proof, authority_index, slot_number } = Decode::decode(i)?;
// Verify (at compile time) that the sizes in babe_primitives are correct
let _: [u8; super::VRF_OUTPUT_LENGTH] = vrf_output;
let _: [u8; super::VRF_PROOF_LENGTH] = vrf_proof;
Some(BabePreDigest {
vrf_proof: VRFProof::from_bytes(&vrf_proof).ok()?,
vrf_output: VRFOutput::from_bytes(&vrf_output).ok()?,
authority_index,
slot_number,
})
}
}
/// A digest item which is usable with BABE consensus.
#[cfg(feature = "std")]
pub trait CompatibleDigestItem: Sized {
/// Construct a digest item which contains a BABE pre-digest.
fn babe_pre_digest(seal: BabePreDigest) -> Self;
/// If this item is an BABE pre-digest, return it.
fn as_babe_pre_digest(&self) -> Option<BabePreDigest>;
/// Construct a digest item which contains a BABE seal.
fn babe_seal(signature: Signature) -> Self;
/// If this item is a BABE signature, return the signature.
fn as_babe_seal(&self) -> Option<Signature>;
/// If this item is a BABE epoch, return it.
fn as_babe_epoch(&self) -> Option<Epoch>;
}
#[cfg(feature = "std")]
impl<Hash> CompatibleDigestItem for DigestItem<Hash> where
Hash: Debug + Send + Sync + Eq + Clone + Codec + 'static
{
fn babe_pre_digest(digest: BabePreDigest) -> Self {
DigestItem::PreRuntime(BABE_ENGINE_ID, digest.encode())
}
fn as_babe_pre_digest(&self) -> Option<BabePreDigest> {
self.try_to(OpaqueDigestItemId::PreRuntime(&BABE_ENGINE_ID))
}
fn babe_seal(signature: Signature) -> Self {
DigestItem::Seal(BABE_ENGINE_ID, signature.encode())
}
fn as_babe_seal(&self) -> Option<Signature> {
self.try_to(OpaqueDigestItemId::Seal(&BABE_ENGINE_ID))
}
fn as_babe_epoch(&self) -> Option<Epoch> {
self.try_to(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID))
}
}
@@ -15,15 +15,22 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Primitives for BABE.
#![deny(warnings, unsafe_code, missing_docs)]
#![deny(warnings)]
#![forbid(unsafe_code, missing_docs, unused_variables, unused_imports)]
#![cfg_attr(not(feature = "std"), no_std)]
mod digest;
use parity_codec::{Encode, Decode};
use rstd::vec::Vec;
use runtime_primitives::ConsensusEngineId;
use substrate_primitives::sr25519::Public;
use substrate_client::decl_runtime_apis;
#[cfg(feature = "std")]
pub use digest::{BabePreDigest, CompatibleDigestItem};
pub use digest::{BABE_VRF_PREFIX, RawBabePreDigest};
/// A Babe authority identifier. Necessarily equivalent to the schnorrkel public key used in
/// the main Babe module. If that ever changes, then this must, too.
pub type AuthorityId = Public;
@@ -49,15 +56,29 @@ pub type SlotNumber = u64;
/// The weight of an authority.
pub type Weight = u64;
/// BABE epoch information
#[derive(Decode, Encode, Default, PartialEq, Eq, Clone)]
#[cfg_attr(any(feature = "std", test), derive(Debug))]
pub struct Epoch {
/// The authorities and their weights
pub authorities: Vec<(AuthorityId, Weight)>,
/// The epoch index
pub epoch_index: u64,
/// Randomness for this epoch
pub randomness: [u8; VRF_OUTPUT_LENGTH],
/// The duration of this epoch
pub duration: SlotNumber,
}
/// An consensus log item for BABE.
#[derive(Decode, Encode)]
#[derive(Decode, Encode, Clone, PartialEq, Eq)]
pub enum ConsensusLog {
/// The epoch has changed. This provides information about the
/// epoch _after_ next: what slot number it will start at, who are the authorities (and their weights)
/// and the next epoch randomness. The information for the _next_ epoch should already
/// be available.
#[codec(index = "1")]
NextEpochData(SlotNumber, Vec<(AuthorityId, Weight)>, [u8; VRF_OUTPUT_LENGTH]),
NextEpochData(Epoch),
/// Disable the authority with given index.
#[codec(index = "2")]
OnDisabled(AuthorityIndex),
@@ -72,6 +93,12 @@ pub struct BabeConfiguration {
/// Dynamic slot duration may be supported in the future.
pub slot_duration: u64,
/// The number of slots per BABE epoch. Currently, only
/// the value provided by this type at genesis will be used.
///
/// Dynamic slot duration may be supported in the future.
pub slots_per_epoch: u64,
/// The expected block time in milliseconds for BABE. Currently,
/// only the value provided by this type at genesis will be used.
///
@@ -116,7 +143,7 @@ decl_runtime_apis! {
/// Dynamic configuration may be supported in the future.
fn startup_data() -> BabeConfiguration;
/// Get the current authorites for Babe.
fn authorities() -> Vec<AuthorityId>;
/// Get the current epoch data for Babe.
fn epoch() -> Epoch;
}
}