// Copyright 2018-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 .
//! Integration of the GRANDPA finality gadget into substrate.
//!
//! This crate is unstable and the API and usage may change.
//!
//! This crate provides a long-running future that produces finality notifications.
//!
//! # Usage
//!
//! First, create a block-import wrapper with the `block_import` function. The
//! GRANDPA worker needs to be linked together with this block import object, so
//! a `LinkHalf` is returned as well. All blocks imported (from network or
//! consensus or otherwise) must pass through this wrapper, otherwise consensus
//! is likely to break in unexpected ways.
//!
//! Next, use the `LinkHalf` and a local configuration to `run_grandpa_voter`.
//! This requires a `Network` implementation. The returned future should be
//! driven to completion and will finalize blocks in the background.
//!
//! # Changing authority sets
//!
//! The rough idea behind changing authority sets in GRANDPA is that at some point,
//! we obtain agreement for some maximum block height that the current set can
//! finalize, and once a block with that height is finalized the next set will
//! pick up finalization from there.
//!
//! Technically speaking, this would be implemented as a voting rule which says,
//! "if there is a signal for a change in N blocks in block B, only vote on
//! chains with length NUM(B) + N if they contain B". This conditional-inclusion
//! logic is complex to compute because it requires looking arbitrarily far
//! back in the chain.
//!
//! Instead, we keep track of a list of all signals we've seen so far (across
//! all forks), sorted ascending by the block number they would be applied at.
//! We never vote on chains with number higher than the earliest handoff block
//! number (this is num(signal) + N). When finalizing a block, we either apply
//! or prune any signaled changes based on whether the signaling block is
//! included in the newly-finalized chain.
use futures::prelude::*;
use futures::StreamExt;
use log::{debug, info};
use sc_client_api::{
backend::{AuxStore, Backend},
LockImportRun, BlockchainEvents, CallExecutor,
ExecutionStrategy, Finalizer, TransactionFor, ExecutorProvider,
};
use sp_blockchain::{HeaderBackend, Error as ClientError, HeaderMetadata};
use parity_scale_codec::{Decode, Encode};
use prometheus_endpoint::{PrometheusError, Registry};
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{NumberFor, Block as BlockT, DigestFor, Zero};
use sc_keystore::KeyStorePtr;
use sp_inherents::InherentDataProviders;
use sp_consensus::{SelectChain, BlockImport};
use sp_core::Pair;
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
use sc_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG};
use serde_json;
use sp_finality_tracker;
use finality_grandpa::Error as GrandpaError;
use finality_grandpa::{voter, BlockNumberOps, voter_set::VoterSet};
use std::{fmt, io};
use std::sync::Arc;
use std::time::Duration;
use std::pin::Pin;
use std::task::{Poll, Context};
// utility logging macro that takes as first argument a conditional to
// decide whether to log under debug or info level (useful to restrict
// logging under initial sync).
macro_rules! afg_log {
($condition:expr, $($msg: expr),+ $(,)?) => {
{
let log_level = if $condition {
log::Level::Debug
} else {
log::Level::Info
};
log::log!(target: "afg", log_level, $($msg),+);
}
};
}
mod authorities;
mod aux_schema;
mod communication;
mod consensus_changes;
mod environment;
mod finality_proof;
mod import;
mod justification;
mod light_import;
mod observer;
mod until_imported;
mod voting_rule;
pub use finality_proof::{FinalityProofProvider, StorageAndProofProvider};
pub use justification::GrandpaJustification;
pub use light_import::light_block_import;
pub use voting_rule::{
BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
};
use aux_schema::PersistentData;
use environment::{Environment, VoterSetState};
use import::GrandpaBlockImport;
use until_imported::UntilGlobalMessageBlocksImported;
use communication::{NetworkBridge, Network as NetworkT};
use sp_finality_grandpa::{AuthorityList, AuthorityPair, AuthoritySignature, SetId};
// Re-export these two because it's just so damn convenient.
pub use sp_finality_grandpa::{AuthorityId, ScheduledChange};
use sp_api::ProvideRuntimeApi;
use std::marker::PhantomData;
#[cfg(test)]
mod tests;
/// A GRANDPA message for a substrate chain.
pub type Message = finality_grandpa::Message<::Hash, NumberFor>;
/// A signed message.
pub type SignedMessage = finality_grandpa::SignedMessage<
::Hash,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// A primary propose message for this chain's block type.
pub type PrimaryPropose = finality_grandpa::PrimaryPropose<::Hash, NumberFor>;
/// A prevote message for this chain's block type.
pub type Prevote = finality_grandpa::Prevote<::Hash, NumberFor>;
/// A precommit message for this chain's block type.
pub type Precommit = finality_grandpa::Precommit<::Hash, NumberFor>;
/// A catch up message for this chain's block type.
pub type CatchUp = finality_grandpa::CatchUp<
::Hash,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// A commit message for this chain's block type.
pub type Commit = finality_grandpa::Commit<
::Hash,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// A compact commit message for this chain's block type.
pub type CompactCommit = finality_grandpa::CompactCommit<
::Hash,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// A global communication input stream for commits and catch up messages. Not
/// exposed publicly, used internally to simplify types in the communication
/// layer.
type CommunicationIn = finality_grandpa::voter::CommunicationIn<
::Hash,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// Global communication input stream for commits and catch up messages, with
/// the hash type not being derived from the block, useful for forcing the hash
/// to some type (e.g. `H256`) when the compiler can't do the inference.
type CommunicationInH = finality_grandpa::voter::CommunicationIn<
H,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// Global communication sink for commits with the hash type not being derived
/// from the block, useful for forcing the hash to some type (e.g. `H256`) when
/// the compiler can't do the inference.
type CommunicationOutH = finality_grandpa::voter::CommunicationOut<
H,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// Configuration for the GRANDPA service.
#[derive(Clone)]
pub struct Config {
/// The expected duration for a message to be gossiped across the network.
pub gossip_duration: Duration,
/// Justification generation period (in blocks). GRANDPA will try to generate justifications
/// at least every justification_period blocks. There are some other events which might cause
/// justification generation.
pub justification_period: u32,
/// Whether the GRANDPA observer protocol is live on the network and thereby
/// a full-node not running as a validator is running the GRANDPA observer
/// protocol (we will only issue catch-up requests to authorities when the
/// observer protocol is enabled).
pub observer_enabled: bool,
/// Whether the node is running as an authority (i.e. running the full GRANDPA protocol).
pub is_authority: bool,
/// Some local identifier of the voter.
pub name: Option,
/// The keystore that manages the keys of this node.
pub keystore: Option,
}
impl Config {
fn name(&self) -> &str {
self.name.as_ref().map(|s| s.as_str()).unwrap_or("")
}
}
/// Errors that can occur while voting in GRANDPA.
#[derive(Debug)]
pub enum Error {
/// An error within grandpa.
Grandpa(GrandpaError),
/// A network error.
Network(String),
/// A blockchain error.
Blockchain(String),
/// Could not complete a round on disk.
Client(ClientError),
/// An invariant has been violated (e.g. not finalizing pending change blocks in-order)
Safety(String),
/// A timer failed to fire.
Timer(io::Error),
}
impl From for Error {
fn from(e: GrandpaError) -> Self {
Error::Grandpa(e)
}
}
impl From for Error {
fn from(e: ClientError) -> Self {
Error::Client(e)
}
}
/// Something which can determine if a block is known.
pub(crate) trait BlockStatus {
/// Return `Ok(Some(number))` or `Ok(None)` depending on whether the block
/// is definitely known and has been imported.
/// If an unexpected error occurs, return that.
fn block_number(&self, hash: Block::Hash) -> Result