// 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 .
//! # BABE (Blind Assignment for Blockchain Extension)
//!
//! BABE is a slot-based block production mechanism which uses a VRF PRNG to
//! randomly perform the slot allocation. On every slot, all the authorities
//! generate a new random number with the VRF function and if it is lower than a
//! given threshold (which is proportional to their weight/stake) they have a
//! right to produce a block. The proof of the VRF function execution will be
//! used by other peer to validate the legitimacy of the slot claim.
//!
//! The engine is also responsible for collecting entropy on-chain which will be
//! used to seed the given VRF PRNG. An epoch is a contiguous number of slots
//! under which we will be using the same authority set. During an epoch all VRF
//! outputs produced as a result of block production will be collected on an
//! on-chain randomness pool. Epoch changes are announced one epoch in advance,
//! i.e. when ending epoch N, we announce the parameters (randomness,
//! authorities, etc.) for epoch N+2.
//!
//! Since the slot assignment is randomized, it is possible that a slot is
//! assigned to multiple validators in which case we will have a temporary fork,
//! or that a slot is assigned to no validator in which case no block is
//! produced. Which means that block times are not deterministic.
//!
//! The protocol has a parameter `c` [0, 1] for which `1 - c` is the probability
//! of a slot being empty. The choice of this parameter affects the security of
//! the protocol relating to maximum tolerable network delays.
//!
//! In addition to the VRF-based slot assignment described above, which we will
//! call primary slots, the engine also supports a deterministic secondary slot
//! assignment. Primary slots take precedence over secondary slots, when
//! authoring the node starts by trying to claim a primary slot and falls back
//! to a secondary slot claim attempt. The secondary slot assignment is done
//! by picking the authority at index:
//!
//! `blake2_256(epoch_randomness ++ slot_number) % authorities_len`.
//!
//! The secondary slots supports either a `SecondaryPlain` or `SecondaryVRF`
//! variant. Comparing with `SecondaryPlain` variant, the `SecondaryVRF` variant
//! generates an additional VRF output. The output is not included in beacon
//! randomness, but can be consumed by parachains.
//!
//! The fork choice rule is weight-based, where weight equals the number of
//! primary blocks in the chain. We will pick the heaviest chain (more primary
//! blocks) and will go with the longest one in case of a tie.
//!
//! An in-depth description and analysis of the protocol can be found here:
//!
#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub use sp_consensus_babe::{
BabeApi, ConsensusLog, BABE_ENGINE_ID, SlotNumber,
BabeEpochConfiguration, BabeGenesisConfiguration,
AuthorityId, AuthorityPair, AuthoritySignature,
BabeAuthorityWeight, VRF_OUTPUT_LENGTH,
digests::{
CompatibleDigestItem, NextEpochDescriptor, NextConfigDescriptor, PreDigest,
PrimaryPreDigest, SecondaryPlainPreDigest,
},
};
pub use sp_consensus::SyncOracle;
use std::{
collections::HashMap, sync::Arc, u64, pin::Pin, time::{Instant, Duration},
any::Any, borrow::Cow, convert::TryInto,
};
use sp_consensus::{ImportResult, CanAuthorWith};
use sp_consensus::import_queue::{
BoxJustificationImport, BoxFinalityProofImport,
};
use sp_core::crypto::Public;
use sp_application_crypto::AppKey;
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
use sp_runtime::{
generic::{BlockId, OpaqueDigestItemId}, Justification,
traits::{Block as BlockT, Header, DigestItemFor, Zero},
};
use sp_api::{ProvideRuntimeApi, NumberFor};
use parking_lot::Mutex;
use sp_inherents::{InherentDataProviders, InherentData};
use sc_telemetry::{telemetry, CONSENSUS_TRACE, CONSENSUS_DEBUG};
use sp_consensus::{
self, BlockImport, Environment, Proposer, BlockCheckParams,
ForkChoiceStrategy, BlockImportParams, BlockOrigin, Error as ConsensusError,
SelectChain, SlotData,
};
use sp_consensus_babe::inherents::BabeInherentData;
use sp_timestamp::{TimestampInherentData, InherentType as TimestampInherent};
use sp_consensus::import_queue::{Verifier, BasicQueue, DefaultImportQueue, CacheKeyId};
use sc_client_api::{
backend::AuxStore,
BlockchainEvents, ProvideUncles,
};
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use futures::channel::mpsc::{channel, Sender, Receiver};
use retain_mut::RetainMut;
use futures::prelude::*;
use log::{debug, info, log, trace, warn};
use prometheus_endpoint::Registry;
use sc_consensus_slots::{
SlotInfo, SlotCompatible, StorageChanges, CheckedHeader, check_equivocation,
};
use sc_consensus_epochs::{
descendent_query, SharedEpochChanges, EpochChangesFor, Epoch as EpochT, ViableEpochDescriptor,
};
use sp_blockchain::{
Result as ClientResult, Error as ClientError,
HeaderBackend, ProvideCache, HeaderMetadata
};
use schnorrkel::SignatureError;
use codec::{Encode, Decode};
use sp_api::ApiExt;
mod verification;
mod migration;
pub mod aux_schema;
pub mod authorship;
#[cfg(test)]
mod tests;
/// BABE epoch information
#[derive(Decode, Encode, 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],
/// Configuration of the epoch.
pub config: BabeEpochConfiguration,
}
impl EpochT for Epoch {
type NextEpochDescriptor = (NextEpochDescriptor, BabeEpochConfiguration);
type SlotNumber = SlotNumber;
fn increment(
&self,
(descriptor, config): (NextEpochDescriptor, BabeEpochConfiguration)
) -> Epoch {
Epoch {
epoch_index: self.epoch_index + 1,
start_slot: self.start_slot + self.duration,
duration: self.duration,
authorities: descriptor.authorities,
randomness: descriptor.randomness,
config,
}
}
fn start_slot(&self) -> SlotNumber {
self.start_slot
}
fn end_slot(&self) -> SlotNumber {
self.start_slot + self.duration
}
}
impl Epoch {
/// Create the genesis epoch (epoch #0). This is defined to start at the slot of
/// the first block, so that has to be provided.
pub fn genesis(
genesis_config: &BabeGenesisConfiguration,
slot_number: SlotNumber
) -> Epoch {
Epoch {
epoch_index: 0,
start_slot: slot_number,
duration: genesis_config.epoch_length,
authorities: genesis_config.genesis_authorities.clone(),
randomness: genesis_config.randomness,
config: BabeEpochConfiguration {
c: genesis_config.c,
allowed_slots: genesis_config.allowed_slots,
},
}
}
}
/// Errors encountered by the babe authorship task.
#[derive(derive_more::Display, Debug)]
pub enum Error {
/// Multiple BABE pre-runtime digests
#[display(fmt = "Multiple BABE pre-runtime digests, rejecting!")]
MultiplePreRuntimeDigests,
/// No BABE pre-runtime digest found
#[display(fmt = "No BABE pre-runtime digest found")]
NoPreRuntimeDigest,
/// Multiple BABE epoch change digests
#[display(fmt = "Multiple BABE epoch change digests, rejecting!")]
MultipleEpochChangeDigests,
/// Multiple BABE config change digests
#[display(fmt = "Multiple BABE config change digests, rejecting!")]
MultipleConfigChangeDigests,
/// Could not extract timestamp and slot
#[display(fmt = "Could not extract timestamp and slot: {:?}", _0)]
Extraction(sp_consensus::Error),
/// Could not fetch epoch
#[display(fmt = "Could not fetch epoch at {:?}", _0)]
FetchEpoch(B::Hash),
/// Header rejected: too far in the future
#[display(fmt = "Header {:?} rejected: too far in the future", _0)]
TooFarInFuture(B::Hash),
/// Parent unavailable. Cannot import
#[display(fmt = "Parent ({}) of {} unavailable. Cannot import", _0, _1)]
ParentUnavailable(B::Hash, B::Hash),
/// Slot number must increase
#[display(fmt = "Slot number must increase: parent slot: {}, this slot: {}", _0, _1)]
SlotNumberMustIncrease(u64, u64),
/// Header has a bad seal
#[display(fmt = "Header {:?} has a bad seal", _0)]
HeaderBadSeal(B::Hash),
/// Header is unsealed
#[display(fmt = "Header {:?} is unsealed", _0)]
HeaderUnsealed(B::Hash),
/// Slot author not found
#[display(fmt = "Slot author not found")]
SlotAuthorNotFound,
/// Secondary slot assignments are disabled for the current epoch.
#[display(fmt = "Secondary slot assignments are disabled for the current epoch.")]
SecondarySlotAssignmentsDisabled,
/// Bad signature
#[display(fmt = "Bad signature on {:?}", _0)]
BadSignature(B::Hash),
/// Invalid author: Expected secondary author
#[display(fmt = "Invalid author: Expected secondary author: {:?}, got: {:?}.", _0, _1)]
InvalidAuthor(AuthorityId, AuthorityId),
/// No secondary author expected.
#[display(fmt = "No secondary author expected.")]
NoSecondaryAuthorExpected,
/// VRF verification of block by author failed
#[display(fmt = "VRF verification of block by author {:?} failed: threshold {} exceeded", _0, _1)]
VRFVerificationOfBlockFailed(AuthorityId, u128),
/// VRF verification failed
#[display(fmt = "VRF verification failed: {:?}", _0)]
VRFVerificationFailed(SignatureError),
/// Could not fetch parent header
#[display(fmt = "Could not fetch parent header: {:?}", _0)]
FetchParentHeader(sp_blockchain::Error),
/// Expected epoch change to happen.
#[display(fmt = "Expected epoch change to happen at {:?}, s{}", _0, _1)]
ExpectedEpochChange(B::Hash, u64),
/// Unexpected config change.
#[display(fmt = "Unexpected config change")]
UnexpectedConfigChange,
/// Unexpected epoch change
#[display(fmt = "Unexpected epoch change")]
UnexpectedEpochChange,
/// Parent block has no associated weight
#[display(fmt = "Parent block of {} has no associated weight", _0)]
ParentBlockNoAssociatedWeight(B::Hash),
#[display(fmt = "Checking inherents failed: {}", _0)]
/// Check Inherents error
CheckInherents(String),
/// Client error
Client(sp_blockchain::Error),
/// Runtime error
Runtime(sp_inherents::Error),
/// Fork tree error
ForkTree(Box>),
}
impl std::convert::From> for String {
fn from(error: Error) -> String {
error.to_string()
}
}
fn babe_err(error: Error) -> Error {
debug!(target: "babe", "{}", error);
error
}
/// Intermediate value passed to block importer.
pub struct BabeIntermediate {
/// The epoch descriptor.
pub epoch_descriptor: ViableEpochDescriptor, Epoch>,
}
/// Intermediate key for Babe engine.
pub static INTERMEDIATE_KEY: &[u8] = b"babe1";
/// A slot duration. Create with `get_or_compute`.
// FIXME: Once Rust has higher-kinded types, the duplication between this
// and `super::babe::Config` can be eliminated.
// https://github.com/paritytech/substrate/issues/2434
#[derive(Clone)]
pub struct Config(sc_consensus_slots::SlotDuration);
impl Config {
/// Either fetch the slot duration from disk or compute it from the genesis
/// state.
pub fn get_or_compute(client: &C) -> ClientResult where
C: AuxStore + ProvideRuntimeApi, C::Api: BabeApi,
{
trace!(target: "babe", "Getting slot duration");
match sc_consensus_slots::SlotDuration::get_or_compute(client, |a, b| {
let has_api_v1 = a.has_api_with::, _>(
&b, |v| v == 1,
)?;
let has_api_v2 = a.has_api_with::, _>(
&b, |v| v == 2,
)?;
if has_api_v1 {
#[allow(deprecated)] {
Ok(a.configuration_before_version_2(b)?.into())
}
} else if has_api_v2 {
a.configuration(b)
} else {
Err(sp_blockchain::Error::VersionInvalid(
"Unsupported or invalid BabeApi version".to_string()
))
}
}).map(Self) {
Ok(s) => Ok(s),
Err(s) => {
warn!(target: "babe", "Failed to get slot duration");
Err(s)
}
}
}
}
impl std::ops::Deref for Config {
type Target = BabeGenesisConfiguration;
fn deref(&self) -> &BabeGenesisConfiguration {
&*self.0
}
}
/// Parameters for BABE.
pub struct BabeParams {
/// The keystore that manages the keys of the node.
pub keystore: SyncCryptoStorePtr,
/// The client to use
pub client: Arc,
/// The SelectChain Strategy
pub select_chain: SC,
/// The environment we are producing blocks for.
pub env: E,
/// The underlying block-import object to supply our produced blocks to.
/// This must be a `BabeBlockImport` or a wrapper of it, otherwise
/// critical consensus logic will be omitted.
pub block_import: I,
/// A sync oracle
pub sync_oracle: SO,
/// Providers for inherent data.
pub inherent_data_providers: InherentDataProviders,
/// Force authoring of blocks even if we are offline
pub force_authoring: bool,
/// The source of timestamps for relative slots
pub babe_link: BabeLink,
/// Checks if the current native implementation can author with a runtime at a given block.
pub can_author_with: CAW,
}
/// Start the babe worker.
pub fn start_babe(BabeParams {
keystore,
client,
select_chain,
env,
block_import,
sync_oracle,
inherent_data_providers,
force_authoring,
babe_link,
can_author_with,
}: BabeParams) -> Result<
BabeWorker,
sp_consensus::Error,
> where
B: BlockT,
C: ProvideRuntimeApi + ProvideCache + ProvideUncles + BlockchainEvents
+ HeaderBackend + HeaderMetadata + Send + Sync + 'static,
C::Api: BabeApi,
SC: SelectChain + 'static,
E: Environment + Send + Sync + 'static,
E::Proposer: Proposer>,
I: BlockImport> + Send
+ Sync + 'static,
Error: std::error::Error + Send + From + From + 'static,
SO: SyncOracle + Send + Sync + Clone + 'static,
CAW: CanAuthorWith + Send + 'static,
{
let config = babe_link.config;
let slot_notification_sinks = Arc::new(Mutex::new(Vec::new()));
let worker = BabeSlotWorker {
client: client.clone(),
block_import: Arc::new(Mutex::new(block_import)),
env,
sync_oracle: sync_oracle.clone(),
force_authoring,
keystore,
epoch_changes: babe_link.epoch_changes.clone(),
slot_notification_sinks: slot_notification_sinks.clone(),
config: config.clone(),
};
register_babe_inherent_data_provider(&inherent_data_providers, config.slot_duration())?;
sc_consensus_uncles::register_uncles_inherent_data_provider(
client,
select_chain.clone(),
&inherent_data_providers,
)?;
info!(target: "babe", "👶 Starting BABE Authorship worker");
let inner = sc_consensus_slots::start_slot_worker(
config.0,
select_chain,
worker,
sync_oracle,
inherent_data_providers,
babe_link.time_source,
can_author_with,
);
Ok(BabeWorker {
inner: Box::pin(inner),
slot_notification_sinks,
})
}
/// Worker for Babe which implements `Future