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

Commit

Permalink
grandpa: Use storage proofs for Grandpa authorities (#3985)
Browse files Browse the repository at this point in the history
  • Loading branch information
jimpo committed Nov 7, 2019
1 parent a73793b commit 5026f21
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 149 deletions.
63 changes: 60 additions & 3 deletions core/finality-grandpa/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ 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 +47,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";

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

Expand All @@ -58,12 +63,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,6 +162,55 @@ pub const PENDING_CHANGE_CALL: &str = "grandpa_pending_change";
/// WASM function call to get current GRANDPA authorities.
pub const AUTHORITIES_CALL: &str = "grandpa_authorities";

/// 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())
}
}

decl_runtime_apis! {
/// APIs for integrating the GRANDPA finality gadget into runtimes.
/// This should be implemented on the runtime side.
Expand All @@ -172,6 +229,6 @@ decl_runtime_apis! {
/// 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)>;
fn grandpa_authorities() -> AuthorityList;
}
}
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
86 changes: 45 additions & 41 deletions core/finality-grandpa/src/finality_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,24 @@
//! 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::{
backend::Backend, blockchain::Backend as BlockchainBackend, CallExecutor, Client,
error::{Error as ClientError, Result as ClientResult},
light::fetcher::{FetchChecker, RemoteCallRequest, StorageProof}, ExecutionStrategy,
light::fetcher::{FetchChecker, RemoteReadRequest, StorageProof},
};
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};
use primitives::{H256, Blake2Hasher, storage::StorageKey};
use substrate_telemetry::{telemetry, CONSENSUS_INFO};
use fg_primitives::AuthorityId;
use fg_primitives::{AuthorityId, AuthorityList, VersionedAuthorityList, GRANDPA_AUTHORITIES_KEY};

use crate::justification::GrandpaJustification;

Expand All @@ -59,9 +60,9 @@ const MAX_FRAGMENTS_IN_PROOF: usize = 8;

/// GRANDPA authority set related methods for the finality proof provider.
pub trait AuthoritySetForFinalityProver<Block: BlockT>: Send + Sync {
/// Call GrandpaApi::grandpa_authorities at given block.
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<(AuthorityId, u64)>>;
/// Prove call of GrandpaApi::grandpa_authorities at given block.
/// 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>;
}

Expand All @@ -72,33 +73,28 @@ impl<B, E, Block: BlockT<Hash=H256>, RA> AuthoritySetForFinalityProver<Block> fo
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: Send + Sync,
{
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<(AuthorityId, u64)>> {
self.executor().call(
block,
"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
)))
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.execution_proof(block, "GrandpaApi_grandpa_authorities",&[]).map(|(_, proof)| proof)
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 execution proof of Grandpa::grandpa_authorities at given block.
/// 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<Vec<(AuthorityId, u64)>>;
) -> ClientResult<AuthorityList>;
}

/// FetchChecker-based implementation of AuthoritySetForFinalityChecker.
Expand All @@ -108,22 +104,30 @@ impl<Block: BlockT> AuthoritySetForFinalityChecker<Block> for Arc<dyn FetchCheck
hash: Block::Hash,
header: Block::Header,
proof: StorageProof,
) -> ClientResult<Vec<(AuthorityId, u64)>> {
let request = RemoteCallRequest {
) -> ClientResult<AuthorityList> {
let storage_key = GRANDPA_AUTHORITIES_KEY.to_vec();
let request = RemoteReadRequest {
block: hash,
header,
method: "GrandpaApi_grandpa_authorities".into(),
call_data: vec![],
keys: vec![storage_key.clone()],
retry_count: None,
};

self.check_execution_proof(&request, proof)
.and_then(|authorities| {
let authorities: Vec<(AuthorityId, u64)> = Decode::decode(&mut &authorities[..])
.map_err(|err| ClientError::CallResultDecode(
"failed to decode GRANDPA authorities set proof".into(), err
))?;
Ok(authorities.into_iter().collect())
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)
})
}
}
Expand Down Expand Up @@ -189,7 +193,7 @@ pub struct FinalityEffects<Header: HeaderT> {
/// 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: Vec<(AuthorityId, u64)>,
pub new_authorities: AuthorityList,
}

/// Single fragment of proof-of-finality.
Expand Down Expand Up @@ -408,7 +412,7 @@ pub(crate) fn prove_finality<Block: BlockT<Hash=H256>, B: BlockchainBackend<Bloc
pub(crate) fn check_finality_proof<Block: BlockT<Hash=H256>, B>(
blockchain: &B,
current_set_id: u64,
current_authorities: Vec<(AuthorityId, u64)>,
current_authorities: AuthorityList,
authorities_provider: &dyn AuthoritySetForFinalityChecker<Block>,
remote_proof: Vec<u8>,
) -> ClientResult<FinalityEffects<Block::Header>>
Expand All @@ -427,7 +431,7 @@ pub(crate) fn check_finality_proof<Block: BlockT<Hash=H256>, B>(
fn do_check_finality_proof<Block: BlockT<Hash=H256>, B, J>(
blockchain: &B,
current_set_id: u64,
current_authorities: Vec<(AuthorityId, u64)>,
current_authorities: AuthorityList,
authorities_provider: &dyn AuthoritySetForFinalityChecker<Block>,
remote_proof: Vec<u8>,
) -> ClientResult<FinalityEffects<Block::Header>>
Expand Down Expand Up @@ -522,12 +526,12 @@ fn check_finality_proof_fragment<Block: BlockT<Hash=H256>, B, J>(

/// Authorities set from initial authorities set or finality effects.
enum AuthoritiesOrEffects<Header: HeaderT> {
Authorities(u64, Vec<(AuthorityId, u64)>),
Authorities(u64, AuthorityList),
Effects(FinalityEffects<Header>),
}

impl<Header: HeaderT> AuthoritiesOrEffects<Header> {
pub fn extract_authorities(self) -> (u64, Vec<(AuthorityId, u64)>) {
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),
Expand Down Expand Up @@ -581,10 +585,10 @@ pub(crate) mod tests {

impl<GetAuthorities, ProveAuthorities> AuthoritySetForFinalityProver<Block> for (GetAuthorities, ProveAuthorities)
where
GetAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<Vec<(AuthorityId, u64)>>,
GetAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<AuthorityList>,
ProveAuthorities: Send + Sync + Fn(BlockId<Block>) -> ClientResult<StorageProof>,
{
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<Vec<(AuthorityId, u64)>> {
fn authorities(&self, block: &BlockId<Block>) -> ClientResult<AuthorityList> {
self.0(*block)
}

Expand All @@ -597,14 +601,14 @@ pub(crate) mod tests {

impl<Closure> AuthoritySetForFinalityChecker<Block> for ClosureAuthoritySetForFinalityChecker<Closure>
where
Closure: Send + Sync + Fn(H256, Header, StorageProof) -> ClientResult<Vec<(AuthorityId, u64)>>,
Closure: Send + Sync + Fn(H256, Header, StorageProof) -> ClientResult<AuthorityList>,
{
fn check_authorities_proof(
&self,
hash: H256,
header: Header,
proof: StorageProof,
) -> ClientResult<Vec<(AuthorityId, u64)>> {
proof: StorageProof
) -> ClientResult<AuthorityList> {
self.0(hash, header, proof)
}
}
Expand Down
Loading

0 comments on commit 5026f21

Please sign in to comment.