Files
pezkuwi-subxt/substrate/srml/grandpa/src/lib.rs
T
DemiMarie-parity f78a780790 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
2019-07-23 10:36:16 +02:00

380 lines
12 KiB
Rust

// Copyright 2017-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/>.
//! GRANDPA Consensus module for runtime.
//!
//! This manages the GRANDPA authority set ready for the native code.
//! These authorities are only for GRANDPA finality, not for consensus overall.
//!
//! In the future, it will also handle misbehavior reports, and on-chain
//! finality notifications.
//!
//! For full integration with GRANDPA, the `GrandpaApi` should be implemented.
//! The necessary items are re-exported via the `fg_primitives` crate.
#![cfg_attr(not(feature = "std"), no_std)]
// re-export since this is necessary for `impl_apis` in runtime.
pub use substrate_finality_grandpa_primitives as fg_primitives;
use rstd::prelude::*;
use parity_codec::{self as codec, Encode, Decode};
use srml_support::{
decl_event, decl_storage, decl_module, dispatch::Result, storage::StorageValue
};
use primitives::{
generic::{DigestItem, OpaqueDigestItemId}, traits::CurrentHeight
};
use fg_primitives::{ScheduledChange, ConsensusLog, GRANDPA_ENGINE_ID};
pub use fg_primitives::{AuthorityId, AuthorityWeight};
use system::{ensure_signed, DigestOf};
mod mock;
mod tests;
pub trait Trait: system::Trait {
/// The event type of this module.
type Event: From<Event> + Into<<Self as system::Trait>::Event>;
}
/// A stored pending change, old format.
// TODO: remove shim
// https://github.com/paritytech/substrate/issues/1614
#[derive(Encode, Decode)]
pub struct OldStoredPendingChange<N> {
/// The block number this was scheduled at.
pub scheduled_at: N,
/// The delay in blocks until it will be applied.
pub delay: N,
/// The next authority set.
pub next_authorities: Vec<(AuthorityId, u64)>,
}
/// A stored pending change.
#[derive(Encode)]
pub struct StoredPendingChange<N> {
/// The block number this was scheduled at.
pub scheduled_at: N,
/// The delay in blocks until it will be applied.
pub delay: N,
/// The next authority set.
pub next_authorities: Vec<(AuthorityId, u64)>,
/// If defined it means the change was forced and the given block number
/// indicates the median last finalized block when the change was signaled.
pub forced: Option<N>,
}
impl<N: Decode> Decode for StoredPendingChange<N> {
fn decode<I: codec::Input>(value: &mut I) -> Option<Self> {
let old = OldStoredPendingChange::decode(value)?;
let forced = <Option<N>>::decode(value).unwrap_or(None);
Some(StoredPendingChange {
scheduled_at: old.scheduled_at,
delay: old.delay,
next_authorities: old.next_authorities,
forced,
})
}
}
/// Current state of the GRANDPA authority set. State transitions must happen in
/// the same order of states defined below, e.g. `Paused` implies a prior
/// `PendingPause`.
#[derive(Decode, Encode)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub enum StoredState<N> {
/// The current authority set is live, and GRANDPA is enabled.
Live,
/// There is a pending pause event which will be enacted at the given block
/// height.
PendingPause {
/// Block at which the intention to pause was scheduled.
scheduled_at: N,
/// Number of blocks after which the change will be enacted.
delay: N
},
/// The current GRANDPA authority set is paused.
Paused,
/// There is a pending resume event which will be enacted at the given block
/// height.
PendingResume {
/// Block at which the intention to resume was scheduled.
scheduled_at: N,
/// Number of blocks after which the change will be enacted.
delay: N,
},
}
decl_event!(
pub enum Event {
/// New authority set has been applied.
NewAuthorities(Vec<(AuthorityId, u64)>),
/// Current authority set has been paused.
Paused,
/// Current authority set has been resumed.
Resumed,
}
);
decl_storage! {
trait Store for Module<T: Trait> as GrandpaFinality {
/// The current authority set.
Authorities get(authorities) config(): Vec<(AuthorityId, AuthorityWeight)>;
/// State of the current authority set.
State get(state): StoredState<T::BlockNumber> = StoredState::Live;
/// Pending change: (signaled at, scheduled change).
PendingChange: Option<StoredPendingChange<T::BlockNumber>>;
/// next block number where we can force a change.
NextForced get(next_forced): Option<T::BlockNumber>;
/// `true` if we are currently stalled.
Stalled get(stalled): Option<(T::BlockNumber, T::BlockNumber)>;
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event() = default;
/// Report some misbehavior.
fn report_misbehavior(origin, _report: Vec<u8>) {
ensure_signed(origin)?;
// FIXME: https://github.com/paritytech/substrate/issues/1112
}
fn on_finalize(block_number: T::BlockNumber) {
// check for scheduled pending authority set changes
if let Some(pending_change) = <PendingChange<T>>::get() {
// emit signal if we're at the block that scheduled the change
if block_number == pending_change.scheduled_at {
if let Some(median) = pending_change.forced {
Self::deposit_log(ConsensusLog::ForcedChange(
median,
ScheduledChange {
delay: pending_change.delay,
next_authorities: pending_change.next_authorities.clone(),
}
))
} else {
Self::deposit_log(ConsensusLog::ScheduledChange(
ScheduledChange{
delay: pending_change.delay,
next_authorities: pending_change.next_authorities.clone(),
}
));
}
}
// enact the change if we've reached the enacting block
if block_number == pending_change.scheduled_at + pending_change.delay {
Authorities::put(&pending_change.next_authorities);
Self::deposit_event(
Event::NewAuthorities(pending_change.next_authorities)
);
<PendingChange<T>>::kill();
}
}
// check for scheduled pending state changes
match <State<T>>::get() {
StoredState::PendingPause { scheduled_at, delay } => {
// signal change to pause
if block_number == scheduled_at {
Self::deposit_log(ConsensusLog::Pause(delay));
}
// enact change to paused state
if block_number == scheduled_at + delay {
<State<T>>::put(StoredState::Paused);
Self::deposit_event(Event::Paused);
}
},
StoredState::PendingResume { scheduled_at, delay } => {
// signal change to resume
if block_number == scheduled_at {
Self::deposit_log(ConsensusLog::Resume(delay));
}
// enact change to live state
if block_number == scheduled_at + delay {
<State<T>>::put(StoredState::Live);
Self::deposit_event(Event::Resumed);
}
},
_ => {},
}
}
}
}
impl<T: Trait> Module<T> {
/// Get the current set of authorities, along with their respective weights.
pub fn grandpa_authorities() -> Vec<(AuthorityId, u64)> {
Authorities::get()
}
pub fn schedule_pause(in_blocks: T::BlockNumber) -> Result {
if let StoredState::Live = <State<T>>::get() {
let scheduled_at = system::ChainContext::<T>::default().current_height();
<State<T>>::put(StoredState::PendingPause {
delay: in_blocks,
scheduled_at,
});
Ok(())
} else {
Err("Attempt to signal GRANDPA pause when the authority set isn't live \
(either paused or already pending pause).")
}
}
pub fn schedule_resume(in_blocks: T::BlockNumber) -> Result {
if let StoredState::Paused = <State<T>>::get() {
let scheduled_at = system::ChainContext::<T>::default().current_height();
<State<T>>::put(StoredState::PendingResume {
delay: in_blocks,
scheduled_at,
});
Ok(())
} else {
Err("Attempt to signal GRANDPA resume when the authority set isn't paused \
(either live or already pending resume).")
}
}
/// Schedule a change in the authorities.
///
/// The change will be applied at the end of execution of the block
/// `in_blocks` after the current block. This value may be 0, in which
/// case the change is applied at the end of the current block.
///
/// If the `forced` parameter is defined, this indicates that the current
/// set has been synchronously determined to be offline and that after
/// `in_blocks` the given change should be applied. The given block number
/// indicates the median last finalized block number and it should be used
/// as the canon block when starting the new grandpa voter.
///
/// No change should be signaled while any change is pending. Returns
/// an error if a change is already pending.
pub fn schedule_change(
next_authorities: Vec<(AuthorityId, u64)>,
in_blocks: T::BlockNumber,
forced: Option<T::BlockNumber>,
) -> Result {
if !<PendingChange<T>>::exists() {
let scheduled_at = system::ChainContext::<T>::default().current_height();
if let Some(_) = forced {
if Self::next_forced().map_or(false, |next| next > scheduled_at) {
return Err("Cannot signal forced change so soon after last.");
}
// only allow the next forced change when twice the window has passed since
// this one.
<NextForced<T>>::put(scheduled_at + in_blocks * 2.into());
}
<PendingChange<T>>::put(StoredPendingChange {
delay: in_blocks,
scheduled_at,
next_authorities,
forced,
});
Ok(())
} else {
Err("Attempt to signal GRANDPA change with one already pending.")
}
}
/// Deposit one of this module's logs.
fn deposit_log(log: ConsensusLog<T::BlockNumber>) {
let log: DigestItem<T::Hash> = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode());
<system::Module<T>>::deposit_log(log.into());
}
}
impl<T: Trait> Module<T> {
pub fn grandpa_log(digest: &DigestOf<T>) -> Option<ConsensusLog<T::BlockNumber>> {
let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
digest.convert_first(|l| l.try_to::<ConsensusLog<T::BlockNumber>>(id))
}
pub fn pending_change(digest: &DigestOf<T>)
-> Option<ScheduledChange<T::BlockNumber>>
{
Self::grandpa_log(digest).and_then(|signal| signal.try_into_change())
}
pub fn forced_change(digest: &DigestOf<T>)
-> Option<(T::BlockNumber, ScheduledChange<T::BlockNumber>)>
{
Self::grandpa_log(digest).and_then(|signal| signal.try_into_forced_change())
}
pub fn pending_pause(digest: &DigestOf<T>)
-> Option<T::BlockNumber>
{
Self::grandpa_log(digest).and_then(|signal| signal.try_into_pause())
}
pub fn pending_resume(digest: &DigestOf<T>)
-> Option<T::BlockNumber>
{
Self::grandpa_log(digest).and_then(|signal| signal.try_into_resume())
}
}
impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
type Key = AuthorityId;
fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I)
where I: Iterator<Item=(&'a T::AccountId, AuthorityId)>
{
// instant changes
if changed {
let next_authorities = validators.map(|(_, k)| (k, 1u64)).collect::<Vec<_>>();
let last_authorities = <Module<T>>::grandpa_authorities();
if next_authorities != last_authorities {
use primitives::traits::Zero;
if let Some((further_wait, median)) = <Stalled<T>>::take() {
let _ = Self::schedule_change(next_authorities, further_wait, Some(median));
} else {
let _ = Self::schedule_change(next_authorities, Zero::zero(), None);
}
}
}
}
fn on_disabled(i: usize) {
Self::deposit_log(ConsensusLog::OnDisabled(i as u64))
}
}
impl<T: Trait> finality_tracker::OnFinalizationStalled<T::BlockNumber> for Module<T> {
fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) {
// when we record old authority sets, we can use `finality_tracker::median`
// to figure out _who_ failed. until then, we can't meaningfully guard
// against `next == last` the way that normal session changes do.
<Stalled<T>>::put((further_wait, median));
}
}