mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-30 00:17:26 +00:00
f78a780790
* 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
334 lines
10 KiB
Rust
334 lines
10 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/>.
|
|
|
|
//! # Aura Module
|
|
//!
|
|
//! - [`aura::Trait`](./trait.Trait.html)
|
|
//! - [`Module`](./struct.Module.html)
|
|
//!
|
|
//! ## Overview
|
|
//!
|
|
//! The Aura module extends Aura consensus by managing offline reporting.
|
|
//!
|
|
//! ## Interface
|
|
//!
|
|
//! ### Public Functions
|
|
//!
|
|
//! - `slot_duration` - Determine the Aura slot-duration based on the Timestamp module configuration.
|
|
//!
|
|
//! ## Related Modules
|
|
//!
|
|
//! - [Staking](../srml_staking/index.html): The Staking module is called in Aura to enforce slashing
|
|
//! if validators miss a certain number of slots (see the [`StakingSlasher`](./struct.StakingSlasher.html)
|
|
//! struct and associated method).
|
|
//! - [Timestamp](../srml_timestamp/index.html): The Timestamp module is used in Aura to track
|
|
//! consensus rounds (via `slots`).
|
|
//! - [Consensus](../srml_consensus/index.html): The Consensus module does not relate directly to Aura,
|
|
//! but serves to manage offline reporting by implementing `ProvideInherent` in a similar way.
|
|
//!
|
|
//! ## References
|
|
//!
|
|
//! If you're interested in hacking on this module, it is useful to understand the interaction with
|
|
//! `substrate/core/inherents/src/lib.rs` and, specifically, the required implementation of
|
|
//! [`ProvideInherent`](../substrate_inherents/trait.ProvideInherent.html) and
|
|
//! [`ProvideInherentData`](../substrate_inherents/trait.ProvideInherentData.html) to create and check inherents.
|
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
pub use timestamp;
|
|
|
|
use rstd::{result, prelude::*};
|
|
use parity_codec::Encode;
|
|
use srml_support::{decl_storage, decl_module, Parameter, storage::StorageValue, traits::Get};
|
|
use primitives::{
|
|
traits::{SaturatedConversion, Saturating, Zero, One, Member, IsMember, TypedKey},
|
|
generic::DigestItem,
|
|
};
|
|
use timestamp::OnTimestampSet;
|
|
#[cfg(feature = "std")]
|
|
use timestamp::TimestampInherentData;
|
|
use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent, MakeFatalError};
|
|
#[cfg(feature = "std")]
|
|
use inherents::{InherentDataProviders, ProvideInherentData};
|
|
use substrate_consensus_aura_primitives::{AURA_ENGINE_ID, ConsensusLog};
|
|
#[cfg(feature = "std")]
|
|
use parity_codec::Decode;
|
|
|
|
mod mock;
|
|
mod tests;
|
|
|
|
/// The Aura inherent identifier.
|
|
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"auraslot";
|
|
|
|
/// The type of the Aura inherent.
|
|
pub type InherentType = u64;
|
|
|
|
/// Auxiliary trait to extract Aura inherent data.
|
|
pub trait AuraInherentData {
|
|
/// Get aura inherent data.
|
|
fn aura_inherent_data(&self) -> result::Result<InherentType, RuntimeString>;
|
|
/// Replace aura inherent data.
|
|
fn aura_replace_inherent_data(&mut self, new: InherentType);
|
|
}
|
|
|
|
impl AuraInherentData for InherentData {
|
|
fn aura_inherent_data(&self) -> result::Result<InherentType, RuntimeString> {
|
|
self.get_data(&INHERENT_IDENTIFIER)
|
|
.and_then(|r| r.ok_or_else(|| "Aura inherent data not found".into()))
|
|
}
|
|
|
|
fn aura_replace_inherent_data(&mut self, new: InherentType) {
|
|
self.replace_data(INHERENT_IDENTIFIER, &new);
|
|
}
|
|
}
|
|
|
|
/// Provides the slot duration inherent data for `Aura`.
|
|
#[cfg(feature = "std")]
|
|
pub struct InherentDataProvider {
|
|
slot_duration: u64,
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl InherentDataProvider {
|
|
pub fn new(slot_duration: u64) -> Self {
|
|
Self {
|
|
slot_duration
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl ProvideInherentData for InherentDataProvider {
|
|
fn on_register(
|
|
&self,
|
|
providers: &InherentDataProviders,
|
|
) -> result::Result<(), RuntimeString> {
|
|
if !providers.has_provider(×tamp::INHERENT_IDENTIFIER) {
|
|
// Add the timestamp inherent data provider, as we require it.
|
|
providers.register_provider(timestamp::InherentDataProvider)
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn inherent_identifier(&self) -> &'static inherents::InherentIdentifier {
|
|
&INHERENT_IDENTIFIER
|
|
}
|
|
|
|
fn provide_inherent_data(
|
|
&self,
|
|
inherent_data: &mut InherentData,
|
|
) -> result::Result<(), RuntimeString> {
|
|
let timestamp = inherent_data.timestamp_inherent_data()?;
|
|
let slot_num = timestamp / self.slot_duration;
|
|
inherent_data.put_data(INHERENT_IDENTIFIER, &slot_num)
|
|
}
|
|
|
|
fn error_to_string(&self, error: &[u8]) -> Option<String> {
|
|
RuntimeString::decode(&mut &error[..]).map(Into::into)
|
|
}
|
|
}
|
|
|
|
/// Something that can handle Aura consensus reports.
|
|
pub trait HandleReport {
|
|
fn handle_report(report: AuraReport);
|
|
}
|
|
|
|
impl HandleReport for () {
|
|
fn handle_report(_report: AuraReport) { }
|
|
}
|
|
|
|
pub trait Trait: timestamp::Trait {
|
|
/// The logic for handling reports.
|
|
type HandleReport: HandleReport;
|
|
|
|
/// The identifier type for an authority.
|
|
type AuthorityId: Member + Parameter + TypedKey + Default;
|
|
}
|
|
|
|
decl_storage! {
|
|
trait Store for Module<T: Trait> as Aura {
|
|
/// The last timestamp.
|
|
LastTimestamp get(last) build(|_| 0.into()): T::Moment;
|
|
|
|
/// The current authorities
|
|
pub Authorities get(authorities) config(): Vec<T::AuthorityId>;
|
|
}
|
|
}
|
|
|
|
decl_module! {
|
|
pub struct Module<T: Trait> for enum Call where origin: T::Origin { }
|
|
}
|
|
|
|
impl<T: Trait> Module<T> {
|
|
fn change_authorities(new: Vec<T::AuthorityId>) {
|
|
<Authorities<T>>::put(&new);
|
|
|
|
let log: DigestItem<T::Hash> = DigestItem::Consensus(
|
|
AURA_ENGINE_ID,
|
|
ConsensusLog::AuthoritiesChange(new).encode()
|
|
);
|
|
<system::Module<T>>::deposit_log(log.into());
|
|
}
|
|
}
|
|
|
|
impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
|
|
type Key = T::AuthorityId;
|
|
|
|
fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I)
|
|
where I: Iterator<Item=(&'a T::AccountId, T::AuthorityId)>
|
|
{
|
|
// instant changes
|
|
if changed {
|
|
let next_authorities = validators.map(|(_, k)| k).collect::<Vec<_>>();
|
|
let last_authorities = <Module<T>>::authorities();
|
|
if next_authorities != last_authorities {
|
|
Self::change_authorities(next_authorities);
|
|
}
|
|
}
|
|
}
|
|
fn on_disabled(i: usize) {
|
|
let log: DigestItem<T::Hash> = DigestItem::Consensus(
|
|
AURA_ENGINE_ID,
|
|
ConsensusLog::<T::AuthorityId>::OnDisabled(i as u64).encode(),
|
|
);
|
|
|
|
<system::Module<T>>::deposit_log(log.into());
|
|
}
|
|
}
|
|
|
|
impl<T: Trait> IsMember<T::AuthorityId> for Module<T> {
|
|
fn is_member(authority_id: &T::AuthorityId) -> bool {
|
|
Self::authorities()
|
|
.iter()
|
|
.any(|id| id == authority_id)
|
|
}
|
|
}
|
|
|
|
/// A report of skipped authorities in Aura.
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "std", derive(Debug))]
|
|
pub struct AuraReport {
|
|
// The first skipped slot.
|
|
start_slot: usize,
|
|
// The number of times authorities were skipped.
|
|
skipped: usize,
|
|
}
|
|
|
|
impl AuraReport {
|
|
/// Call the closure with (`validator_indices`, `punishment_count`) for each
|
|
/// validator to punish.
|
|
pub fn punish<F>(&self, validator_count: usize, mut punish_with: F)
|
|
where F: FnMut(usize, usize)
|
|
{
|
|
// If all validators have been skipped, then it implies some sort of
|
|
// systematic problem common to all rather than a minority of validators
|
|
// not fulfilling their specific duties. In this case, it doesn't make
|
|
// sense to punish anyone, so we guard against it.
|
|
if self.skipped < validator_count {
|
|
for index in 0..self.skipped {
|
|
punish_with((self.start_slot + index) % validator_count, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Trait> Module<T> {
|
|
/// Determine the Aura slot-duration based on the Timestamp module configuration.
|
|
pub fn slot_duration() -> T::Moment {
|
|
// we double the minimum block-period so each author can always propose within
|
|
// the majority of its slot.
|
|
<T as timestamp::Trait>::MinimumPeriod::get().saturating_mul(2.into())
|
|
}
|
|
|
|
fn on_timestamp_set<H: HandleReport>(now: T::Moment, slot_duration: T::Moment) {
|
|
let last = Self::last();
|
|
<Self as Store>::LastTimestamp::put(now.clone());
|
|
|
|
if last.is_zero() {
|
|
return;
|
|
}
|
|
|
|
assert!(!slot_duration.is_zero(), "Aura slot duration cannot be zero.");
|
|
|
|
let last_slot = last / slot_duration.clone();
|
|
let first_skipped = last_slot.clone() + One::one();
|
|
let cur_slot = now / slot_duration;
|
|
|
|
assert!(last_slot < cur_slot, "Only one block may be authored per slot.");
|
|
if cur_slot == first_skipped { return }
|
|
|
|
let skipped_slots = cur_slot - last_slot - One::one();
|
|
|
|
H::handle_report(AuraReport {
|
|
start_slot: first_skipped.saturated_into::<usize>(),
|
|
skipped: skipped_slots.saturated_into::<usize>(),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<T: Trait> OnTimestampSet<T::Moment> for Module<T> {
|
|
fn on_timestamp_set(moment: T::Moment) {
|
|
Self::on_timestamp_set::<T::HandleReport>(moment, Self::slot_duration())
|
|
}
|
|
}
|
|
|
|
/// A type for performing slashing based on Aura reports.
|
|
pub struct StakingSlasher<T>(::rstd::marker::PhantomData<T>);
|
|
|
|
impl<T: staking::Trait + Trait> HandleReport for StakingSlasher<T> {
|
|
fn handle_report(report: AuraReport) {
|
|
use staking::SessionInterface;
|
|
let validators = T::SessionInterface::validators();
|
|
|
|
report.punish(
|
|
validators.len(),
|
|
|idx, slash_count| {
|
|
let v = validators[idx].clone();
|
|
staking::Module::<T>::on_offline_validator(v, slash_count);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
impl<T: Trait> ProvideInherent for Module<T> {
|
|
type Call = timestamp::Call<T>;
|
|
type Error = MakeFatalError<RuntimeString>;
|
|
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
|
|
|
|
fn create_inherent(_: &InherentData) -> Option<Self::Call> {
|
|
None
|
|
}
|
|
|
|
/// Verify the validity of the inherent using the timestamp.
|
|
fn check_inherent(call: &Self::Call, data: &InherentData) -> result::Result<(), Self::Error> {
|
|
let timestamp = match call {
|
|
timestamp::Call::set(ref timestamp) => timestamp.clone(),
|
|
_ => return Ok(()),
|
|
};
|
|
|
|
let timestamp_based_slot = timestamp / Self::slot_duration();
|
|
|
|
let seal_slot = data.aura_inherent_data()?.saturated_into();
|
|
|
|
if timestamp_based_slot == seal_slot {
|
|
Ok(())
|
|
} else {
|
|
Err(RuntimeString::from("timestamp set in block doesn't match slot in seal").into())
|
|
}
|
|
}
|
|
}
|