From 515153fa855e3163c78ad515873eabf3f34da805 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Sun, 21 Oct 2018 11:11:53 +0200 Subject: [PATCH] localize messages to set-index and add pending changes --- substrate/Cargo.lock | 1 + substrate/core/finality-grandpa/Cargo.toml | 2 + .../core/finality-grandpa/src/authorities.rs | 98 +++++++++++++++++++ substrate/core/finality-grandpa/src/lib.rs | 41 ++++++-- 4 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 substrate/core/finality-grandpa/src/authorities.rs diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 34c1f606c9..6377a2a15c 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -3027,6 +3027,7 @@ dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-client 0.1.0", diff --git a/substrate/core/finality-grandpa/Cargo.toml b/substrate/core/finality-grandpa/Cargo.toml index b507ffd6d7..88b11d7c75 100644 --- a/substrate/core/finality-grandpa/Cargo.toml +++ b/substrate/core/finality-grandpa/Cargo.toml @@ -6,9 +6,11 @@ authors = ["Parity Technologies "] [dependencies] futures = "0.1.17" parity-codec = "2.1" +parity-codec-derive = "2.0" sr-primitives = { path = "../sr-primitives" } substrate-primitives = { path = "../primitives" } substrate-client = { path = "../client" } +substrate-network = { path = "../network" } log = "0.4" tokio = "0.1.7" diff --git a/substrate/core/finality-grandpa/src/authorities.rs b/substrate/core/finality-grandpa/src/authorities.rs new file mode 100644 index 0000000000..31d61ef5e1 --- /dev/null +++ b/substrate/core/finality-grandpa/src/authorities.rs @@ -0,0 +1,98 @@ +// Copyright 2018 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 . + +//! Utilities for dealing with authorities, authority sets, and handoffs. + +use substrate_primitives::AuthorityId; +use std::ops::Add; + +/// A shared authority set. +pub(crate) struct SharedAuthoritySet { + inner: RwLock>, +} + +impl SharedAuthoritySet { + /// The genesis authority set. + pub(crate) fn genesis(initial: Vec<(AuthorityId, usize)>) -> Self { + SharedAuthoritySet { + inner: RwLock::new(AuthoritySet { + current_authorities: initial, + set_id: 0, + pending_changes: Vec::new(), + }) + } + } + + /// Execute some work using the inner authority set. + pub(crate) fn with(&self, f: F) -> U + where F: FnOnce(&AuthoritySet) -> U + { + f(&*self.inner.read()) + } + +impl SharedAuthoritySet { + /// Note an upcoming pending transition. + pub(crate) fn add_pending_change(&self, pending: PendingChange) { + let idx = self.pending_changes + .binary_search_by_key(|change| change.effective_number()) + .unwrap_or_else(|i| i); + + self.pending_changes.insert(idx); + } + + /// Get the earliest limit-block number, if any. + pub(crate) fn current_limit(&self) -> Option<&N> { + self.pending_changes.get(0).map(|change| &change.effective_number()); + } +} + +impl From> for SharedAuthoritySet { + fn from(set: AuthoritySet) -> Self { + SharedAuthoritySet { inner: RwLock::new(set) } + } +} + +/// A set of authorities. +#[derive(Encode, Decode)] +pub(crate) struct AuthoritySet { + current_authorities: Vec<(AuthorityId, usize)>, + set_id: u64, + pending_changes: Vec>, +} + +/// A pending change to the authority set. +/// +/// This will be applied when the announcing block is at some depth within +/// the finalized chain. +#[derive(Encode, Decode)] +pub(crate) struct PendingChange { + /// The new authorities and weights to apply. + pub(crate) next_authorities: Vec<(AuthorityId, usize)>, + /// How deep in the finalized chain the announcing block must be + /// before the change is applied. + pub(crate) finalization_depth: N, + /// The announcing block's height. + pub(crate) canon_height: N, + /// The announcing block's hash. + pub(crate) canon_hash: H, +} + +impl PendingChange { + /// Returns the effective number. + fn effective_number(&self) -> N { + self.canon_height + self.finalization_depth + } +} diff --git a/substrate/core/finality-grandpa/src/lib.rs b/substrate/core/finality-grandpa/src/lib.rs index 8296502058..2a6e7814e4 100644 --- a/substrate/core/finality-grandpa/src/lib.rs +++ b/substrate/core/finality-grandpa/src/lib.rs @@ -23,6 +23,7 @@ extern crate futures; extern crate substrate_client as client; extern crate sr_primitives as runtime_primitives; extern crate substrate_primitives; +extern crate substrate_network as network; extern crate tokio; extern crate parity_codec as codec; @@ -38,6 +39,9 @@ extern crate parking_lot; #[cfg(test)] extern crate substrate_keyring as keyring; +#[macro_use] +extern crate parity_codec_derive; + use futures::prelude::*; use futures::stream::Fuse; use futures::sync::mpsc; @@ -57,7 +61,13 @@ use std::collections::{VecDeque, HashMap}; use std::sync::Arc; use std::time::{Instant, Duration}; +mod authorities; + const LAST_COMPLETED_KEY: &[u8] = b"grandpa_completed_round"; +const AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters"; + +/// round-number, round-state, set indicator +type LastCompleted = (u64, RoundState, u64); /// A GRANDPA message for a substrate chain. pub type Message = grandpa::Message<::Hash>; @@ -69,8 +79,7 @@ pub struct Config { /// The expected duration for a message to be gossiped across the network. pub gossip_duration: Duration, /// The voters. - // TODO: make dynamic - pub voters: Vec, + pub genesis_voters: Vec, /// The local signing key. pub local_key: Option>, } @@ -298,15 +307,23 @@ impl Drop for ClearOnDrop { } } -fn round_localized_payload(round: u64, message: &E) -> Vec { +fn localized_payload(round: u64, set_id: u64, message: &E) -> Vec { let mut v = message.encode(); + round.using_encoded(|s| v.extend(s)); + set_id.using_encoded(|s| v.extend(s)); + v } // converts a message stream into a stream of signed messages. // the output stream checks signatures also. -fn checked_message_stream(inner: S, round: u64, voters: Vec) +fn checked_message_stream( + round: u64, + set_id: u64, + inner: S, + voters: Vec, +) -> impl Stream,Error=Error> where S: Stream,Error=()> { @@ -326,7 +343,7 @@ fn checked_message_stream(inner: S, round: u64, voters: Vec(inner: S, round: u64, voters: Vec( + round: u64, + set_id: u64, local_key: Option>, voters: Vec, - round: u64, network: N, ) -> ( impl Stream,Error=Error>, @@ -357,7 +375,7 @@ fn outgoing_messages( .map(move |msg: Message| { // when locals exist, sign messages on import if let Some((ref pair, local_id)) = locals { - let encoded = round_localized_payload(round, &msg); + let encoded = localized_payload(round, set_id, &msg); let signature = pair.sign(&encoded[..]); let signed = SignedMessage:: { message: msg, @@ -480,17 +498,20 @@ impl voter::Environment for Environment( - self.network.messages_for(round), round, - self.config.voters.clone(), + set_id, + self.network.messages_for(round), + self.config.genesis_voters.clone(), ); let (out_rx, outgoing) = outgoing_messages::( + round, + set_id, self.config.local_key.clone(), self.config.voters.clone(), - round, self.network.clone(), );