// This file is part of Substrate.
// Copyright (C) 2018-2021 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 .
use std::collections::{BTreeMap, HashMap};
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use futures::prelude::*;
use futures_timer::Delay;
use log::{debug, warn};
use parity_scale_codec::{Decode, Encode};
use parking_lot::RwLock;
use sc_client_api::{backend::{Backend, apply_aux}, utils::is_descendent_of};
use finality_grandpa::{
BlockNumberOps, Error as GrandpaError, round::State as RoundState,
voter, voter_set::VoterSet,
};
use sp_blockchain::HeaderMetadata;
use sp_runtime::generic::BlockId;
use sp_runtime::traits::{
Block as BlockT, Header as HeaderT, NumberFor, Zero,
};
use sc_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO};
use crate::{
local_authority_id, CommandOrError, Commit, Config, Error, NewAuthoritySet, Precommit, Prevote,
PrimaryPropose, SignedMessage, VoterCommand,
};
use sp_consensus::SelectChain;
use crate::authorities::{AuthoritySet, SharedAuthoritySet};
use crate::communication::Network as NetworkT;
use crate::notification::GrandpaJustificationSender;
use crate::justification::GrandpaJustification;
use crate::until_imported::UntilVoteTargetImported;
use crate::voting_rule::VotingRule;
use sp_finality_grandpa::{
AuthorityId, AuthoritySignature, Equivocation, EquivocationProof,
GrandpaApi, RoundNumber, SetId,
};
use prometheus_endpoint::{register, Counter, Gauge, PrometheusError, U64};
type HistoricalVotes = finality_grandpa::HistoricalVotes<
::Hash,
NumberFor,
AuthoritySignature,
AuthorityId,
>;
/// Data about a completed round. The set of votes that is stored must be
/// minimal, i.e. at most one equivocation is stored per voter.
#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub struct CompletedRound {
/// The round number.
pub number: RoundNumber,
/// The round state (prevote ghost, estimate, finalized, etc.)
pub state: RoundState>,
/// The target block base used for voting in the round.
pub base: (Block::Hash, NumberFor),
/// All the votes observed in the round.
pub votes: Vec>,
}
// Data about last completed rounds within a single voter set. Stores
// NUM_LAST_COMPLETED_ROUNDS and always contains data about at least one round
// (genesis).
#[derive(Debug, Clone, PartialEq)]
pub struct CompletedRounds {
rounds: Vec>,
set_id: SetId,
voters: Vec,
}
// NOTE: the current strategy for persisting completed rounds is very naive
// (update everything) and we also rely on cloning to do atomic updates,
// therefore this value should be kept small for now.
const NUM_LAST_COMPLETED_ROUNDS: usize = 2;
impl Encode for CompletedRounds {
fn encode(&self) -> Vec {
let v = Vec::from_iter(&self.rounds);
(&v, &self.set_id, &self.voters).encode()
}
}
impl parity_scale_codec::EncodeLike for CompletedRounds {}
impl Decode for CompletedRounds {
fn decode(value: &mut I) -> Result {
<(Vec>, SetId, Vec)>::decode(value)
.map(|(rounds, set_id, voters)| CompletedRounds {
rounds,
set_id,
voters,
})
}
}
impl CompletedRounds {
/// Create a new completed rounds tracker with NUM_LAST_COMPLETED_ROUNDS capacity.
pub(crate) fn new(
genesis: CompletedRound,
set_id: SetId,
voters: &AuthoritySet>,
)
-> CompletedRounds
{
let mut rounds = Vec::with_capacity(NUM_LAST_COMPLETED_ROUNDS);
rounds.push(genesis);
let voters = voters.current_authorities.iter().map(|(a, _)| a.clone()).collect();
CompletedRounds { rounds, set_id, voters }
}
/// Get the set-id and voter set of the completed rounds.
pub fn set_info(&self) -> (SetId, &[AuthorityId]) {
(self.set_id, &self.voters[..])
}
/// Iterate over all completed rounds.
pub fn iter(&self) -> impl Iterator> {
self.rounds.iter().rev()
}
/// Returns the last (latest) completed round.
pub fn last(&self) -> &CompletedRound {
self.rounds.first()
.expect("inner is never empty; always contains at least genesis; qed")
}
/// Push a new completed round, oldest round is evicted if number of rounds
/// is higher than `NUM_LAST_COMPLETED_ROUNDS`.
pub fn push(&mut self, completed_round: CompletedRound) {
use std::cmp::Reverse;
match self.rounds.binary_search_by_key(
&Reverse(completed_round.number),
|completed_round| Reverse(completed_round.number),
) {
Ok(idx) => self.rounds[idx] = completed_round,
Err(idx) => self.rounds.insert(idx, completed_round),
};
if self.rounds.len() > NUM_LAST_COMPLETED_ROUNDS {
self.rounds.pop();
}
}
}
/// A map with voter status information for currently live rounds,
/// which votes have we cast and what are they.
pub type CurrentRounds = BTreeMap>;
/// The state of the current voter set, whether it is currently active or not
/// and information related to the previously completed rounds. Current round
/// voting status is used when restarting the voter, i.e. it will re-use the
/// previous votes for a given round if appropriate (same round and same local
/// key).
#[derive(Debug, Decode, Encode, PartialEq)]
pub enum VoterSetState {
/// The voter is live, i.e. participating in rounds.
Live {
/// The previously completed rounds.
completed_rounds: CompletedRounds,
/// Voter status for the currently live rounds.
current_rounds: CurrentRounds,
},
/// The voter is paused, i.e. not casting or importing any votes.
Paused {
/// The previously completed rounds.
completed_rounds: CompletedRounds,
},
}
impl VoterSetState {
/// Create a new live VoterSetState with round 0 as a completed round using
/// the given genesis state and the given authorities. Round 1 is added as a
/// current round (with state `HasVoted::No`).
pub(crate) fn live(
set_id: SetId,
authority_set: &AuthoritySet>,
genesis_state: (Block::Hash, NumberFor),
) -> VoterSetState {
let state = RoundState::genesis((genesis_state.0, genesis_state.1));
let completed_rounds = CompletedRounds::new(
CompletedRound {
number: 0,
state,
base: (genesis_state.0, genesis_state.1),
votes: Vec::new(),
},
set_id,
authority_set,
);
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(1, HasVoted::No);
VoterSetState::Live {
completed_rounds,
current_rounds,
}
}
/// Returns the last completed rounds.
pub(crate) fn completed_rounds(&self) -> CompletedRounds {
match self {
VoterSetState::Live { completed_rounds, .. } =>
completed_rounds.clone(),
VoterSetState::Paused { completed_rounds } =>
completed_rounds.clone(),
}
}
/// Returns the last completed round.
pub(crate) fn last_completed_round(&self) -> CompletedRound {
match self {
VoterSetState::Live { completed_rounds, .. } =>
completed_rounds.last().clone(),
VoterSetState::Paused { completed_rounds } =>
completed_rounds.last().clone(),
}
}
/// Returns the voter set state validating that it includes the given round
/// in current rounds and that the voter isn't paused.
pub fn with_current_round(&self, round: RoundNumber)
-> Result<(&CompletedRounds, &CurrentRounds), Error>
{
if let VoterSetState::Live { completed_rounds, current_rounds } = self {
if current_rounds.contains_key(&round) {
Ok((completed_rounds, current_rounds))
} else {
let msg = "Voter acting on a live round we are not tracking.";
Err(Error::Safety(msg.to_string()))
}
} else {
let msg = "Voter acting while in paused state.";
Err(Error::Safety(msg.to_string()))
}
}
}
/// Whether we've voted already during a prior run of the program.
#[derive(Clone, Debug, Decode, Encode, PartialEq)]
pub enum HasVoted {
/// Has not voted already in this round.
No,
/// Has voted in this round.
Yes(AuthorityId, Vote),
}
/// The votes cast by this voter already during a prior run of the program.
#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum Vote {
/// Has cast a proposal.
Propose(PrimaryPropose),
/// Has cast a prevote.
Prevote(Option>, Prevote),
/// Has cast a precommit (implies prevote.)
Precommit(Option>, Prevote, Precommit),
}
impl HasVoted {
/// Returns the proposal we should vote with (if any.)
pub fn propose(&self) -> Option<&PrimaryPropose> {
match self {
HasVoted::Yes(_, Vote::Propose(propose)) =>
Some(propose),
HasVoted::Yes(_, Vote::Prevote(propose, _)) | HasVoted::Yes(_, Vote::Precommit(propose, _, _)) =>
propose.as_ref(),
_ => None,
}
}
/// Returns the prevote we should vote with (if any.)
pub fn prevote(&self) -> Option<&Prevote> {
match self {
HasVoted::Yes(_, Vote::Prevote(_, prevote)) | HasVoted::Yes(_, Vote::Precommit(_, prevote, _)) =>
Some(prevote),
_ => None,
}
}
/// Returns the precommit we should vote with (if any.)
pub fn precommit(&self) -> Option<&Precommit> {
match self {
HasVoted::Yes(_, Vote::Precommit(_, _, precommit)) =>
Some(precommit),
_ => None,
}
}
/// Returns true if the voter can still propose, false otherwise.
pub fn can_propose(&self) -> bool {
self.propose().is_none()
}
/// Returns true if the voter can still prevote, false otherwise.
pub fn can_prevote(&self) -> bool {
self.prevote().is_none()
}
/// Returns true if the voter can still precommit, false otherwise.
pub fn can_precommit(&self) -> bool {
self.precommit().is_none()
}
}
/// A voter set state meant to be shared safely across multiple owners.
#[derive(Clone)]
pub struct SharedVoterSetState {
/// The inner shared `VoterSetState`.
inner: Arc>>,
/// A tracker for the rounds that we are actively participating on (i.e. voting)
/// and the authority id under which we are doing it.
voting: Arc>>,
}
impl From> for SharedVoterSetState {
fn from(set_state: VoterSetState) -> Self {
SharedVoterSetState::new(set_state)
}
}
impl SharedVoterSetState {
/// Create a new shared voter set tracker with the given state.
pub(crate) fn new(state: VoterSetState) -> Self {
SharedVoterSetState {
inner: Arc::new(RwLock::new(state)),
voting: Arc::new(RwLock::new(HashMap::new())),
}
}
/// Read the inner voter set state.
pub(crate) fn read(&self) -> parking_lot::RwLockReadGuard> {
self.inner.read()
}
/// Get the authority id that we are using to vote on the given round, if any.
pub(crate) fn voting_on(&self, round: RoundNumber) -> Option {
self.voting.read().get(&round).cloned()
}
/// Note that we started voting on the give round with the given authority id.
pub(crate) fn started_voting_on(&self, round: RoundNumber, local_id: AuthorityId) {
self.voting.write().insert(round, local_id);
}
/// Note that we have finished voting on the given round. If we were voting on
/// the given round, the authority id that we were using to do it will be
/// cleared.
pub(crate) fn finished_voting_on(&self, round: RoundNumber) {
self.voting.write().remove(&round);
}
/// Return vote status information for the current round.
pub(crate) fn has_voted(&self, round: RoundNumber) -> HasVoted {
match &*self.inner.read() {
VoterSetState::Live { current_rounds, .. } => {
current_rounds.get(&round).and_then(|has_voted| match has_voted {
HasVoted::Yes(id, vote) =>
Some(HasVoted::Yes(id.clone(), vote.clone())),
_ => None,
})
.unwrap_or(HasVoted::No)
},
_ => HasVoted::No,
}
}
// NOTE: not exposed outside of this module intentionally.
fn with(&self, f: F) -> R
where F: FnOnce(&mut VoterSetState) -> R
{
f(&mut *self.inner.write())
}
}
/// Prometheus metrics for GRANDPA.
#[derive(Clone)]
pub(crate) struct Metrics {
finality_grandpa_round: Gauge,
finality_grandpa_prevotes: Counter,
finality_grandpa_precommits: Counter,
}
impl Metrics {
pub(crate) fn register(
registry: &prometheus_endpoint::Registry,
) -> Result {
Ok(Self {
finality_grandpa_round: register(
Gauge::new("finality_grandpa_round", "Highest completed GRANDPA round.")?,
registry,
)?,
finality_grandpa_prevotes: register(
Counter::new(
"finality_grandpa_prevotes_total",
"Total number of GRANDPA prevotes cast locally.",
)?,
registry,
)?,
finality_grandpa_precommits: register(
Counter::new(
"finality_grandpa_precommits_total",
"Total number of GRANDPA precommits cast locally.",
)?,
registry,
)?,
})
}
}
/// The environment we run GRANDPA in.
pub(crate) struct Environment, SC, VR> {
pub(crate) client: Arc,
pub(crate) select_chain: SC,
pub(crate) voters: Arc>,
pub(crate) config: Config,
pub(crate) authority_set: SharedAuthoritySet>,
pub(crate) network: crate::communication::NetworkBridge,
pub(crate) set_id: SetId,
pub(crate) voter_set_state: SharedVoterSetState,
pub(crate) voting_rule: VR,
pub(crate) metrics: Option,
pub(crate) justification_sender: Option>,
pub(crate) _phantom: PhantomData,
}
impl, SC, VR> Environment {
/// Updates the voter set state using the given closure. The write lock is
/// held during evaluation of the closure and the environment's voter set
/// state is set to its result if successful.
pub(crate) fn update_voter_set_state(&self, f: F) -> Result<(), Error> where
F: FnOnce(&VoterSetState) -> Result