mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 09:21:05 +00:00
grandpa: Use storage proofs for Grandpa authorities (#3985)
This commit is contained in:
@@ -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 {
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
|
||||
@@ -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};
|
||||
@@ -55,7 +55,7 @@ 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,
|
||||
@@ -63,7 +63,7 @@ struct V0PendingChange<H, N> {
|
||||
|
||||
#[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>>,
|
||||
}
|
||||
@@ -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)?
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -34,13 +34,14 @@
|
||||
//! 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;
|
||||
@@ -48,9 +49,9 @@ 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;
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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>>
|
||||
@@ -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>>
|
||||
@@ -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),
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,14 +57,12 @@ use log::{debug, error, info};
|
||||
use futures::sync::mpsc;
|
||||
use client::{
|
||||
BlockchainEvents, CallExecutor, Client, backend::Backend, error::Error as ClientError,
|
||||
ExecutionStrategy,
|
||||
};
|
||||
use client::blockchain::HeaderBackend;
|
||||
use codec::Encode;
|
||||
use codec::{Decode, Encode};
|
||||
use sr_primitives::generic::BlockId;
|
||||
use sr_primitives::traits::{
|
||||
NumberFor, Block as BlockT, DigestFor, ProvideRuntimeApi
|
||||
};
|
||||
use fg_primitives::{GrandpaApi, AuthorityPair};
|
||||
use sr_primitives::traits::{NumberFor, Block as BlockT, DigestFor, Zero};
|
||||
use keystore::KeyStorePtr;
|
||||
use inherents::InherentDataProviders;
|
||||
use consensus_common::SelectChain;
|
||||
@@ -108,7 +106,7 @@ use environment::{Environment, VoterSetState};
|
||||
use import::GrandpaBlockImport;
|
||||
use until_imported::UntilGlobalMessageBlocksImported;
|
||||
use communication::NetworkBridge;
|
||||
use fg_primitives::{AuthoritySignature, SetId, AuthorityWeight};
|
||||
use fg_primitives::{AuthorityList, AuthorityPair, AuthoritySignature, SetId};
|
||||
|
||||
// Re-export these two because it's just so damn convenient.
|
||||
pub use fg_primitives::{AuthorityId, ScheduledChange};
|
||||
@@ -295,7 +293,7 @@ pub(crate) struct NewAuthoritySet<H, N> {
|
||||
pub(crate) canon_number: N,
|
||||
pub(crate) canon_hash: H,
|
||||
pub(crate) set_id: SetId,
|
||||
pub(crate) authorities: Vec<(AuthorityId, AuthorityWeight)>,
|
||||
pub(crate) authorities: AuthorityList,
|
||||
}
|
||||
|
||||
/// Commands issued to the voter.
|
||||
@@ -367,11 +365,44 @@ pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>, RA, SC> {
|
||||
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, PRA, SC>(
|
||||
pub fn block_import<B, E, Block: BlockT<Hash=H256>, RA, SC>(
|
||||
client: Arc<Client<B, E, Block, RA>>,
|
||||
api: &PRA,
|
||||
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
|
||||
select_chain: SC,
|
||||
) -> Result<(
|
||||
GrandpaBlockImport<B, E, Block, RA, SC>,
|
||||
@@ -381,12 +412,8 @@ where
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
PRA: ProvideRuntimeApi,
|
||||
PRA::Api: GrandpaApi<Block>,
|
||||
SC: SelectChain<Block>,
|
||||
{
|
||||
use sr_primitives::traits::Zero;
|
||||
|
||||
let chain_info = client.info();
|
||||
let genesis_hash = chain_info.chain.genesis_hash;
|
||||
|
||||
@@ -395,12 +422,11 @@ where
|
||||
genesis_hash,
|
||||
<NumberFor<Block>>::zero(),
|
||||
|| {
|
||||
let genesis_authorities = api.runtime_api()
|
||||
.grandpa_authorities(&BlockId::number(Zero::zero()))?;
|
||||
let authorities = genesis_authorities_provider.get()?;
|
||||
telemetry!(CONSENSUS_DEBUG; "afg.loading_authorities";
|
||||
"authorities_len" => ?genesis_authorities.len()
|
||||
"authorities_len" => ?authorities.len()
|
||||
);
|
||||
Ok(genesis_authorities)
|
||||
Ok(authorities)
|
||||
}
|
||||
)?;
|
||||
|
||||
|
||||
@@ -34,17 +34,18 @@ use consensus_common::{
|
||||
};
|
||||
use network::config::{BoxFinalityProofRequestBuilder, FinalityProofRequestBuilder};
|
||||
use sr_primitives::Justification;
|
||||
use sr_primitives::traits::{
|
||||
NumberFor, Block as BlockT, Header as HeaderT, ProvideRuntimeApi, DigestFor,
|
||||
};
|
||||
use fg_primitives::{self, GrandpaApi, AuthorityId};
|
||||
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::finality_proof::{
|
||||
AuthoritySetForFinalityChecker, ProvableJustification, make_finality_proof_request,
|
||||
};
|
||||
use crate::justification::GrandpaJustification;
|
||||
|
||||
/// LightAuthoritySet is saved under this key in aux storage.
|
||||
@@ -53,21 +54,23 @@ const LIGHT_AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters";
|
||||
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, PRA>(
|
||||
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>>,
|
||||
api: Arc<PRA>,
|
||||
) -> Result<GrandpaLightBlockImport<B, E, Block, RA>, ClientError>
|
||||
where
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
PRA: ProvideRuntimeApi,
|
||||
PRA::Api: GrandpaApi<Block>,
|
||||
{
|
||||
let info = client.info();
|
||||
let import_data = load_aux_import_data(info.chain.finalized_hash, &*client, api)?;
|
||||
let import_data = load_aux_import_data(
|
||||
info.chain.finalized_hash,
|
||||
&*client,
|
||||
genesis_authorities_provider,
|
||||
)?;
|
||||
Ok(GrandpaLightBlockImport {
|
||||
client,
|
||||
backend,
|
||||
@@ -110,7 +113,7 @@ struct LightImportData<Block: BlockT<Hash=H256>> {
|
||||
#[derive(Debug, Encode, Decode)]
|
||||
struct LightAuthoritySet {
|
||||
set_id: u64,
|
||||
authorities: Vec<(AuthorityId, u64)>,
|
||||
authorities: AuthorityList,
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA> GrandpaLightBlockImport<B, E, Block, RA> {
|
||||
@@ -194,7 +197,7 @@ impl<B, E, Block: BlockT<Hash=H256>, RA> FinalityProofImport<Block>
|
||||
|
||||
impl LightAuthoritySet {
|
||||
/// Get a genesis set with given authorities.
|
||||
pub fn genesis(initial: Vec<(AuthorityId, u64)>) -> Self {
|
||||
pub fn genesis(initial: AuthorityList) -> Self {
|
||||
LightAuthoritySet {
|
||||
set_id: fg_primitives::SetId::default(),
|
||||
authorities: initial,
|
||||
@@ -207,12 +210,12 @@ impl LightAuthoritySet {
|
||||
}
|
||||
|
||||
/// Get latest authorities set.
|
||||
pub fn authorities(&self) -> Vec<(AuthorityId, u64)> {
|
||||
pub fn authorities(&self) -> AuthorityList {
|
||||
self.authorities.clone()
|
||||
}
|
||||
|
||||
/// Set new authorities set.
|
||||
pub fn update(&mut self, set_id: u64, authorities: Vec<(AuthorityId, u64)>) {
|
||||
pub fn update(&mut self, set_id: u64, authorities: AuthorityList) {
|
||||
self.set_id = set_id;
|
||||
std::mem::replace(&mut self.authorities, authorities);
|
||||
}
|
||||
@@ -472,17 +475,14 @@ fn do_finalize_block<B, C, Block: BlockT<Hash=H256>>(
|
||||
}
|
||||
|
||||
/// Load light import aux data from the store.
|
||||
fn load_aux_import_data<B, Block: BlockT<Hash=H256>, PRA>(
|
||||
fn load_aux_import_data<B, Block: BlockT<Hash=H256>>(
|
||||
last_finalized: Block::Hash,
|
||||
aux_store: &B,
|
||||
api: Arc<PRA>,
|
||||
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
|
||||
) -> Result<LightImportData<Block>, ClientError>
|
||||
where
|
||||
B: AuxStore,
|
||||
PRA: ProvideRuntimeApi,
|
||||
PRA::Api: GrandpaApi<Block>,
|
||||
{
|
||||
use sr_primitives::traits::Zero;
|
||||
let authority_set = match load_decode(aux_store, LIGHT_AUTHORITY_SET_KEY)? {
|
||||
Some(authority_set) => authority_set,
|
||||
None => {
|
||||
@@ -490,7 +490,7 @@ fn load_aux_import_data<B, Block: BlockT<Hash=H256>, PRA>(
|
||||
from genesis on what appears to be first startup.");
|
||||
|
||||
// no authority set on disk: fetch authorities from genesis state
|
||||
let genesis_authorities = api.runtime_api().grandpa_authorities(&BlockId::number(Zero::zero()))?;
|
||||
let genesis_authorities = genesis_authorities_provider.get()?;
|
||||
|
||||
let authority_set = LightAuthoritySet::genesis(genesis_authorities);
|
||||
let encoded = authority_set.encode();
|
||||
@@ -546,6 +546,7 @@ fn on_post_finalization_error(error: ClientError, value_type: &str) -> Consensus
|
||||
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};
|
||||
@@ -622,20 +623,19 @@ pub mod tests {
|
||||
}
|
||||
|
||||
/// 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, PRA>(
|
||||
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>>,
|
||||
api: Arc<PRA>,
|
||||
) -> Result<NoJustificationsImport<B, E, Block, RA>, ClientError>
|
||||
where
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
PRA: ProvideRuntimeApi,
|
||||
PRA::Api: GrandpaApi<Block>,
|
||||
{
|
||||
light_block_import(client, backend, authority_set_provider, api).map(NoJustificationsImport)
|
||||
light_block_import(client, backend, genesis_authorities_provider, authority_set_provider)
|
||||
.map(NoJustificationsImport)
|
||||
}
|
||||
|
||||
fn import_block(
|
||||
@@ -729,14 +729,14 @@ pub mod tests {
|
||||
#[test]
|
||||
fn aux_data_updated_on_start() {
|
||||
let aux_store = InMemoryAuxStore::<Block>::new();
|
||||
let api = Arc::new(TestApi::new(vec![(AuthorityId::from_slice(&[1; 32]), 1)]));
|
||||
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();
|
||||
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());
|
||||
}
|
||||
@@ -744,7 +744,7 @@ pub mod tests {
|
||||
#[test]
|
||||
fn aux_data_loaded_on_restart() {
|
||||
let aux_store = InMemoryAuxStore::<Block>::new();
|
||||
let api = Arc::new(TestApi::new(vec![(AuthorityId::from_slice(&[1; 32]), 1)]));
|
||||
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();
|
||||
@@ -766,7 +766,7 @@ pub mod tests {
|
||||
).unwrap();
|
||||
|
||||
// importer uses it on start
|
||||
let data = load_aux_import_data(Default::default(), &aux_store, api).unwrap();
|
||||
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())]);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ use codec::Decode;
|
||||
use sr_primitives::traits::{ApiRef, ProvideRuntimeApi, Header as HeaderT};
|
||||
use sr_primitives::generic::{BlockId, DigestItem};
|
||||
use primitives::{NativeOrEncoded, ExecutionContext, crypto::Public};
|
||||
use fg_primitives::{GRANDPA_ENGINE_ID, AuthorityId};
|
||||
use fg_primitives::{GRANDPA_ENGINE_ID, AuthorityList, GrandpaApi};
|
||||
use state_machine::{backend::InMemory, prove_read, read_proof_check};
|
||||
|
||||
use authorities::AuthoritySet;
|
||||
@@ -137,8 +137,8 @@ impl TestNetFactory for GrandpaTestNet {
|
||||
let import = light_block_import_without_justifications(
|
||||
client.clone(),
|
||||
backend.clone(),
|
||||
&self.test_config,
|
||||
authorities_provider,
|
||||
Arc::new(self.test_config.clone())
|
||||
).expect("Could not create block import for fresh peer.");
|
||||
let finality_proof_req_builder = import.0.create_finality_proof_request_builder();
|
||||
let proof_import = Box::new(import.clone());
|
||||
@@ -188,11 +188,11 @@ impl Future for Exit {
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub(crate) struct TestApi {
|
||||
genesis_authorities: Vec<(AuthorityId, u64)>,
|
||||
genesis_authorities: AuthorityList,
|
||||
}
|
||||
|
||||
impl TestApi {
|
||||
pub fn new(genesis_authorities: Vec<(AuthorityId, u64)>) -> Self {
|
||||
pub fn new(genesis_authorities: AuthorityList) -> Self {
|
||||
TestApi {
|
||||
genesis_authorities,
|
||||
}
|
||||
@@ -271,19 +271,20 @@ impl GrandpaApi<Block> for RuntimeApi {
|
||||
_: ExecutionContext,
|
||||
_: Option<()>,
|
||||
_: Vec<u8>,
|
||||
) -> Result<NativeOrEncoded<Vec<(AuthorityId, u64)>>> {
|
||||
) -> Result<NativeOrEncoded<AuthorityList>> {
|
||||
Ok(self.inner.genesis_authorities.clone()).map(NativeOrEncoded::Native)
|
||||
}
|
||||
}
|
||||
|
||||
impl GenesisAuthoritySetProvider<Block> for TestApi {
|
||||
fn get(&self) -> Result<AuthorityList> {
|
||||
Ok(self.genesis_authorities.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthoritySetForFinalityProver<Block> for TestApi {
|
||||
fn authorities(&self, block: &BlockId<Block>) -> Result<Vec<(AuthorityId, u64)>> {
|
||||
let runtime_api = RuntimeApi { inner: self.clone() };
|
||||
runtime_api.GrandpaApi_grandpa_authorities_runtime_api_impl(block, ExecutionContext::Syncing, None, Vec::new())
|
||||
.map(|v| match v {
|
||||
NativeOrEncoded::Native(value) => value,
|
||||
_ => unreachable!("only providing native values"),
|
||||
})
|
||||
fn authorities(&self, _block: &BlockId<Block>) -> Result<AuthorityList> {
|
||||
Ok(self.genesis_authorities.clone())
|
||||
}
|
||||
|
||||
fn prove_authorities(&self, block: &BlockId<Block>) -> Result<StorageProof> {
|
||||
@@ -303,7 +304,7 @@ impl AuthoritySetForFinalityChecker<Block> for TestApi {
|
||||
_hash: <Block as BlockT>::Hash,
|
||||
header: <Block as BlockT>::Header,
|
||||
proof: StorageProof,
|
||||
) -> Result<Vec<(AuthorityId, u64)>> {
|
||||
) -> Result<AuthorityList> {
|
||||
let results = read_proof_check::<Blake2Hasher, _>(
|
||||
*header.state_root(), proof, vec![b"authorities"]
|
||||
)
|
||||
@@ -320,7 +321,7 @@ impl AuthoritySetForFinalityChecker<Block> for TestApi {
|
||||
|
||||
const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500);
|
||||
|
||||
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)).collect()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user