Reorganising the repository - external renames and moves (#4074)

* Adding first rough ouline of the repository structure

* Remove old CI stuff

* add title

* formatting fixes

* move node-exits job's script to scripts dir

* Move docs into subdir

* move to bin

* move maintainence scripts, configs and helpers into its own dir

* add .local to ignore

* move core->client

* start up 'test' area

* move test client

* move test runtime

* make test move compile

* Add dependencies rule enforcement.

* Fix indexing.

* Update docs to reflect latest changes

* Moving /srml->/paint

* update docs

* move client/sr-* -> primitives/

* clean old readme

* remove old broken code in rhd

* update lock

* Step 1.

* starting to untangle client

* Fix after merge.

* start splitting out client interfaces

* move children and blockchain interfaces

* Move trie and state-machine to primitives.

* Fix WASM builds.

* fixing broken imports

* more interface moves

* move backend and light to interfaces

* move CallExecutor

* move cli off client

* moving around more interfaces

* re-add consensus crates into the mix

* fix subkey path

* relieve client from executor

* starting to pull out client from grandpa

* move is_decendent_of out of client

* grandpa still depends on client directly

* lemme tests pass

* rename srml->paint

* Make it compile.

* rename interfaces->client-api

* Move keyring to primitives.

* fixup libp2p dep

* fix broken use

* allow dependency enforcement to fail

* move fork-tree

* Moving wasm-builder

* make env

* move build-script-utils

* fixup broken crate depdencies and names

* fix imports for authority discovery

* fix typo

* update cargo.lock

* fixing imports

* Fix paths and add missing crates

* re-add missing crates
This commit is contained in:
Benjamin Kampmann
2019-11-14 21:51:17 +01:00
committed by Bastian Köcher
parent becc3b0a4f
commit 60e5011c72
809 changed files with 7801 additions and 6464 deletions
@@ -0,0 +1,42 @@
[package]
name = "substrate-finality-grandpa"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
fork-tree = { path = "../../utils/fork-tree" }
futures = "0.1.29"
futures03 = { package = "futures-preview", version = "0.3.0-alpha.19", features = ["compat"] }
log = "0.4.8"
parking_lot = "0.9.0"
tokio-executor = "0.1.8"
tokio-timer = "0.2.11"
rand = "0.7.2"
codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] }
sr-primitives = { path = "../../primitives/sr-primitives" }
consensus_common = { package = "substrate-consensus-common", path = "../../primitives/consensus/common" }
primitives = { package = "substrate-primitives", path = "../../primitives/core" }
substrate-telemetry = { path = "../telemetry" }
keystore = { package = "substrate-keystore", path = "../keystore" }
serde_json = "1.0.41"
client-api = { package = "substrate-client-api", path = "../api" }
client = { package = "substrate-client", path = "../" }
header-metadata = { package = "substrate-header-metadata", path = "../header-metadata" }
inherents = { package = "substrate-inherents", path = "../../primitives/inherents" }
network = { package = "substrate-network", path = "../network" }
paint-finality-tracker = { path = "../../paint/finality-tracker" }
fg_primitives = { package = "substrate-finality-grandpa-primitives", path = "../../primitives/finality-grandpa" }
grandpa = { package = "finality-grandpa", version = "0.9.0", features = ["derive-codec"] }
[dev-dependencies]
grandpa = { package = "finality-grandpa", version = "0.9.0", features = ["derive-codec", "test-helpers"] }
network = { package = "substrate-network", path = "../network", features = ["test-helpers"] }
keyring = { package = "substrate-keyring", path = "../../primitives/keyring" }
test-client = { package = "substrate-test-runtime-client", path = "../../test/utils/runtime/client"}
babe_primitives = { package = "substrate-consensus-babe-primitives", path = "../../primitives/consensus/babe" }
state_machine = { package = "substrate-state-machine", path = "../../primitives/state-machine" }
env_logger = "0.7.0"
tokio = "0.1.22"
tempfile = "3.1.0"
sr-api = { path = "../../primitives/sr-api" }
@@ -0,0 +1,787 @@
// Copyright 2018-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/>.
//! Utilities for dealing with authorities, authority sets, and handoffs.
use fork_tree::ForkTree;
use parking_lot::RwLock;
use grandpa::voter_set::VoterSet;
use codec::{Encode, Decode};
use log::{debug, info};
use substrate_telemetry::{telemetry, CONSENSUS_INFO};
use fg_primitives::{AuthorityId, AuthorityList};
use std::cmp::Ord;
use std::fmt::Debug;
use std::ops::Add;
use std::sync::Arc;
/// A shared authority set.
pub(crate) struct SharedAuthoritySet<H, N> {
inner: Arc<RwLock<AuthoritySet<H, N>>>,
}
impl<H, N> Clone for SharedAuthoritySet<H, N> {
fn clone(&self) -> Self {
SharedAuthoritySet { inner: self.inner.clone() }
}
}
impl<H, N> SharedAuthoritySet<H, N> {
/// Acquire a reference to the inner read-write lock.
pub(crate) fn inner(&self) -> &RwLock<AuthoritySet<H, N>> {
&*self.inner
}
}
impl<H: Eq, N> SharedAuthoritySet<H, N>
where N: Add<Output=N> + Ord + Clone + Debug,
H: Clone + Debug
{
/// Get the earliest limit-block number, if any.
pub(crate) fn current_limit(&self) -> Option<N> {
self.inner.read().current_limit()
}
/// Get the current set ID. This is incremented every time the set changes.
pub(crate) fn set_id(&self) -> u64 {
self.inner.read().set_id
}
/// Get the current authorities and their weights (for the current set ID).
pub(crate) fn current_authorities(&self) -> VoterSet<AuthorityId> {
self.inner.read().current_authorities.iter().cloned().collect()
}
}
impl<H, N> From<AuthoritySet<H, N>> for SharedAuthoritySet<H, N> {
fn from(set: AuthoritySet<H, N>) -> Self {
SharedAuthoritySet { inner: Arc::new(RwLock::new(set)) }
}
}
/// Status of the set after changes were applied.
#[derive(Debug)]
pub(crate) struct Status<H, N> {
/// Whether internal changes were made.
pub(crate) changed: bool,
/// `Some` when underlying authority set has changed, containing the
/// block where that set changed.
pub(crate) new_set_block: Option<(H, N)>,
}
/// A set of authorities.
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub(crate) struct AuthoritySet<H, N> {
pub(crate) current_authorities: AuthorityList,
pub(crate) set_id: u64,
// Tree of pending standard changes across forks. Standard changes are
// enacted on finality and must be enacted (i.e. finalized) in-order across
// a given branch
pub(crate) pending_standard_changes: ForkTree<H, N, PendingChange<H, N>>,
// Pending forced changes across different forks (at most one per fork).
// Forced changes are enacted on block depth (not finality), for this reason
// only one forced change should exist per fork.
pub(crate) pending_forced_changes: Vec<PendingChange<H, N>>,
}
impl<H, N> AuthoritySet<H, N>
where H: PartialEq,
N: Ord,
{
/// Get a genesis set with given authorities.
pub(crate) fn genesis(initial: AuthorityList) -> Self {
AuthoritySet {
current_authorities: initial,
set_id: 0,
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
}
}
/// Get the current set id and a reference to the current authority set.
pub(crate) fn current(&self) -> (u64, &[(AuthorityId, u64)]) {
(self.set_id, &self.current_authorities[..])
}
}
impl<H: Eq, N> AuthoritySet<H, N>
where
N: Add<Output=N> + Ord + Clone + Debug,
H: Clone + Debug
{
fn add_standard_change<F, E>(
&mut self,
pending: PendingChange<H, N>,
is_descendent_of: &F,
) -> Result<(), fork_tree::Error<E>> where
F: Fn(&H, &H) -> Result<bool, E>,
E: std::error::Error,
{
let hash = pending.canon_hash.clone();
let number = pending.canon_height.clone();
debug!(target: "afg", "Inserting potential standard set change signaled at block {:?} \
(delayed by {:?} blocks).",
(&number, &hash), pending.delay);
self.pending_standard_changes.import(
hash.clone(),
number.clone(),
pending,
is_descendent_of,
)?;
debug!(target: "afg", "There are now {} alternatives for the next pending standard change (roots), \
and a total of {} pending standard changes (across all forks).",
self.pending_standard_changes.roots().count(),
self.pending_standard_changes.iter().count(),
);
Ok(())
}
fn add_forced_change<F, E>(
&mut self,
pending: PendingChange<H, N>,
is_descendent_of: &F,
) -> Result<(), fork_tree::Error<E>> where
F: Fn(&H, &H) -> Result<bool, E>,
E: std::error::Error,
{
for change in self.pending_forced_changes.iter() {
if change.canon_hash == pending.canon_hash ||
is_descendent_of(&change.canon_hash, &pending.canon_hash)?
{
return Err(fork_tree::Error::UnfinalizedAncestor);
}
}
// ordered first by effective number and then by signal-block number.
let key = (pending.effective_number(), pending.canon_height.clone());
let idx = self.pending_forced_changes
.binary_search_by_key(&key, |change| (
change.effective_number(),
change.canon_height.clone(),
))
.unwrap_or_else(|i| i);
debug!(target: "afg", "Inserting potential forced set change at block {:?} \
(delayed by {:?} blocks).",
(&pending.canon_height, &pending.canon_hash), pending.delay);
self.pending_forced_changes.insert(idx, pending);
debug!(target: "afg", "There are now {} pending forced changes.", self.pending_forced_changes.len());
Ok(())
}
/// Note an upcoming pending transition. Multiple pending standard changes
/// on the same branch can be added as long as they don't overlap. Forced
/// changes are restricted to one per fork. This method assumes that changes
/// on the same branch will be added in-order. The given function
/// `is_descendent_of` should return `true` if the second hash (target) is a
/// descendent of the first hash (base).
pub(crate) fn add_pending_change<F, E>(
&mut self,
pending: PendingChange<H, N>,
is_descendent_of: &F,
) -> Result<(), fork_tree::Error<E>> where
F: Fn(&H, &H) -> Result<bool, E>,
E: std::error::Error,
{
match pending.delay_kind {
DelayKind::Best { .. } => {
self.add_forced_change(pending, is_descendent_of)
},
DelayKind::Finalized => {
self.add_standard_change(pending, is_descendent_of)
},
}
}
/// Inspect pending changes. Standard pending changes are iterated first,
/// and the changes in the tree are traversed in pre-order, afterwards all
/// forced changes are iterated.
pub(crate) fn pending_changes(&self) -> impl Iterator<Item=&PendingChange<H, N>> {
self.pending_standard_changes.iter().map(|(_, _, c)| c)
.chain(self.pending_forced_changes.iter())
}
/// Get the earliest limit-block number, if any. If there are pending changes across
/// different forks, this method will return the earliest effective number (across the
/// different branches). Only standard changes are taken into account for the current
/// limit, since any existing forced change should preclude the voter from voting.
pub(crate) fn current_limit(&self) -> Option<N> {
self.pending_standard_changes.roots()
.min_by_key(|&(_, _, c)| c.effective_number())
.map(|(_, _, c)| c.effective_number())
}
/// Apply or prune any pending transitions based on a best-block trigger.
///
/// Returns `Ok((median, new_set))` when a forced change has occurred. The
/// median represents the median last finalized block at the time the change
/// was signaled, and it should be used as the canon block when starting the
/// new grandpa voter. Only alters the internal state in this case.
///
/// These transitions are always forced and do not lead to justifications
/// which light clients can follow.
pub(crate) fn apply_forced_changes<F, E>(
&self,
best_hash: H,
best_number: N,
is_descendent_of: &F,
) -> Result<Option<(N, Self)>, E>
where F: Fn(&H, &H) -> Result<bool, E>,
{
let mut new_set = None;
for change in self.pending_forced_changes.iter()
.take_while(|c| c.effective_number() <= best_number) // to prevent iterating too far
.filter(|c| c.effective_number() == best_number)
{
// check if the given best block is in the same branch as the block that signaled the change.
if is_descendent_of(&change.canon_hash, &best_hash)? {
// apply this change: make the set canonical
info!(target: "finality", "Applying authority set change forced at block #{:?}",
change.canon_height);
telemetry!(CONSENSUS_INFO; "afg.applying_forced_authority_set_change";
"block" => ?change.canon_height
);
let median_last_finalized = match change.delay_kind {
DelayKind::Best { ref median_last_finalized } => median_last_finalized.clone(),
_ => unreachable!("pending_forced_changes only contains forced changes; forced changes have delay kind Best; qed."),
};
new_set = Some((median_last_finalized, AuthoritySet {
current_authorities: change.next_authorities.clone(),
set_id: self.set_id + 1,
pending_standard_changes: ForkTree::new(), // new set, new changes.
pending_forced_changes: Vec::new(),
}));
break;
}
// we don't wipe forced changes until another change is
// applied
}
Ok(new_set)
}
/// Apply or prune any pending transitions based on a finality trigger. This
/// method ensures that if there are multiple changes in the same branch,
/// finalizing this block won't finalize past multiple transitions (i.e.
/// transitions must be finalized in-order). The given function
/// `is_descendent_of` should return `true` if the second hash (target) is a
/// descendent of the first hash (base).
///
/// When the set has changed, the return value will be `Ok(Some((H, N)))`
/// which is the canonical block where the set last changed (i.e. the given
/// hash and number).
pub(crate) fn apply_standard_changes<F, E>(
&mut self,
finalized_hash: H,
finalized_number: N,
is_descendent_of: &F,
) -> Result<Status<H, N>, fork_tree::Error<E>>
where F: Fn(&H, &H) -> Result<bool, E>,
E: std::error::Error,
{
let mut status = Status {
changed: false,
new_set_block: None,
};
match self.pending_standard_changes.finalize_with_descendent_if(
&finalized_hash,
finalized_number.clone(),
is_descendent_of,
|change| change.effective_number() <= finalized_number
)? {
fork_tree::FinalizationResult::Changed(change) => {
status.changed = true;
// if we are able to finalize any standard change then we can
// discard all pending forced changes (on different forks)
self.pending_forced_changes.clear();
if let Some(change) = change {
info!(target: "finality", "Applying authority set change scheduled at block #{:?}",
change.canon_height);
telemetry!(CONSENSUS_INFO; "afg.applying_scheduled_authority_set_change";
"block" => ?change.canon_height
);
self.current_authorities = change.next_authorities;
self.set_id += 1;
status.new_set_block = Some((
finalized_hash,
finalized_number,
));
}
},
fork_tree::FinalizationResult::Unchanged => {},
}
Ok(status)
}
/// Check whether the given finalized block number enacts any standard
/// authority set change (without triggering it), ensuring that if there are
/// multiple changes in the same branch, finalizing this block won't
/// finalize past multiple transitions (i.e. transitions must be finalized
/// in-order). Returns `Some(true)` if the block being finalized enacts a
/// change that can be immediately applied, `Some(false)` if the block being
/// finalized enacts a change but it cannot be applied yet since there are
/// other dependent changes, and `None` if no change is enacted. The given
/// function `is_descendent_of` should return `true` if the second hash
/// (target) is a descendent of the first hash (base).
pub fn enacts_standard_change<F, E>(
&self,
finalized_hash: H,
finalized_number: N,
is_descendent_of: &F,
) -> Result<Option<bool>, fork_tree::Error<E>>
where F: Fn(&H, &H) -> Result<bool, E>,
E: std::error::Error,
{
self.pending_standard_changes.finalizes_any_with_descendent_if(
&finalized_hash,
finalized_number.clone(),
is_descendent_of,
|change| change.effective_number() == finalized_number
)
}
}
/// Kinds of delays for pending changes.
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub(crate) enum DelayKind<N> {
/// Depth in finalized chain.
Finalized,
/// Depth in best chain. The median last finalized block is calculated at the time the
/// change was signaled.
Best { median_last_finalized: N },
}
/// A pending change to the authority set.
///
/// This will be applied when the announcing block is at some depth within
/// the finalized or unfinalized chain.
#[derive(Debug, Clone, Encode, PartialEq)]
pub(crate) struct PendingChange<H, N> {
/// The new authorities and weights to apply.
pub(crate) next_authorities: AuthorityList,
/// How deep in the chain the announcing block must be
/// before the change is applied.
pub(crate) delay: N,
/// The announcing block's height.
pub(crate) canon_height: N,
/// The announcing block's hash.
pub(crate) canon_hash: H,
/// The delay kind.
pub(crate) delay_kind: DelayKind<N>,
}
impl<H: Decode, N: Decode> Decode for PendingChange<H, N> {
fn decode<I: codec::Input>(value: &mut I) -> Result<Self, codec::Error> {
let next_authorities = Decode::decode(value)?;
let delay = Decode::decode(value)?;
let canon_height = Decode::decode(value)?;
let canon_hash = Decode::decode(value)?;
let delay_kind = DelayKind::decode(value).unwrap_or(DelayKind::Finalized);
Ok(PendingChange {
next_authorities,
delay,
canon_height,
canon_hash,
delay_kind,
})
}
}
impl<H, N: Add<Output=N> + Clone> PendingChange<H, N> {
/// Returns the effective number this change will be applied at.
pub fn effective_number(&self) -> N {
self.canon_height.clone() + self.delay.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::crypto::Public;
fn static_is_descendent_of<A>(value: bool)
-> impl Fn(&A, &A) -> Result<bool, std::io::Error>
{
move |_, _| Ok(value)
}
fn is_descendent_of<A, F>(f: F) -> impl Fn(&A, &A) -> Result<bool, std::io::Error>
where F: Fn(&A, &A) -> bool
{
move |base, hash| Ok(f(base, hash))
}
#[test]
fn changes_iterated_in_pre_order() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
};
let change_a = PendingChange {
next_authorities: Vec::new(),
delay: 10,
canon_height: 5,
canon_hash: "hash_a",
delay_kind: DelayKind::Finalized,
};
let change_b = PendingChange {
next_authorities: Vec::new(),
delay: 0,
canon_height: 5,
canon_hash: "hash_b",
delay_kind: DelayKind::Finalized,
};
let change_c = PendingChange {
next_authorities: Vec::new(),
delay: 5,
canon_height: 10,
canon_hash: "hash_c",
delay_kind: DelayKind::Finalized,
};
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(false)).unwrap();
authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(false)).unwrap();
authorities.add_pending_change(change_c.clone(), &is_descendent_of(|base, hash| match (*base, *hash) {
("hash_a", "hash_c") => true,
("hash_b", "hash_c") => false,
_ => unreachable!(),
})).unwrap();
// forced changes are iterated last
let change_d = PendingChange {
next_authorities: Vec::new(),
delay: 2,
canon_height: 1,
canon_hash: "hash_d",
delay_kind: DelayKind::Best { median_last_finalized: 0 },
};
let change_e = PendingChange {
next_authorities: Vec::new(),
delay: 2,
canon_height: 0,
canon_hash: "hash_e",
delay_kind: DelayKind::Best { median_last_finalized: 0 },
};
authorities.add_pending_change(change_d.clone(), &static_is_descendent_of(false)).unwrap();
authorities.add_pending_change(change_e.clone(), &static_is_descendent_of(false)).unwrap();
assert_eq!(
authorities.pending_changes().collect::<Vec<_>>(),
vec![&change_b, &change_a, &change_c, &change_e, &change_d],
);
}
#[test]
fn apply_change() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
};
let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)];
let set_b = vec![(AuthorityId::from_slice(&[2; 32]), 5)];
// two competing changes at the same height on different forks
let change_a = PendingChange {
next_authorities: set_a.clone(),
delay: 10,
canon_height: 5,
canon_hash: "hash_a",
delay_kind: DelayKind::Finalized,
};
let change_b = PendingChange {
next_authorities: set_b.clone(),
delay: 10,
canon_height: 5,
canon_hash: "hash_b",
delay_kind: DelayKind::Finalized,
};
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(true)).unwrap();
authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(true)).unwrap();
assert_eq!(
authorities.pending_changes().collect::<Vec<_>>(),
vec![&change_b, &change_a],
);
// finalizing "hash_c" won't enact the change signaled at "hash_a" but it will prune out "hash_b"
let status = authorities.apply_standard_changes("hash_c", 11, &is_descendent_of(|base, hash| match (*base, *hash) {
("hash_a", "hash_c") => true,
("hash_b", "hash_c") => false,
_ => unreachable!(),
})).unwrap();
assert!(status.changed);
assert_eq!(status.new_set_block, None);
assert_eq!(
authorities.pending_changes().collect::<Vec<_>>(),
vec![&change_a],
);
// finalizing "hash_d" will enact the change signaled at "hash_a"
let status = authorities.apply_standard_changes("hash_d", 15, &is_descendent_of(|base, hash| match (*base, *hash) {
("hash_a", "hash_d") => true,
_ => unreachable!(),
})).unwrap();
assert!(status.changed);
assert_eq!(status.new_set_block, Some(("hash_d", 15)));
assert_eq!(authorities.current_authorities, set_a);
assert_eq!(authorities.set_id, 1);
assert_eq!(authorities.pending_changes().count(), 0);
}
#[test]
fn disallow_multiple_changes_being_finalized_at_once() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
};
let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)];
let set_c = vec![(AuthorityId::from_slice(&[2; 32]), 5)];
// two competing changes at the same height on different forks
let change_a = PendingChange {
next_authorities: set_a.clone(),
delay: 10,
canon_height: 5,
canon_hash: "hash_a",
delay_kind: DelayKind::Finalized,
};
let change_c = PendingChange {
next_authorities: set_c.clone(),
delay: 10,
canon_height: 30,
canon_hash: "hash_c",
delay_kind: DelayKind::Finalized,
};
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(true)).unwrap();
authorities.add_pending_change(change_c.clone(), &static_is_descendent_of(true)).unwrap();
let is_descendent_of = is_descendent_of(|base, hash| match (*base, *hash) {
("hash_a", "hash_b") => true,
("hash_a", "hash_c") => true,
("hash_a", "hash_d") => true,
("hash_c", "hash_b") => false,
("hash_c", "hash_d") => true,
("hash_b", "hash_c") => true,
_ => unreachable!(),
});
// trying to finalize past `change_c` without finalizing `change_a` first
match authorities.apply_standard_changes("hash_d", 40, &is_descendent_of) {
Err(fork_tree::Error::UnfinalizedAncestor) => {},
_ => unreachable!(),
}
let status = authorities.apply_standard_changes("hash_b", 15, &is_descendent_of).unwrap();
assert!(status.changed);
assert_eq!(status.new_set_block, Some(("hash_b", 15)));
assert_eq!(authorities.current_authorities, set_a);
assert_eq!(authorities.set_id, 1);
// after finalizing `change_a` it should be possible to finalize `change_c`
let status = authorities.apply_standard_changes("hash_d", 40, &is_descendent_of).unwrap();
assert!(status.changed);
assert_eq!(status.new_set_block, Some(("hash_d", 40)));
assert_eq!(authorities.current_authorities, set_c);
assert_eq!(authorities.set_id, 2);
}
#[test]
fn enacts_standard_change_works() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
};
let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)];
let change_a = PendingChange {
next_authorities: set_a.clone(),
delay: 10,
canon_height: 5,
canon_hash: "hash_a",
delay_kind: DelayKind::Finalized,
};
let change_b = PendingChange {
next_authorities: set_a.clone(),
delay: 10,
canon_height: 20,
canon_hash: "hash_b",
delay_kind: DelayKind::Finalized,
};
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(false)).unwrap();
authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(true)).unwrap();
let is_descendent_of = is_descendent_of(|base, hash| match (*base, *hash) {
("hash_a", "hash_d") => true,
("hash_a", "hash_e") => true,
("hash_b", "hash_d") => true,
("hash_b", "hash_e") => true,
("hash_a", "hash_c") => false,
("hash_b", "hash_c") => false,
_ => unreachable!(),
});
// "hash_c" won't finalize the existing change since it isn't a descendent
assert_eq!(
authorities.enacts_standard_change("hash_c", 15, &is_descendent_of).unwrap(),
None,
);
// "hash_d" at depth 14 won't work either
assert_eq!(
authorities.enacts_standard_change("hash_d", 14, &is_descendent_of).unwrap(),
None,
);
// but it should work at depth 15 (change height + depth)
assert_eq!(
authorities.enacts_standard_change("hash_d", 15, &is_descendent_of).unwrap(),
Some(true),
);
// finalizing "hash_e" at depth 20 will trigger change at "hash_b", but
// it can't be applied yet since "hash_a" must be applied first
assert_eq!(
authorities.enacts_standard_change("hash_e", 30, &is_descendent_of).unwrap(),
Some(false),
);
}
#[test]
fn forced_changes() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
};
let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)];
let set_b = vec![(AuthorityId::from_slice(&[2; 32]), 5)];
let change_a = PendingChange {
next_authorities: set_a.clone(),
delay: 10,
canon_height: 5,
canon_hash: "hash_a",
delay_kind: DelayKind::Best { median_last_finalized: 42 },
};
let change_b = PendingChange {
next_authorities: set_b.clone(),
delay: 10,
canon_height: 5,
canon_hash: "hash_b",
delay_kind: DelayKind::Best { median_last_finalized: 0 },
};
authorities.add_pending_change(change_a, &static_is_descendent_of(false)).unwrap();
authorities.add_pending_change(change_b, &static_is_descendent_of(false)).unwrap();
// there's an effective change triggered at block 15 but not a standard one.
// so this should do nothing.
assert_eq!(
authorities.enacts_standard_change("hash_c", 15, &static_is_descendent_of(true)).unwrap(),
None,
);
// throw a standard change into the mix to prove that it's discarded
// for being on the same fork.
//
// NOTE: after https://github.com/paritytech/substrate/issues/1861
// this should still be rejected based on the "span" rule -- it overlaps
// with another change on the same fork.
let change_c = PendingChange {
next_authorities: set_b.clone(),
delay: 3,
canon_height: 8,
canon_hash: "hash_a8",
delay_kind: DelayKind::Best { median_last_finalized: 0 },
};
let is_descendent_of_a = is_descendent_of(|base: &&str, _| {
base.starts_with("hash_a")
});
assert!(authorities.add_pending_change(change_c, &is_descendent_of_a).is_err());
// too early.
assert!(authorities.apply_forced_changes("hash_a10", 10, &static_is_descendent_of(true)).unwrap().is_none());
// too late.
assert!(authorities.apply_forced_changes("hash_a16", 16, &static_is_descendent_of(true)).unwrap().is_none());
// on time -- chooses the right change.
assert_eq!(
authorities.apply_forced_changes("hash_a15", 15, &is_descendent_of_a).unwrap().unwrap(),
(42, AuthoritySet {
current_authorities: set_a,
set_id: 1,
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
})
);
}
}
@@ -0,0 +1,611 @@
// Copyright 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/>.
//! Schema for stuff in the aux-db.
use std::fmt::Debug;
use std::sync::Arc;
use codec::{Encode, Decode};
use client_api::backend::AuxStore;
use client_api::error::{Result as ClientResult, Error as ClientError};
use fork_tree::ForkTree;
use grandpa::round::State as RoundState;
use sr_primitives::traits::{Block as BlockT, NumberFor};
use log::{info, warn};
use fg_primitives::{AuthorityList, SetId, RoundNumber};
use crate::authorities::{AuthoritySet, SharedAuthoritySet, PendingChange, DelayKind};
use crate::consensus_changes::{SharedConsensusChanges, ConsensusChanges};
use crate::environment::{
CompletedRound, CompletedRounds, CurrentRounds, HasVoted, SharedVoterSetState, VoterSetState,
};
use crate::NewAuthoritySet;
const VERSION_KEY: &[u8] = b"grandpa_schema_version";
const SET_STATE_KEY: &[u8] = b"grandpa_completed_round";
const AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters";
const CONSENSUS_CHANGES_KEY: &[u8] = b"grandpa_consensus_changes";
const CURRENT_VERSION: u32 = 2;
/// The voter set state.
#[derive(Debug, Clone, Encode, Decode)]
#[cfg_attr(test, derive(PartialEq))]
pub enum V1VoterSetState<H, N> {
/// The voter set state, currently paused.
Paused(RoundNumber, RoundState<H, N>),
/// The voter set state, currently live.
Live(RoundNumber, RoundState<H, N>),
}
type V0VoterSetState<H, N> = (RoundNumber, RoundState<H, N>);
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
struct V0PendingChange<H, N> {
next_authorities: AuthorityList,
delay: N,
canon_height: N,
canon_hash: H,
}
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
struct V0AuthoritySet<H, N> {
current_authorities: AuthorityList,
set_id: SetId,
pending_changes: Vec<V0PendingChange<H, N>>,
}
impl<H, N> Into<AuthoritySet<H, N>> for V0AuthoritySet<H, N>
where H: Clone + Debug + PartialEq,
N: Clone + Debug + Ord,
{
fn into(self) -> AuthoritySet<H, N> {
let mut pending_standard_changes = ForkTree::new();
for old_change in self.pending_changes {
let new_change = PendingChange {
next_authorities: old_change.next_authorities,
delay: old_change.delay,
canon_height: old_change.canon_height,
canon_hash: old_change.canon_hash,
delay_kind: DelayKind::Finalized,
};
if let Err(err) = pending_standard_changes.import::<_, ClientError>(
new_change.canon_hash.clone(),
new_change.canon_height.clone(),
new_change,
// previously we only supported at most one pending change per fork
&|_, _| Ok(false),
) {
warn!(target: "afg", "Error migrating pending authority set change: {:?}.", err);
warn!(target: "afg", "Node is in a potentially inconsistent state.");
}
}
AuthoritySet {
current_authorities: self.current_authorities,
set_id: self.set_id,
pending_forced_changes: Vec::new(),
pending_standard_changes
}
}
}
pub(crate) fn load_decode<B: AuxStore, T: Decode>(backend: &B, key: &[u8]) -> ClientResult<Option<T>> {
match backend.get_aux(key)? {
None => Ok(None),
Some(t) => T::decode(&mut &t[..])
.map_err(
|e| ClientError::Backend(format!("GRANDPA DB is corrupted: {}", e.what())),
)
.map(Some)
}
}
/// Persistent data kept between runs.
pub(crate) struct PersistentData<Block: BlockT> {
pub(crate) authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
pub(crate) consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
pub(crate) set_state: SharedVoterSetState<Block>,
}
fn migrate_from_version0<Block: BlockT, B, G>(
backend: &B,
genesis_round: &G,
) -> ClientResult<Option<(
AuthoritySet<Block::Hash, NumberFor<Block>>,
VoterSetState<Block>,
)>> where B: AuxStore,
G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
{
CURRENT_VERSION.using_encoded(|s|
backend.insert_aux(&[(VERSION_KEY, s)], &[])
)?;
if let Some(old_set) = load_decode::<_, V0AuthoritySet<Block::Hash, NumberFor<Block>>>(
backend,
AUTHORITY_SET_KEY,
)? {
let new_set: AuthoritySet<Block::Hash, NumberFor<Block>> = old_set.into();
backend.insert_aux(&[(AUTHORITY_SET_KEY, new_set.encode().as_slice())], &[])?;
let (last_round_number, last_round_state) = match load_decode::<_, V0VoterSetState<Block::Hash, NumberFor<Block>>>(
backend,
SET_STATE_KEY,
)? {
Some((number, state)) => (number, state),
None => (0, genesis_round()),
};
let set_id = new_set.current().0;
let base = last_round_state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(last_round_number + 1, HasVoted::No);
let set_state = VoterSetState::Live {
completed_rounds: CompletedRounds::new(
CompletedRound {
number: last_round_number,
state: last_round_state,
votes: Vec::new(),
base,
},
set_id,
&new_set,
),
current_rounds,
};
backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?;
return Ok(Some((new_set, set_state)));
}
Ok(None)
}
fn migrate_from_version1<Block: BlockT, B, G>(
backend: &B,
genesis_round: &G,
) -> ClientResult<Option<(
AuthoritySet<Block::Hash, NumberFor<Block>>,
VoterSetState<Block>,
)>> where B: AuxStore,
G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
{
CURRENT_VERSION.using_encoded(|s|
backend.insert_aux(&[(VERSION_KEY, s)], &[])
)?;
if let Some(set) = load_decode::<_, AuthoritySet<Block::Hash, NumberFor<Block>>>(
backend,
AUTHORITY_SET_KEY,
)? {
let set_id = set.current().0;
let completed_rounds = |number, state, base| CompletedRounds::new(
CompletedRound {
number,
state,
votes: Vec::new(),
base,
},
set_id,
&set,
);
let set_state = match load_decode::<_, V1VoterSetState<Block::Hash, NumberFor<Block>>>(
backend,
SET_STATE_KEY,
)? {
Some(V1VoterSetState::Paused(last_round_number, set_state)) => {
let base = set_state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
VoterSetState::Paused {
completed_rounds: completed_rounds(last_round_number, set_state, base),
}
},
Some(V1VoterSetState::Live(last_round_number, set_state)) => {
let base = set_state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(last_round_number + 1, HasVoted::No);
VoterSetState::Live {
completed_rounds: completed_rounds(last_round_number, set_state, base),
current_rounds,
}
},
None => {
let set_state = genesis_round();
let base = set_state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
VoterSetState::live(
set_id,
&set,
base,
)
},
};
backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?;
return Ok(Some((set, set_state)));
}
Ok(None)
}
/// Load or initialize persistent data from backend.
pub(crate) fn load_persistent<Block: BlockT, B, G>(
backend: &B,
genesis_hash: Block::Hash,
genesis_number: NumberFor<Block>,
genesis_authorities: G,
)
-> ClientResult<PersistentData<Block>>
where
B: AuxStore,
G: FnOnce() -> ClientResult<AuthorityList>,
{
let version: Option<u32> = load_decode(backend, VERSION_KEY)?;
let consensus_changes = load_decode(backend, CONSENSUS_CHANGES_KEY)?
.unwrap_or_else(ConsensusChanges::<Block::Hash, NumberFor<Block>>::empty);
let make_genesis_round = move || RoundState::genesis((genesis_hash, genesis_number));
match version {
None => {
if let Some((new_set, set_state)) = migrate_from_version0::<Block, _, _>(backend, &make_genesis_round)? {
return Ok(PersistentData {
authority_set: new_set.into(),
consensus_changes: Arc::new(consensus_changes.into()),
set_state: set_state.into(),
});
}
},
Some(1) => {
if let Some((new_set, set_state)) = migrate_from_version1::<Block, _, _>(backend, &make_genesis_round)? {
return Ok(PersistentData {
authority_set: new_set.into(),
consensus_changes: Arc::new(consensus_changes.into()),
set_state: set_state.into(),
});
}
},
Some(2) => {
if let Some(set) = load_decode::<_, AuthoritySet<Block::Hash, NumberFor<Block>>>(
backend,
AUTHORITY_SET_KEY,
)? {
let set_state = match load_decode::<_, VoterSetState<Block>>(
backend,
SET_STATE_KEY,
)? {
Some(state) => state,
None => {
let state = make_genesis_round();
let base = state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
VoterSetState::live(
set.current().0,
&set,
base,
)
}
};
return Ok(PersistentData {
authority_set: set.into(),
consensus_changes: Arc::new(consensus_changes.into()),
set_state: set_state.into(),
});
}
}
Some(other) => return Err(ClientError::Backend(
format!("Unsupported GRANDPA DB version: {:?}", other)
).into()),
}
// genesis.
info!(target: "afg", "Loading GRANDPA authority set \
from genesis on what appears to be first startup.");
let genesis_authorities = genesis_authorities()?;
let genesis_set = AuthoritySet::genesis(genesis_authorities.clone());
let state = make_genesis_round();
let base = state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
let genesis_state = VoterSetState::live(
0,
&genesis_set,
base,
);
backend.insert_aux(
&[
(AUTHORITY_SET_KEY, genesis_set.encode().as_slice()),
(SET_STATE_KEY, genesis_state.encode().as_slice()),
],
&[],
)?;
Ok(PersistentData {
authority_set: genesis_set.into(),
set_state: genesis_state.into(),
consensus_changes: Arc::new(consensus_changes.into()),
})
}
/// Update the authority set on disk after a change.
///
/// If there has just been a handoff, pass a `new_set` parameter that describes the
/// handoff. `set` in all cases should reflect the current authority set, with all
/// changes and handoffs applied.
pub(crate) fn update_authority_set<Block: BlockT, F, R>(
set: &AuthoritySet<Block::Hash, NumberFor<Block>>,
new_set: Option<&NewAuthoritySet<Block::Hash, NumberFor<Block>>>,
write_aux: F
) -> R where
F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
{
// write new authority set state to disk.
let encoded_set = set.encode();
if let Some(new_set) = new_set {
// we also overwrite the "last completed round" entry with a blank slate
// because from the perspective of the finality gadget, the chain has
// reset.
let set_state = VoterSetState::<Block>::live(
new_set.set_id,
&set,
(new_set.canon_hash, new_set.canon_number),
);
let encoded = set_state.encode();
write_aux(&[
(AUTHORITY_SET_KEY, &encoded_set[..]),
(SET_STATE_KEY, &encoded[..]),
])
} else {
write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..])])
}
}
/// Write voter set state.
pub(crate) fn write_voter_set_state<Block: BlockT, B: AuxStore>(
backend: &B,
state: &VoterSetState<Block>,
) -> ClientResult<()> {
backend.insert_aux(
&[(SET_STATE_KEY, state.encode().as_slice())],
&[]
)
}
/// Update the consensus changes.
pub(crate) fn update_consensus_changes<H, N, F, R>(
set: &ConsensusChanges<H, N>,
write_aux: F
) -> R where
H: Encode + Clone,
N: Encode + Clone,
F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
{
write_aux(&[(CONSENSUS_CHANGES_KEY, set.encode().as_slice())])
}
#[cfg(test)]
pub(crate) fn load_authorities<B: AuxStore, H: Decode, N: Decode>(backend: &B)
-> Option<AuthoritySet<H, N>> {
load_decode::<_, AuthoritySet<H, N>>(backend, AUTHORITY_SET_KEY)
.expect("backend error")
}
#[cfg(test)]
mod test {
use fg_primitives::AuthorityId;
use primitives::H256;
use test_client;
use super::*;
#[test]
fn load_decode_from_v0_migrates_data_format() {
let client = test_client::new();
let authorities = vec![(AuthorityId::default(), 100)];
let set_id = 3;
let round_number: RoundNumber = 42;
let round_state = RoundState::<H256, u64> {
prevote_ghost: Some((H256::random(), 32)),
finalized: None,
estimate: None,
completable: false,
};
{
let authority_set = V0AuthoritySet::<H256, u64> {
current_authorities: authorities.clone(),
pending_changes: Vec::new(),
set_id,
};
let voter_set_state = (round_number, round_state.clone());
client.insert_aux(
&[
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
],
&[],
).unwrap();
}
assert_eq!(
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
None,
);
// should perform the migration
load_persistent::<test_client::runtime::Block, _, _>(
&client,
H256::random(),
0,
|| unreachable!(),
).unwrap();
assert_eq!(
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
Some(2),
);
let PersistentData { authority_set, set_state, .. } = load_persistent::<test_client::runtime::Block, _, _>(
&client,
H256::random(),
0,
|| unreachable!(),
).unwrap();
assert_eq!(
*authority_set.inner().read(),
AuthoritySet {
current_authorities: authorities.clone(),
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
set_id,
},
);
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(round_number + 1, HasVoted::No);
assert_eq!(
&*set_state.read(),
&VoterSetState::Live {
completed_rounds: CompletedRounds::new(
CompletedRound {
number: round_number,
state: round_state.clone(),
base: round_state.prevote_ghost.unwrap(),
votes: vec![],
},
set_id,
&*authority_set.inner().read(),
),
current_rounds,
},
);
}
#[test]
fn load_decode_from_v1_migrates_data_format() {
let client = test_client::new();
let authorities = vec![(AuthorityId::default(), 100)];
let set_id = 3;
let round_number: RoundNumber = 42;
let round_state = RoundState::<H256, u64> {
prevote_ghost: Some((H256::random(), 32)),
finalized: None,
estimate: None,
completable: false,
};
{
let authority_set = AuthoritySet::<H256, u64> {
current_authorities: authorities.clone(),
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
set_id,
};
let voter_set_state = V1VoterSetState::Live(round_number, round_state.clone());
client.insert_aux(
&[
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
(VERSION_KEY, 1u32.encode().as_slice()),
],
&[],
).unwrap();
}
assert_eq!(
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
Some(1),
);
// should perform the migration
load_persistent::<test_client::runtime::Block, _, _>(
&client,
H256::random(),
0,
|| unreachable!(),
).unwrap();
assert_eq!(
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
Some(2),
);
let PersistentData { authority_set, set_state, .. } = load_persistent::<test_client::runtime::Block, _, _>(
&client,
H256::random(),
0,
|| unreachable!(),
).unwrap();
assert_eq!(
*authority_set.inner().read(),
AuthoritySet {
current_authorities: authorities.clone(),
pending_standard_changes: ForkTree::new(),
pending_forced_changes: Vec::new(),
set_id,
},
);
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(round_number + 1, HasVoted::No);
assert_eq!(
&*set_state.read(),
&VoterSetState::Live {
completed_rounds: CompletedRounds::new(
CompletedRound {
number: round_number,
state: round_state.clone(),
base: round_state.prevote_ghost.unwrap(),
votes: vec![],
},
set_id,
&*authority_set.inner().read(),
),
current_rounds,
},
);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,110 @@
// Copyright 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/>.
//! Periodic rebroadcast of neighbor packets.
use std::time::{Instant, Duration};
use codec::Encode;
use futures::prelude::*;
use futures::sync::mpsc;
use log::{debug, warn};
use tokio_timer::Delay;
use network::PeerId;
use sr_primitives::traits::{NumberFor, Block as BlockT};
use super::{gossip::{NeighborPacket, GossipMessage}, Network};
// how often to rebroadcast, if no other
const REBROADCAST_AFTER: Duration = Duration::from_secs(2 * 60);
fn rebroadcast_instant() -> Instant {
Instant::now() + REBROADCAST_AFTER
}
/// A sender used to send neighbor packets to a background job.
#[derive(Clone)]
pub(super) struct NeighborPacketSender<B: BlockT>(
mpsc::UnboundedSender<(Vec<PeerId>, NeighborPacket<NumberFor<B>>)>
);
impl<B: BlockT> NeighborPacketSender<B> {
/// Send a neighbor packet for the background worker to gossip to peers.
pub fn send(
&self,
who: Vec<network::PeerId>,
neighbor_packet: NeighborPacket<NumberFor<B>>,
) {
if let Err(err) = self.0.unbounded_send((who, neighbor_packet)) {
debug!(target: "afg", "Failed to send neighbor packet: {:?}", err);
}
}
}
/// Does the work of sending neighbor packets, asynchronously.
///
/// It may rebroadcast the last neighbor packet periodically when no
/// progress is made.
pub(super) fn neighbor_packet_worker<B, N>(net: N) -> (
impl Future<Item = (), Error = ()> + Send + 'static,
NeighborPacketSender<B>,
) where
B: BlockT,
N: Network<B>,
{
let mut last = None;
let (tx, mut rx) = mpsc::unbounded::<(Vec<PeerId>, NeighborPacket<NumberFor<B>>)>();
let mut delay = Delay::new(rebroadcast_instant());
let work = futures::future::poll_fn(move || {
loop {
match rx.poll().expect("unbounded receivers do not error; qed") {
Async::Ready(None) => return Ok(Async::Ready(())),
Async::Ready(Some((to, packet))) => {
// send to peers.
net.send_message(to.clone(), GossipMessage::<B>::from(packet.clone()).encode());
// rebroadcasting network.
delay.reset(rebroadcast_instant());
last = Some((to, packet));
}
Async::NotReady => break,
}
}
// has to be done in a loop because it needs to be polled after
// re-scheduling.
loop {
match delay.poll() {
Err(e) => {
warn!(target: "afg", "Could not rebroadcast neighbor packets: {:?}", e);
delay.reset(rebroadcast_instant());
}
Ok(Async::Ready(())) => {
delay.reset(rebroadcast_instant());
if let Some((ref to, ref packet)) = last {
// send to peers.
net.send_message(to.clone(), GossipMessage::<B>::from(packet.clone()).encode());
}
}
Ok(Async::NotReady) => return Ok(Async::NotReady),
}
}
});
(work, NeighborPacketSender(tx))
}
@@ -0,0 +1,506 @@
// Copyright 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/>.
//! Tests for the communication portion of the GRANDPA crate.
use futures::sync::mpsc;
use futures::prelude::*;
use network::consensus_gossip as network_gossip;
use network::test::{Block, Hash};
use network_gossip::Validator;
use tokio::runtime::current_thread;
use std::sync::Arc;
use keyring::Ed25519Keyring;
use codec::Encode;
use sr_primitives::traits::NumberFor;
use crate::environment::SharedVoterSetState;
use fg_primitives::AuthorityList;
use super::gossip::{self, GossipValidator};
use super::{AuthorityId, VoterSet, Round, SetId};
enum Event {
MessagesFor(Hash, mpsc::UnboundedSender<network_gossip::TopicNotification>),
RegisterValidator(Arc<dyn network_gossip::Validator<Block>>),
GossipMessage(Hash, Vec<u8>, bool),
SendMessage(Vec<network::PeerId>, Vec<u8>),
Report(network::PeerId, i32),
Announce(Hash),
}
#[derive(Clone)]
struct TestNetwork {
sender: mpsc::UnboundedSender<Event>,
}
impl super::Network<Block> for TestNetwork {
type In = mpsc::UnboundedReceiver<network_gossip::TopicNotification>;
/// Get a stream of messages for a specific gossip topic.
fn messages_for(&self, topic: Hash) -> Self::In {
let (tx, rx) = mpsc::unbounded();
let _ = self.sender.unbounded_send(Event::MessagesFor(topic, tx));
rx
}
/// Register a gossip validator.
fn register_validator(&self, validator: Arc<dyn network_gossip::Validator<Block>>) {
let _ = self.sender.unbounded_send(Event::RegisterValidator(validator));
}
/// Gossip a message out to all connected peers.
///
/// Force causes it to be sent to all peers, even if they've seen it already.
/// Only should be used in case of consensus stall.
fn gossip_message(&self, topic: Hash, data: Vec<u8>, force: bool) {
let _ = self.sender.unbounded_send(Event::GossipMessage(topic, data, force));
}
/// Send a message to a bunch of specific peers, even if they've seen it already.
fn send_message(&self, who: Vec<network::PeerId>, data: Vec<u8>) {
let _ = self.sender.unbounded_send(Event::SendMessage(who, data));
}
/// Register a message with the gossip service, it isn't broadcast right
/// away to any peers, but may be sent to new peers joining or when asked to
/// broadcast the topic. Useful to register previous messages on node
/// startup.
fn register_gossip_message(&self, _topic: Hash, _data: Vec<u8>) {
// NOTE: only required to restore previous state on startup
// not required for tests currently
}
/// Report a peer's cost or benefit after some action.
fn report(&self, who: network::PeerId, cost_benefit: i32) {
let _ = self.sender.unbounded_send(Event::Report(who, cost_benefit));
}
/// Inform peers that a block with given hash should be downloaded.
fn announce(&self, block: Hash, _associated_data: Vec<u8>) {
let _ = self.sender.unbounded_send(Event::Announce(block));
}
/// Notify the sync service to try syncing the given chain.
fn set_sync_fork_request(&self, _peers: Vec<network::PeerId>, _hash: Hash, _number: NumberFor<Block>) {}
}
impl network_gossip::ValidatorContext<Block> for TestNetwork {
fn broadcast_topic(&mut self, _: Hash, _: bool) { }
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) { }
fn send_message(&mut self, who: &network::PeerId, data: Vec<u8>) {
<Self as super::Network<Block>>::send_message(self, vec![who.clone()], data);
}
fn send_topic(&mut self, _: &network::PeerId, _: Hash, _: bool) { }
}
struct Tester {
net_handle: super::NetworkBridge<Block, TestNetwork>,
gossip_validator: Arc<GossipValidator<Block>>,
events: mpsc::UnboundedReceiver<Event>,
}
impl Tester {
fn filter_network_events<F>(self, mut pred: F) -> impl Future<Item=Self,Error=()>
where F: FnMut(Event) -> bool
{
let mut s = Some(self);
futures::future::poll_fn(move || loop {
match s.as_mut().unwrap().events.poll().expect("concluded early") {
Async::Ready(None) => panic!("concluded early"),
Async::Ready(Some(item)) => if pred(item) {
return Ok(Async::Ready(s.take().unwrap()))
},
Async::NotReady => return Ok(Async::NotReady),
}
})
}
}
// some random config (not really needed)
fn config() -> crate::Config {
crate::Config {
gossip_duration: std::time::Duration::from_millis(10),
justification_period: 256,
keystore: None,
name: None,
is_authority: true,
observer_enabled: true,
}
}
// dummy voter set state
fn voter_set_state() -> SharedVoterSetState<Block> {
use crate::authorities::AuthoritySet;
use crate::environment::VoterSetState;
use grandpa::round::State as RoundState;
use primitives::H256;
let state = RoundState::genesis((H256::zero(), 0));
let base = state.prevote_ghost.unwrap();
let voters = AuthoritySet::genesis(Vec::new());
let set_state = VoterSetState::live(
0,
&voters,
base,
);
set_state.into()
}
// needs to run in a tokio runtime.
fn make_test_network() -> (
impl Future<Item=Tester,Error=()>,
TestNetwork,
) {
let (tx, rx) = mpsc::unbounded();
let net = TestNetwork { sender: tx };
#[derive(Clone)]
struct Exit;
impl Future for Exit {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
Ok(Async::NotReady)
}
}
let (bridge, startup_work) = super::NetworkBridge::new(
net.clone(),
config(),
voter_set_state(),
Exit,
);
(
startup_work.map(move |()| Tester {
gossip_validator: bridge.validator.clone(),
net_handle: bridge,
events: rx,
}),
net,
)
}
fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList {
keys.iter()
.map(|key| key.clone().public().into())
.map(|id| (id, 1))
.collect()
}
struct NoopContext;
impl network_gossip::ValidatorContext<Block> for NoopContext {
fn broadcast_topic(&mut self, _: Hash, _: bool) { }
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) { }
fn send_message(&mut self, _: &network::PeerId, _: Vec<u8>) { }
fn send_topic(&mut self, _: &network::PeerId, _: Hash, _: bool) { }
}
#[test]
fn good_commit_leads_to_relay() {
let private = [Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
let public = make_ids(&private[..]);
let voter_set = Arc::new(public.iter().cloned().collect::<VoterSet<AuthorityId>>());
let round = 1;
let set_id = 1;
let commit = {
let target_hash: Hash = [1; 32].into();
let target_number = 500;
let precommit = grandpa::Precommit { target_hash: target_hash.clone(), target_number };
let payload = super::localized_payload(
round, set_id, &grandpa::Message::Precommit(precommit.clone())
);
let mut precommits = Vec::new();
let mut auth_data = Vec::new();
for (i, key) in private.iter().enumerate() {
precommits.push(precommit.clone());
let signature = fg_primitives::AuthoritySignature::from(key.sign(&payload[..]));
auth_data.push((signature, public[i].0.clone()))
}
grandpa::CompactCommit {
target_hash,
target_number,
precommits,
auth_data,
}
};
let encoded_commit = gossip::GossipMessage::<Block>::Commit(gossip::FullCommitMessage {
round: Round(round),
set_id: SetId(set_id),
message: commit,
}).encode();
let id = network::PeerId::random();
let global_topic = super::global_topic::<Block>(set_id);
let test = make_test_network().0
.and_then(move |tester| {
// register a peer.
tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL);
Ok((tester, id))
})
.and_then(move |(tester, id)| {
// start round, dispatch commit, and wait for broadcast.
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
{
let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
match action {
gossip::Action::ProcessAndDiscard(t, _) => assert_eq!(t, global_topic),
_ => panic!("wrong expected outcome from initial commit validation"),
}
}
let commit_to_send = encoded_commit.clone();
// asking for global communication will cause the test network
// to send us an event asking us for a stream. use it to
// send a message.
let sender_id = id.clone();
let send_message = tester.filter_network_events(move |event| match event {
Event::MessagesFor(topic, sender) => {
if topic != global_topic { return false }
let _ = sender.unbounded_send(network_gossip::TopicNotification {
message: commit_to_send.clone(),
sender: Some(sender_id.clone()),
});
true
}
_ => false,
});
// when the commit comes in, we'll tell the callback it was good.
let handle_commit = commits_in.into_future()
.map(|(item, _)| {
match item.unwrap() {
grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
callback.run(grandpa::voter::CommitProcessingOutcome::good());
},
_ => panic!("commit expected"),
}
})
.map_err(|_| panic!("could not process commit"));
// once the message is sent and commit is "handled" we should have
// a repropagation event coming from the network.
send_message.join(handle_commit).and_then(move |(tester, ())| {
tester.filter_network_events(move |event| match event {
Event::GossipMessage(topic, data, false) => {
if topic == global_topic && data == encoded_commit {
true
} else {
panic!("Trying to gossip something strange")
}
}
_ => false,
})
})
.map_err(|_| panic!("could not watch for gossip message"))
.map(|_| ())
});
current_thread::block_on_all(test).unwrap();
}
#[test]
fn bad_commit_leads_to_report() {
let private = [Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
let public = make_ids(&private[..]);
let voter_set = Arc::new(public.iter().cloned().collect::<VoterSet<AuthorityId>>());
let round = 1;
let set_id = 1;
let commit = {
let target_hash: Hash = [1; 32].into();
let target_number = 500;
let precommit = grandpa::Precommit { target_hash: target_hash.clone(), target_number };
let payload = super::localized_payload(
round, set_id, &grandpa::Message::Precommit(precommit.clone())
);
let mut precommits = Vec::new();
let mut auth_data = Vec::new();
for (i, key) in private.iter().enumerate() {
precommits.push(precommit.clone());
let signature = fg_primitives::AuthoritySignature::from(key.sign(&payload[..]));
auth_data.push((signature, public[i].0.clone()))
}
grandpa::CompactCommit {
target_hash,
target_number,
precommits,
auth_data,
}
};
let encoded_commit = gossip::GossipMessage::<Block>::Commit(gossip::FullCommitMessage {
round: Round(round),
set_id: SetId(set_id),
message: commit,
}).encode();
let id = network::PeerId::random();
let global_topic = super::global_topic::<Block>(set_id);
let test = make_test_network().0
.and_then(move |tester| {
// register a peer.
tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::FULL);
Ok((tester, id))
})
.and_then(move |(tester, id)| {
// start round, dispatch commit, and wait for broadcast.
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
{
let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
match action {
gossip::Action::ProcessAndDiscard(t, _) => assert_eq!(t, global_topic),
_ => panic!("wrong expected outcome from initial commit validation"),
}
}
let commit_to_send = encoded_commit.clone();
// asking for global communication will cause the test network
// to send us an event asking us for a stream. use it to
// send a message.
let sender_id = id.clone();
let send_message = tester.filter_network_events(move |event| match event {
Event::MessagesFor(topic, sender) => {
if topic != global_topic { return false }
let _ = sender.unbounded_send(network_gossip::TopicNotification {
message: commit_to_send.clone(),
sender: Some(sender_id.clone()),
});
true
}
_ => false,
});
// when the commit comes in, we'll tell the callback it was good.
let handle_commit = commits_in.into_future()
.map(|(item, _)| {
match item.unwrap() {
grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
callback.run(grandpa::voter::CommitProcessingOutcome::bad());
},
_ => panic!("commit expected"),
}
})
.map_err(|_| panic!("could not process commit"));
// once the message is sent and commit is "handled" we should have
// a report event coming from the network.
send_message.join(handle_commit).and_then(move |(tester, ())| {
tester.filter_network_events(move |event| match event {
Event::Report(who, cost_benefit) => {
if who == id && cost_benefit == super::cost::INVALID_COMMIT {
true
} else {
panic!("reported unknown peer or unexpected cost");
}
}
_ => false,
})
})
.map_err(|_| panic!("could not watch for peer report"))
.map(|_| ())
});
current_thread::block_on_all(test).unwrap();
}
#[test]
fn peer_with_higher_view_leads_to_catch_up_request() {
let id = network::PeerId::random();
let (tester, mut net) = make_test_network();
let test = tester
.and_then(move |tester| {
// register a peer with authority role.
tester.gossip_validator.new_peer(&mut NoopContext, &id, network::config::Roles::AUTHORITY);
Ok((tester, id))
})
.and_then(move |(tester, id)| {
// send neighbor message at round 10 and height 50
let result = tester.gossip_validator.validate(
&mut net,
&id,
&gossip::GossipMessage::<Block>::from(gossip::NeighborPacket {
set_id: SetId(0),
round: Round(10),
commit_finalized_height: 50,
}).encode(),
);
// neighbor packets are always discard
match result {
network_gossip::ValidationResult::Discard => {},
_ => panic!("wrong expected outcome from neighbor validation"),
}
// a catch up request should be sent to the peer for round - 1
tester.filter_network_events(move |event| match event {
Event::SendMessage(peers, message) => {
assert_eq!(
peers,
vec![id.clone()],
);
assert_eq!(
message,
gossip::GossipMessage::<Block>::CatchUpRequest(
gossip::CatchUpRequestMessage {
set_id: SetId(0),
round: Round(9),
}
).encode(),
);
true
},
_ => false,
})
.map_err(|_| panic!("could not watch for peer send message"))
.map(|_| ())
});
current_thread::block_on_all(test).unwrap();
}
@@ -0,0 +1,78 @@
// Copyright 2018-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/>.
use std::sync::Arc;
use codec::{Encode, Decode};
/// Consensus-related data changes tracker.
#[derive(Clone, Debug, Encode, Decode)]
pub(crate) struct ConsensusChanges<H, N> {
pending_changes: Vec<(N, H)>,
}
impl<H, N> ConsensusChanges<H, N> {
/// Create empty consensus changes.
pub(crate) fn empty() -> Self {
ConsensusChanges { pending_changes: Vec::new(), }
}
}
impl<H: Copy + PartialEq, N: Copy + Ord> ConsensusChanges<H, N> {
/// Returns reference to all pending changes.
pub fn pending_changes(&self) -> &[(N, H)] {
&self.pending_changes
}
/// Note unfinalized change of consensus-related data.
pub(crate) fn note_change(&mut self, at: (N, H)) {
let idx = self.pending_changes
.binary_search_by_key(&at.0, |change| change.0)
.unwrap_or_else(|i| i);
self.pending_changes.insert(idx, at);
}
/// Finalize all pending consensus changes that are finalized by given block.
/// Returns true if there any changes were finalized.
pub(crate) fn finalize<F: Fn(N) -> ::client_api::error::Result<Option<H>>>(
&mut self,
block: (N, H),
canonical_at_height: F,
) -> ::client_api::error::Result<(bool, bool)> {
let (split_idx, has_finalized_changes) = self.pending_changes.iter()
.enumerate()
.take_while(|(_, &(at_height, _))| at_height <= block.0)
.fold((None, Ok(false)), |(_, has_finalized_changes), (idx, ref at)|
(
Some(idx),
has_finalized_changes
.and_then(|has_finalized_changes| if has_finalized_changes {
Ok(has_finalized_changes)
} else {
canonical_at_height(at.0).map(|can_hash| Some(at.1) == can_hash)
}),
));
let altered_changes = split_idx.is_some();
if let Some(split_idx) = split_idx {
self.pending_changes = self.pending_changes.split_off(split_idx + 1);
}
has_finalized_changes.map(|has_finalized_changes| (altered_changes, has_finalized_changes))
}
}
/// Thread-safe consensus changes tracker reference.
pub(crate) type SharedConsensusChanges<H, N> = Arc<parking_lot::Mutex<ConsensusChanges<H, N>>>;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,990 @@
// Copyright 2018-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/>.
//! GRANDPA block finality proof generation and check.
//!
//! Finality of block B is proved by providing:
//! 1) the justification for the descendant block F;
//! 2) headers sub-chain (B; F] if B != F;
//! 3) proof of GRANDPA::authorities() if the set changes at block F.
//!
//! Since earliest possible justification is returned, the GRANDPA authorities set
//! at the block F is guaranteed to be the same as in the block B (this is because block
//! that enacts new GRANDPA authorities set always comes with justification). It also
//! means that the `set_id` is the same at blocks B and F.
//!
//! Let U be the last finalized block known to caller. If authorities set has changed several
//! times in the (U; F] interval, multiple finality proof fragments are returned (one for each
//! authority set change) and they must be verified in-order.
//!
//! Finality proof provider can choose how to provide finality proof on its own. The incomplete
//! finality proof (that finalizes some block C that is ancestor of the B and descendant
//! of the U) could be returned.
use std::iter;
use std::sync::Arc;
use log::{trace, warn};
use client_api::{
backend::Backend, blockchain::Backend as BlockchainBackend, CallExecutor,
error::{Error as ClientError, Result as ClientResult},
light::{FetchChecker, RemoteReadRequest},
StorageProof,
};
use client::Client;
use codec::{Encode, Decode};
use grandpa::BlockNumberOps;
use sr_primitives::{
Justification, generic::BlockId,
traits::{NumberFor, Block as BlockT, Header as HeaderT, One},
};
use primitives::{H256, Blake2Hasher, storage::StorageKey};
use substrate_telemetry::{telemetry, CONSENSUS_INFO};
use fg_primitives::{AuthorityId, AuthorityList, VersionedAuthorityList, GRANDPA_AUTHORITIES_KEY};
use crate::justification::GrandpaJustification;
/// Maximum number of fragments that we want to return in a single prove_finality call.
const MAX_FRAGMENTS_IN_PROOF: usize = 8;
/// GRANDPA authority set related methods for the finality proof provider.
pub trait AuthoritySetForFinalityProver<Block: BlockT>: Send + Sync {
/// Read GRANDPA_AUTHORITIES_KEY from storage at given block.
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<AuthorityList>;
/// Prove storage read of GRANDPA_AUTHORITIES_KEY at given block.
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<StorageProof>;
}
/// Client-based implementation of AuthoritySetForFinalityProver.
impl<B, E, Block: BlockT<Hash=H256>, RA> AuthoritySetForFinalityProver<Block> for Client<B, E, Block, RA>
where
B: Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: Send + Sync,
{
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<AuthorityList> {
let storage_key = StorageKey(GRANDPA_AUTHORITIES_KEY.to_vec());
self.storage(block, &storage_key)?
.and_then(|encoded| VersionedAuthorityList::decode(&mut encoded.0.as_slice()).ok())
.map(|versioned| versioned.into())
.ok_or(ClientError::InvalidAuthoritiesSet)
}
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<StorageProof> {
self.read_proof(block, iter::once(GRANDPA_AUTHORITIES_KEY))
}
}
/// GRANDPA authority set related methods for the finality proof checker.
pub trait AuthoritySetForFinalityChecker<Block: BlockT>: Send + Sync {
/// Check storage read proof of GRANDPA_AUTHORITIES_KEY at given block.
fn check_authorities_proof(
&self,
hash: Block::Hash,
header: Block::Header,
proof: StorageProof,
) -> ClientResult<AuthorityList>;
}
/// FetchChecker-based implementation of AuthoritySetForFinalityChecker.
impl<Block: BlockT> AuthoritySetForFinalityChecker<Block> for Arc<dyn FetchChecker<Block>> {
fn check_authorities_proof(
&self,
hash: Block::Hash,
header: Block::Header,
proof: StorageProof,
) -> ClientResult<AuthorityList> {
let storage_key = GRANDPA_AUTHORITIES_KEY.to_vec();
let request = RemoteReadRequest {
block: hash,
header,
keys: vec![storage_key.clone()],
retry_count: None,
};
self.check_read_proof(&request, proof)
.and_then(|results| {
let maybe_encoded = results.get(&storage_key)
.expect(
"storage_key is listed in the request keys; \
check_read_proof must return a value for each requested key;
qed"
);
maybe_encoded
.as_ref()
.and_then(|encoded| {
VersionedAuthorityList::decode(&mut encoded.as_slice()).ok()
})
.map(|versioned| versioned.into())
.ok_or(ClientError::InvalidAuthoritiesSet)
})
}
}
/// Finality proof provider for serving network requests.
pub struct FinalityProofProvider<B, Block: BlockT<Hash=H256>> {
backend: Arc<B>,
authority_provider: Arc<dyn AuthoritySetForFinalityProver<Block>>,
}
impl<B, Block: BlockT<Hash=H256>> FinalityProofProvider<B, Block>
where B: Backend<Block, Blake2Hasher> + Send + Sync + 'static
{
/// Create new finality proof provider using:
///
/// - backend for accessing blockchain data;
/// - authority_provider for calling and proving runtime methods.
pub fn new(
backend: Arc<B>,
authority_provider: Arc<dyn AuthoritySetForFinalityProver<Block>>,
) -> Self {
FinalityProofProvider { backend, authority_provider }
}
}
impl<B, Block> network::FinalityProofProvider<Block> for FinalityProofProvider<B, Block>
where
Block: BlockT<Hash=H256>,
NumberFor<Block>: BlockNumberOps,
B: Backend<Block, Blake2Hasher> + Send + Sync + 'static,
{
fn prove_finality(
&self,
for_block: Block::Hash,
request: &[u8],
) -> Result<Option<Vec<u8>>, ClientError> {
let request: FinalityProofRequest<Block::Hash> = Decode::decode(&mut &request[..])
.map_err(|e| {
warn!(target: "finality", "Unable to decode finality proof request: {}", e.what());
ClientError::Backend(format!("Invalid finality proof request"))
})?;
match request {
FinalityProofRequest::Original(request) => prove_finality::<_, _, GrandpaJustification<Block>>(
&*self.backend.blockchain(),
&*self.authority_provider,
request.authorities_set_id,
request.last_finalized,
for_block,
),
}
}
}
/// The effects of block finality.
#[derive(Debug, PartialEq)]
pub struct FinalityEffects<Header: HeaderT> {
/// The (ordered) set of headers that could be imported.
pub headers_to_import: Vec<Header>,
/// The hash of the block that could be finalized.
pub block: Header::Hash,
/// The justification for the block.
pub justification: Vec<u8>,
/// New authorities set id that should be applied starting from block.
pub new_set_id: u64,
/// New authorities set that should be applied starting from block.
pub new_authorities: AuthorityList,
}
/// Single fragment of proof-of-finality.
///
/// Finality for block B is proved by providing:
/// 1) the justification for the descendant block F;
/// 2) headers sub-chain (B; F] if B != F;
/// 3) proof of GRANDPA::authorities() if the set changes at block F.
#[derive(Debug, PartialEq, Encode, Decode)]
struct FinalityProofFragment<Header: HeaderT> {
/// The hash of block F for which justification is provided.
pub block: Header::Hash,
/// Justification of the block F.
pub justification: Vec<u8>,
/// The set of headers in the range (U; F] that we believe are unknown to the caller. Ordered.
pub unknown_headers: Vec<Header>,
/// Optional proof of execution of GRANDPA::authorities().
pub authorities_proof: Option<StorageProof>,
}
/// Proof of finality is the ordered set of finality fragments, where:
/// - last fragment provides justification for the best possible block from the requested range;
/// - all other fragments provide justifications for GRANDPA authorities set changes within requested range.
type FinalityProof<Header> = Vec<FinalityProofFragment<Header>>;
/// Finality proof request data.
#[derive(Debug, Encode, Decode)]
enum FinalityProofRequest<H: Encode + Decode> {
/// Original version of the request.
Original(OriginalFinalityProofRequest<H>),
}
/// Original version of finality proof request.
#[derive(Debug, Encode, Decode)]
struct OriginalFinalityProofRequest<H: Encode + Decode> {
/// The authorities set id we are waiting proof from.
///
/// The first justification in the proof must be signed by this authority set.
pub authorities_set_id: u64,
/// Hash of the last known finalized block.
pub last_finalized: H,
}
/// Prepare data blob associated with finality proof request.
pub(crate) fn make_finality_proof_request<H: Encode + Decode>(last_finalized: H, authorities_set_id: u64) -> Vec<u8> {
FinalityProofRequest::Original(OriginalFinalityProofRequest {
authorities_set_id,
last_finalized,
}).encode()
}
/// Prepare proof-of-finality for the best possible block in the range: (begin; end].
///
/// It is assumed that the caller already have a proof-of-finality for the block 'begin'.
/// It is assumed that the caller already knows all blocks in the range (begin; end].
///
/// Returns None if there are no finalized blocks unknown to the caller.
pub(crate) fn prove_finality<Block: BlockT<Hash=H256>, B: BlockchainBackend<Block>, J>(
blockchain: &B,
authorities_provider: &dyn AuthoritySetForFinalityProver<Block>,
authorities_set_id: u64,
begin: Block::Hash,
end: Block::Hash,
) -> ::client_api::error::Result<Option<Vec<u8>>>
where
J: ProvableJustification<Block::Header>,
{
let begin_id = BlockId::Hash(begin);
let begin_number = blockchain.expect_block_number_from_id(&begin_id)?;
// early-return if we sure that there are no blocks finalized AFTER begin block
let info = blockchain.info();
if info.finalized_number <= begin_number {
trace!(
target: "finality",
"Requested finality proof for descendant of #{} while we only have finalized #{}. Returning empty proof.",
begin_number,
info.finalized_number,
);
return Ok(None);
}
// check if blocks range is valid. It is the caller responsibility to ensure
// that it only asks peers that know about whole blocks range
let end_number = blockchain.expect_block_number_from_id(&BlockId::Hash(end))?;
if begin_number + One::one() > end_number {
return Err(ClientError::Backend(
format!("Cannot generate finality proof for invalid range: {}..{}", begin_number, end_number),
));
}
// early-return if we sure that the block is NOT a part of canonical chain
let canonical_begin = blockchain.expect_block_hash_from_id(&BlockId::Number(begin_number))?;
if begin != canonical_begin {
return Err(ClientError::Backend(
format!("Cannot generate finality proof for non-canonical block: {}", begin),
));
}
// iterate justifications && try to prove finality
let mut fragment_index = 0;
let mut current_authorities = authorities_provider.authorities(&begin_id)?;
let mut current_number = begin_number + One::one();
let mut finality_proof = Vec::new();
let mut unknown_headers = Vec::new();
let mut latest_proof_fragment = None;
loop {
let current_id = BlockId::Number(current_number);
// check if header is unknown to the caller
if current_number > end_number {
let unknown_header = blockchain.expect_header(current_id)?;
unknown_headers.push(unknown_header);
}
if let Some(justification) = blockchain.justification(current_id)? {
// check if the current block enacts new GRANDPA authorities set
let parent_id = BlockId::Number(current_number - One::one());
let new_authorities = authorities_provider.authorities(&parent_id)?;
let new_authorities_proof = if current_authorities != new_authorities {
current_authorities = new_authorities;
Some(authorities_provider.prove_authorities(&parent_id)?)
} else {
None
};
// prepare finality proof for the current block
let current = blockchain.expect_block_hash_from_id(&BlockId::Number(current_number))?;
let proof_fragment = FinalityProofFragment {
block: current,
justification,
unknown_headers: ::std::mem::replace(&mut unknown_headers, Vec::new()),
authorities_proof: new_authorities_proof,
};
// append justification to finality proof if required
let justifies_end_block = current_number >= end_number;
let justifies_authority_set_change = proof_fragment.authorities_proof.is_some();
if justifies_end_block || justifies_authority_set_change {
// check if the proof is generated by the requested authority set
if finality_proof.is_empty() {
let justification_check_result = J::decode_and_verify(
&proof_fragment.justification,
authorities_set_id,
&current_authorities,
);
if justification_check_result.is_err() {
trace!(
target: "finality",
"Can not provide finality proof with requested set id #{}\
(possible forced change?). Returning empty proof.",
authorities_set_id,
);
return Ok(None);
}
}
finality_proof.push(proof_fragment);
latest_proof_fragment = None;
} else {
latest_proof_fragment = Some(proof_fragment);
}
// we don't need to provide more justifications
if justifies_end_block {
break;
}
}
// we can't provide more justifications
if current_number == info.finalized_number {
// append last justification - even if we can't generate finality proof for
// the end block, we try to generate it for the latest possible block
if let Some(latest_proof_fragment) = latest_proof_fragment.take() {
finality_proof.push(latest_proof_fragment);
fragment_index += 1;
if fragment_index == MAX_FRAGMENTS_IN_PROOF {
break;
}
}
break;
}
// else search for the next justification
current_number = current_number + One::one();
}
if finality_proof.is_empty() {
trace!(
target: "finality",
"No justifications found when making finality proof for {}. Returning empty proof.",
end,
);
Ok(None)
} else {
trace!(
target: "finality",
"Built finality proof for {} of {} fragments. Last fragment for {}.",
end,
finality_proof.len(),
finality_proof.last().expect("checked that !finality_proof.is_empty(); qed").block,
);
Ok(Some(finality_proof.encode()))
}
}
/// Check GRANDPA proof-of-finality for the given block.
///
/// Returns the vector of headers that MUST be validated + imported
/// AND if at least one of those headers is invalid, all other MUST be considered invalid.
pub(crate) fn check_finality_proof<Block: BlockT<Hash=H256>, B>(
blockchain: &B,
current_set_id: u64,
current_authorities: AuthorityList,
authorities_provider: &dyn AuthoritySetForFinalityChecker<Block>,
remote_proof: Vec<u8>,
) -> ClientResult<FinalityEffects<Block::Header>>
where
NumberFor<Block>: BlockNumberOps,
B: BlockchainBackend<Block>,
{
do_check_finality_proof::<_, _, GrandpaJustification<Block>>(
blockchain,
current_set_id,
current_authorities,
authorities_provider,
remote_proof)
}
fn do_check_finality_proof<Block: BlockT<Hash=H256>, B, J>(
blockchain: &B,
current_set_id: u64,
current_authorities: AuthorityList,
authorities_provider: &dyn AuthoritySetForFinalityChecker<Block>,
remote_proof: Vec<u8>,
) -> ClientResult<FinalityEffects<Block::Header>>
where
NumberFor<Block>: BlockNumberOps,
B: BlockchainBackend<Block>,
J: ProvableJustification<Block::Header>,
{
// decode finality proof
let proof = FinalityProof::<Block::Header>::decode(&mut &remote_proof[..])
.map_err(|_| ClientError::BadJustification("failed to decode finality proof".into()))?;
// empty proof can't prove anything
if proof.is_empty() {
return Err(ClientError::BadJustification("empty proof of finality".into()));
}
// iterate and verify proof fragments
let last_fragment_index = proof.len() - 1;
let mut authorities = AuthoritiesOrEffects::Authorities(current_set_id, current_authorities);
for (proof_fragment_index, proof_fragment) in proof.into_iter().enumerate() {
// check that proof is non-redundant. The proof still can be valid, but
// we do not want peer to spam us with redundant data
if proof_fragment_index != last_fragment_index {
let has_unknown_headers = !proof_fragment.unknown_headers.is_empty();
let has_new_authorities = proof_fragment.authorities_proof.is_some();
if has_unknown_headers || !has_new_authorities {
return Err(ClientError::BadJustification("redundant proof of finality".into()));
}
}
authorities = check_finality_proof_fragment::<_, _, J>(
blockchain,
authorities,
authorities_provider,
proof_fragment)?;
}
let effects = authorities.extract_effects().expect("at least one loop iteration is guaranteed
because proof is not empty;\
check_finality_proof_fragment is called on every iteration;\
check_finality_proof_fragment always returns FinalityEffects;\
qed");
telemetry!(CONSENSUS_INFO; "afg.finality_proof_ok";
"set_id" => ?effects.new_set_id, "finalized_header_hash" => ?effects.block);
Ok(effects)
}
/// Check finality proof for the single block.
fn check_finality_proof_fragment<Block: BlockT<Hash=H256>, B, J>(
blockchain: &B,
authority_set: AuthoritiesOrEffects<Block::Header>,
authorities_provider: &dyn AuthoritySetForFinalityChecker<Block>,
proof_fragment: FinalityProofFragment<Block::Header>,
) -> ClientResult<AuthoritiesOrEffects<Block::Header>>
where
NumberFor<Block>: BlockNumberOps,
B: BlockchainBackend<Block>,
J: Decode + ProvableJustification<Block::Header>,
{
// verify justification using previous authorities set
let (mut current_set_id, mut current_authorities) = authority_set.extract_authorities();
let justification: J = Decode::decode(&mut &proof_fragment.justification[..])
.map_err(|_| ClientError::JustificationDecode)?;
justification.verify(current_set_id, &current_authorities)?;
// and now verify new authorities proof (if provided)
if let Some(new_authorities_proof) = proof_fragment.authorities_proof {
// it is safe to query header here, because its non-finality proves that it can't be pruned
let header = blockchain.expect_header(BlockId::Hash(proof_fragment.block))?;
let parent_hash = *header.parent_hash();
let parent_header = blockchain.expect_header(BlockId::Hash(parent_hash))?;
current_authorities = authorities_provider.check_authorities_proof(
parent_hash,
parent_header,
new_authorities_proof,
)?;
current_set_id = current_set_id + 1;
}
Ok(AuthoritiesOrEffects::Effects(FinalityEffects {
headers_to_import: proof_fragment.unknown_headers,
block: proof_fragment.block,
justification: proof_fragment.justification,
new_set_id: current_set_id,
new_authorities: current_authorities,
}))
}
/// Authorities set from initial authorities set or finality effects.
enum AuthoritiesOrEffects<Header: HeaderT> {
Authorities(u64, AuthorityList),
Effects(FinalityEffects<Header>),
}
impl<Header: HeaderT> AuthoritiesOrEffects<Header> {
pub fn extract_authorities(self) -> (u64, AuthorityList) {
match self {
AuthoritiesOrEffects::Authorities(set_id, authorities) => (set_id, authorities),
AuthoritiesOrEffects::Effects(effects) => (effects.new_set_id, effects.new_authorities),
}
}
pub fn extract_effects(self) -> Option<FinalityEffects<Header>> {
match self {
AuthoritiesOrEffects::Authorities(_, _) => None,
AuthoritiesOrEffects::Effects(effects) => Some(effects),
}
}
}
/// Justification used to prove block finality.
pub(crate) trait ProvableJustification<Header: HeaderT>: Encode + Decode {
/// Verify justification with respect to authorities set and authorities set id.
fn verify(&self, set_id: u64, authorities: &[(AuthorityId, u64)]) -> ClientResult<()>;
/// Decode and verify justification.
fn decode_and_verify(
justification: &Justification,
set_id: u64,
authorities: &[(AuthorityId, u64)],
) -> ClientResult<Self> {
let justification = Self::decode(&mut &**justification)
.map_err(|_| ClientError::JustificationDecode)?;
justification.verify(set_id, authorities)?;
Ok(justification)
}
}
impl<Block: BlockT<Hash=H256>> ProvableJustification<Block::Header> for GrandpaJustification<Block>
where
NumberFor<Block>: BlockNumberOps,
{
fn verify(&self, set_id: u64, authorities: &[(AuthorityId, u64)]) -> ClientResult<()> {
GrandpaJustification::verify(self, set_id, &authorities.iter().cloned().collect())
}
}
#[cfg(test)]
pub(crate) mod tests {
use test_client::runtime::{Block, Header, H256};
use client_api::NewBlockState;
use test_client::client::in_mem::Blockchain as InMemoryBlockchain;
use super::*;
use primitives::crypto::Public;
type FinalityProof = super::FinalityProof<Header>;
impl<GetAuthorities, ProveAuthorities> AuthoritySetForFinalityProver<Block> for (GetAuthorities, ProveAuthorities)
where
GetAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<AuthorityList>,
ProveAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<StorageProof>,
{
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<AuthorityList> {
self.0(*block)
}
fn prove_authorities(&self, block: &BlockId<Block>) -> ClientResult<StorageProof> {
self.1(*block)
}
}
struct ClosureAuthoritySetForFinalityChecker<Closure>(pub Closure);
impl<Closure> AuthoritySetForFinalityChecker<Block> for ClosureAuthoritySetForFinalityChecker<Closure>
where
Closure: Send + Sync + Fn(H256, Header, StorageProof) -> ClientResult<AuthorityList>,
{
fn check_authorities_proof(
&self,
hash: H256,
header: Header,
proof: StorageProof
) -> ClientResult<AuthorityList> {
self.0(hash, header, proof)
}
}
#[derive(Debug, PartialEq, Encode, Decode)]
pub struct TestJustification(pub bool, pub Vec<u8>);
impl ProvableJustification<Header> for TestJustification {
fn verify(&self, _set_id: u64, _authorities: &[(AuthorityId, u64)]) -> ClientResult<()> {
if self.0 {
Ok(())
} else {
Err(ClientError::BadJustification("test".into()))
}
}
}
fn header(number: u64) -> Header {
let parent_hash = match number {
0 => Default::default(),
_ => header(number - 1).hash(),
};
Header::new(number, H256::from_low_u64_be(0), H256::from_low_u64_be(0), parent_hash, Default::default())
}
fn side_header(number: u64) -> Header {
Header::new(
number,
H256::from_low_u64_be(0),
H256::from_low_u64_be(1),
header(number - 1).hash(),
Default::default(),
)
}
fn second_side_header(number: u64) -> Header {
Header::new(
number,
H256::from_low_u64_be(0),
H256::from_low_u64_be(1),
side_header(number - 1).hash(),
Default::default(),
)
}
fn test_blockchain() -> InMemoryBlockchain<Block> {
let blockchain = InMemoryBlockchain::<Block>::new();
blockchain.insert(header(0).hash(), header(0), Some(vec![0]), None, NewBlockState::Final).unwrap();
blockchain.insert(header(1).hash(), header(1), Some(vec![1]), None, NewBlockState::Final).unwrap();
blockchain.insert(header(2).hash(), header(2), None, None, NewBlockState::Best).unwrap();
blockchain.insert(header(3).hash(), header(3), Some(vec![3]), None, NewBlockState::Final).unwrap();
blockchain
}
#[test]
fn finality_prove_fails_with_invalid_range() {
let blockchain = test_blockchain();
// their last finalized is: 2
// they request for proof-of-finality of: 2
// => range is invalid
prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|_| unreachable!("should return before calling GetAuthorities"),
|_| unreachable!("should return before calling ProveAuthorities"),
),
0,
header(2).hash(),
header(2).hash(),
).unwrap_err();
}
#[test]
fn finality_proof_is_none_if_no_more_last_finalized_blocks() {
let blockchain = test_blockchain();
blockchain.insert(header(4).hash(), header(4), None, None, NewBlockState::Best).unwrap();
// our last finalized is: 3
// their last finalized is: 3
// => we can't provide any additional justifications
let proof_of_4 = prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|_| unreachable!("should return before calling GetAuthorities"),
|_| unreachable!("should return before calling ProveAuthorities"),
),
0,
header(3).hash(),
header(4).hash(),
).unwrap();
assert_eq!(proof_of_4, None);
}
#[test]
fn finality_proof_fails_for_non_canonical_block() {
let blockchain = test_blockchain();
blockchain.insert(header(4).hash(), header(4), None, None, NewBlockState::Best).unwrap();
blockchain.insert(side_header(4).hash(), side_header(4), None, None, NewBlockState::Best).unwrap();
blockchain.insert(second_side_header(5).hash(), second_side_header(5), None, None, NewBlockState::Best)
.unwrap();
blockchain.insert(header(5).hash(), header(5), Some(vec![5]), None, NewBlockState::Final).unwrap();
// chain is 1 -> 2 -> 3 -> 4 -> 5
// \> 4' -> 5'
// and the best finalized is 5
// => when requesting for (4'; 5'], error is returned
prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|_| unreachable!("should return before calling GetAuthorities"),
|_| unreachable!("should return before calling ProveAuthorities"),
),
0,
side_header(4).hash(),
second_side_header(5).hash(),
).unwrap_err();
}
#[test]
fn finality_proof_is_none_if_no_justification_known() {
let blockchain = test_blockchain();
blockchain.insert(header(4).hash(), header(4), None, None, NewBlockState::Final).unwrap();
// block 4 is finalized without justification
// => we can't prove finality
let proof_of_4 = prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]),
|_| unreachable!("authorities didn't change => ProveAuthorities won't be called"),
),
0,
header(3).hash(),
header(4).hash(),
).unwrap();
assert_eq!(proof_of_4, None);
}
#[test]
fn finality_proof_works_without_authorities_change() {
let blockchain = test_blockchain();
let just4 = TestJustification(true, vec![4]).encode();
let just5 = TestJustification(true, vec![5]).encode();
blockchain.insert(header(4).hash(), header(4), Some(just4), None, NewBlockState::Final).unwrap();
blockchain.insert(header(5).hash(), header(5), Some(just5.clone()), None, NewBlockState::Final).unwrap();
// blocks 4 && 5 are finalized with justification
// => since authorities are the same, we only need justification for 5
let proof_of_5: FinalityProof = Decode::decode(&mut &prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]),
|_| unreachable!("should return before calling ProveAuthorities"),
),
0,
header(3).hash(),
header(5).hash(),
).unwrap().unwrap()[..]).unwrap();
assert_eq!(proof_of_5, vec![FinalityProofFragment {
block: header(5).hash(),
justification: just5,
unknown_headers: Vec::new(),
authorities_proof: None,
}]);
}
#[test]
fn finality_proof_finalized_earlier_block_if_no_justification_for_target_is_known() {
let blockchain = test_blockchain();
blockchain.insert(header(4).hash(), header(4), Some(vec![4]), None, NewBlockState::Final).unwrap();
blockchain.insert(header(5).hash(), header(5), None, None, NewBlockState::Final).unwrap();
// block 4 is finalized with justification + we request for finality of 5
// => we can't prove finality of 5, but providing finality for 4 is still useful for requester
let proof_of_5: FinalityProof = Decode::decode(&mut &prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]),
|_| unreachable!("should return before calling ProveAuthorities"),
),
0,
header(3).hash(),
header(5).hash(),
).unwrap().unwrap()[..]).unwrap();
assert_eq!(proof_of_5, vec![FinalityProofFragment {
block: header(4).hash(),
justification: vec![4],
unknown_headers: Vec::new(),
authorities_proof: None,
}]);
}
#[test]
fn finality_proof_works_with_authorities_change() {
let blockchain = test_blockchain();
let just4 = TestJustification(true, vec![4]).encode();
let just5 = TestJustification(true, vec![5]).encode();
let just7 = TestJustification(true, vec![7]).encode();
blockchain.insert(header(4).hash(), header(4), Some(just4), None, NewBlockState::Final).unwrap();
blockchain.insert(header(5).hash(), header(5), Some(just5.clone()), None, NewBlockState::Final).unwrap();
blockchain.insert(header(6).hash(), header(6), None, None, NewBlockState::Final).unwrap();
blockchain.insert(header(7).hash(), header(7), Some(just7.clone()), None, NewBlockState::Final).unwrap();
// when querying for finality of 6, we assume that the #6 is the last block known to the requester
// => since we only have justification for #7, we provide #7
let proof_of_6: FinalityProof = Decode::decode(&mut &prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|block_id| match block_id {
BlockId::Hash(h) if h == header(3).hash() => Ok(
vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)]
),
BlockId::Number(3) => Ok(vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)]),
BlockId::Number(4) => Ok(vec![(AuthorityId::from_slice(&[4u8; 32]), 1u64)]),
BlockId::Number(6) => Ok(vec![(AuthorityId::from_slice(&[6u8; 32]), 1u64)]),
_ => unreachable!("no other authorities should be fetched: {:?}", block_id),
},
|block_id| match block_id {
BlockId::Number(4) => Ok(StorageProof::new(vec![vec![40]])),
BlockId::Number(6) => Ok(StorageProof::new(vec![vec![60]])),
_ => unreachable!("no other authorities should be proved: {:?}", block_id),
},
),
0,
header(3).hash(),
header(6).hash(),
).unwrap().unwrap()[..]).unwrap();
// initial authorities set (which start acting from #4) is [3; 32]
assert_eq!(proof_of_6, vec![
// new authorities set starts acting from #5 => we do not provide fragment for #4
// first fragment provides justification for #5 && authorities set that starts acting from #5
FinalityProofFragment {
block: header(5).hash(),
justification: just5,
unknown_headers: Vec::new(),
authorities_proof: Some(StorageProof::new(vec![vec![40]])),
},
// last fragment provides justification for #7 && unknown#7
FinalityProofFragment {
block: header(7).hash(),
justification: just7,
unknown_headers: vec![header(7)],
authorities_proof: Some(StorageProof::new(vec![vec![60]])),
},
]);
}
#[test]
fn finality_proof_check_fails_when_proof_decode_fails() {
let blockchain = test_blockchain();
// when we can't decode proof from Vec<u8>
do_check_finality_proof::<_, _, TestJustification>(
&blockchain,
1,
vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)],
&ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")),
vec![42],
).unwrap_err();
}
#[test]
fn finality_proof_check_fails_when_proof_is_empty() {
let blockchain = test_blockchain();
// when decoded proof has zero length
do_check_finality_proof::<_, _, TestJustification>(
&blockchain,
1,
vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)],
&ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")),
Vec::<TestJustification>::new().encode(),
).unwrap_err();
}
#[test]
fn finality_proof_check_fails_when_intemediate_fragment_has_unknown_headers() {
let blockchain = test_blockchain();
// when intermediate (#0) fragment has non-empty unknown headers
do_check_finality_proof::<_, _, TestJustification>(
&blockchain,
1,
vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)],
&ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")),
vec![FinalityProofFragment {
block: header(4).hash(),
justification: TestJustification(true, vec![7]).encode(),
unknown_headers: vec![header(4)],
authorities_proof: Some(StorageProof::new(vec![vec![42]])),
}, FinalityProofFragment {
block: header(5).hash(),
justification: TestJustification(true, vec![8]).encode(),
unknown_headers: vec![header(5)],
authorities_proof: None,
}].encode(),
).unwrap_err();
}
#[test]
fn finality_proof_check_fails_when_intemediate_fragment_has_no_authorities_proof() {
let blockchain = test_blockchain();
// when intermediate (#0) fragment has empty authorities proof
do_check_finality_proof::<_, _, TestJustification>(
&blockchain,
1,
vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)],
&ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")),
vec![FinalityProofFragment {
block: header(4).hash(),
justification: TestJustification(true, vec![7]).encode(),
unknown_headers: Vec::new(),
authorities_proof: None,
}, FinalityProofFragment {
block: header(5).hash(),
justification: TestJustification(true, vec![8]).encode(),
unknown_headers: vec![header(5)],
authorities_proof: None,
}].encode(),
).unwrap_err();
}
#[test]
fn finality_proof_check_works() {
let blockchain = test_blockchain();
let effects = do_check_finality_proof::<_, _, TestJustification>(
&blockchain,
1,
vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)],
&ClosureAuthoritySetForFinalityChecker(|_, _, _| Ok(vec![(AuthorityId::from_slice(&[4u8; 32]), 1u64)])),
vec![FinalityProofFragment {
block: header(2).hash(),
justification: TestJustification(true, vec![7]).encode(),
unknown_headers: Vec::new(),
authorities_proof: Some(StorageProof::new(vec![vec![42]])),
}, FinalityProofFragment {
block: header(4).hash(),
justification: TestJustification(true, vec![8]).encode(),
unknown_headers: vec![header(4)],
authorities_proof: None,
}].encode(),
).unwrap();
assert_eq!(effects, FinalityEffects {
headers_to_import: vec![header(4)],
block: header(4).hash(),
justification: TestJustification(true, vec![8]).encode(),
new_set_id: 2,
new_authorities: vec![(AuthorityId::from_slice(&[4u8; 32]), 1u64)],
});
}
#[test]
fn finality_proof_is_none_if_first_justification_is_generated_by_unknown_set() {
// this is the case for forced change: set_id has been forcibly increased on full node
// and ligh node missed that
// => justification verification will fail on light node anyways, so we do not return
// finality proof at all
let blockchain = test_blockchain();
let just4 = TestJustification(false, vec![4]).encode(); // false makes verification fail
blockchain.insert(header(4).hash(), header(4), Some(just4), None, NewBlockState::Final).unwrap();
let proof_of_4 = prove_finality::<_, _, TestJustification>(
&blockchain,
&(
|_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]),
|_| unreachable!("should return before calling ProveAuthorities"),
),
0,
header(3).hash(),
header(4).hash(),
).unwrap();
assert!(proof_of_4.is_none());
}
}
@@ -0,0 +1,602 @@
// Copyright 2018-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/>.
use std::{sync::Arc, collections::HashMap};
use log::{debug, trace, info};
use codec::Encode;
use futures::sync::mpsc;
use parking_lot::RwLockWriteGuard;
use client_api::{
backend::Backend, blockchain,
CallExecutor, blockchain::HeaderBackend, well_known_cache_keys,
utils::is_descendent_of,
};
use client::Client;
use consensus_common::{
BlockImport, Error as ConsensusError,
BlockCheckParams, BlockImportParams, ImportResult, JustificationImport,
SelectChain,
};
use fg_primitives::{GRANDPA_ENGINE_ID, ScheduledChange, ConsensusLog};
use sr_primitives::Justification;
use sr_primitives::generic::{BlockId, OpaqueDigestItemId};
use sr_primitives::traits::{
Block as BlockT, DigestFor, Header as HeaderT, NumberFor, Zero,
};
use primitives::{H256, Blake2Hasher};
use crate::{Error, CommandOrError, NewAuthoritySet, VoterCommand};
use crate::authorities::{AuthoritySet, SharedAuthoritySet, DelayKind, PendingChange};
use crate::consensus_changes::SharedConsensusChanges;
use crate::environment::finalize_block;
use crate::justification::GrandpaJustification;
/// A block-import handler for GRANDPA.
///
/// This scans each imported block for signals of changing authority set.
/// If the block being imported enacts an authority set change then:
/// - If the current authority set is still live: we import the block
/// - Otherwise, the block must include a valid justification.
///
/// When using GRANDPA, the block import worker should be using this block import
/// object.
pub struct GrandpaBlockImport<B, E, Block: BlockT<Hash=H256>, RA, SC> {
inner: Arc<Client<B, E, Block, RA>>,
select_chain: SC,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
}
impl<B, E, Block: BlockT<Hash=H256>, RA, SC: Clone> Clone for
GrandpaBlockImport<B, E, Block, RA, SC>
{
fn clone(&self) -> Self {
GrandpaBlockImport {
inner: self.inner.clone(),
select_chain: self.select_chain.clone(),
authority_set: self.authority_set.clone(),
send_voter_commands: self.send_voter_commands.clone(),
consensus_changes: self.consensus_changes.clone(),
}
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA, SC> JustificationImport<Block>
for GrandpaBlockImport<B, E, Block, RA, SC> where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
SC: SelectChain<Block>,
{
type Error = ConsensusError;
fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor<Block>)> {
let mut out = Vec::new();
let chain_info = self.inner.info().chain;
// request justifications for all pending changes for which change blocks have already been imported
let authorities = self.authority_set.inner().read();
for pending_change in authorities.pending_changes() {
if pending_change.delay_kind == DelayKind::Finalized &&
pending_change.effective_number() > chain_info.finalized_number &&
pending_change.effective_number() <= chain_info.best_number
{
let effective_block_hash = if !pending_change.delay.is_zero() {
self.select_chain.finality_target(
pending_change.canon_hash,
Some(pending_change.effective_number()),
)
} else {
Ok(Some(pending_change.canon_hash))
};
if let Ok(Some(hash)) = effective_block_hash {
if let Ok(Some(header)) = self.inner.header(&BlockId::Hash(hash)) {
if *header.number() == pending_change.effective_number() {
out.push((header.hash(), *header.number()));
}
}
}
}
}
out
}
fn import_justification(
&mut self,
hash: Block::Hash,
number: NumberFor<Block>,
justification: Justification,
) -> Result<(), Self::Error> {
self.import_justification(hash, number, justification, false)
}
}
enum AppliedChanges<H, N> {
Standard(bool), // true if the change is ready to be applied (i.e. it's a root)
Forced(NewAuthoritySet<H, N>),
None,
}
impl<H, N> AppliedChanges<H, N> {
fn needs_justification(&self) -> bool {
match *self {
AppliedChanges::Standard(_) => true,
AppliedChanges::Forced(_) | AppliedChanges::None => false,
}
}
}
struct PendingSetChanges<'a, Block: 'a + BlockT> {
just_in_case: Option<(
AuthoritySet<Block::Hash, NumberFor<Block>>,
RwLockWriteGuard<'a, AuthoritySet<Block::Hash, NumberFor<Block>>>,
)>,
applied_changes: AppliedChanges<Block::Hash, NumberFor<Block>>,
do_pause: bool,
}
impl<'a, Block: 'a + BlockT> PendingSetChanges<'a, Block> {
// revert the pending set change explicitly.
fn revert(self) { }
fn defuse(mut self) -> (AppliedChanges<Block::Hash, NumberFor<Block>>, bool) {
self.just_in_case = None;
let applied_changes = ::std::mem::replace(&mut self.applied_changes, AppliedChanges::None);
(applied_changes, self.do_pause)
}
}
impl<'a, Block: 'a + BlockT> Drop for PendingSetChanges<'a, Block> {
fn drop(&mut self) {
if let Some((old_set, mut authorities)) = self.just_in_case.take() {
*authorities = old_set;
}
}
}
fn find_scheduled_change<B: BlockT>(header: &B::Header)
-> Option<ScheduledChange<NumberFor<B>>>
{
let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
let filter_log = |log: ConsensusLog<NumberFor<B>>| match log {
ConsensusLog::ScheduledChange(change) => Some(change),
_ => None,
};
// find the first consensus digest with the right ID which converts to
// the right kind of consensus log.
header.digest().convert_first(|l| l.try_to(id).and_then(filter_log))
}
fn find_forced_change<B: BlockT>(header: &B::Header)
-> Option<(NumberFor<B>, ScheduledChange<NumberFor<B>>)>
{
let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
let filter_log = |log: ConsensusLog<NumberFor<B>>| match log {
ConsensusLog::ForcedChange(delay, change) => Some((delay, change)),
_ => None,
};
// find the first consensus digest with the right ID which converts to
// the right kind of consensus log.
header.digest().convert_first(|l| l.try_to(id).and_then(filter_log))
}
impl<B, E, Block: BlockT<Hash=H256>, RA, SC>
GrandpaBlockImport<B, E, Block, RA, SC>
where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
// check for a new authority set change.
fn check_new_change(&self, header: &Block::Header, hash: Block::Hash)
-> Option<PendingChange<Block::Hash, NumberFor<Block>>>
{
// check for forced change.
if let Some((median_last_finalized, change)) = find_forced_change::<Block>(header) {
return Some(PendingChange {
next_authorities: change.next_authorities,
delay: change.delay,
canon_height: *header.number(),
canon_hash: hash,
delay_kind: DelayKind::Best { median_last_finalized },
});
}
// check normal scheduled change.
let change = find_scheduled_change::<Block>(header)?;
Some(PendingChange {
next_authorities: change.next_authorities,
delay: change.delay,
canon_height: *header.number(),
canon_hash: hash,
delay_kind: DelayKind::Finalized,
})
}
fn make_authorities_changes<'a>(&'a self, block: &mut BlockImportParams<Block>, hash: Block::Hash)
-> Result<PendingSetChanges<'a, Block>, ConsensusError>
{
// when we update the authorities, we need to hold the lock
// until the block is written to prevent a race if we need to restore
// the old authority set on error or panic.
struct InnerGuard<'a, T: 'a> {
old: Option<T>,
guard: Option<RwLockWriteGuard<'a, T>>,
}
impl<'a, T: 'a> InnerGuard<'a, T> {
fn as_mut(&mut self) -> &mut T {
&mut **self.guard.as_mut().expect("only taken on deconstruction; qed")
}
fn set_old(&mut self, old: T) {
if self.old.is_none() {
// ignore "newer" old changes.
self.old = Some(old);
}
}
fn consume(mut self) -> Option<(T, RwLockWriteGuard<'a, T>)> {
if let Some(old) = self.old.take() {
Some((old, self.guard.take().expect("only taken on deconstruction; qed")))
} else {
None
}
}
}
impl<'a, T: 'a> Drop for InnerGuard<'a, T> {
fn drop(&mut self) {
if let (Some(mut guard), Some(old)) = (self.guard.take(), self.old.take()) {
*guard = old;
}
}
}
let number = block.header.number().clone();
let maybe_change = self.check_new_change(
&block.header,
hash,
);
// returns a function for checking whether a block is a descendent of another
// consistent with querying client directly after importing the block.
let parent_hash = *block.header.parent_hash();
let is_descendent_of = is_descendent_of(&*self.inner, Some((&hash, &parent_hash)));
let mut guard = InnerGuard {
guard: Some(self.authority_set.inner().write()),
old: None,
};
// whether to pause the old authority set -- happens after import
// of a forced change block.
let mut do_pause = false;
// add any pending changes.
if let Some(change) = maybe_change {
let old = guard.as_mut().clone();
guard.set_old(old);
if let DelayKind::Best { .. } = change.delay_kind {
do_pause = true;
}
guard.as_mut().add_pending_change(
change,
&is_descendent_of,
).map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?;
}
let applied_changes = {
let forced_change_set = guard.as_mut().apply_forced_changes(hash, number, &is_descendent_of)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))
.map_err(ConsensusError::from)?;
if let Some((median_last_finalized_number, new_set)) = forced_change_set {
let new_authorities = {
let (set_id, new_authorities) = new_set.current();
// we will use the median last finalized number as a hint
// for the canon block the new authority set should start
// with. we use the minimum between the median and the local
// best finalized block.
let best_finalized_number = self.inner.info().chain.finalized_number;
let canon_number = best_finalized_number.min(median_last_finalized_number);
let canon_hash =
self.inner.header(&BlockId::Number(canon_number))
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
.expect("the given block number is less or equal than the current best finalized number; \
current best finalized number must exist in chain; qed.")
.hash();
NewAuthoritySet {
canon_number,
canon_hash,
set_id,
authorities: new_authorities.to_vec(),
}
};
let old = ::std::mem::replace(guard.as_mut(), new_set);
guard.set_old(old);
AppliedChanges::Forced(new_authorities)
} else {
let did_standard = guard.as_mut().enacts_standard_change(hash, number, &is_descendent_of)
.map_err(|e| ConsensusError::ClientImport(e.to_string()))
.map_err(ConsensusError::from)?;
if let Some(root) = did_standard {
AppliedChanges::Standard(root)
} else {
AppliedChanges::None
}
}
};
// consume the guard safely and write necessary changes.
let just_in_case = guard.consume();
if let Some((_, ref authorities)) = just_in_case {
let authorities_change = match applied_changes {
AppliedChanges::Forced(ref new) => Some(new),
AppliedChanges::Standard(_) => None, // the change isn't actually applied yet.
AppliedChanges::None => None,
};
crate::aux_schema::update_authority_set::<Block, _, _>(
authorities,
authorities_change,
|insert| block.auxiliary.extend(
insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))
)
);
}
Ok(PendingSetChanges { just_in_case, applied_changes, do_pause })
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA, SC> BlockImport<Block>
for GrandpaBlockImport<B, E, Block, RA, SC> where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
type Error = ConsensusError;
fn import_block(
&mut self,
mut block: BlockImportParams<Block>,
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
let hash = block.post_header().hash();
let number = block.header.number().clone();
// early exit if block already in chain, otherwise the check for
// authority changes will error when trying to re-import a change block
match self.inner.status(BlockId::Hash(hash)) {
Ok(blockchain::BlockStatus::InChain) => return Ok(ImportResult::AlreadyInChain),
Ok(blockchain::BlockStatus::Unknown) => {},
Err(e) => return Err(ConsensusError::ClientImport(e.to_string()).into()),
}
let pending_changes = self.make_authorities_changes(&mut block, hash)?;
// we don't want to finalize on `inner.import_block`
let mut justification = block.justification.take();
let enacts_consensus_change = !new_cache.is_empty();
let import_result = (&*self.inner).import_block(block, new_cache);
let mut imported_aux = {
match import_result {
Ok(ImportResult::Imported(aux)) => aux,
Ok(r) => {
debug!(target: "afg", "Restoring old authority set after block import result: {:?}", r);
pending_changes.revert();
return Ok(r);
},
Err(e) => {
debug!(target: "afg", "Restoring old authority set after block import error: {:?}", e);
pending_changes.revert();
return Err(ConsensusError::ClientImport(e.to_string()).into());
},
}
};
let (applied_changes, do_pause) = pending_changes.defuse();
// Send the pause signal after import but BEFORE sending a `ChangeAuthorities` message.
if do_pause {
let _ = self.send_voter_commands.unbounded_send(
VoterCommand::Pause(format!("Forced change scheduled after inactivity"))
);
}
let needs_justification = applied_changes.needs_justification();
match applied_changes {
AppliedChanges::Forced(new) => {
// NOTE: when we do a force change we are "discrediting" the old set so we
// ignore any justifications from them. this block may contain a justification
// which should be checked and imported below against the new authority
// triggered by this forced change. the new grandpa voter will start at the
// last median finalized block (which is before the block that enacts the
// change), full nodes syncing the chain will not be able to successfully
// import justifications for those blocks since their local authority set view
// is still of the set before the forced change was enacted, still after #1867
// they should import the block and discard the justification, and they will
// then request a justification from sync if it's necessary (which they should
// then be able to successfully validate).
let _ = self.send_voter_commands.unbounded_send(VoterCommand::ChangeAuthorities(new));
// we must clear all pending justifications requests, presumably they won't be
// finalized hence why this forced changes was triggered
imported_aux.clear_justification_requests = true;
},
AppliedChanges::Standard(false) => {
// we can't apply this change yet since there are other dependent changes that we
// need to apply first, drop any justification that might have been provided with
// the block to make sure we request them from `sync` which will ensure they'll be
// applied in-order.
justification.take();
},
_ => {},
}
match justification {
Some(justification) => {
self.import_justification(hash, number, justification, needs_justification).unwrap_or_else(|err| {
if needs_justification || enacts_consensus_change {
debug!(target: "finality", "Imported block #{} that enacts authority set change with \
invalid justification: {:?}, requesting justification from peers.", number, err);
imported_aux.bad_justification = true;
imported_aux.needs_justification = true;
}
});
},
None => {
if needs_justification {
trace!(
target: "finality",
"Imported unjustified block #{} that enacts authority set change, waiting for finality for enactment.",
number,
);
imported_aux.needs_justification = true;
}
// we have imported block with consensus data changes, but without justification
// => remember to create justification when next block will be finalized
if enacts_consensus_change {
self.consensus_changes.lock().note_change((number, hash));
}
}
}
Ok(ImportResult::Imported(imported_aux))
}
fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
self.inner.check_block(block)
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA, SC>
GrandpaBlockImport<B, E, Block, RA, SC>
{
pub(crate) fn new(
inner: Arc<Client<B, E, Block, RA>>,
select_chain: SC,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
) -> GrandpaBlockImport<B, E, Block, RA, SC> {
GrandpaBlockImport {
inner,
select_chain,
authority_set,
send_voter_commands,
consensus_changes,
}
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA, SC>
GrandpaBlockImport<B, E, Block, RA, SC>
where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: Send + Sync,
{
/// Import a block justification and finalize the block.
///
/// If `enacts_change` is set to true, then finalizing this block *must*
/// enact an authority set change, the function will panic otherwise.
fn import_justification(
&mut self,
hash: Block::Hash,
number: NumberFor<Block>,
justification: Justification,
enacts_change: bool,
) -> Result<(), ConsensusError> {
let justification = GrandpaJustification::decode_and_verify_finalizes(
&justification,
(hash, number),
self.authority_set.set_id(),
&self.authority_set.current_authorities(),
);
let justification = match justification {
Err(e) => return Err(ConsensusError::ClientImport(e.to_string()).into()),
Ok(justification) => justification,
};
let result = finalize_block(
&*self.inner,
&self.authority_set,
&self.consensus_changes,
None,
hash,
number,
justification.into(),
);
match result {
Err(CommandOrError::VoterCommand(command)) => {
info!(target: "finality", "Imported justification for block #{} that triggers \
command {}, signaling voter.", number, command);
// send the command to the voter
let _ = self.send_voter_commands.unbounded_send(command);
},
Err(CommandOrError::Error(e)) => {
return Err(match e {
Error::Grandpa(error) => ConsensusError::ClientImport(error.to_string()),
Error::Network(error) => ConsensusError::ClientImport(error),
Error::Blockchain(error) => ConsensusError::ClientImport(error),
Error::Client(error) => ConsensusError::ClientImport(error.to_string()),
Error::Safety(error) => ConsensusError::ClientImport(error),
Error::Timer(error) => ConsensusError::ClientImport(error.to_string()),
}.into());
},
Ok(_) => {
assert!(!enacts_change, "returns Ok when no authority set change should be enacted; qed;");
},
}
Ok(())
}
}
@@ -0,0 +1,224 @@
// Copyright 2018-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/>.
use std::collections::{HashMap, HashSet};
use client::Client;
use client_api::{CallExecutor, backend::Backend, error::Error as ClientError};
use codec::{Encode, Decode};
use grandpa::voter_set::VoterSet;
use grandpa::{Error as GrandpaError};
use sr_primitives::generic::BlockId;
use sr_primitives::traits::{NumberFor, Block as BlockT, Header as HeaderT};
use primitives::{H256, Blake2Hasher};
use fg_primitives::AuthorityId;
use crate::{Commit, Error};
use crate::communication;
/// A GRANDPA justification for block finality, it includes a commit message and
/// an ancestry proof including all headers routing all precommit target blocks
/// to the commit target block. Due to the current voting strategy the precommit
/// targets should be the same as the commit target, since honest voters don't
/// vote past authority set change blocks.
///
/// This is meant to be stored in the db and passed around the network to other
/// nodes, and are used by syncing nodes to prove authority set handoffs.
#[derive(Encode, Decode)]
pub struct GrandpaJustification<Block: BlockT> {
round: u64,
pub(crate) commit: Commit<Block>,
votes_ancestries: Vec<Block::Header>,
}
impl<Block: BlockT<Hash=H256>> GrandpaJustification<Block> {
/// Create a GRANDPA justification from the given commit. This method
/// assumes the commit is valid and well-formed.
pub(crate) fn from_commit<B, E, RA>(
client: &Client<B, E, Block, RA>,
round: u64,
commit: Commit<Block>,
) -> Result<GrandpaJustification<Block>, Error> where
B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
RA: Send + Sync,
{
let mut votes_ancestries_hashes = HashSet::new();
let mut votes_ancestries = Vec::new();
let error = || {
let msg = "invalid precommits for target commit".to_string();
Err(Error::Client(ClientError::BadJustification(msg)))
};
for signed in commit.precommits.iter() {
let mut current_hash = signed.precommit.target_hash.clone();
loop {
if current_hash == commit.target_hash { break; }
match client.header(&BlockId::Hash(current_hash))? {
Some(current_header) => {
if *current_header.number() <= commit.target_number {
return error();
}
let parent_hash = current_header.parent_hash().clone();
if votes_ancestries_hashes.insert(current_hash) {
votes_ancestries.push(current_header);
}
current_hash = parent_hash;
},
_ => return error(),
}
}
}
Ok(GrandpaJustification { round, commit, votes_ancestries })
}
/// Decode a GRANDPA justification and validate the commit and the votes'
/// ancestry proofs finalize the given block.
pub(crate) fn decode_and_verify_finalizes(
encoded: &[u8],
finalized_target: (Block::Hash, NumberFor<Block>),
set_id: u64,
voters: &VoterSet<AuthorityId>,
) -> Result<GrandpaJustification<Block>, ClientError> where
NumberFor<Block>: grandpa::BlockNumberOps,
{
let justification = GrandpaJustification::<Block>::decode(&mut &*encoded)
.map_err(|_| ClientError::JustificationDecode)?;
if (justification.commit.target_hash, justification.commit.target_number) != finalized_target {
let msg = "invalid commit target in grandpa justification".to_string();
Err(ClientError::BadJustification(msg))
} else {
justification.verify(set_id, voters).map(|_| justification)
}
}
/// Validate the commit and the votes' ancestry proofs.
pub(crate) fn verify(&self, set_id: u64, voters: &VoterSet<AuthorityId>) -> Result<(), ClientError>
where
NumberFor<Block>: grandpa::BlockNumberOps,
{
use grandpa::Chain;
let ancestry_chain = AncestryChain::<Block>::new(&self.votes_ancestries);
match grandpa::validate_commit(
&self.commit,
voters,
&ancestry_chain,
) {
Ok(ref result) if result.ghost().is_some() => {},
_ => {
let msg = "invalid commit in grandpa justification".to_string();
return Err(ClientError::BadJustification(msg));
}
}
let mut visited_hashes = HashSet::new();
for signed in self.commit.precommits.iter() {
if let Err(_) = communication::check_message_sig::<Block>(
&grandpa::Message::Precommit(signed.precommit.clone()),
&signed.id,
&signed.signature,
self.round,
set_id,
) {
return Err(ClientError::BadJustification(
"invalid signature for precommit in grandpa justification".to_string()).into());
}
if self.commit.target_hash == signed.precommit.target_hash {
continue;
}
match ancestry_chain.ancestry(self.commit.target_hash, signed.precommit.target_hash) {
Ok(route) => {
// ancestry starts from parent hash but the precommit target hash has been visited
visited_hashes.insert(signed.precommit.target_hash);
for hash in route {
visited_hashes.insert(hash);
}
},
_ => {
return Err(ClientError::BadJustification(
"invalid precommit ancestry proof in grandpa justification".to_string()).into());
},
}
}
let ancestry_hashes = self.votes_ancestries
.iter()
.map(|h: &Block::Header| h.hash())
.collect();
if visited_hashes != ancestry_hashes {
return Err(ClientError::BadJustification(
"invalid precommit ancestries in grandpa justification with unused headers".to_string()).into());
}
Ok(())
}
}
/// A utility trait implementing `grandpa::Chain` using a given set of headers.
/// This is useful when validating commits, using the given set of headers to
/// verify a valid ancestry route to the target commit block.
struct AncestryChain<Block: BlockT> {
ancestry: HashMap<Block::Hash, Block::Header>,
}
impl<Block: BlockT> AncestryChain<Block> {
fn new(ancestry: &[Block::Header]) -> AncestryChain<Block> {
let ancestry: HashMap<_, _> = ancestry
.iter()
.cloned()
.map(|h: Block::Header| (h.hash(), h))
.collect();
AncestryChain { ancestry }
}
}
impl<Block: BlockT> grandpa::Chain<Block::Hash, NumberFor<Block>> for AncestryChain<Block> where
NumberFor<Block>: grandpa::BlockNumberOps
{
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
let mut route = Vec::new();
let mut current_hash = block;
loop {
if current_hash == base { break; }
match self.ancestry.get(&current_hash) {
Some(current_header) => {
current_hash = *current_header.parent_hash();
route.push(current_hash);
},
_ => return Err(GrandpaError::NotDescendent),
}
}
route.pop(); // remove the base
Ok(route)
}
fn best_chain_containing(&self, _block: Block::Hash) -> Option<(Block::Hash, NumberFor<Block>)> {
None
}
}
@@ -0,0 +1,957 @@
// Copyright 2018-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/>.
//! 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 log::{debug, error, info};
use futures::sync::mpsc;
use client_api::{
BlockchainEvents, CallExecutor, backend::Backend, error::Error as ClientError,
ExecutionStrategy, HeaderBackend
};
use client::Client;
use codec::{Decode, Encode};
use sr_primitives::generic::BlockId;
use sr_primitives::traits::{NumberFor, Block as BlockT, DigestFor, Zero};
use keystore::KeyStorePtr;
use inherents::InherentDataProviders;
use consensus_common::SelectChain;
use primitives::{H256, Blake2Hasher, Pair};
use substrate_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG, CONSENSUS_WARN};
use serde_json;
use paint_finality_tracker;
use grandpa::Error as GrandpaError;
use grandpa::{voter, BlockNumberOps, voter_set::VoterSet};
use std::fmt;
use std::sync::Arc;
use std::time::Duration;
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 communication::Network;
pub use finality_proof::FinalityProofProvider;
pub use justification::GrandpaJustification;
pub use light_import::light_block_import;
pub use observer::run_grandpa_observer;
pub use voting_rule::{
BeforeBestBlock, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
};
use aux_schema::PersistentData;
use environment::{Environment, VoterSetState};
use import::GrandpaBlockImport;
use until_imported::UntilGlobalMessageBlocksImported;
use communication::NetworkBridge;
use fg_primitives::{AuthorityList, AuthorityPair, AuthoritySignature, SetId};
// Re-export these two because it's just so damn convenient.
pub use fg_primitives::{AuthorityId, ScheduledChange};
#[cfg(test)]
mod tests;
/// A GRANDPA message for a substrate chain.
pub type Message<Block> = grandpa::Message<<Block as BlockT>::Hash, NumberFor<Block>>;
/// A signed message.
pub type SignedMessage<Block> = grandpa::SignedMessage<
<Block as BlockT>::Hash,
NumberFor<Block>,
AuthoritySignature,
AuthorityId,
>;
/// A primary propose message for this chain's block type.
pub type PrimaryPropose<Block> = grandpa::PrimaryPropose<<Block as BlockT>::Hash, NumberFor<Block>>;
/// A prevote message for this chain's block type.
pub type Prevote<Block> = grandpa::Prevote<<Block as BlockT>::Hash, NumberFor<Block>>;
/// A precommit message for this chain's block type.
pub type Precommit<Block> = grandpa::Precommit<<Block as BlockT>::Hash, NumberFor<Block>>;
/// A catch up message for this chain's block type.
pub type CatchUp<Block> = grandpa::CatchUp<
<Block as BlockT>::Hash,
NumberFor<Block>,
AuthoritySignature,
AuthorityId,
>;
/// A commit message for this chain's block type.
pub type Commit<Block> = grandpa::Commit<
<Block as BlockT>::Hash,
NumberFor<Block>,
AuthoritySignature,
AuthorityId,
>;
/// A compact commit message for this chain's block type.
pub type CompactCommit<Block> = grandpa::CompactCommit<
<Block as BlockT>::Hash,
NumberFor<Block>,
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<Block> = grandpa::voter::CommunicationIn<
<Block as BlockT>::Hash,
NumberFor<Block>,
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<Block, H> = grandpa::voter::CommunicationIn<
H,
NumberFor<Block>,
AuthoritySignature,
AuthorityId,
>;
/// A global communication sink for commits. Not exposed publicly, used
/// internally to simplify types in the communication layer.
type CommunicationOut<Block> = grandpa::voter::CommunicationOut<
<Block as BlockT>::Hash,
NumberFor<Block>,
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<Block, H> = grandpa::voter::CommunicationOut<
H,
NumberFor<Block>,
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<String>,
/// The keystore that manages the keys of this node.
pub keystore: Option<keystore::KeyStorePtr>,
}
impl Config {
fn name(&self) -> &str {
self.name.as_ref().map(|s| s.as_str()).unwrap_or("<unknown>")
}
}
/// 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(tokio_timer::Error),
}
impl From<GrandpaError> for Error {
fn from(e: GrandpaError) -> Self {
Error::Grandpa(e)
}
}
impl From<ClientError> for Error {
fn from(e: ClientError) -> Self {
Error::Client(e)
}
}
/// Something which can determine if a block is known.
pub(crate) trait BlockStatus<Block: BlockT> {
/// 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<Option<NumberFor<Block>>, Error>;
}
impl<B, E, Block: BlockT<Hash=H256>, RA> BlockStatus<Block> for Arc<Client<B, E, Block, RA>> where
B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
RA: Send + Sync,
NumberFor<Block>: BlockNumberOps,
{
fn block_number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, Error> {
self.block_number_from_id(&BlockId::Hash(hash))
.map_err(|e| Error::Blockchain(format!("{:?}", e)))
}
}
/// Something that one can ask to do a block sync request.
pub(crate) trait BlockSyncRequester<Block: BlockT> {
/// Notifies the sync service to try and sync the given block from the given
/// peers.
///
/// If the given vector of peers is empty then the underlying implementation
/// should make a best effort to fetch the block from any peers it is
/// connected to (NOTE: this assumption will change in the future #3629).
fn set_sync_fork_request(&self, peers: Vec<network::PeerId>, hash: Block::Hash, number: NumberFor<Block>);
}
impl<Block, N> BlockSyncRequester<Block> for NetworkBridge<Block, N> where
Block: BlockT,
N: communication::Network<Block>,
{
fn set_sync_fork_request(&self, peers: Vec<network::PeerId>, hash: Block::Hash, number: NumberFor<Block>) {
NetworkBridge::set_sync_fork_request(self, peers, hash, number)
}
}
/// A new authority set along with the canonical block it changed at.
#[derive(Debug)]
pub(crate) struct NewAuthoritySet<H, N> {
pub(crate) canon_number: N,
pub(crate) canon_hash: H,
pub(crate) set_id: SetId,
pub(crate) authorities: AuthorityList,
}
/// Commands issued to the voter.
#[derive(Debug)]
pub(crate) enum VoterCommand<H, N> {
/// Pause the voter for given reason.
Pause(String),
/// New authorities.
ChangeAuthorities(NewAuthoritySet<H, N>)
}
impl<H, N> fmt::Display for VoterCommand<H, N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
VoterCommand::Pause(ref reason) => write!(f, "Pausing voter: {}", reason),
VoterCommand::ChangeAuthorities(_) => write!(f, "Changing authorities"),
}
}
}
/// Signals either an early exit of a voter or an error.
#[derive(Debug)]
pub(crate) enum CommandOrError<H, N> {
/// An error occurred.
Error(Error),
/// A command to the voter.
VoterCommand(VoterCommand<H, N>),
}
impl<H, N> From<Error> for CommandOrError<H, N> {
fn from(e: Error) -> Self {
CommandOrError::Error(e)
}
}
impl<H, N> From<ClientError> for CommandOrError<H, N> {
fn from(e: ClientError) -> Self {
CommandOrError::Error(Error::Client(e))
}
}
impl<H, N> From<grandpa::Error> for CommandOrError<H, N> {
fn from(e: grandpa::Error) -> Self {
CommandOrError::Error(Error::from(e))
}
}
impl<H, N> From<VoterCommand<H, N>> for CommandOrError<H, N> {
fn from(e: VoterCommand<H, N>) -> Self {
CommandOrError::VoterCommand(e)
}
}
impl<H: fmt::Debug, N: fmt::Debug> ::std::error::Error for CommandOrError<H, N> { }
impl<H, N> fmt::Display for CommandOrError<H, N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CommandOrError::Error(ref e) => write!(f, "{:?}", e),
CommandOrError::VoterCommand(ref cmd) => write!(f, "{}", cmd),
}
}
}
pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>, RA, SC> {
client: Arc<Client<B, E, Block, RA>>,
select_chain: SC,
persistent_data: PersistentData<Block>,
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
}
/// Provider for the Grandpa authority set configured on the genesis block.
pub trait GenesisAuthoritySetProvider<Block: BlockT> {
/// Get the authority set at the genesis block.
fn get(&self) -> Result<AuthorityList, ClientError>;
}
impl<B, E, Block: BlockT<Hash=H256>, RA> GenesisAuthoritySetProvider<Block> for Client<B, E, Block, RA>
where
B: Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: Send + Sync,
{
fn get(&self) -> Result<AuthorityList, ClientError> {
// This implementation uses the Grandpa runtime API instead of reading directly from the
// `GRANDPA_AUTHORITIES_KEY` as the data may have been migrated since the genesis block of
// the chain, whereas the runtime API is backwards compatible.
self.executor()
.call(
&BlockId::Number(Zero::zero()),
"GrandpaApi_grandpa_authorities",
&[],
ExecutionStrategy::NativeElseWasm,
None,
)
.and_then(|call_result| {
Decode::decode(&mut &call_result[..])
.map_err(|err| ClientError::CallResultDecode(
"failed to decode GRANDPA authorities set proof".into(), err
))
})
}
}
/// Make block importer and link half necessary to tie the background voter
/// to it.
pub fn block_import<B, E, Block: BlockT<Hash=H256>, RA, SC>(
client: Arc<Client<B, E, Block, RA>>,
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
select_chain: SC,
) -> Result<(
GrandpaBlockImport<B, E, Block, RA, SC>,
LinkHalf<B, E, Block, RA, SC>
), ClientError>
where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: Send + Sync,
SC: SelectChain<Block>,
{
let chain_info = client.info();
let genesis_hash = chain_info.chain.genesis_hash;
let persistent_data = aux_schema::load_persistent(
&*client,
genesis_hash,
<NumberFor<Block>>::zero(),
|| {
let authorities = genesis_authorities_provider.get()?;
telemetry!(CONSENSUS_DEBUG; "afg.loading_authorities";
"authorities_len" => ?authorities.len()
);
Ok(authorities)
}
)?;
let (voter_commands_tx, voter_commands_rx) = mpsc::unbounded();
Ok((
GrandpaBlockImport::new(
client.clone(),
select_chain.clone(),
persistent_data.authority_set.clone(),
voter_commands_tx,
persistent_data.consensus_changes.clone(),
),
LinkHalf {
client,
select_chain,
persistent_data,
voter_commands_rx,
},
))
}
fn global_communication<Block: BlockT<Hash=H256>, B, E, N, RA>(
set_id: SetId,
voters: &Arc<VoterSet<AuthorityId>>,
client: &Arc<Client<B, E, Block, RA>>,
network: &NetworkBridge<Block, N>,
keystore: &Option<KeyStorePtr>,
) -> (
impl Stream<
Item = CommunicationInH<Block, H256>,
Error = CommandOrError<H256, NumberFor<Block>>,
>,
impl Sink<
SinkItem = CommunicationOutH<Block, H256>,
SinkError = CommandOrError<H256, NumberFor<Block>>,
>,
) where
B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
N: Network<Block>,
RA: Send + Sync,
NumberFor<Block>: BlockNumberOps,
{
let is_voter = is_voter(voters, keystore).is_some();
// verification stream
let (global_in, global_out) = network.global_communication(
communication::SetId(set_id),
voters.clone(),
is_voter,
);
// block commit and catch up messages until relevant blocks are imported.
let global_in = UntilGlobalMessageBlocksImported::new(
client.import_notification_stream(),
network.clone(),
client.clone(),
global_in,
"global",
);
let global_in = global_in.map_err(CommandOrError::from);
let global_out = global_out.sink_map_err(CommandOrError::from);
(global_in, global_out)
}
/// Register the finality tracker inherent data provider (which is used by
/// GRANDPA), if not registered already.
fn register_finality_tracker_inherent_data_provider<B, E, Block: BlockT<Hash=H256>, RA>(
client: Arc<Client<B, E, Block, RA>>,
inherent_data_providers: &InherentDataProviders,
) -> Result<(), consensus_common::Error> where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
RA: Send + Sync + 'static,
{
if !inherent_data_providers.has_provider(&paint_finality_tracker::INHERENT_IDENTIFIER) {
inherent_data_providers
.register_provider(paint_finality_tracker::InherentDataProvider::new(move || {
#[allow(deprecated)]
{
let info = client.info().chain;
telemetry!(CONSENSUS_INFO; "afg.finalized";
"finalized_number" => ?info.finalized_number,
"finalized_hash" => ?info.finalized_hash,
);
Ok(info.finalized_number)
}
}))
.map_err(|err| consensus_common::Error::InherentData(err.into()))
} else {
Ok(())
}
}
/// Parameters used to run Grandpa.
pub struct GrandpaParams<B, E, Block: BlockT<Hash=H256>, N, RA, SC, VR, X> {
/// Configuration for the GRANDPA service.
pub config: Config,
/// A link to the block import worker.
pub link: LinkHalf<B, E, Block, RA, SC>,
/// The Network instance.
pub network: N,
/// The inherent data providers.
pub inherent_data_providers: InherentDataProviders,
/// Handle to a future that will resolve on exit.
pub on_exit: X,
/// If supplied, can be used to hook on telemetry connection established events.
pub telemetry_on_connect: Option<mpsc::UnboundedReceiver<()>>,
/// A voting rule used to potentially restrict target votes.
pub voting_rule: VR,
}
/// Run a GRANDPA voter as a task. Provide configuration and a link to a
/// block import worker that has already been instantiated with `block_import`.
pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, VR, X>(
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, VR, X>,
) -> client_api::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
Block::Hash: Ord,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
N: Network<Block> + Send + Sync + 'static,
N::In: Send + 'static,
SC: SelectChain<Block> + 'static,
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
NumberFor<Block>: BlockNumberOps,
DigestFor<Block>: Encode,
RA: Send + Sync + 'static,
X: Future<Item=(),Error=()> + Clone + Send + 'static,
{
let GrandpaParams {
config,
link,
network,
inherent_data_providers,
on_exit,
telemetry_on_connect,
voting_rule,
} = grandpa_params;
let LinkHalf {
client,
select_chain,
persistent_data,
voter_commands_rx,
} = link;
let (network, network_startup) = NetworkBridge::new(
network,
config.clone(),
persistent_data.set_state.clone(),
on_exit.clone(),
);
register_finality_tracker_inherent_data_provider(client.clone(), &inherent_data_providers)?;
let conf = config.clone();
let telemetry_task = if let Some(telemetry_on_connect) = telemetry_on_connect {
let authorities = persistent_data.authority_set.clone();
let events = telemetry_on_connect
.for_each(move |_| {
let curr = authorities.current_authorities();
let mut auths = curr.voters().into_iter().map(|(p, _)| p);
let maybe_authority_id = authority_id(&mut auths, &conf.keystore)
.unwrap_or(Default::default());
telemetry!(CONSENSUS_INFO; "afg.authority_set";
"authority_id" => maybe_authority_id.to_string(),
"authority_set_id" => ?authorities.set_id(),
"authorities" => {
let authorities: Vec<String> = curr.voters()
.iter().map(|(id, _)| id.to_string()).collect();
serde_json::to_string(&authorities)
.expect("authorities is always at least an empty vector; elements are always of type string")
}
);
Ok(())
})
.then(|_| -> Result<(), ()> { Ok(()) });
futures::future::Either::A(events)
} else {
futures::future::Either::B(futures::future::empty())
};
let voter_work = VoterWork::new(
client,
config,
network,
select_chain,
voting_rule,
persistent_data,
voter_commands_rx,
);
let voter_work = voter_work
.map(|_| ())
.map_err(|e| {
error!("GRANDPA Voter failed: {:?}", e);
telemetry!(CONSENSUS_WARN; "afg.voter_failed"; "e" => ?e);
});
let voter_work = network_startup.and_then(move |()| voter_work);
// Make sure that `telemetry_task` doesn't accidentally finish and kill grandpa.
let telemetry_task = telemetry_task
.then(|_| futures::future::empty::<(), ()>());
Ok(voter_work.select(on_exit).select2(telemetry_task).then(|_| Ok(())))
}
/// Future that powers the voter.
#[must_use]
struct VoterWork<B, E, Block: BlockT, N: Network<Block>, RA, SC, VR> {
voter: Box<dyn Future<Item = (), Error = CommandOrError<Block::Hash, NumberFor<Block>>> + Send>,
env: Arc<Environment<B, E, Block, N, RA, SC, VR>>,
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
}
impl<B, E, Block, N, RA, SC, VR> VoterWork<B, E, Block, N, RA, SC, VR>
where
Block: BlockT<Hash=H256>,
N: Network<Block> + Sync,
N::In: Send + 'static,
NumberFor<Block>: BlockNumberOps,
RA: 'static + Send + Sync,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
B: Backend<Block, Blake2Hasher> + 'static,
SC: SelectChain<Block> + 'static,
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
{
fn new(
client: Arc<Client<B, E, Block, RA>>,
config: Config,
network: NetworkBridge<Block, N>,
select_chain: SC,
voting_rule: VR,
persistent_data: PersistentData<Block>,
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
) -> Self {
let voters = persistent_data.authority_set.current_authorities();
let env = Arc::new(Environment {
client,
select_chain,
voting_rule,
voters: Arc::new(voters),
config,
network,
set_id: persistent_data.authority_set.set_id(),
authority_set: persistent_data.authority_set.clone(),
consensus_changes: persistent_data.consensus_changes.clone(),
voter_set_state: persistent_data.set_state.clone(),
});
let mut work = VoterWork {
// `voter` is set to a temporary value and replaced below when
// calling `rebuild_voter`.
voter: Box::new(futures::empty()) as Box<_>,
env,
voter_commands_rx,
};
work.rebuild_voter();
work
}
/// Rebuilds the `self.voter` field using the current authority set
/// state. This method should be called when we know that the authority set
/// has changed (e.g. as signalled by a voter command).
fn rebuild_voter(&mut self) {
debug!(target: "afg", "{}: Starting new voter with set ID {}", self.env.config.name(), self.env.set_id);
let authority_id = is_voter(&self.env.voters, &self.env.config.keystore)
.map(|ap| ap.public())
.unwrap_or(Default::default());
telemetry!(CONSENSUS_DEBUG; "afg.starting_new_voter";
"name" => ?self.env.config.name(),
"set_id" => ?self.env.set_id,
"authority_id" => authority_id.to_string(),
);
let chain_info = self.env.client.info();
telemetry!(CONSENSUS_INFO; "afg.authority_set";
"number" => ?chain_info.chain.finalized_number,
"hash" => ?chain_info.chain.finalized_hash,
"authority_id" => authority_id.to_string(),
"authority_set_id" => ?self.env.set_id,
"authorities" => {
let authorities: Vec<String> = self.env.voters.voters()
.iter().map(|(id, _)| id.to_string()).collect();
serde_json::to_string(&authorities)
.expect("authorities is always at least an empty vector; elements are always of type string")
},
);
match &*self.env.voter_set_state.read() {
VoterSetState::Live { completed_rounds, .. } => {
let last_finalized = (
chain_info.chain.finalized_hash,
chain_info.chain.finalized_number,
);
let global_comms = global_communication(
self.env.set_id,
&self.env.voters,
&self.env.client,
&self.env.network,
&self.env.config.keystore,
);
let last_completed_round = completed_rounds.last();
let voter = voter::Voter::new(
self.env.clone(),
(*self.env.voters).clone(),
global_comms,
last_completed_round.number,
last_completed_round.state.clone(),
last_finalized,
);
self.voter = Box::new(voter);
},
VoterSetState::Paused { .. } =>
self.voter = Box::new(futures::empty()),
};
}
fn handle_voter_command(
&mut self,
command: VoterCommand<Block::Hash, NumberFor<Block>>
) -> Result<(), Error> {
match command {
VoterCommand::ChangeAuthorities(new) => {
let voters: Vec<String> = new.authorities.iter().map(move |(a, _)| {
format!("{}", a)
}).collect();
telemetry!(CONSENSUS_INFO; "afg.voter_command_change_authorities";
"number" => ?new.canon_number,
"hash" => ?new.canon_hash,
"voters" => ?voters,
"set_id" => ?new.set_id,
);
self.env.update_voter_set_state(|_| {
// start the new authority set using the block where the
// set changed (not where the signal happened!) as the base.
let set_state = VoterSetState::live(
new.set_id,
&*self.env.authority_set.inner().read(),
(new.canon_hash, new.canon_number),
);
aux_schema::write_voter_set_state(&*self.env.client, &set_state)?;
Ok(Some(set_state))
})?;
self.env = Arc::new(Environment {
voters: Arc::new(new.authorities.into_iter().collect()),
set_id: new.set_id,
voter_set_state: self.env.voter_set_state.clone(),
// Fields below are simply transferred and not updated.
client: self.env.client.clone(),
select_chain: self.env.select_chain.clone(),
config: self.env.config.clone(),
authority_set: self.env.authority_set.clone(),
consensus_changes: self.env.consensus_changes.clone(),
network: self.env.network.clone(),
voting_rule: self.env.voting_rule.clone(),
});
self.rebuild_voter();
Ok(())
}
VoterCommand::Pause(reason) => {
info!(target: "afg", "Pausing old validator set: {}", reason);
// not racing because old voter is shut down.
self.env.update_voter_set_state(|voter_set_state| {
let completed_rounds = voter_set_state.completed_rounds();
let set_state = VoterSetState::Paused { completed_rounds };
aux_schema::write_voter_set_state(&*self.env.client, &set_state)?;
Ok(Some(set_state))
})?;
self.rebuild_voter();
Ok(())
}
}
}
}
impl<B, E, Block, N, RA, SC, VR> Future for VoterWork<B, E, Block, N, RA, SC, VR>
where
Block: BlockT<Hash=H256>,
N: Network<Block> + Sync,
N::In: Send + 'static,
NumberFor<Block>: BlockNumberOps,
RA: 'static + Send + Sync,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
B: Backend<Block, Blake2Hasher> + 'static,
SC: SelectChain<Block> + 'static,
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
{
type Item = ();
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.voter.poll() {
Ok(Async::NotReady) => {}
Ok(Async::Ready(())) => {
// voters don't conclude naturally
return Err(Error::Safety("GRANDPA voter has concluded.".into()))
}
Err(CommandOrError::Error(e)) => {
// return inner observer error
return Err(e)
}
Err(CommandOrError::VoterCommand(command)) => {
// some command issued internally
self.handle_voter_command(command)?;
futures::task::current().notify();
}
}
match self.voter_commands_rx.poll() {
Ok(Async::NotReady) => {}
Err(_) => {
// the `voter_commands_rx` stream should not fail.
return Ok(Async::Ready(()))
}
Ok(Async::Ready(None)) => {
// the `voter_commands_rx` stream should never conclude since it's never closed.
return Ok(Async::Ready(()))
}
Ok(Async::Ready(Some(command))) => {
// some command issued externally
self.handle_voter_command(command)?;
futures::task::current().notify();
}
}
Ok(Async::NotReady)
}
}
#[deprecated(since = "1.1.0", note = "Please switch to run_grandpa_voter.")]
pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA, SC, VR, X>(
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, VR, X>,
) -> ::client_api::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
Block::Hash: Ord,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
N: Network<Block> + Send + Sync + 'static,
N::In: Send + 'static,
SC: SelectChain<Block> + 'static,
NumberFor<Block>: BlockNumberOps,
DigestFor<Block>: Encode,
RA: Send + Sync + 'static,
VR: VotingRule<Block, Client<B, E, Block, RA>> + Clone + 'static,
X: Future<Item=(),Error=()> + Clone + Send + 'static,
{
run_grandpa_voter(grandpa_params)
}
/// When GRANDPA is not initialized we still need to register the finality
/// tracker inherent provider which might be expected by the runtime for block
/// authoring. Additionally, we register a gossip message validator that
/// discards all GRANDPA messages (otherwise, we end up banning nodes that send
/// us a `Neighbor` message, since there is no registered gossip validator for
/// the engine id defined in the message.)
pub fn setup_disabled_grandpa<B, E, Block: BlockT<Hash=H256>, RA, N>(
client: Arc<Client<B, E, Block, RA>>,
inherent_data_providers: &InherentDataProviders,
network: N,
) -> Result<(), consensus_common::Error> where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
RA: Send + Sync + 'static,
N: Network<Block> + Send + Sync + 'static,
N::In: Send + 'static,
{
register_finality_tracker_inherent_data_provider(
client,
inherent_data_providers,
)?;
network.register_validator(Arc::new(network::consensus_gossip::DiscardAll));
Ok(())
}
/// Checks if this node is a voter in the given voter set.
///
/// Returns the key pair of the node that is being used in the current voter set or `None`.
fn is_voter(
voters: &Arc<VoterSet<AuthorityId>>,
keystore: &Option<KeyStorePtr>,
) -> Option<AuthorityPair> {
match keystore {
Some(keystore) => voters.voters().iter()
.find_map(|(p, _)| keystore.read().key_pair::<AuthorityPair>(&p).ok()),
None => None,
}
}
/// Returns the authority id of this node, if available.
fn authority_id<'a, I>(
authorities: &mut I,
keystore: &Option<KeyStorePtr>,
) -> Option<AuthorityId> where
I: Iterator<Item = &'a AuthorityId>,
{
match keystore {
Some(keystore) => {
authorities
.find_map(|p| {
keystore.read().key_pair::<AuthorityPair>(&p)
.ok()
.map(|ap| ap.public())
})
}
None => None,
}
}
@@ -0,0 +1,778 @@
// Copyright 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/>.
use std::collections::HashMap;
use std::sync::Arc;
use log::{info, trace, warn};
use parking_lot::RwLock;
use client::Client;
use client_api::{
CallExecutor,
backend::{AuxStore, Backend, Finalizer},
blockchain::HeaderBackend,
error::Error as ClientError,
well_known_cache_keys,
};
use codec::{Encode, Decode};
use consensus_common::{
import_queue::Verifier,
BlockOrigin, BlockImport, FinalityProofImport, BlockImportParams, ImportResult, ImportedAux,
BlockCheckParams, Error as ConsensusError,
};
use network::config::{BoxFinalityProofRequestBuilder, FinalityProofRequestBuilder};
use sr_primitives::Justification;
use sr_primitives::traits::{NumberFor, Block as BlockT, Header as HeaderT, DigestFor};
use fg_primitives::{self, AuthorityList};
use sr_primitives::generic::BlockId;
use primitives::{H256, Blake2Hasher};
use crate::GenesisAuthoritySetProvider;
use crate::aux_schema::load_decode;
use crate::consensus_changes::ConsensusChanges;
use crate::environment::canonical_at_height;
use crate::finality_proof::{
AuthoritySetForFinalityChecker, ProvableJustification, make_finality_proof_request,
};
use crate::justification::GrandpaJustification;
/// LightAuthoritySet is saved under this key in aux storage.
const LIGHT_AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters";
/// ConsensusChanges is saver under this key in aux storage.
const LIGHT_CONSENSUS_CHANGES_KEY: &[u8] = b"grandpa_consensus_changes";
/// Create light block importer.
pub fn light_block_import<B, E, Block: BlockT<Hash=H256>, RA>(
client: Arc<Client<B, E, Block, RA>>,
backend: Arc<B>,
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
authority_set_provider: Arc<dyn AuthoritySetForFinalityChecker<Block>>,
) -> Result<GrandpaLightBlockImport<B, E, Block, RA>, ClientError>
where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: Send + Sync,
{
let info = client.info();
let import_data = load_aux_import_data(
info.chain.finalized_hash,
&*client,
genesis_authorities_provider,
)?;
Ok(GrandpaLightBlockImport {
client,
backend,
authority_set_provider,
data: Arc::new(RwLock::new(import_data)),
})
}
/// A light block-import handler for GRANDPA.
///
/// It is responsible for:
/// - checking GRANDPA justifications;
/// - fetching finality proofs for blocks that are enacting consensus changes.
pub struct GrandpaLightBlockImport<B, E, Block: BlockT<Hash=H256>, RA> {
client: Arc<Client<B, E, Block, RA>>,
backend: Arc<B>,
authority_set_provider: Arc<dyn AuthoritySetForFinalityChecker<Block>>,
data: Arc<RwLock<LightImportData<Block>>>,
}
impl<B, E, Block: BlockT<Hash=H256>, RA> Clone for GrandpaLightBlockImport<B, E, Block, RA> {
fn clone(&self) -> Self {
GrandpaLightBlockImport {
client: self.client.clone(),
backend: self.backend.clone(),
authority_set_provider: self.authority_set_provider.clone(),
data: self.data.clone(),
}
}
}
/// Mutable data of light block importer.
struct LightImportData<Block: BlockT<Hash=H256>> {
last_finalized: Block::Hash,
authority_set: LightAuthoritySet,
consensus_changes: ConsensusChanges<Block::Hash, NumberFor<Block>>,
}
/// Latest authority set tracker.
#[derive(Debug, Encode, Decode)]
struct LightAuthoritySet {
set_id: u64,
authorities: AuthorityList,
}
impl<B, E, Block: BlockT<Hash=H256>, RA> GrandpaLightBlockImport<B, E, Block, RA> {
/// Create finality proof request builder.
pub fn create_finality_proof_request_builder(&self) -> BoxFinalityProofRequestBuilder<Block> {
Box::new(GrandpaFinalityProofRequestBuilder(self.data.clone())) as _
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA> BlockImport<Block>
for GrandpaLightBlockImport<B, E, Block, RA> where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
type Error = ConsensusError;
fn import_block(
&mut self,
block: BlockImportParams<Block>,
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
do_import_block::<_, _, _, GrandpaJustification<Block>>(
&*self.client, &mut *self.data.write(), block, new_cache
)
}
fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
self.client.check_block(block)
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA> FinalityProofImport<Block>
for GrandpaLightBlockImport<B, E, Block, RA> where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
type Error = ConsensusError;
fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor<Block>)> {
let mut out = Vec::new();
let chain_info = self.client.info().chain;
let data = self.data.read();
for (pending_number, pending_hash) in data.consensus_changes.pending_changes() {
if *pending_number > chain_info.finalized_number && *pending_number <= chain_info.best_number {
out.push((pending_hash.clone(), *pending_number));
}
}
out
}
fn import_finality_proof(
&mut self,
hash: Block::Hash,
number: NumberFor<Block>,
finality_proof: Vec<u8>,
verifier: &mut dyn Verifier<Block>,
) -> Result<(Block::Hash, NumberFor<Block>), Self::Error> {
do_import_finality_proof::<_, _, _, GrandpaJustification<Block>>(
&*self.client,
self.backend.clone(),
&*self.authority_set_provider,
&mut *self.data.write(),
hash,
number,
finality_proof,
verifier,
)
}
}
impl LightAuthoritySet {
/// Get a genesis set with given authorities.
pub fn genesis(initial: AuthorityList) -> Self {
LightAuthoritySet {
set_id: fg_primitives::SetId::default(),
authorities: initial,
}
}
/// Get latest set id.
pub fn set_id(&self) -> u64 {
self.set_id
}
/// Get latest authorities set.
pub fn authorities(&self) -> AuthorityList {
self.authorities.clone()
}
/// Set new authorities set.
pub fn update(&mut self, set_id: u64, authorities: AuthorityList) {
self.set_id = set_id;
std::mem::replace(&mut self.authorities, authorities);
}
}
struct GrandpaFinalityProofRequestBuilder<B: BlockT<Hash=H256>>(Arc<RwLock<LightImportData<B>>>);
impl<B: BlockT<Hash=H256>> FinalityProofRequestBuilder<B> for GrandpaFinalityProofRequestBuilder<B> {
fn build_request_data(&mut self, _hash: &B::Hash) -> Vec<u8> {
let data = self.0.read();
make_finality_proof_request(
data.last_finalized,
data.authority_set.set_id(),
)
}
}
/// Try to import new block.
fn do_import_block<B, C, Block: BlockT<Hash=H256>, J>(
mut client: C,
data: &mut LightImportData<Block>,
mut block: BlockImportParams<Block>,
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
) -> Result<ImportResult, ConsensusError>
where
C: HeaderBackend<Block>
+ AuxStore
+ Finalizer<Block, Blake2Hasher, B>
+ BlockImport<Block>
+ Clone,
B: Backend<Block, Blake2Hasher> + 'static,
NumberFor<Block>: grandpa::BlockNumberOps,
DigestFor<Block>: Encode,
J: ProvableJustification<Block::Header>,
{
let hash = block.post_header().hash();
let number = block.header.number().clone();
// we don't want to finalize on `inner.import_block`
let justification = block.justification.take();
let enacts_consensus_change = !new_cache.is_empty();
let import_result = client.import_block(block, new_cache);
let mut imported_aux = match import_result {
Ok(ImportResult::Imported(aux)) => aux,
Ok(r) => return Ok(r),
Err(e) => return Err(ConsensusError::ClientImport(e.to_string()).into()),
};
match justification {
Some(justification) => {
trace!(
target: "finality",
"Imported block {}{}. Importing justification.",
if enacts_consensus_change { " which enacts consensus changes" } else { "" },
hash,
);
do_import_justification::<_, _, _, J>(client, data, hash, number, justification)
},
None if enacts_consensus_change => {
trace!(
target: "finality",
"Imported block {} which enacts consensus changes. Requesting finality proof.",
hash,
);
// remember that we need finality proof for this block
imported_aux.needs_finality_proof = true;
data.consensus_changes.note_change((number, hash));
Ok(ImportResult::Imported(imported_aux))
},
None => Ok(ImportResult::Imported(imported_aux)),
}
}
/// Try to import finality proof.
fn do_import_finality_proof<B, C, Block: BlockT<Hash=H256>, J>(
client: C,
backend: Arc<B>,
authority_set_provider: &dyn AuthoritySetForFinalityChecker<Block>,
data: &mut LightImportData<Block>,
_hash: Block::Hash,
_number: NumberFor<Block>,
finality_proof: Vec<u8>,
verifier: &mut dyn Verifier<Block>,
) -> Result<(Block::Hash, NumberFor<Block>), ConsensusError>
where
C: HeaderBackend<Block>
+ AuxStore
+ Finalizer<Block, Blake2Hasher, B>
+ BlockImport<Block>
+ Clone,
B: Backend<Block, Blake2Hasher> + 'static,
DigestFor<Block>: Encode,
NumberFor<Block>: grandpa::BlockNumberOps,
J: ProvableJustification<Block::Header>,
{
let authority_set_id = data.authority_set.set_id();
let authorities = data.authority_set.authorities();
let finality_effects = crate::finality_proof::check_finality_proof(
backend.blockchain(),
authority_set_id,
authorities,
authority_set_provider,
finality_proof,
).map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
// try to import all new headers
let block_origin = BlockOrigin::NetworkBroadcast;
for header_to_import in finality_effects.headers_to_import {
let (block_to_import, new_authorities) = verifier.verify(block_origin, header_to_import, None, None)
.map_err(|e| ConsensusError::ClientImport(e))?;
assert!(block_to_import.justification.is_none(), "We have passed None as justification to verifier.verify");
let mut cache = HashMap::new();
if let Some(authorities) = new_authorities {
cache.insert(well_known_cache_keys::AUTHORITIES, authorities.encode());
}
do_import_block::<_, _, _, J>(client.clone(), data, block_to_import, cache)?;
}
// try to import latest justification
let finalized_block_hash = finality_effects.block;
let finalized_block_number = backend.blockchain()
.expect_block_number_from_id(&BlockId::Hash(finality_effects.block))
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
do_finalize_block(
client,
data,
finalized_block_hash,
finalized_block_number,
finality_effects.justification.encode(),
)?;
// apply new authorities set
data.authority_set.update(
finality_effects.new_set_id,
finality_effects.new_authorities,
);
Ok((finalized_block_hash, finalized_block_number))
}
/// Try to import justification.
fn do_import_justification<B, C, Block: BlockT<Hash=H256>, J>(
client: C,
data: &mut LightImportData<Block>,
hash: Block::Hash,
number: NumberFor<Block>,
justification: Justification,
) -> Result<ImportResult, ConsensusError>
where
C: HeaderBackend<Block>
+ AuxStore
+ Finalizer<Block, Blake2Hasher, B>
+ Clone,
B: Backend<Block, Blake2Hasher> + 'static,
NumberFor<Block>: grandpa::BlockNumberOps,
J: ProvableJustification<Block::Header>,
{
// with justification, we have two cases
//
// optimistic: the same GRANDPA authorities set has generated intermediate justification
// => justification is verified using current authorities set + we could proceed further
//
// pessimistic scenario: the GRANDPA authorities set has changed
// => we need to fetch new authorities set (i.e. finality proof) from remote node
// first, try to behave optimistically
let authority_set_id = data.authority_set.set_id();
let justification = J::decode_and_verify(
&justification,
authority_set_id,
&data.authority_set.authorities(),
);
// BadJustification error means that justification has been successfully decoded, but
// it isn't valid within current authority set
let justification = match justification {
Err(ClientError::BadJustification(_)) => {
trace!(
target: "finality",
"Justification for {} is not valid within current authorities set. Requesting finality proof.",
hash,
);
let mut imported_aux = ImportedAux::default();
imported_aux.needs_finality_proof = true;
return Ok(ImportResult::Imported(imported_aux));
},
Err(e) => {
trace!(
target: "finality",
"Justification for {} is not valid. Bailing.",
hash,
);
return Err(ConsensusError::ClientImport(e.to_string()).into());
},
Ok(justification) => {
trace!(
target: "finality",
"Justification for {} is valid. Finalizing the block.",
hash,
);
justification
},
};
// finalize the block
do_finalize_block(client, data, hash, number, justification.encode())
}
/// Finalize the block.
fn do_finalize_block<B, C, Block: BlockT<Hash=H256>>(
client: C,
data: &mut LightImportData<Block>,
hash: Block::Hash,
number: NumberFor<Block>,
justification: Justification,
) -> Result<ImportResult, ConsensusError>
where
C: HeaderBackend<Block>
+ AuxStore
+ Finalizer<Block, Blake2Hasher, B>
+ Clone,
B: Backend<Block, Blake2Hasher> + 'static,
NumberFor<Block>: grandpa::BlockNumberOps,
{
// finalize the block
client.finalize_block(BlockId::Hash(hash), Some(justification), true).map_err(|e| {
warn!(target: "finality", "Error applying finality to block {:?}: {:?}", (hash, number), e);
ConsensusError::ClientImport(e.to_string())
})?;
// forget obsoleted consensus changes
let consensus_finalization_res = data.consensus_changes
.finalize((number, hash), |at_height| canonical_at_height(client.clone(), (hash, number), true, at_height));
match consensus_finalization_res {
Ok((true, _)) => require_insert_aux(
&client,
LIGHT_CONSENSUS_CHANGES_KEY,
&data.consensus_changes,
"consensus changes",
)?,
Ok(_) => (),
Err(error) => return Err(on_post_finalization_error(error, "consensus changes")),
}
// update last finalized block reference
data.last_finalized = hash;
// we just finalized this block, so if we were importing it, it is now the new best
Ok(ImportResult::imported(true))
}
/// Load light import aux data from the store.
fn load_aux_import_data<B, Block: BlockT<Hash=H256>>(
last_finalized: Block::Hash,
aux_store: &B,
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
) -> Result<LightImportData<Block>, ClientError>
where
B: AuxStore,
{
let authority_set = match load_decode(aux_store, LIGHT_AUTHORITY_SET_KEY)? {
Some(authority_set) => authority_set,
None => {
info!(target: "afg", "Loading GRANDPA authorities \
from genesis on what appears to be first startup.");
// no authority set on disk: fetch authorities from genesis state
let genesis_authorities = genesis_authorities_provider.get()?;
let authority_set = LightAuthoritySet::genesis(genesis_authorities);
let encoded = authority_set.encode();
aux_store.insert_aux(&[(LIGHT_AUTHORITY_SET_KEY, &encoded[..])], &[])?;
authority_set
},
};
let consensus_changes = match load_decode(aux_store, LIGHT_CONSENSUS_CHANGES_KEY)? {
Some(consensus_changes) => consensus_changes,
None => {
let consensus_changes = ConsensusChanges::<Block::Hash, NumberFor<Block>>::empty();
let encoded = authority_set.encode();
aux_store.insert_aux(&[(LIGHT_CONSENSUS_CHANGES_KEY, &encoded[..])], &[])?;
consensus_changes
},
};
Ok(LightImportData {
last_finalized,
authority_set,
consensus_changes,
})
}
/// Insert into aux store. If failed, return error && show inconsistency warning.
fn require_insert_aux<T: Encode, A: AuxStore>(
store: &A,
key: &[u8],
value: &T,
value_type: &str,
) -> Result<(), ConsensusError> {
let encoded = value.encode();
let update_res = store.insert_aux(&[(key, &encoded[..])], &[]);
if let Err(error) = update_res {
return Err(on_post_finalization_error(error, value_type));
}
Ok(())
}
/// Display inconsistency warning.
fn on_post_finalization_error(error: ClientError, value_type: &str) -> ConsensusError {
warn!(target: "finality", "Failed to write updated {} to disk. Bailing.", value_type);
warn!(target: "finality", "Node is in a potentially inconsistent state.");
ConsensusError::ClientImport(error.to_string())
}
#[cfg(test)]
pub mod tests {
use super::*;
use consensus_common::ForkChoiceStrategy;
use fg_primitives::AuthorityId;
use primitives::{H256, crypto::Public};
use test_client::client::in_mem::Blockchain as InMemoryAuxStore;
use test_client::runtime::{Block, Header};
use crate::tests::TestApi;
use crate::finality_proof::tests::TestJustification;
pub struct NoJustificationsImport<B, E, Block: BlockT<Hash=H256>, RA>(
pub GrandpaLightBlockImport<B, E, Block, RA>
);
impl<B, E, Block: BlockT<Hash=H256>, RA> Clone
for NoJustificationsImport<B, E, Block, RA> where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
fn clone(&self) -> Self {
NoJustificationsImport(self.0.clone())
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA> BlockImport<Block>
for NoJustificationsImport<B, E, Block, RA> where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
type Error = ConsensusError;
fn import_block(
&mut self,
mut block: BlockImportParams<Block>,
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
block.justification.take();
self.0.import_block(block, new_cache)
}
fn check_block(
&mut self,
block: BlockCheckParams<Block>,
) -> Result<ImportResult, Self::Error> {
self.0.check_block(block)
}
}
impl<B, E, Block: BlockT<Hash=H256>, RA> FinalityProofImport<Block>
for NoJustificationsImport<B, E, Block, RA> where
NumberFor<Block>: grandpa::BlockNumberOps,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: Send + Sync,
{
type Error = ConsensusError;
fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor<Block>)> {
self.0.on_start()
}
fn import_finality_proof(
&mut self,
hash: Block::Hash,
number: NumberFor<Block>,
finality_proof: Vec<u8>,
verifier: &mut dyn Verifier<Block>,
) -> Result<(Block::Hash, NumberFor<Block>), Self::Error> {
self.0.import_finality_proof(hash, number, finality_proof, verifier)
}
}
/// Creates light block import that ignores justifications that came outside of finality proofs.
pub fn light_block_import_without_justifications<B, E, Block: BlockT<Hash=H256>, RA>(
client: Arc<Client<B, E, Block, RA>>,
backend: Arc<B>,
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
authority_set_provider: Arc<dyn AuthoritySetForFinalityChecker<Block>>,
) -> Result<NoJustificationsImport<B, E, Block, RA>, ClientError>
where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: Send + Sync,
{
light_block_import(client, backend, genesis_authorities_provider, authority_set_provider)
.map(NoJustificationsImport)
}
fn import_block(
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
justification: Option<Justification>,
) -> ImportResult {
let (client, _backend) = test_client::new_light();
let mut import_data = LightImportData {
last_finalized: Default::default(),
authority_set: LightAuthoritySet::genesis(vec![(AuthorityId::from_slice(&[1; 32]), 1)]),
consensus_changes: ConsensusChanges::empty(),
};
let block = BlockImportParams {
origin: BlockOrigin::Own,
header: Header {
number: 1,
parent_hash: client.info().chain.best_hash,
state_root: Default::default(),
digest: Default::default(),
extrinsics_root: Default::default(),
},
justification,
post_digests: Vec::new(),
body: None,
finalized: false,
auxiliary: Vec::new(),
fork_choice: ForkChoiceStrategy::LongestChain,
allow_missing_state: true,
};
do_import_block::<_, _, _, TestJustification>(
&client,
&mut import_data,
block,
new_cache,
).unwrap()
}
#[test]
fn finality_proof_not_required_when_consensus_data_does_not_changes_and_no_justification_provided() {
assert_eq!(import_block(HashMap::new(), None), ImportResult::Imported(ImportedAux {
clear_justification_requests: false,
needs_justification: false,
bad_justification: false,
needs_finality_proof: false,
is_new_best: true,
header_only: false,
}));
}
#[test]
fn finality_proof_not_required_when_consensus_data_does_not_changes_and_correct_justification_provided() {
let justification = TestJustification(true, Vec::new()).encode();
assert_eq!(import_block(HashMap::new(), Some(justification)), ImportResult::Imported(ImportedAux {
clear_justification_requests: false,
needs_justification: false,
bad_justification: false,
needs_finality_proof: false,
is_new_best: true,
header_only: false,
}));
}
#[test]
fn finality_proof_required_when_consensus_data_changes_and_no_justification_provided() {
let mut cache = HashMap::new();
cache.insert(well_known_cache_keys::AUTHORITIES, vec![AuthorityId::from_slice(&[2; 32])].encode());
assert_eq!(import_block(cache, None), ImportResult::Imported(ImportedAux {
clear_justification_requests: false,
needs_justification: false,
bad_justification: false,
needs_finality_proof: true,
is_new_best: true,
header_only: false,
}));
}
#[test]
fn finality_proof_required_when_consensus_data_changes_and_incorrect_justification_provided() {
let justification = TestJustification(false, Vec::new()).encode();
let mut cache = HashMap::new();
cache.insert(well_known_cache_keys::AUTHORITIES, vec![AuthorityId::from_slice(&[2; 32])].encode());
assert_eq!(
import_block(cache, Some(justification)),
ImportResult::Imported(ImportedAux {
clear_justification_requests: false,
needs_justification: false,
bad_justification: false,
needs_finality_proof: true,
is_new_best: false,
header_only: false,
},
));
}
#[test]
fn aux_data_updated_on_start() {
let aux_store = InMemoryAuxStore::<Block>::new();
let api = TestApi::new(vec![(AuthorityId::from_slice(&[1; 32]), 1)]);
// when aux store is empty initially
assert!(aux_store.get_aux(LIGHT_AUTHORITY_SET_KEY).unwrap().is_none());
assert!(aux_store.get_aux(LIGHT_CONSENSUS_CHANGES_KEY).unwrap().is_none());
// it is updated on importer start
load_aux_import_data(Default::default(), &aux_store, &api).unwrap();
assert!(aux_store.get_aux(LIGHT_AUTHORITY_SET_KEY).unwrap().is_some());
assert!(aux_store.get_aux(LIGHT_CONSENSUS_CHANGES_KEY).unwrap().is_some());
}
#[test]
fn aux_data_loaded_on_restart() {
let aux_store = InMemoryAuxStore::<Block>::new();
let api = TestApi::new(vec![(AuthorityId::from_slice(&[1; 32]), 1)]);
// when aux store is non-empty initially
let mut consensus_changes = ConsensusChanges::<H256, u64>::empty();
consensus_changes.note_change((42, Default::default()));
aux_store.insert_aux(
&[
(
LIGHT_AUTHORITY_SET_KEY,
LightAuthoritySet::genesis(
vec![(AuthorityId::from_slice(&[42; 32]), 2)]
).encode().as_slice(),
),
(
LIGHT_CONSENSUS_CHANGES_KEY,
consensus_changes.encode().as_slice(),
),
],
&[],
).unwrap();
// importer uses it on start
let data = load_aux_import_data(Default::default(), &aux_store, &api).unwrap();
assert_eq!(data.authority_set.authorities(), vec![(AuthorityId::from_slice(&[42; 32]), 2)]);
assert_eq!(data.consensus_changes.pending_changes(), &[(42, Default::default())]);
}
}
@@ -0,0 +1,378 @@
// Copyright 2018-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/>.
use std::sync::Arc;
use futures::prelude::*;
use futures::{future, sync::mpsc};
use grandpa::{
BlockNumberOps, Error as GrandpaError, voter, voter_set::VoterSet
};
use log::{debug, info, warn};
use consensus_common::SelectChain;
use client_api::{CallExecutor, backend::Backend};
use client::Client;
use sr_primitives::traits::{NumberFor, Block as BlockT};
use primitives::{H256, Blake2Hasher};
use crate::{
global_communication, CommandOrError, CommunicationIn, Config, environment,
LinkHalf, Network, Error, aux_schema::PersistentData, VoterCommand, VoterSetState,
};
use crate::authorities::SharedAuthoritySet;
use crate::communication::NetworkBridge;
use crate::consensus_changes::SharedConsensusChanges;
use fg_primitives::AuthorityId;
struct ObserverChain<'a, Block: BlockT, B, E, RA>(&'a Client<B, E, Block, RA>);
impl<'a, Block: BlockT<Hash=H256>, B, E, RA> grandpa::Chain<Block::Hash, NumberFor<Block>>
for ObserverChain<'a, Block, B, E, RA> where
B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
NumberFor<Block>: BlockNumberOps,
{
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
environment::ancestry(&self.0, base, block)
}
fn best_chain_containing(&self, _block: Block::Hash) -> Option<(Block::Hash, NumberFor<Block>)> {
// only used by voter
None
}
}
fn grandpa_observer<B, E, Block: BlockT<Hash=H256>, RA, S, F>(
client: &Arc<Client<B, E, Block, RA>>,
authority_set: &SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
consensus_changes: &SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
voters: &Arc<VoterSet<AuthorityId>>,
last_finalized_number: NumberFor<Block>,
commits: S,
note_round: F,
) -> impl Future<Item=(), Error=CommandOrError<H256, NumberFor<Block>>> where
NumberFor<Block>: BlockNumberOps,
B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
RA: Send + Sync,
S: Stream<
Item = CommunicationIn<Block>,
Error = CommandOrError<Block::Hash, NumberFor<Block>>,
>,
F: Fn(u64),
{
let authority_set = authority_set.clone();
let consensus_changes = consensus_changes.clone();
let client = client.clone();
let voters = voters.clone();
let observer = commits.fold(last_finalized_number, move |last_finalized_number, global| {
let (round, commit, callback) = match global {
voter::CommunicationIn::Commit(round, commit, callback) => {
let commit = grandpa::Commit::from(commit);
(round, commit, callback)
},
voter::CommunicationIn::CatchUp(..) => {
// ignore catch up messages
return future::ok(last_finalized_number);
},
};
// if the commit we've received targets a block lower or equal to the last
// finalized, ignore it and continue with the current state
if commit.target_number <= last_finalized_number {
return future::ok(last_finalized_number);
}
let validation_result = match grandpa::validate_commit(
&commit,
&voters,
&ObserverChain(&*client),
) {
Ok(r) => r,
Err(e) => return future::err(e.into()),
};
if let Some(_) = validation_result.ghost() {
let finalized_hash = commit.target_hash;
let finalized_number = commit.target_number;
// commit is valid, finalize the block it targets
match environment::finalize_block(
&client,
&authority_set,
&consensus_changes,
None,
finalized_hash,
finalized_number,
(round, commit).into(),
) {
Ok(_) => {},
Err(e) => return future::err(e),
};
// note that we've observed completion of this round through the commit,
// and that implies that the next round has started.
note_round(round + 1);
grandpa::process_commit_validation_result(validation_result, callback);
// proceed processing with new finalized block number
future::ok(finalized_number)
} else {
debug!(target: "afg", "Received invalid commit: ({:?}, {:?})", round, commit);
grandpa::process_commit_validation_result(validation_result, callback);
// commit is invalid, continue processing commits with the current state
future::ok(last_finalized_number)
}
});
observer.map(|_| ())
}
/// Run a GRANDPA observer as a task, the observer will finalize blocks only by
/// listening for and validating GRANDPA commits instead of following the full
/// protocol. Provide configuration and a link to a block import worker that has
/// already been instantiated with `block_import`.
pub fn run_grandpa_observer<B, E, Block: BlockT<Hash=H256>, N, RA, SC>(
config: Config,
link: LinkHalf<B, E, Block, RA, SC>,
network: N,
on_exit: impl Future<Item=(),Error=()> + Clone + Send + 'static,
) -> ::client_api::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
N: Network<Block> + Send + Sync + 'static,
N::In: Send + 'static,
SC: SelectChain<Block> + 'static,
NumberFor<Block>: BlockNumberOps,
RA: Send + Sync + 'static,
{
let LinkHalf {
client,
select_chain: _,
persistent_data,
voter_commands_rx,
} = link;
let (network, network_startup) = NetworkBridge::new(
network,
config.clone(),
persistent_data.set_state.clone(),
on_exit.clone(),
);
let observer_work = ObserverWork::new(
client,
network,
persistent_data,
config.keystore.clone(),
voter_commands_rx
);
let observer_work = observer_work
.map(|_| ())
.map_err(|e| {
warn!("GRANDPA Observer failed: {:?}", e);
});
let observer_work = network_startup.and_then(move |()| observer_work);
Ok(observer_work.select(on_exit).map(|_| ()).map_err(|_| ()))
}
/// Future that powers the observer.
#[must_use]
struct ObserverWork<B: BlockT<Hash=H256>, N: Network<B>, E, Backend, RA> {
observer: Box<dyn Future<Item = (), Error = CommandOrError<B::Hash, NumberFor<B>>> + Send>,
client: Arc<Client<Backend, E, B, RA>>,
network: NetworkBridge<B, N>,
persistent_data: PersistentData<B>,
keystore: Option<keystore::KeyStorePtr>,
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<B::Hash, NumberFor<B>>>,
}
impl<B, N, E, Bk, RA> ObserverWork<B, N, E, Bk, RA>
where
B: BlockT<Hash=H256>,
N: Network<B>,
N::In: Send + 'static,
NumberFor<B>: BlockNumberOps,
RA: 'static + Send + Sync,
E: CallExecutor<B, Blake2Hasher> + Send + Sync + 'static,
Bk: Backend<B, Blake2Hasher> + 'static,
{
fn new(
client: Arc<Client<Bk, E, B, RA>>,
network: NetworkBridge<B, N>,
persistent_data: PersistentData<B>,
keystore: Option<keystore::KeyStorePtr>,
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<B::Hash, NumberFor<B>>>,
) -> Self {
let mut work = ObserverWork {
// `observer` is set to a temporary value and replaced below when
// calling `rebuild_observer`.
observer: Box::new(futures::empty()) as Box<_>,
client,
network,
persistent_data,
keystore,
voter_commands_rx,
};
work.rebuild_observer();
work
}
/// Rebuilds the `self.observer` field using the current authority set
/// state. This method should be called when we know that the authority set
/// has changed (e.g. as signalled by a voter command).
fn rebuild_observer(&mut self) {
let set_id = self.persistent_data.authority_set.set_id();
let voters = Arc::new(self.persistent_data.authority_set.current_authorities());
// start global communication stream for the current set
let (global_in, _) = global_communication(
set_id,
&voters,
&self.client,
&self.network,
&self.keystore,
);
let last_finalized_number = self.client.info().chain.finalized_number;
// NOTE: since we are not using `round_communication` we have to
// manually note the round with the gossip validator, otherwise we won't
// relay round messages. we want all full nodes to contribute to vote
// availability.
let note_round = {
let network = self.network.clone();
let voters = voters.clone();
move |round| network.note_round(
crate::communication::Round(round),
crate::communication::SetId(set_id),
&*voters,
)
};
// create observer for the current set
let observer = grandpa_observer(
&self.client,
&self.persistent_data.authority_set,
&self.persistent_data.consensus_changes,
&voters,
last_finalized_number,
global_in,
note_round,
);
self.observer = Box::new(observer);
}
fn handle_voter_command(
&mut self,
command: VoterCommand<B::Hash, NumberFor<B>>,
) -> Result<(), Error> {
// the observer doesn't use the voter set state, but we need to
// update it on-disk in case we restart as validator in the future.
self.persistent_data.set_state = match command {
VoterCommand::Pause(reason) => {
info!(target: "afg", "Pausing old validator set: {}", reason);
let completed_rounds = self.persistent_data.set_state.read().completed_rounds();
let set_state = VoterSetState::Paused { completed_rounds };
crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?;
set_state
},
VoterCommand::ChangeAuthorities(new) => {
// start the new authority set using the block where the
// set changed (not where the signal happened!) as the base.
let set_state = VoterSetState::live(
new.set_id,
&*self.persistent_data.authority_set.inner().read(),
(new.canon_hash, new.canon_number),
);
crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?;
set_state
},
}.into();
self.rebuild_observer();
Ok(())
}
}
impl<B, N, E, Bk, RA> Future for ObserverWork<B, N, E, Bk, RA>
where
B: BlockT<Hash=H256>,
N: Network<B>,
N::In: Send + 'static,
NumberFor<B>: BlockNumberOps,
RA: 'static + Send + Sync,
E: CallExecutor<B, Blake2Hasher> + Send + Sync + 'static,
Bk: Backend<B, Blake2Hasher> + 'static,
{
type Item = ();
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.observer.poll() {
Ok(Async::NotReady) => {}
Ok(Async::Ready(())) => {
// observer commit stream doesn't conclude naturally; this could reasonably be an error.
return Ok(Async::Ready(()))
}
Err(CommandOrError::Error(e)) => {
// return inner observer error
return Err(e)
}
Err(CommandOrError::VoterCommand(command)) => {
// some command issued internally
self.handle_voter_command(command)?;
futures::task::current().notify();
}
}
match self.voter_commands_rx.poll() {
Ok(Async::NotReady) => {}
Err(_) => {
// the `voter_commands_rx` stream should not fail.
return Ok(Async::Ready(()))
}
Ok(Async::Ready(None)) => {
// the `voter_commands_rx` stream should never conclude since it's never closed.
return Ok(Async::Ready(()))
}
Ok(Async::Ready(Some(command))) => {
// some command issued externally
self.handle_voter_command(command)?;
futures::task::current().notify();
}
}
Ok(Async::NotReady)
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,940 @@
// 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/>.
//! Helper stream for waiting until one or more blocks are imported before
//! passing through inner items. This is done in a generic way to support
//! many different kinds of items.
//!
//! This is used for votes and commit messages currently.
use super::{
BlockStatus as BlockStatusT,
BlockSyncRequester as BlockSyncRequesterT,
CommunicationIn,
Error,
SignedMessage,
};
use log::{debug, warn};
use client_api::{BlockImportNotification, ImportNotifications};
use futures::prelude::*;
use futures::stream::Fuse;
use futures03::{StreamExt as _, TryStreamExt as _};
use grandpa::voter;
use parking_lot::Mutex;
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use tokio_timer::Interval;
use std::collections::{HashMap, VecDeque};
use std::sync::{atomic::{AtomicUsize, Ordering}, Arc};
use std::time::{Duration, Instant};
use fg_primitives::AuthorityId;
const LOG_PENDING_INTERVAL: Duration = Duration::from_secs(15);
// something which will block until imported.
pub(crate) trait BlockUntilImported<Block: BlockT>: Sized {
// the type that is blocked on.
type Blocked;
/// new incoming item. For all internal items,
/// check if they require to be waited for.
/// if so, call the `Wait` closure.
/// if they are ready, call the `Ready` closure.
fn schedule_wait<S, Wait, Ready>(
input: Self::Blocked,
status_check: &S,
wait: Wait,
ready: Ready,
) -> Result<(), Error> where
S: BlockStatusT<Block>,
Wait: FnMut(Block::Hash, NumberFor<Block>, Self),
Ready: FnMut(Self::Blocked);
/// called when the wait has completed. The canonical number is passed through
/// for further checks.
fn wait_completed(self, canon_number: NumberFor<Block>) -> Option<Self::Blocked>;
}
/// Buffering imported messages until blocks with given hashes are imported.
pub(crate) struct UntilImported<Block: BlockT, BlockStatus, BlockSyncRequester, I, M: BlockUntilImported<Block>> {
import_notifications: Fuse<Box<dyn Stream<Item = BlockImportNotification<Block>, Error = ()> + Send>>,
block_sync_requester: BlockSyncRequester,
status_check: BlockStatus,
inner: Fuse<I>,
ready: VecDeque<M::Blocked>,
check_pending: Interval,
/// Mapping block hashes to their block number, the point in time it was
/// first encountered (Instant) and a list of GRANDPA messages referencing
/// the block hash.
pending: HashMap<Block::Hash, (NumberFor<Block>, Instant, Vec<M>)>,
identifier: &'static str,
}
impl<Block, BlockStatus, BlockSyncRequester, I, M> UntilImported<Block, BlockStatus, BlockSyncRequester, I, M> where
Block: BlockT,
BlockStatus: BlockStatusT<Block>,
M: BlockUntilImported<Block>,
I: Stream,
{
/// Create a new `UntilImported` wrapper.
pub(crate) fn new(
import_notifications: ImportNotifications<Block>,
block_sync_requester: BlockSyncRequester,
status_check: BlockStatus,
stream: I,
identifier: &'static str,
) -> Self {
// how often to check if pending messages that are waiting for blocks to be
// imported can be checked.
//
// the import notifications interval takes care of most of this; this is
// used in the event of missed import notifications
const CHECK_PENDING_INTERVAL: Duration = Duration::from_secs(5);
let now = Instant::now();
let check_pending = Interval::new(now + CHECK_PENDING_INTERVAL, CHECK_PENDING_INTERVAL);
UntilImported {
import_notifications: {
let stream = import_notifications.map::<_, fn(_) -> _>(|v| Ok::<_, ()>(v)).compat();
Box::new(stream) as Box<dyn Stream<Item = _, Error = _> + Send>
}.fuse(),
block_sync_requester,
status_check,
inner: stream.fuse(),
ready: VecDeque::new(),
check_pending,
pending: HashMap::new(),
identifier,
}
}
}
impl<Block, BStatus, BSyncRequester, I, M> Stream for UntilImported<Block, BStatus, BSyncRequester, I, M> where
Block: BlockT,
BStatus: BlockStatusT<Block>,
BSyncRequester: BlockSyncRequesterT<Block>,
I: Stream<Item=M::Blocked,Error=Error>,
M: BlockUntilImported<Block>,
{
type Item = M::Blocked;
type Error = Error;
fn poll(&mut self) -> Poll<Option<M::Blocked>, Error> {
loop {
match self.inner.poll()? {
Async::Ready(None) => return Ok(Async::Ready(None)),
Async::Ready(Some(input)) => {
// new input: schedule wait of any parts which require
// blocks to be known.
let ready = &mut self.ready;
let pending = &mut self.pending;
M::schedule_wait(
input,
&self.status_check,
|target_hash, target_number, wait| pending
.entry(target_hash)
.or_insert_with(|| (target_number, Instant::now(), Vec::new()))
.2
.push(wait),
|ready_item| ready.push_back(ready_item),
)?;
}
Async::NotReady => break,
}
}
loop {
match self.import_notifications.poll() {
Err(_) => return Err(Error::Network(format!("Failed to get new message"))),
Ok(Async::Ready(None)) => return Ok(Async::Ready(None)),
Ok(Async::Ready(Some(notification))) => {
// new block imported. queue up all messages tied to that hash.
if let Some((_, _, messages)) = self.pending.remove(&notification.hash) {
let canon_number = notification.header.number().clone();
let ready_messages = messages.into_iter()
.filter_map(|m| m.wait_completed(canon_number));
self.ready.extend(ready_messages);
}
}
Ok(Async::NotReady) => break,
}
}
let mut update_interval = false;
while let Async::Ready(Some(_)) = self.check_pending.poll().map_err(Error::Timer)? {
update_interval = true;
}
if update_interval {
let mut known_keys = Vec::new();
for (&block_hash, &mut (block_number, ref mut last_log, ref v)) in &mut self.pending {
if let Some(number) = self.status_check.block_number(block_hash)? {
known_keys.push((block_hash, number));
} else {
let next_log = *last_log + LOG_PENDING_INTERVAL;
if Instant::now() >= next_log {
debug!(
target: "afg",
"Waiting to import block {} before {} {} messages can be imported. \
Requesting network sync service to retrieve block from. \
Possible fork?",
block_hash,
v.len(),
self.identifier,
);
// NOTE: when sending an empty vec of peers the
// underlying should make a best effort to sync the
// block from any peers it knows about.
self.block_sync_requester.set_sync_fork_request(
vec![],
block_hash,
block_number,
);
*last_log = next_log;
}
}
}
for (known_hash, canon_number) in known_keys {
if let Some((_, _, pending_messages)) = self.pending.remove(&known_hash) {
let ready_messages = pending_messages.into_iter()
.filter_map(|m| m.wait_completed(canon_number));
self.ready.extend(ready_messages);
}
}
}
if let Some(ready) = self.ready.pop_front() {
return Ok(Async::Ready(Some(ready)))
}
if self.import_notifications.is_done() && self.inner.is_done() {
Ok(Async::Ready(None))
} else {
Ok(Async::NotReady)
}
}
}
fn warn_authority_wrong_target<H: ::std::fmt::Display>(hash: H, id: AuthorityId) {
warn!(
target: "afg",
"Authority {:?} signed GRANDPA message with \
wrong block number for hash {}",
id,
hash,
);
}
impl<Block: BlockT> BlockUntilImported<Block> for SignedMessage<Block> {
type Blocked = Self;
fn schedule_wait<BlockStatus, Wait, Ready>(
msg: Self::Blocked,
status_check: &BlockStatus,
mut wait: Wait,
mut ready: Ready,
) -> Result<(), Error> where
BlockStatus: BlockStatusT<Block>,
Wait: FnMut(Block::Hash, NumberFor<Block>, Self),
Ready: FnMut(Self::Blocked),
{
let (&target_hash, target_number) = msg.target();
if let Some(number) = status_check.block_number(target_hash)? {
if number != target_number {
warn_authority_wrong_target(target_hash, msg.id);
} else {
ready(msg);
}
} else {
wait(target_hash, target_number, msg)
}
Ok(())
}
fn wait_completed(self, canon_number: NumberFor<Block>) -> Option<Self::Blocked> {
let (&target_hash, target_number) = self.target();
if canon_number != target_number {
warn_authority_wrong_target(target_hash, self.id);
None
} else {
Some(self)
}
}
}
/// Helper type definition for the stream which waits until vote targets for
/// signed messages are imported.
pub(crate) type UntilVoteTargetImported<Block, BlockStatus, BlockSyncRequester, I> = UntilImported<
Block,
BlockStatus,
BlockSyncRequester,
I,
SignedMessage<Block>,
>;
/// This blocks a global message import, i.e. a commit or catch up messages,
/// until all blocks referenced in its votes are known.
///
/// This is used for compact commits and catch up messages which have already
/// been checked for structural soundness (e.g. valid signatures).
pub(crate) struct BlockGlobalMessage<Block: BlockT> {
inner: Arc<(AtomicUsize, Mutex<Option<CommunicationIn<Block>>>)>,
target_number: NumberFor<Block>,
}
impl<Block: BlockT> BlockUntilImported<Block> for BlockGlobalMessage<Block> {
type Blocked = CommunicationIn<Block>;
fn schedule_wait<BlockStatus, Wait, Ready>(
input: Self::Blocked,
status_check: &BlockStatus,
mut wait: Wait,
mut ready: Ready,
) -> Result<(), Error> where
BlockStatus: BlockStatusT<Block>,
Wait: FnMut(Block::Hash, NumberFor<Block>, Self),
Ready: FnMut(Self::Blocked),
{
use std::collections::hash_map::Entry;
enum KnownOrUnknown<N> {
Known(N),
Unknown(N),
}
impl<N> KnownOrUnknown<N> {
fn number(&self) -> &N {
match *self {
KnownOrUnknown::Known(ref n) => n,
KnownOrUnknown::Unknown(ref n) => n,
}
}
}
let mut checked_hashes: HashMap<_, KnownOrUnknown<NumberFor<Block>>> = HashMap::new();
let mut unknown_count = 0;
{
// returns false when should early exit.
let mut query_known = |target_hash, perceived_number| -> Result<bool, Error> {
// check integrity: all votes for same hash have same number.
let canon_number = match checked_hashes.entry(target_hash) {
Entry::Occupied(entry) => entry.get().number().clone(),
Entry::Vacant(entry) => {
if let Some(number) = status_check.block_number(target_hash)? {
entry.insert(KnownOrUnknown::Known(number));
number
} else {
entry.insert(KnownOrUnknown::Unknown(perceived_number));
unknown_count += 1;
perceived_number
}
}
};
if canon_number != perceived_number {
// invalid global message: messages targeting wrong number
// or at least different from other vote in same global
// message.
return Ok(false);
}
Ok(true)
};
match input {
voter::CommunicationIn::Commit(_, ref commit, ..) => {
// add known hashes from all precommits.
let precommit_targets = commit.precommits
.iter()
.map(|c| (c.target_number, c.target_hash));
for (target_number, target_hash) in precommit_targets {
if !query_known(target_hash, target_number)? {
return Ok(())
}
}
},
voter::CommunicationIn::CatchUp(ref catch_up, ..) => {
// add known hashes from all prevotes and precommits.
let prevote_targets = catch_up.prevotes
.iter()
.map(|s| (s.prevote.target_number, s.prevote.target_hash));
let precommit_targets = catch_up.precommits
.iter()
.map(|s| (s.precommit.target_number, s.precommit.target_hash));
let targets = prevote_targets.chain(precommit_targets);
for (target_number, target_hash) in targets {
if !query_known(target_hash, target_number)? {
return Ok(())
}
}
},
};
}
// none of the hashes in the global message were unknown.
// we can just return the message directly.
if unknown_count == 0 {
ready(input);
return Ok(())
}
let locked_global = Arc::new((AtomicUsize::new(unknown_count), Mutex::new(Some(input))));
// schedule waits for all unknown messages.
// when the last one of these has `wait_completed` called on it,
// the global message will be returned.
//
// in the future, we may want to issue sync requests to the network
// if this is taking a long time.
for (hash, is_known) in checked_hashes {
if let KnownOrUnknown::Unknown(target_number) = is_known {
wait(hash, target_number, BlockGlobalMessage {
inner: locked_global.clone(),
target_number,
})
}
}
Ok(())
}
fn wait_completed(self, canon_number: NumberFor<Block>) -> Option<Self::Blocked> {
if self.target_number != canon_number {
// if we return without deducting the counter, then none of the other
// handles can return the commit message.
return None;
}
let mut last_count = self.inner.0.load(Ordering::Acquire);
// CAS loop to ensure that we always have a last reader.
loop {
if last_count == 1 { // we are the last one left.
return self.inner.1.lock().take();
}
let prev_value = self.inner.0.compare_and_swap(
last_count,
last_count - 1,
Ordering::SeqCst,
);
if prev_value == last_count {
return None;
} else {
last_count = prev_value;
}
}
}
}
/// A stream which gates off incoming global messages, i.e. commit and catch up
/// messages, until all referenced block hashes have been imported.
pub(crate) type UntilGlobalMessageBlocksImported<Block, BlockStatus, BlockSyncRequester, I> = UntilImported<
Block,
BlockStatus,
BlockSyncRequester,
I,
BlockGlobalMessage<Block>,
>;
#[cfg(test)]
mod tests {
use super::*;
use crate::{CatchUp, CompactCommit};
use tokio::runtime::current_thread::Runtime;
use tokio_timer::Delay;
use test_client::runtime::{Block, Hash, Header};
use consensus_common::BlockOrigin;
use client_api::BlockImportNotification;
use futures::future::Either;
use futures03::channel::mpsc;
use grandpa::Precommit;
#[derive(Clone)]
struct TestChainState {
sender: mpsc::UnboundedSender<BlockImportNotification<Block>>,
known_blocks: Arc<Mutex<HashMap<Hash, u64>>>,
}
impl TestChainState {
fn new() -> (Self, ImportNotifications<Block>) {
let (tx, rx) = mpsc::unbounded();
let state = TestChainState {
sender: tx,
known_blocks: Arc::new(Mutex::new(HashMap::new())),
};
(state, rx)
}
fn block_status(&self) -> TestBlockStatus {
TestBlockStatus { inner: self.known_blocks.clone() }
}
fn import_header(&self, header: Header) {
let hash = header.hash();
let number = header.number().clone();
self.known_blocks.lock().insert(hash, number);
self.sender.unbounded_send(BlockImportNotification {
hash,
origin: BlockOrigin::File,
header,
is_new_best: false,
retracted: vec![],
}).unwrap();
}
}
struct TestBlockStatus {
inner: Arc<Mutex<HashMap<Hash, u64>>>,
}
impl BlockStatusT<Block> for TestBlockStatus {
fn block_number(&self, hash: Hash) -> Result<Option<u64>, Error> {
Ok(self.inner.lock().get(&hash).map(|x| x.clone()))
}
}
#[derive(Clone)]
struct TestBlockSyncRequester {
requests: Arc<Mutex<Vec<(Hash, NumberFor<Block>)>>>,
}
impl Default for TestBlockSyncRequester {
fn default() -> Self {
TestBlockSyncRequester {
requests: Arc::new(Mutex::new(Vec::new())),
}
}
}
impl BlockSyncRequesterT<Block> for TestBlockSyncRequester {
fn set_sync_fork_request(&self, _peers: Vec<network::PeerId>, hash: Hash, number: NumberFor<Block>) {
self.requests.lock().push((hash, number));
}
}
fn make_header(number: u64) -> Header {
Header::new(
number,
Default::default(),
Default::default(),
Default::default(),
Default::default(),
)
}
// unwrap the commit from `CommunicationIn` returning its fields in a tuple,
// panics if the given message isn't a commit
fn unapply_commit(msg: CommunicationIn<Block>) -> (u64, CompactCommit::<Block>) {
match msg {
voter::CommunicationIn::Commit(round, commit, ..) => (round, commit),
_ => panic!("expected commit"),
}
}
// unwrap the catch up from `CommunicationIn` returning its inner representation,
// panics if the given message isn't a catch up
fn unapply_catch_up(msg: CommunicationIn<Block>) -> CatchUp<Block> {
match msg {
voter::CommunicationIn::CatchUp(catch_up, ..) => catch_up,
_ => panic!("expected catch up"),
}
}
fn message_all_dependencies_satisfied<F>(
msg: CommunicationIn<Block>,
enact_dependencies: F,
) -> CommunicationIn<Block> where
F: FnOnce(&TestChainState),
{
let (chain_state, import_notifications) = TestChainState::new();
let block_status = chain_state.block_status();
// enact all dependencies before importing the message
enact_dependencies(&chain_state);
let (global_tx, global_rx) = futures::sync::mpsc::unbounded();
let until_imported = UntilGlobalMessageBlocksImported::new(
import_notifications,
TestBlockSyncRequester::default(),
block_status,
global_rx.map_err(|_| panic!("should never error")),
"global",
);
global_tx.unbounded_send(msg).unwrap();
let work = until_imported.into_future();
let mut runtime = Runtime::new().unwrap();
runtime.block_on(work).map_err(|(e, _)| e).unwrap().0.unwrap()
}
fn blocking_message_on_dependencies<F>(
msg: CommunicationIn<Block>,
enact_dependencies: F,
) -> CommunicationIn<Block> where
F: FnOnce(&TestChainState),
{
let (chain_state, import_notifications) = TestChainState::new();
let block_status = chain_state.block_status();
let (global_tx, global_rx) = futures::sync::mpsc::unbounded();
let until_imported = UntilGlobalMessageBlocksImported::new(
import_notifications,
TestBlockSyncRequester::default(),
block_status,
global_rx.map_err(|_| panic!("should never error")),
"global",
);
global_tx.unbounded_send(msg).unwrap();
// NOTE: needs to be cloned otherwise it is moved to the stream and
// dropped too early.
let inner_chain_state = chain_state.clone();
let work = until_imported
.into_future()
.select2(Delay::new(Instant::now() + Duration::from_millis(100)))
.then(move |res| match res {
Err(_) => panic!("neither should have had error"),
Ok(Either::A(_)) => panic!("timeout should have fired first"),
Ok(Either::B((_, until_imported))) => {
// timeout fired. push in the headers.
enact_dependencies(&inner_chain_state);
until_imported
}
});
let mut runtime = Runtime::new().unwrap();
runtime.block_on(work).map_err(|(e, _)| e).unwrap().0.unwrap()
}
#[test]
fn blocking_commit_message() {
let h1 = make_header(5);
let h2 = make_header(6);
let h3 = make_header(7);
let unknown_commit = CompactCommit::<Block> {
target_hash: h1.hash(),
target_number: 5,
precommits: vec![
Precommit {
target_hash: h2.hash(),
target_number: 6,
},
Precommit {
target_hash: h3.hash(),
target_number: 7,
},
],
auth_data: Vec::new(), // not used
};
let unknown_commit = || voter::CommunicationIn::Commit(
0,
unknown_commit.clone(),
voter::Callback::Blank,
);
let res = blocking_message_on_dependencies(
unknown_commit(),
|chain_state| {
chain_state.import_header(h1);
chain_state.import_header(h2);
chain_state.import_header(h3);
},
);
assert_eq!(
unapply_commit(res),
unapply_commit(unknown_commit()),
);
}
#[test]
fn commit_message_all_known() {
let h1 = make_header(5);
let h2 = make_header(6);
let h3 = make_header(7);
let known_commit = CompactCommit::<Block> {
target_hash: h1.hash(),
target_number: 5,
precommits: vec![
Precommit {
target_hash: h2.hash(),
target_number: 6,
},
Precommit {
target_hash: h3.hash(),
target_number: 7,
},
],
auth_data: Vec::new(), // not used
};
let known_commit = || voter::CommunicationIn::Commit(
0,
known_commit.clone(),
voter::Callback::Blank,
);
let res = message_all_dependencies_satisfied(
known_commit(),
|chain_state| {
chain_state.import_header(h1);
chain_state.import_header(h2);
chain_state.import_header(h3);
},
);
assert_eq!(
unapply_commit(res),
unapply_commit(known_commit()),
);
}
#[test]
fn blocking_catch_up_message() {
let h1 = make_header(5);
let h2 = make_header(6);
let h3 = make_header(7);
let signed_prevote = |header: &Header| {
grandpa::SignedPrevote {
id: Default::default(),
signature: Default::default(),
prevote: grandpa::Prevote {
target_hash: header.hash(),
target_number: *header.number(),
},
}
};
let signed_precommit = |header: &Header| {
grandpa::SignedPrecommit {
id: Default::default(),
signature: Default::default(),
precommit: grandpa::Precommit {
target_hash: header.hash(),
target_number: *header.number(),
},
}
};
let prevotes = vec![
signed_prevote(&h1),
signed_prevote(&h3),
];
let precommits = vec![
signed_precommit(&h1),
signed_precommit(&h2),
];
let unknown_catch_up = grandpa::CatchUp {
round_number: 1,
prevotes,
precommits,
base_hash: h1.hash(),
base_number: *h1.number(),
};
let unknown_catch_up = || voter::CommunicationIn::CatchUp(
unknown_catch_up.clone(),
voter::Callback::Blank,
);
let res = blocking_message_on_dependencies(
unknown_catch_up(),
|chain_state| {
chain_state.import_header(h1);
chain_state.import_header(h2);
chain_state.import_header(h3);
},
);
assert_eq!(
unapply_catch_up(res),
unapply_catch_up(unknown_catch_up()),
);
}
#[test]
fn catch_up_message_all_known() {
let h1 = make_header(5);
let h2 = make_header(6);
let h3 = make_header(7);
let signed_prevote = |header: &Header| {
grandpa::SignedPrevote {
id: Default::default(),
signature: Default::default(),
prevote: grandpa::Prevote {
target_hash: header.hash(),
target_number: *header.number(),
},
}
};
let signed_precommit = |header: &Header| {
grandpa::SignedPrecommit {
id: Default::default(),
signature: Default::default(),
precommit: grandpa::Precommit {
target_hash: header.hash(),
target_number: *header.number(),
},
}
};
let prevotes = vec![
signed_prevote(&h1),
signed_prevote(&h3),
];
let precommits = vec![
signed_precommit(&h1),
signed_precommit(&h2),
];
let unknown_catch_up = grandpa::CatchUp {
round_number: 1,
prevotes,
precommits,
base_hash: h1.hash(),
base_number: *h1.number(),
};
let unknown_catch_up = || voter::CommunicationIn::CatchUp(
unknown_catch_up.clone(),
voter::Callback::Blank,
);
let res = message_all_dependencies_satisfied(
unknown_catch_up(),
|chain_state| {
chain_state.import_header(h1);
chain_state.import_header(h2);
chain_state.import_header(h3);
},
);
assert_eq!(
unapply_catch_up(res),
unapply_catch_up(unknown_catch_up()),
);
}
#[test]
fn request_block_sync_for_needed_blocks() {
let (chain_state, import_notifications) = TestChainState::new();
let block_status = chain_state.block_status();
let (global_tx, global_rx) = futures::sync::mpsc::unbounded();
let block_sync_requester = TestBlockSyncRequester::default();
let until_imported = UntilGlobalMessageBlocksImported::new(
import_notifications,
block_sync_requester.clone(),
block_status,
global_rx.map_err(|_| panic!("should never error")),
"global",
);
let h1 = make_header(5);
let h2 = make_header(6);
let h3 = make_header(7);
// we create a commit message, with precommits for blocks 6 and 7 which
// we haven't imported.
let unknown_commit = CompactCommit::<Block> {
target_hash: h1.hash(),
target_number: 5,
precommits: vec![
Precommit {
target_hash: h2.hash(),
target_number: 6,
},
Precommit {
target_hash: h3.hash(),
target_number: 7,
},
],
auth_data: Vec::new(), // not used
};
let unknown_commit = || voter::CommunicationIn::Commit(
0,
unknown_commit.clone(),
voter::Callback::Blank,
);
// we send the commit message and spawn the until_imported stream
global_tx.unbounded_send(unknown_commit()).unwrap();
let mut runtime = Runtime::new().unwrap();
runtime.spawn(until_imported.into_future().map(|_| ()).map_err(|_| ()));
// assert that we will make sync requests
let assert = futures::future::poll_fn::<(), (), _>(|| {
let block_sync_requests = block_sync_requester.requests.lock();
// we request blocks targeted by the precommits that aren't imported
if block_sync_requests.contains(&(h2.hash(), *h2.number())) &&
block_sync_requests.contains(&(h3.hash(), *h3.number()))
{
return Ok(Async::Ready(()));
}
Ok(Async::NotReady)
});
// the `until_imported` stream doesn't request the blocks immediately,
// but it should request them after a small timeout
let timeout = Delay::new(Instant::now() + Duration::from_secs(60));
let test = assert.select2(timeout).map(|res| match res {
Either::A(_) => {},
Either::B(_) => panic!("timed out waiting for block sync request"),
}).map_err(|_| ());
runtime.block_on(test).unwrap();
}
}
@@ -0,0 +1,270 @@
// Copyright 2018-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/>.
//! Handling custom voting rules for GRANDPA.
//!
//! This exposes the `VotingRule` trait used to implement arbitrary voting
//! restrictions that are taken into account by the GRANDPA environment when
//! selecting a finality target to vote on.
use std::sync::Arc;
use client_api::blockchain::HeaderBackend;
use sr_primitives::generic::BlockId;
use sr_primitives::traits::{Block as BlockT, Header, NumberFor, One, Zero};
/// A trait for custom voting rules in GRANDPA.
pub trait VotingRule<Block, B>: Send + Sync where
Block: BlockT,
B: HeaderBackend<Block>,
{
/// Restrict the given `current_target` vote, returning the block hash and
/// number of the block to vote on, and `None` in case the vote should not
/// be restricted. `base` is the block that we're basing our votes on in
/// order to pick our target (e.g. last round estimate), and `best_target`
/// is the initial best vote target before any vote rules were applied. When
/// applying multiple `VotingRule`s both `base` and `best_target` should
/// remain unchanged.
///
/// The contract of this interface requires that when restricting a vote, the
/// returned value **must** be an ancestor of the given `current_target`,
/// this also means that a variant must be maintained throughout the
/// execution of voting rules wherein `current_target <= best_target`.
fn restrict_vote(
&self,
backend: &B,
base: &Block::Header,
best_target: &Block::Header,
current_target: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)>;
}
impl<Block, B> VotingRule<Block, B> for () where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn restrict_vote(
&self,
_backend: &B,
_base: &Block::Header,
_best_target: &Block::Header,
_current_target: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)> {
None
}
}
/// A custom voting rule that guarantees that our vote is always behind the best
/// block, in the best case exactly one block behind it.
#[derive(Clone)]
pub struct BeforeBestBlock;
impl<Block, B> VotingRule<Block, B> for BeforeBestBlock where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn restrict_vote(
&self,
_backend: &B,
_base: &Block::Header,
best_target: &Block::Header,
current_target: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)> {
if current_target.number().is_zero() {
return None;
}
if current_target.number() == best_target.number() {
return Some((
current_target.parent_hash().clone(),
*current_target.number() - One::one(),
));
}
None
}
}
/// A custom voting rule that limits votes towards 3/4 of the unfinalized chain,
/// using the given `base` and `best_target` to figure where the 3/4 target
/// should fall.
pub struct ThreeQuartersOfTheUnfinalizedChain;
impl<Block, B> VotingRule<Block, B> for ThreeQuartersOfTheUnfinalizedChain where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn restrict_vote(
&self,
backend: &B,
base: &Block::Header,
best_target: &Block::Header,
current_target: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)> {
// target a vote towards 3/4 of the unfinalized chain (rounding up)
let target_number = {
let two = NumberFor::<Block>::one() + One::one();
let three = two + One::one();
let four = three + One::one();
let diff = *best_target.number() - *base.number();
let diff = ((diff * three) + two) / four;
*base.number() + diff
};
// our current target is already lower than this rule would restrict
if target_number >= *current_target.number() {
return None;
}
let mut target_header = current_target.clone();
let mut target_hash = current_target.hash();
// walk backwards until we find the target block
loop {
if *target_header.number() < target_number {
unreachable!(
"we are traversing backwards from a known block; \
blocks are stored contiguously; \
qed"
);
}
if *target_header.number() == target_number {
return Some((target_hash, target_number));
}
target_hash = *target_header.parent_hash();
target_header = backend.header(BlockId::Hash(target_hash)).ok()?
.expect("Header known to exist due to the existence of one of its descendents; qed");
}
}
}
struct VotingRules<Block, B> {
rules: Arc<Vec<Box<dyn VotingRule<Block, B>>>>,
}
impl<B, Block> Clone for VotingRules<B, Block> {
fn clone(&self) -> Self {
VotingRules {
rules: self.rules.clone(),
}
}
}
impl<Block, B> VotingRule<Block, B> for VotingRules<Block, B> where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn restrict_vote(
&self,
backend: &B,
base: &Block::Header,
best_target: &Block::Header,
current_target: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)> {
let restricted_target = self.rules.iter().fold(
current_target.clone(),
|current_target, rule| {
rule.restrict_vote(
backend,
base,
best_target,
&current_target,
)
.and_then(|(hash, _)| backend.header(BlockId::Hash(hash)).ok())
.and_then(std::convert::identity)
.unwrap_or(current_target)
},
);
let restricted_hash = restricted_target.hash();
if restricted_hash != current_target.hash() {
Some((restricted_hash, *restricted_target.number()))
} else {
None
}
}
}
/// A builder of a composite voting rule that applies a set of rules to
/// progressively restrict the vote.
pub struct VotingRulesBuilder<Block, B> {
rules: Vec<Box<dyn VotingRule<Block, B>>>,
}
impl<Block, B> Default for VotingRulesBuilder<Block, B> where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn default() -> Self {
VotingRulesBuilder::new()
.add(BeforeBestBlock)
.add(ThreeQuartersOfTheUnfinalizedChain)
}
}
impl<Block, B> VotingRulesBuilder<Block, B> where
Block: BlockT,
B: HeaderBackend<Block>,
{
/// Return a new voting rule builder using the given backend.
pub fn new() -> Self {
VotingRulesBuilder {
rules: Vec::new(),
}
}
/// Add a new voting rule to the builder.
pub fn add<R>(mut self, rule: R) -> Self where
R: VotingRule<Block, B> + 'static,
{
self.rules.push(Box::new(rule));
self
}
/// Add all given voting rules to the builder.
pub fn add_all<I>(mut self, rules: I) -> Self where
I: IntoIterator<Item=Box<dyn VotingRule<Block, B>>>,
{
self.rules.extend(rules);
self
}
/// Return a new `VotingRule` that applies all of the previously added
/// voting rules in-order.
pub fn build(self) -> impl VotingRule<Block, B> + Clone {
VotingRules {
rules: Arc::new(self.rules),
}
}
}
impl<Block, B> VotingRule<Block, B> for Box<dyn VotingRule<Block, B>> where
Block: BlockT,
B: HeaderBackend<Block>,
{
fn restrict_vote(
&self,
backend: &B,
base: &Block::Header,
best_target: &Block::Header,
current_target: &Block::Header,
) -> Option<(Block::Hash, NumberFor<Block>)> {
(**self).restrict_vote(backend, base, best_target, current_target)
}
}