Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

grandpa: Use storage proofs for Grandpa authorities #3734

Merged
merged 8 commits into from
Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions core/finality-grandpa/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"

[dependencies]
client = { package = "substrate-client", path = "../../client", default-features = false }
app-crypto = { package = "substrate-application-crypto", path = "../../application-crypto", default-features = false }
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
sr-primitives = { path = "../../sr-primitives", default-features = false }
Expand All @@ -15,7 +14,6 @@ serde = { version = "1.0.101", optional = true, features = ["derive"] }
[features]
default = ["std"]
std = [
"client/std",
"codec/std",
"sr-primitives/std",
"rstd/std",
Expand Down
78 changes: 56 additions & 22 deletions core/finality-grandpa/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ extern crate alloc;

#[cfg(feature = "std")]
use serde::Serialize;
use codec::{Encode, Decode, Codec};
use codec::{Encode, Decode, Input, Codec};
use sr_primitives::{ConsensusEngineId, RuntimeDebug};
use client::decl_runtime_apis;
use rstd::borrow::Cow;
use rstd::vec::Vec;

mod app {
Expand All @@ -46,6 +46,10 @@ pub type AuthoritySignature = app::Signature;
/// The `ConsensusEngineId` of GRANDPA.
pub const GRANDPA_ENGINE_ID: ConsensusEngineId = *b"FRNK";

/// The storage key for the current set of weighted Grandpa authorities.
/// The value stored is an encoded VersionedAuthorityList.
pub const GRANDPA_AUTHORITIES_KEY: &'static [u8] = b":grandpa_authorities";
Copy link
Contributor

@rphmeier rphmeier Oct 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I'd keep an eye out for (that the runtime API approach made handling-of easier) is upgradeability.

We want to upgrade GRANDPA to use BLS keys at some point

We could also have a storage item :grandpa_protocol_version which holds the protocol version key. We'd need 2 merkle branches, but it would allow us to change the key type later on without issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how the runtime API would make this sort of upgrade any easier? Are keys already encoded with the key type? If so, there's no difference, I'd think, and if not, then the GrandpaApi_grandpa_authorities return type would have to change anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the runtime API has an implicit version, that's why

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we could store the "authorities version" alongside the authorities in the same key. So the value is an encoded (u8, AuthorityList). That way it's still just one merkle proof.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure. that will require a more adaptive Decode implementation later on, but it should be fine.

Copy link
Contributor Author

@jimpo jimpo Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like 0349a8b?


/// The weight of an authority.
pub type AuthorityWeight = u64;

Expand All @@ -58,12 +62,15 @@ pub type SetId = u64;
/// The round indicator.
pub type RoundNumber = u64;

/// A list of Grandpa authorities with associated weights.
pub type AuthorityList = Vec<(AuthorityId, AuthorityWeight)>;

/// A scheduled change of authority set.
#[cfg_attr(feature = "std", derive(Serialize))]
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)]
pub struct ScheduledChange<N> {
/// The new authorities after the change, along with their respective weights.
pub next_authorities: Vec<(AuthorityId, AuthorityWeight)>,
pub next_authorities: AuthorityList,
/// The number of blocks to delay.
pub delay: N,
}
Expand Down Expand Up @@ -154,24 +161,51 @@ pub const PENDING_CHANGE_CALL: &str = "grandpa_pending_change";
/// WASM function call to get current GRANDPA authorities.
pub const AUTHORITIES_CALL: &str = "grandpa_authorities";

decl_runtime_apis! {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will need to re-add the runtime API, it's required for exposing equivocation reporting functionality (#3868). But I can re-add it in my PR after this is merged.

/// APIs for integrating the GRANDPA finality gadget into runtimes.
/// This should be implemented on the runtime side.
///
/// This is primarily used for negotiating authority-set changes for the
/// gadget. GRANDPA uses a signaling model of changing authority sets:
/// changes should be signaled with a delay of N blocks, and then automatically
/// applied in the runtime after those N blocks have passed.
///
/// The consensus protocol will coordinate the handoff externally.
#[api_version(2)]
pub trait GrandpaApi {
/// Get the current GRANDPA authorities and weights. This should not change except
/// for when changes are scheduled and the corresponding delay has passed.
///
/// When called at block B, it will return the set of authorities that should be
/// used to finalize descendants of this block (B+1, B+2, ...). The block B itself
/// is finalized by the authorities from block B-1.
fn grandpa_authorities() -> Vec<(AuthorityId, AuthorityWeight)>;
/// The current version of the stored AuthorityList type. The encoding version MUST be updated any
/// time the AuthorityList type changes.
const AUTHORITIES_VERISON: u8 = 1;

/// An AuthorityList that is encoded with a version specifier. The encoding version is updated any
/// time the AuthorityList type changes. This ensures that encodings of different versions of an
/// AuthorityList are differentiable. Attempting to decode an authority list with an unknown
/// version will fail.
#[derive(Default)]
pub struct VersionedAuthorityList<'a>(Cow<'a, AuthorityList>);

impl<'a> From<AuthorityList> for VersionedAuthorityList<'a> {
fn from(authorities: AuthorityList) -> Self {
VersionedAuthorityList(Cow::Owned(authorities))
}
}

impl<'a> From<&'a AuthorityList> for VersionedAuthorityList<'a> {
fn from(authorities: &'a AuthorityList) -> Self {
VersionedAuthorityList(Cow::Borrowed(authorities))
}
}

