// This file is part of Substrate.
// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program 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.
// This program 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 this program. 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.
#![warn(missing_docs)]
use futures::{prelude::*, StreamExt};
use log::{debug, error, info};
use parity_scale_codec::Decode;
use parking_lot::RwLock;
use prometheus_endpoint::{PrometheusError, Registry};
use sc_client_api::{
backend::{AuxStore, Backend},
utils::is_descendent_of,
BlockchainEvents, CallExecutor, ExecutionStrategy, ExecutorProvider, Finalizer, LockImportRun,
StorageProvider, TransactionFor,
};
use sc_consensus::BlockImport;
use sc_network_common::protocol::ProtocolName;
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO};
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
use sp_api::ProvideRuntimeApi;
use sp_application_crypto::AppKey;
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, Result as ClientResult};
use sp_consensus::SelectChain;
use sp_core::crypto::ByteArray;
use sp_finality_grandpa::{
AuthorityList, AuthoritySignature, SetId, CLIENT_LOG_TARGET as LOG_TARGET,
};
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, NumberFor, Zero},
};
pub use finality_grandpa::BlockNumberOps;
use finality_grandpa::{voter, voter_set::VoterSet, Error as GrandpaError};
use std::{
fmt, io,
pin::Pin,
sync::Arc,
task::{Context, Poll},
time::Duration,
};
// 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! grandpa_log {
($condition:expr, $($msg: expr),+ $(,)?) => {
{
let log_level = if $condition {
log::Level::Debug
} else {
log::Level::Info
};
log::log!(target: LOG_TARGET, log_level, $($msg),+);
}
};
}
mod authorities;
mod aux_schema;
mod communication;
mod environment;
mod finality_proof;
mod import;
mod justification;
mod notification;
mod observer;
mod until_imported;
mod voting_rule;
pub mod warp_proof;
pub use authorities::{AuthoritySet, AuthoritySetChanges, SharedAuthoritySet};
pub use aux_schema::best_justification;
pub use communication::grandpa_protocol_name::standard_name as protocol_standard_name;
pub use finality_grandpa::voter::report;
pub use finality_proof::{FinalityProof, FinalityProofError, FinalityProofProvider};
pub use import::{find_forced_change, find_scheduled_change, GrandpaBlockImport};
pub use justification::GrandpaJustification;
pub use notification::{GrandpaJustificationSender, GrandpaJustificationStream};
pub use observer::run_grandpa_observer;
pub use voting_rule::{
BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRuleResult,
VotingRulesBuilder,
};
use aux_schema::PersistentData;
use communication::{Network as NetworkT, NetworkBridge};
use environment::{Environment, VoterSetState};
use until_imported::UntilGlobalMessageBlocksImported;
// Re-export these two because it's just so damn convenient.
pub use sp_finality_grandpa::{
AuthorityId, AuthorityPair, CatchUp, Commit, CompactCommit, GrandpaApi, Message, Precommit,
Prevote, PrimaryPropose, ScheduledChange, SignedMessage,
};
use std::marker::PhantomData;
#[cfg(test)]
mod tests;
/// 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 = 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 =
voter::CommunicationIn, 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 =
voter::CommunicationOut, AuthoritySignature, AuthorityId>;
/// Shared voter state for querying.
pub struct SharedVoterState {
inner: Arc + Sync + Send>>>>,
}
impl SharedVoterState {
/// Create a new empty `SharedVoterState` instance.
pub fn empty() -> Self {
Self { inner: Arc::new(RwLock::new(None)) }
}
fn reset(
&self,
voter_state: Box + Sync + Send>,
) -> Option<()> {
let mut shared_voter_state = self.inner.try_write_for(Duration::from_secs(1))?;
*shared_voter_state = Some(voter_state);
Some(())
}
/// Get the inner `VoterState` instance.
pub fn voter_state(&self) -> Option> {
self.inner.read().as_ref().map(|vs| vs.get())
}
}
impl Clone for SharedVoterState {
fn clone(&self) -> Self {
SharedVoterState { inner: self.inner.clone() }
}
}
/// 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,
/// The role of the local node (i.e. authority, full-node or light).
pub local_role: sc_network::config::Role,
/// Some local identifier of the voter.
pub name: Option,
/// The keystore that manages the keys of this node.
pub keystore: Option,
/// TelemetryHandle instance.
pub telemetry: Option,
/// Chain specific GRANDPA protocol name. See [`crate::protocol_standard_name`].
pub protocol_name: ProtocolName,
}
impl Config {
fn name(&self) -> &str {
self.name.as_deref().unwrap_or("")
}
}
/// Errors that can occur while voting in GRANDPA.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// An error within grandpa.
#[error("grandpa error: {0}")]
Grandpa(#[from] GrandpaError),
/// A network error.
#[error("network error: {0}")]
Network(String),
/// A blockchain error.
#[error("blockchain error: {0}")]
Blockchain(String),
/// Could not complete a round on disk.
#[error("could not complete a round on disk: {0}")]
Client(#[from] ClientError),
/// Could not sign outgoing message
#[error("could not sign outgoing message: {0}")]
Signing(String),
/// An invariant has been violated (e.g. not finalizing pending change blocks in-order)
#[error("safety invariant has been violated: {0}")]
Safety(String),
/// A timer failed to fire.
#[error("a timer failed to fire: {0}")]
Timer(io::Error),
/// A runtime api request failed.
#[error("runtime API request failed: {0}")]
RuntimeApi(sp_api::ApiError),
}
/// 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