impl<'a> Into<AuthorityList> for VersionedAuthorityList<'a> {
fn into(self) -> AuthorityList {
self.0.into_owned()
}
}

impl<'a> Encode for VersionedAuthorityList<'a> {
fn size_hint(&self) -> usize {
(AUTHORITIES_VERISON, self.0.as_ref()).size_hint()
}

fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
(AUTHORITIES_VERISON, self.0.as_ref()).using_encoded(f)
}
}

impl<'a> Decode for VersionedAuthorityList<'a> {
fn decode<I: Input>(value: &mut I) -> Result<Self, codec::Error> {
let (version, authorities): (u8, AuthorityList) = Decode::decode(value)?;
if version != AUTHORITIES_VERISON {
return Err("unknown Grandpa authorities version".into());
}
Ok(authorities.into())
}
}
8 changes: 4 additions & 4 deletions core/finality-grandpa/src/authorities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use grandpa::voter_set::VoterSet;
use codec::{Encode, Decode};
use log::{debug, info};
use substrate_telemetry::{telemetry, CONSENSUS_INFO};
use fg_primitives::AuthorityId;
use fg_primitives::{AuthorityId, AuthorityList};

use std::cmp::Ord;
use std::fmt::Debug;
Expand Down Expand Up @@ -86,7 +86,7 @@ pub(crate) struct Status<H, N> {
/// A set of authorities.
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub(crate) struct AuthoritySet<H, N> {
pub(crate) current_authorities: Vec<(AuthorityId, u64)>,
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
Expand All @@ -103,7 +103,7 @@ where H: PartialEq,
N: Ord,
{
/// Get a genesis set with given authorities.
pub(crate) fn genesis(initial: Vec<(AuthorityId, u64)>) -> Self {
pub(crate) fn genesis(initial: AuthorityList) -> Self {
AuthoritySet {
current_authorities: initial,
set_id: 0,
Expand Down Expand Up @@ -390,7 +390,7 @@ pub(crate) enum DelayKind<N> {
#[derive(Debug, Clone, Encode, PartialEq)]
pub(crate) struct PendingChange<H, N> {
/// The new authorities and weights to apply.
pub(crate) next_authorities: Vec<(AuthorityId, u64)>,
pub(crate) next_authorities: AuthorityList,
/// How deep in the chain the announcing block must be
/// before the change is applied.
pub(crate) delay: N,
Expand Down
9 changes: 5 additions & 4 deletions core/finality-grandpa/src/aux_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ 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::{AuthorityId, AuthorityWeight, SetId, RoundNumber};
use fg_primitives::{AuthorityList, SetId, RoundNumber};

use crate::authorities::{AuthoritySet, SharedAuthoritySet, PendingChange, DelayKind};
use crate::consensus_changes::{SharedConsensusChanges, ConsensusChanges};
Expand Down Expand Up @@ -55,15 +55,15 @@ type V0VoterSetState<H, N> = (RoundNumber, RoundState<H, N>);

#[derive(Debug, Clone, Encode, Decode, PartialEq)]
struct V0PendingChange<H, N> {
next_authorities: Vec<(AuthorityId, AuthorityWeight)>,
next_authorities: AuthorityList,
delay: N,
canon_height: N,
canon_hash: H,
}

#[derive(Debug, Clone, Encode, Decode, PartialEq)]
struct V0AuthoritySet<H, N> {
current_authorities: Vec<(AuthorityId, AuthorityWeight)>,
current_authorities: AuthorityList,
set_id: SetId,
pending_changes: Vec<V0PendingChange<H, N>>,
}
Expand Down Expand Up @@ -266,7 +266,7 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
-> ClientResult<PersistentData<Block>>
where
B: AuxStore,
G: FnOnce() -> ClientResult<Vec<(AuthorityId, AuthorityWeight)>>,
G: FnOnce() -> ClientResult<AuthorityList>,
{
let version: Option<u32> = load_decode(backend, VERSION_KEY)?;
let consensus_changes = load_decode(backend, CONSENSUS_CHANGES_KEY)?
Expand Down Expand Up @@ -426,6 +426,7 @@ pub(crate) fn load_authorities<B: AuxStore, H: Decode, N: Decode>(backend: &B)

#[cfg(test)]
mod test {
use fg_primitives::AuthorityId;
use primitives::H256;
use test_client;
use super::*;
Expand Down
3 changes: 2 additions & 1 deletion core/finality-grandpa/src/communication/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ 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};

Expand Down Expand Up @@ -200,7 +201,7 @@ fn make_test_network() -> (
)
}

fn make_ids(keys: &[Ed25519Keyring]) -> Vec<(AuthorityId, u64)> {
fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList {
keys.iter()
.map(|key| key.clone().public().into())
.map(|id| (id, 1))
Expand Down
Loading