mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 23:31:04 +00:00
grandpa: remove light-client specific block import pipeline (#7546)
* grandpa: remove light-client specific block import * consensus, network: remove finality proofs
This commit is contained in:
@@ -17,7 +17,6 @@
|
||||
//! Schema for stuff in the aux-db.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sc_client_api::backend::AuxStore;
|
||||
use sp_blockchain::{Result as ClientResult, Error as ClientError};
|
||||
@@ -28,7 +27,6 @@ use log::{info, warn};
|
||||
use sp_finality_grandpa::{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,
|
||||
};
|
||||
@@ -38,7 +36,6 @@ const VERSION_KEY: &[u8] = b"grandpa_schema_version";
|
||||
const SET_STATE_KEY: &[u8] = b"grandpa_completed_round";
|
||||
const CONCLUDED_ROUNDS: &[u8] = b"grandpa_concluded_rounds";
|
||||
const AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters";
|
||||
const CONSENSUS_CHANGES_KEY: &[u8] = b"grandpa_consensus_changes";
|
||||
|
||||
const CURRENT_VERSION: u32 = 2;
|
||||
|
||||
@@ -122,7 +119,6 @@ pub(crate) fn load_decode<B: AuxStore, T: Decode>(backend: &B, key: &[u8]) -> Cl
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
@@ -272,8 +268,6 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
|
||||
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));
|
||||
|
||||
@@ -282,7 +276,6 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
|
||||
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(),
|
||||
});
|
||||
}
|
||||
@@ -291,7 +284,6 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
|
||||
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(),
|
||||
});
|
||||
}
|
||||
@@ -321,7 +313,6 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
|
||||
|
||||
return Ok(PersistentData {
|
||||
authority_set: set.into(),
|
||||
consensus_changes: Arc::new(consensus_changes.into()),
|
||||
set_state: set_state.into(),
|
||||
});
|
||||
}
|
||||
@@ -359,7 +350,6 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
|
||||
Ok(PersistentData {
|
||||
authority_set: genesis_set.into(),
|
||||
set_state: genesis_state.into(),
|
||||
consensus_changes: Arc::new(consensus_changes.into()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -421,18 +411,6 @@ pub(crate) fn write_concluded_round<Block: BlockT, B: AuxStore>(
|
||||
backend.insert_aux(&[(&key[..], round_data.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>> {
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright 2018-2020 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 parity_scale_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) -> ::sp_blockchain::Result<Option<H>>>(
|
||||
&mut self,
|
||||
block: (N, H),
|
||||
canonical_at_height: F,
|
||||
) -> ::sp_blockchain::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>>>;
|
||||
@@ -34,10 +34,10 @@ use finality_grandpa::{
|
||||
BlockNumberOps, Error as GrandpaError, round::State as RoundState,
|
||||
voter, voter_set::VoterSet,
|
||||
};
|
||||
use sp_blockchain::{HeaderBackend, HeaderMetadata, Error as ClientError};
|
||||
use sp_blockchain::HeaderMetadata;
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_runtime::traits::{
|
||||
Block as BlockT, Header as HeaderT, NumberFor, One, Zero,
|
||||
Block as BlockT, Header as HeaderT, NumberFor, Zero,
|
||||
};
|
||||
use sc_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO};
|
||||
|
||||
@@ -50,7 +50,6 @@ use sp_consensus::SelectChain;
|
||||
|
||||
use crate::authorities::{AuthoritySet, SharedAuthoritySet};
|
||||
use crate::communication::Network as NetworkT;
|
||||
use crate::consensus_changes::SharedConsensusChanges;
|
||||
use crate::notification::GrandpaJustificationSender;
|
||||
use crate::justification::GrandpaJustification;
|
||||
use crate::until_imported::UntilVoteTargetImported;
|
||||
@@ -440,7 +439,6 @@ pub(crate) struct Environment<Backend, Block: BlockT, C, N: NetworkT<Block>, SC,
|
||||
pub(crate) voters: Arc<VoterSet<AuthorityId>>,
|
||||
pub(crate) config: Config,
|
||||
pub(crate) authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
pub(crate) consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
||||
pub(crate) network: crate::communication::NetworkBridge<Block, N>,
|
||||
pub(crate) set_id: SetId,
|
||||
pub(crate) voter_set_state: SharedVoterSetState<Block>,
|
||||
@@ -1115,7 +1113,6 @@ where
|
||||
finalize_block(
|
||||
self.client.clone(),
|
||||
&self.authority_set,
|
||||
&self.consensus_changes,
|
||||
Some(self.config.justification_period.into()),
|
||||
hash,
|
||||
number,
|
||||
@@ -1180,7 +1177,6 @@ impl<Block: BlockT> From<GrandpaJustification<Block>> for JustificationOrCommit<
|
||||
pub(crate) fn finalize_block<BE, Block, Client>(
|
||||
client: Arc<Client>,
|
||||
authority_set: &SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
consensus_changes: &SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
||||
justification_period: Option<NumberFor<Block>>,
|
||||
hash: Block::Hash,
|
||||
number: NumberFor<Block>,
|
||||
@@ -1215,15 +1211,6 @@ where
|
||||
|
||||
// FIXME #1483: clone only when changed
|
||||
let old_authority_set = authority_set.clone();
|
||||
// holds the old consensus changes in case it is changed below, needed for
|
||||
// reverting in case of failure
|
||||
let mut old_consensus_changes = None;
|
||||
|
||||
let mut consensus_changes = consensus_changes.lock();
|
||||
let canon_at_height = |canon_number| {
|
||||
// "true" because the block is finalized
|
||||
canonical_at_height(&*client, (hash, number), true, canon_number)
|
||||
};
|
||||
|
||||
let update_res: Result<_, Error> = client.lock_import_and_run(|import_op| {
|
||||
let status = authority_set.apply_standard_changes(
|
||||
@@ -1233,26 +1220,6 @@ where
|
||||
initial_sync,
|
||||
).map_err(|e| Error::Safety(e.to_string()))?;
|
||||
|
||||
// check if this is this is the first finalization of some consensus changes
|
||||
let (alters_consensus_changes, finalizes_consensus_changes) = consensus_changes
|
||||
.finalize((number, hash), &canon_at_height)?;
|
||||
|
||||
if alters_consensus_changes {
|
||||
old_consensus_changes = Some(consensus_changes.clone());
|
||||
|
||||
let write_result = crate::aux_schema::update_consensus_changes(
|
||||
&*consensus_changes,
|
||||
|insert| apply_aux(import_op, insert, &[]),
|
||||
);
|
||||
|
||||
if let Err(e) = write_result {
|
||||
warn!(target: "afg", "Failed to write updated consensus changes to disk. Bailing.");
|
||||
warn!(target: "afg", "Node is in a potentially inconsistent state.");
|
||||
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
|
||||
// send a justification notification if a sender exists and in case of error log it.
|
||||
fn notify_justification<Block: BlockT>(
|
||||
justification_sender: Option<&GrandpaJustificationSender<Block>>,
|
||||
@@ -1280,9 +1247,7 @@ where
|
||||
let mut justification_required =
|
||||
// justification is always required when block that enacts new authorities
|
||||
// set is finalized
|
||||
status.new_set_block.is_some() ||
|
||||
// justification is required when consensus changes are finalized
|
||||
finalizes_consensus_changes;
|
||||
status.new_set_block.is_some();
|
||||
|
||||
// justification is required every N blocks to be able to prove blocks
|
||||
// finalization to remote nodes
|
||||
@@ -1387,57 +1352,7 @@ where
|
||||
Err(e) => {
|
||||
*authority_set = old_authority_set;
|
||||
|
||||
if let Some(old_consensus_changes) = old_consensus_changes {
|
||||
*consensus_changes = old_consensus_changes;
|
||||
}
|
||||
|
||||
Err(CommandOrError::Error(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Using the given base get the block at the given height on this chain. The
|
||||
/// target block must be an ancestor of base, therefore `height <= base.height`.
|
||||
pub(crate) fn canonical_at_height<Block: BlockT, C: HeaderBackend<Block>>(
|
||||
provider: &C,
|
||||
base: (Block::Hash, NumberFor<Block>),
|
||||
base_is_canonical: bool,
|
||||
height: NumberFor<Block>,
|
||||
) -> Result<Option<Block::Hash>, ClientError> {
|
||||
if height > base.1 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if height == base.1 {
|
||||
if base_is_canonical {
|
||||
return Ok(Some(base.0));
|
||||
} else {
|
||||
return Ok(provider.hash(height).unwrap_or(None));
|
||||
}
|
||||
} else if base_is_canonical {
|
||||
return Ok(provider.hash(height).unwrap_or(None));
|
||||
}
|
||||
|
||||
let one = NumberFor::<Block>::one();
|
||||
|
||||
// start by getting _canonical_ block with number at parent position and then iterating
|
||||
// backwards by hash.
|
||||
let mut current = match provider.header(BlockId::Number(base.1 - one))? {
|
||||
Some(header) => header,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
// we've already checked that base > height above.
|
||||
let mut steps = base.1 - height - one;
|
||||
|
||||
while steps > NumberFor::<Block>::zero() {
|
||||
current = match provider.header(BlockId::Hash(*current.parent_hash()))? {
|
||||
Some(header) => header,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
steps -= one;
|
||||
}
|
||||
|
||||
Ok(Some(current.hash()))
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// NOTE: should be removed with: https://github.com/paritytech/substrate/pull/7339
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! GRANDPA block finality proof generation and check.
|
||||
//!
|
||||
//! Finality of block B is proved by providing:
|
||||
@@ -37,7 +40,7 @@
|
||||
//! of the U) could be returned.
|
||||
|
||||
use std::sync::Arc;
|
||||
use log::{trace, warn};
|
||||
use log::trace;
|
||||
|
||||
use sp_blockchain::{Backend as BlockchainBackend, Error as ClientError, Result as ClientResult};
|
||||
use sc_client_api::{
|
||||
@@ -206,34 +209,6 @@ impl<B, Block> FinalityProofProvider<B, Block>
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, Block> sc_network::config::FinalityProofProvider<Block> for FinalityProofProvider<B, Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
B: Backend<Block> + 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: "afg", "Unable to decode finality proof request: {}", e.what());
|
||||
ClientError::Backend("Invalid finality proof request".to_string())
|
||||
})?;
|
||||
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> {
|
||||
@@ -290,14 +265,6 @@ struct OriginalFinalityProofRequest<H: Encode + Decode> {
|
||||
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'.
|
||||
|
||||
@@ -41,7 +41,6 @@ use sp_runtime::traits::{
|
||||
|
||||
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;
|
||||
use crate::notification::GrandpaJustificationSender;
|
||||
@@ -61,7 +60,6 @@ pub struct GrandpaBlockImport<Backend, Block: BlockT, Client, SC> {
|
||||
select_chain: SC,
|
||||
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
send_voter_commands: TracingUnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
|
||||
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
||||
authority_set_hard_forks: HashMap<Block::Hash, PendingChange<Block::Hash, NumberFor<Block>>>,
|
||||
justification_sender: GrandpaJustificationSender<Block>,
|
||||
_phantom: PhantomData<Backend>,
|
||||
@@ -76,7 +74,6 @@ impl<Backend, Block: BlockT, Client, SC: Clone> Clone for
|
||||
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(),
|
||||
authority_set_hard_forks: self.authority_set_hard_forks.clone(),
|
||||
justification_sender: self.justification_sender.clone(),
|
||||
_phantom: PhantomData,
|
||||
@@ -439,7 +436,6 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
|
||||
|
||||
// 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 = {
|
||||
@@ -517,7 +513,7 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
|
||||
);
|
||||
|
||||
import_res.unwrap_or_else(|err| {
|
||||
if needs_justification || enacts_consensus_change {
|
||||
if needs_justification {
|
||||
debug!(target: "afg", "Imported block #{} that enacts authority set change with \
|
||||
invalid justification: {:?}, requesting justification from peers.", number, err);
|
||||
imported_aux.bad_justification = true;
|
||||
@@ -535,12 +531,6 @@ impl<BE, Block: BlockT, Client, SC> BlockImport<Block>
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +551,6 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
|
||||
select_chain: SC,
|
||||
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
send_voter_commands: TracingUnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
|
||||
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
||||
authority_set_hard_forks: Vec<(SetId, PendingChange<Block::Hash, NumberFor<Block>>)>,
|
||||
justification_sender: GrandpaJustificationSender<Block>,
|
||||
) -> GrandpaBlockImport<Backend, Block, Client, SC> {
|
||||
@@ -605,7 +594,6 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
|
||||
select_chain,
|
||||
authority_set,
|
||||
send_voter_commands,
|
||||
consensus_changes,
|
||||
authority_set_hard_forks,
|
||||
justification_sender,
|
||||
_phantom: PhantomData,
|
||||
@@ -646,7 +634,6 @@ where
|
||||
let result = finalize_block(
|
||||
self.inner.clone(),
|
||||
&self.authority_set,
|
||||
&self.consensus_changes,
|
||||
None,
|
||||
hash,
|
||||
number,
|
||||
|
||||
@@ -112,12 +112,10 @@ macro_rules! afg_log {
|
||||
mod authorities;
|
||||
mod aux_schema;
|
||||
mod communication;
|
||||
mod consensus_changes;
|
||||
mod environment;
|
||||
mod finality_proof;
|
||||
mod import;
|
||||
mod justification;
|
||||
mod light_import;
|
||||
mod notification;
|
||||
mod observer;
|
||||
mod until_imported;
|
||||
@@ -128,7 +126,6 @@ pub use finality_proof::{FinalityProofFragment, FinalityProofProvider, StorageAn
|
||||
pub use notification::{GrandpaJustificationSender, GrandpaJustificationStream};
|
||||
pub use import::GrandpaBlockImport;
|
||||
pub use justification::GrandpaJustification;
|
||||
pub use light_import::{light_block_import, GrandpaLightBlockImport};
|
||||
pub use voting_rule::{
|
||||
BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRulesBuilder
|
||||
};
|
||||
@@ -588,7 +585,6 @@ where
|
||||
select_chain.clone(),
|
||||
persistent_data.authority_set.clone(),
|
||||
voter_commands_tx,
|
||||
persistent_data.consensus_changes.clone(),
|
||||
authority_set_hard_forks,
|
||||
justification_sender.clone(),
|
||||
),
|
||||
@@ -844,7 +840,6 @@ where
|
||||
network: network.clone(),
|
||||
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,
|
||||
metrics: metrics.as_ref().map(|m| m.environment.clone()),
|
||||
justification_sender: Some(justification_sender),
|
||||
@@ -989,7 +984,6 @@ where
|
||||
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(),
|
||||
metrics: self.env.metrics.clone(),
|
||||
|
||||
@@ -1,880 +0,0 @@
|
||||
// Copyright 2019-2020 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 sc_client_api::backend::{AuxStore, Backend, Finalizer, TransactionFor};
|
||||
use sp_blockchain::{HeaderBackend, Error as ClientError, well_known_cache_keys};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_consensus::{
|
||||
import_queue::Verifier,
|
||||
BlockOrigin, BlockImport, FinalityProofImport, BlockImportParams, ImportResult, ImportedAux,
|
||||
BlockCheckParams, Error as ConsensusError,
|
||||
};
|
||||
use sc_network::config::{BoxFinalityProofRequestBuilder, FinalityProofRequestBuilder};
|
||||
use sp_runtime::Justification;
|
||||
use sp_runtime::traits::{NumberFor, Block as BlockT, Header as HeaderT, DigestFor};
|
||||
use sp_finality_grandpa::{self, AuthorityList};
|
||||
use sp_runtime::generic::BlockId;
|
||||
|
||||
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<BE, Block: BlockT, Client>(
|
||||
client: Arc<Client>,
|
||||
backend: Arc<BE>,
|
||||
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
|
||||
authority_set_provider: Arc<dyn AuthoritySetForFinalityChecker<Block>>,
|
||||
) -> Result<GrandpaLightBlockImport<BE, Block, Client>, ClientError>
|
||||
where
|
||||
BE: Backend<Block>,
|
||||
Client: crate::ClientForGrandpa<Block, BE>,
|
||||
{
|
||||
let info = client.info();
|
||||
let import_data = load_aux_import_data(
|
||||
info.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<BE, Block: BlockT, Client> {
|
||||
client: Arc<Client>,
|
||||
backend: Arc<BE>,
|
||||
authority_set_provider: Arc<dyn AuthoritySetForFinalityChecker<Block>>,
|
||||
data: Arc<RwLock<LightImportData<Block>>>,
|
||||
}
|
||||
|
||||
impl<BE, Block: BlockT, Client> Clone for GrandpaLightBlockImport<BE, Block, Client> {
|
||||
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> {
|
||||
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<BE, Block: BlockT, Client> GrandpaLightBlockImport<BE, Block, Client> {
|
||||
/// Create finality proof request builder.
|
||||
pub fn create_finality_proof_request_builder(&self) -> BoxFinalityProofRequestBuilder<Block> {
|
||||
Box::new(GrandpaFinalityProofRequestBuilder(self.data.clone())) as _
|
||||
}
|
||||
}
|
||||
|
||||
impl<BE, Block: BlockT, Client> BlockImport<Block>
|
||||
for GrandpaLightBlockImport<BE, Block, Client> where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
BE: Backend<Block> + 'static,
|
||||
for<'a> &'a Client:
|
||||
HeaderBackend<Block>
|
||||
+ BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<BE, Block>>
|
||||
+ Finalizer<Block, BE>
|
||||
+ AuxStore,
|
||||
{
|
||||
type Error = ConsensusError;
|
||||
type Transaction = TransactionFor<BE, Block>;
|
||||
|
||||
fn import_block(
|
||||
&mut self,
|
||||
block: BlockImportParams<Block, Self::Transaction>,
|
||||
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<BE, Block: BlockT, Client> FinalityProofImport<Block>
|
||||
for GrandpaLightBlockImport<BE, Block, Client> where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
BE: Backend<Block> + 'static,
|
||||
for<'a> &'a Client:
|
||||
HeaderBackend<Block>
|
||||
+ BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<BE, Block>>
|
||||
+ Finalizer<Block, BE>
|
||||
+ AuxStore,
|
||||
{
|
||||
type Error = ConsensusError;
|
||||
|
||||
fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor<Block>)> {
|
||||
let mut out = Vec::new();
|
||||
let chain_info = (&*self.client).info();
|
||||
|
||||
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, *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: sp_finality_grandpa::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;
|
||||
self.authorities = authorities;
|
||||
}
|
||||
}
|
||||
|
||||
struct GrandpaFinalityProofRequestBuilder<B: BlockT>(Arc<RwLock<LightImportData<B>>>);
|
||||
|
||||
impl<B: BlockT> 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, J>(
|
||||
mut client: C,
|
||||
data: &mut LightImportData<Block>,
|
||||
mut block: BlockImportParams<Block, TransactionFor<B, Block>>,
|
||||
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
|
||||
) -> Result<ImportResult, ConsensusError>
|
||||
where
|
||||
C: HeaderBackend<Block>
|
||||
+ AuxStore
|
||||
+ Finalizer<Block, B>
|
||||
+ BlockImport<Block, Transaction = TransactionFor<B, Block>>
|
||||
+ Clone,
|
||||
B: Backend<Block> + 'static,
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
J: ProvableJustification<Block::Header>,
|
||||
{
|
||||
let hash = block.post_hash();
|
||||
let number = *block.header.number();
|
||||
|
||||
// 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())),
|
||||
};
|
||||
|
||||
match justification {
|
||||
Some(justification) => {
|
||||
trace!(
|
||||
target: "afg",
|
||||
"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: "afg",
|
||||
"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, 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, B>
|
||||
+ BlockImport<Block, Transaction = TransactionFor<B, Block>>
|
||||
+ Clone,
|
||||
B: Backend<Block> + 'static,
|
||||
DigestFor<Block>: Encode,
|
||||
NumberFor<Block>: finality_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::<_, _, J>(
|
||||
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.convert_transaction(),
|
||||
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.clone(),
|
||||
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,
|
||||
);
|
||||
|
||||
// store new authorities set
|
||||
require_insert_aux(
|
||||
&client,
|
||||
LIGHT_AUTHORITY_SET_KEY,
|
||||
&data.authority_set,
|
||||
"authority set",
|
||||
)?;
|
||||
|
||||
Ok((finalized_block_hash, finalized_block_number))
|
||||
}
|
||||
|
||||
/// Try to import justification.
|
||||
fn do_import_justification<B, C, Block: BlockT, 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, B>
|
||||
+ Clone,
|
||||
B: Backend<Block> + 'static,
|
||||
NumberFor<Block>: finality_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: "afg",
|
||||
"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: "afg",
|
||||
"Justification for {} is not valid. Bailing.",
|
||||
hash,
|
||||
);
|
||||
|
||||
return Err(ConsensusError::ClientImport(e.to_string()));
|
||||
},
|
||||
Ok(justification) => {
|
||||
trace!(
|
||||
target: "afg",
|
||||
"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>(
|
||||
client: C,
|
||||
data: &mut LightImportData<Block>,
|
||||
hash: Block::Hash,
|
||||
number: NumberFor<Block>,
|
||||
justification: Justification,
|
||||
) -> Result<ImportResult, ConsensusError>
|
||||
where
|
||||
C: HeaderBackend<Block>
|
||||
+ AuxStore
|
||||
+ Finalizer<Block, B>
|
||||
+ Clone,
|
||||
B: Backend<Block> + 'static,
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
{
|
||||
// finalize the block
|
||||
client.finalize_block(BlockId::Hash(hash), Some(justification), true).map_err(|e| {
|
||||
warn!(target: "afg", "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, (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>(
|
||||
last_finalized: Block::Hash,
|
||||
aux_store: &B,
|
||||
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
|
||||
) -> Result<LightImportData<Block>, ClientError>
|
||||
where
|
||||
B: AuxStore,
|
||||
Block: BlockT,
|
||||
{
|
||||
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: "afg", "Failed to write updated {} to disk. Bailing.", value_type);
|
||||
warn!(target: "afg", "Node is in a potentially inconsistent state.");
|
||||
ConsensusError::ClientImport(error.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use sp_consensus::{import_queue::CacheKeyId, ForkChoiceStrategy, BlockImport};
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
use sp_core::{H256, crypto::Public};
|
||||
use sc_client_api::{in_mem::Blockchain as InMemoryAuxStore, StorageProof, BlockBackend};
|
||||
use substrate_test_runtime_client::runtime::{Block, Header};
|
||||
use crate::tests::TestApi;
|
||||
use crate::finality_proof::{
|
||||
FinalityProofFragment,
|
||||
tests::{TestJustification, ClosureAuthoritySetForFinalityChecker},
|
||||
};
|
||||
|
||||
struct OkVerifier;
|
||||
|
||||
impl Verifier<Block> for OkVerifier {
|
||||
fn verify(
|
||||
&mut self,
|
||||
origin: BlockOrigin,
|
||||
header: Header,
|
||||
_justification: Option<Justification>,
|
||||
_body: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||
Ok((BlockImportParams::new(origin, header), None))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoJustificationsImport<BE, Block: BlockT, Client>(
|
||||
pub GrandpaLightBlockImport<BE, Block, Client>
|
||||
);
|
||||
|
||||
impl<BE, Block: BlockT, Client> Clone
|
||||
for NoJustificationsImport<BE, Block, Client> where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
BE: Backend<Block> + 'static,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
NoJustificationsImport(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<BE, Block: BlockT, Client> BlockImport<Block>
|
||||
for NoJustificationsImport<BE, Block, Client> where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
BE: Backend<Block> + 'static,
|
||||
for <'a > &'a Client:
|
||||
HeaderBackend<Block>
|
||||
+ BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<BE, Block>>
|
||||
+ Finalizer<Block, BE>
|
||||
+ AuxStore,
|
||||
GrandpaLightBlockImport<BE, Block, Client>:
|
||||
BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = ConsensusError>
|
||||
{
|
||||
type Error = ConsensusError;
|
||||
type Transaction = TransactionFor<BE, Block>;
|
||||
|
||||
fn import_block(
|
||||
&mut self,
|
||||
mut block: BlockImportParams<Block, Self::Transaction>,
|
||||
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<BE, Block: BlockT, Client> FinalityProofImport<Block>
|
||||
for NoJustificationsImport<BE, Block, Client> where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
BE: Backend<Block> + 'static,
|
||||
DigestFor<Block>: Encode,
|
||||
for <'a > &'a Client:
|
||||
HeaderBackend<Block>
|
||||
+ BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<BE, Block>>
|
||||
+ Finalizer<Block, BE>
|
||||
+ AuxStore,
|
||||
{
|
||||
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<BE, Block: BlockT, Client>(
|
||||
client: Arc<Client>,
|
||||
backend: Arc<BE>,
|
||||
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
|
||||
authority_set_provider: Arc<dyn AuthoritySetForFinalityChecker<Block>>,
|
||||
) -> Result<NoJustificationsImport<BE, Block, Client>, ClientError>
|
||||
where
|
||||
BE: Backend<Block> + 'static,
|
||||
Client: crate::ClientForGrandpa<Block, BE>,
|
||||
{
|
||||
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,
|
||||
substrate_test_runtime_client::client::Client<substrate_test_runtime_client::LightBackend, substrate_test_runtime_client::LightExecutor, Block, substrate_test_runtime_client::runtime::RuntimeApi>,
|
||||
Arc<substrate_test_runtime_client::LightBackend>,
|
||||
) {
|
||||
let (client, backend) = substrate_test_runtime_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 mut block = BlockImportParams::new(
|
||||
BlockOrigin::Own,
|
||||
Header {
|
||||
number: 1,
|
||||
parent_hash: client.chain_info().best_hash,
|
||||
state_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
},
|
||||
);
|
||||
block.justification = justification;
|
||||
block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||
|
||||
(
|
||||
do_import_block::<_, _, _, TestJustification>(
|
||||
&client,
|
||||
&mut import_data,
|
||||
block,
|
||||
new_cache,
|
||||
).unwrap(),
|
||||
client,
|
||||
backend,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finality_proof_not_required_when_consensus_data_does_not_changes_and_no_justification_provided() {
|
||||
assert_eq!(import_block(HashMap::new(), None).0, 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((0, vec![(AuthorityId::from_slice(&[1; 32]), 1)]), Vec::new()).encode();
|
||||
assert_eq!(import_block(HashMap::new(), Some(justification)).0, 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).0, 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((0, vec![]), 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)).0,
|
||||
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())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authority_set_is_updated_on_finality_proof_import() {
|
||||
let initial_set_id = 0;
|
||||
let initial_set = vec![(AuthorityId::from_slice(&[1; 32]), 1)];
|
||||
let updated_set = vec![(AuthorityId::from_slice(&[2; 32]), 2)];
|
||||
let babe_set_signal = vec![AuthorityId::from_slice(&[42; 32])].encode();
|
||||
|
||||
// import block #1 without justification
|
||||
let mut cache = HashMap::new();
|
||||
cache.insert(well_known_cache_keys::AUTHORITIES, babe_set_signal);
|
||||
let (_, client, backend) = import_block(cache, None);
|
||||
|
||||
// import finality proof for block #1
|
||||
let hash = client.block_hash(1).unwrap().unwrap();
|
||||
let mut verifier = OkVerifier;
|
||||
let mut import_data = LightImportData {
|
||||
last_finalized: Default::default(),
|
||||
authority_set: LightAuthoritySet::genesis(initial_set.clone()),
|
||||
consensus_changes: ConsensusChanges::empty(),
|
||||
};
|
||||
|
||||
// import finality proof
|
||||
do_import_finality_proof::<_, _, _, TestJustification>(
|
||||
&client,
|
||||
backend,
|
||||
&ClosureAuthoritySetForFinalityChecker(
|
||||
|_, _, _| Ok(updated_set.clone())
|
||||
),
|
||||
&mut import_data,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
vec![
|
||||
FinalityProofFragment::<Header> {
|
||||
block: hash,
|
||||
justification: TestJustification(
|
||||
(initial_set_id, initial_set.clone()),
|
||||
Vec::new(),
|
||||
).encode(),
|
||||
unknown_headers: Vec::new(),
|
||||
authorities_proof: Some(StorageProof::new(vec![])),
|
||||
},
|
||||
].encode(),
|
||||
&mut verifier,
|
||||
).unwrap();
|
||||
|
||||
// verify that new authorities set has been saved to the aux storage
|
||||
let data = load_aux_import_data(Default::default(), &client, &TestApi::new(initial_set)).unwrap();
|
||||
assert_eq!(data.authority_set.authorities(), updated_set);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,6 @@ use crate::{
|
||||
};
|
||||
use crate::authorities::SharedAuthoritySet;
|
||||
use crate::communication::{Network as NetworkT, NetworkBridge};
|
||||
use crate::consensus_changes::SharedConsensusChanges;
|
||||
use crate::notification::GrandpaJustificationSender;
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
use std::marker::{PhantomData, Unpin};
|
||||
@@ -68,7 +67,6 @@ impl<'a, Block, Client> finality_grandpa::Chain<Block::Hash, NumberFor<Block>>
|
||||
fn grandpa_observer<BE, Block: BlockT, Client, S, F>(
|
||||
client: &Arc<Client>,
|
||||
authority_set: &SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
consensus_changes: &SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
||||
voters: &Arc<VoterSet<AuthorityId>>,
|
||||
justification_sender: &Option<GrandpaJustificationSender<Block>>,
|
||||
last_finalized_number: NumberFor<Block>,
|
||||
@@ -83,7 +81,6 @@ where
|
||||
Client: crate::ClientForGrandpa<Block, BE>,
|
||||
{
|
||||
let authority_set = authority_set.clone();
|
||||
let consensus_changes = consensus_changes.clone();
|
||||
let client = client.clone();
|
||||
let voters = voters.clone();
|
||||
let justification_sender = justification_sender.clone();
|
||||
@@ -123,7 +120,6 @@ where
|
||||
match environment::finalize_block(
|
||||
client.clone(),
|
||||
&authority_set,
|
||||
&consensus_changes,
|
||||
None,
|
||||
finalized_hash,
|
||||
finalized_number,
|
||||
@@ -293,7 +289,6 @@ where
|
||||
let observer = grandpa_observer(
|
||||
&self.client,
|
||||
&self.persistent_data.authority_set,
|
||||
&self.persistent_data.consensus_changes,
|
||||
&voters,
|
||||
&self.justification_sender,
|
||||
last_finalized_number,
|
||||
|
||||
@@ -25,7 +25,7 @@ use sc_network_test::{
|
||||
Block, BlockImportAdapter, Hash, PassThroughVerifier, Peer, PeersClient, PeersFullClient,
|
||||
TestClient, TestNetFactory, FullPeerConfig,
|
||||
};
|
||||
use sc_network::config::{ProtocolConfig, BoxFinalityProofRequestBuilder};
|
||||
use sc_network::config::ProtocolConfig;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use futures_timer::Delay;
|
||||
use tokio::runtime::{Runtime, Handle};
|
||||
@@ -36,22 +36,21 @@ use sp_api::{ApiRef, StorageProof, ProvideRuntimeApi};
|
||||
use substrate_test_runtime_client::runtime::BlockNumber;
|
||||
use sp_consensus::{
|
||||
BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, ImportResult, BlockImport,
|
||||
import_queue::{BoxJustificationImport, BoxFinalityProofImport},
|
||||
import_queue::BoxJustificationImport,
|
||||
};
|
||||
use std::{collections::{HashMap, HashSet}, pin::Pin};
|
||||
use parity_scale_codec::Decode;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, HashFor};
|
||||
use sp_runtime::generic::{BlockId, DigestItem};
|
||||
use sp_core::{H256, crypto::Public};
|
||||
use sp_core::H256;
|
||||
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
||||
use sp_finality_grandpa::{GRANDPA_ENGINE_ID, AuthorityList, EquivocationProof, GrandpaApi, OpaqueKeyOwnershipProof};
|
||||
use sp_state_machine::{InMemoryBackend, prove_read, read_proof_check};
|
||||
|
||||
use authorities::AuthoritySet;
|
||||
use finality_proof::{
|
||||
FinalityProofProvider, AuthoritySetForFinalityProver, AuthoritySetForFinalityChecker,
|
||||
AuthoritySetForFinalityProver, AuthoritySetForFinalityChecker,
|
||||
};
|
||||
use consensus_changes::ConsensusChanges;
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_consensus::LongestChain;
|
||||
use sc_keystore::LocalKeystore;
|
||||
@@ -117,8 +116,6 @@ impl TestNetFactory for GrandpaTestNet {
|
||||
-> (
|
||||
BlockImportAdapter<Transaction>,
|
||||
Option<BoxJustificationImport<Block>>,
|
||||
Option<BoxFinalityProofImport<Block>>,
|
||||
Option<BoxFinalityProofRequestBuilder<Block>>,
|
||||
PeerData,
|
||||
)
|
||||
{
|
||||
@@ -133,48 +130,15 @@ impl TestNetFactory for GrandpaTestNet {
|
||||
(
|
||||
BlockImportAdapter::new_full(import),
|
||||
Some(justification_import),
|
||||
None,
|
||||
None,
|
||||
Mutex::new(Some(link)),
|
||||
)
|
||||
},
|
||||
PeersClient::Light(ref client, ref backend) => {
|
||||
use crate::light_import::tests::light_block_import_without_justifications;
|
||||
|
||||
let authorities_provider = Arc::new(self.test_config.clone());
|
||||
// forbid direct finalization using justification that came with the block
|
||||
// => light clients will try to fetch finality proofs
|
||||
let import = light_block_import_without_justifications(
|
||||
client.clone(),
|
||||
backend.clone(),
|
||||
&self.test_config,
|
||||
authorities_provider,
|
||||
).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());
|
||||
(
|
||||
BlockImportAdapter::new_light(import),
|
||||
None,
|
||||
Some(proof_import),
|
||||
Some(finality_proof_req_builder),
|
||||
Mutex::new(None),
|
||||
)
|
||||
PeersClient::Light(..) => {
|
||||
panic!("Light client is not used in tests.");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn make_finality_proof_provider(
|
||||
&self,
|
||||
client: PeersClient
|
||||
) -> Option<Arc<dyn sc_network::config::FinalityProofProvider<Block>>> {
|
||||
match client {
|
||||
PeersClient::Full(_, ref backend) => {
|
||||
Some(Arc::new(FinalityProofProvider::new(backend.clone(), self.test_config.clone())))
|
||||
},
|
||||
PeersClient::Light(_, _) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn peer(&mut self, i: usize) -> &mut GrandpaPeer {
|
||||
&mut self.peers[i]
|
||||
}
|
||||
@@ -679,24 +643,6 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
block_until_complete(wait_for, &net, &mut runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn justification_is_emitted_when_consensus_data_changes() {
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
|
||||
let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 3);
|
||||
|
||||
// import block#1 WITH consensus data change
|
||||
let new_authorities = vec![sp_consensus_babe::AuthorityId::from_slice(&[42; 32])];
|
||||
net.peer(0).push_authorities_change_block(new_authorities);
|
||||
net.block_until_sync();
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
run_to_completion(&mut runtime, 1, net.clone(), peers);
|
||||
|
||||
// ... and check that there's justification for block#1
|
||||
assert!(net.lock().peer(0).client().justification(&BlockId::Number(1)).unwrap().is_some(),
|
||||
"Missing justification for block#1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn justification_is_generated_periodically() {
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
@@ -717,25 +663,6 @@ fn justification_is_generated_periodically() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn consensus_changes_works() {
|
||||
let mut changes = ConsensusChanges::<H256, u64>::empty();
|
||||
|
||||
// pending changes are not finalized
|
||||
changes.note_change((10, H256::from_low_u64_be(1)));
|
||||
assert_eq!(changes.finalize((5, H256::from_low_u64_be(5)), |_| Ok(None)).unwrap(), (false, false));
|
||||
|
||||
// no change is selected from competing pending changes
|
||||
changes.note_change((1, H256::from_low_u64_be(1)));
|
||||
changes.note_change((1, H256::from_low_u64_be(101)));
|
||||
assert_eq!(changes.finalize((10, H256::from_low_u64_be(10)), |_| Ok(Some(H256::from_low_u64_be(1001)))).unwrap(), (true, false));
|
||||
|
||||
// change is selected from competing pending changes
|
||||
changes.note_change((1, H256::from_low_u64_be(1)));
|
||||
changes.note_change((1, H256::from_low_u64_be(101)));
|
||||
assert_eq!(changes.finalize((10, H256::from_low_u64_be(10)), |_| Ok(Some(H256::from_low_u64_be(1)))).unwrap(), (true, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_justifications_on_change_blocks() {
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
@@ -944,7 +871,6 @@ fn allows_reimporting_change_blocks() {
|
||||
needs_justification: true,
|
||||
clear_justification_requests: false,
|
||||
bad_justification: false,
|
||||
needs_finality_proof: false,
|
||||
is_new_best: true,
|
||||
header_only: false,
|
||||
}),
|
||||
@@ -1069,7 +995,7 @@ fn voter_persists_its_votes() {
|
||||
Poll::Pending => return Poll::Pending,
|
||||
Poll::Ready(None) => return Poll::Ready(()),
|
||||
Poll::Ready(Some(())) => {
|
||||
let (_block_import, _, _, _, link) =
|
||||
let (_block_import, _, link) =
|
||||
this.net.lock()
|
||||
.make_block_import::<
|
||||
TransactionFor<substrate_test_runtime_client::Backend, Block>
|
||||
@@ -1144,7 +1070,7 @@ fn voter_persists_its_votes() {
|
||||
};
|
||||
|
||||
let set_state = {
|
||||
let (_, _, _, _, link) = net.lock()
|
||||
let (_, _, link) = net.lock()
|
||||
.make_block_import::<
|
||||
TransactionFor<substrate_test_runtime_client::Backend, Block>
|
||||
>(client);
|
||||
@@ -1311,100 +1237,6 @@ fn finalize_3_voters_1_light_observer() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
let peers = &[Ed25519Keyring::Alice];
|
||||
let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 1);
|
||||
net.add_light_peer();
|
||||
|
||||
// import block#1 WITH consensus data change. Light client ignores justification
|
||||
// && instead fetches finality proof for block #1
|
||||
net.peer(0).push_authorities_change_block(vec![sp_consensus_babe::AuthorityId::from_slice(&[42; 32])]);
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
run_to_completion(&mut runtime, 1, net.clone(), peers);
|
||||
net.lock().block_until_sync();
|
||||
|
||||
// check that the block#1 is finalized on light client
|
||||
runtime.block_on(futures::future::poll_fn(move |cx| {
|
||||
if net.lock().peer(1).client().info().finalized_number == 1 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
net.lock().poll(cx);
|
||||
Poll::Pending
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_different() {
|
||||
// for debug: to ensure that without forced change light client will sync finality proof
|
||||
const FORCE_CHANGE: bool = true;
|
||||
|
||||
sp_tracing::try_init_simple();
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
|
||||
// two of these guys are offline.
|
||||
let genesis_authorities = if FORCE_CHANGE {
|
||||
vec![
|
||||
Ed25519Keyring::Alice,
|
||||
Ed25519Keyring::Bob,
|
||||
Ed25519Keyring::Charlie,
|
||||
Ed25519Keyring::One,
|
||||
Ed25519Keyring::Two,
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
Ed25519Keyring::Alice,
|
||||
Ed25519Keyring::Bob,
|
||||
Ed25519Keyring::Charlie,
|
||||
]
|
||||
};
|
||||
let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
|
||||
let api = TestApi::new(make_ids(&genesis_authorities));
|
||||
|
||||
let voters = make_ids(peers_a);
|
||||
let net = GrandpaTestNet::new(api, 3);
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
|
||||
// best is #1
|
||||
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
|
||||
// add a forced transition at block 5.
|
||||
let mut block = builder.build().unwrap().block;
|
||||
if FORCE_CHANGE {
|
||||
add_forced_change(&mut block, 0, ScheduledChange {
|
||||
next_authorities: voters.clone(),
|
||||
delay: 3,
|
||||
});
|
||||
}
|
||||
block
|
||||
});
|
||||
|
||||
// ensure block#10 enacts authorities set change => justification is generated
|
||||
// normally it will reach light client, but because of the forced change, it will not
|
||||
net.lock().peer(0).push_blocks(8, false); // best is #9
|
||||
net.lock().peer(0).push_authorities_change_block(
|
||||
vec![sp_consensus_babe::AuthorityId::from_slice(&[42; 32])]
|
||||
); // #10
|
||||
net.lock().peer(0).push_blocks(1, false); // best is #11
|
||||
net.lock().block_until_sync();
|
||||
|
||||
// finalize block #11 on full clients
|
||||
run_to_completion(&mut runtime, 11, net.clone(), peers_a);
|
||||
|
||||
// request finalization by light client
|
||||
net.lock().add_light_peer();
|
||||
net.lock().block_until_sync();
|
||||
|
||||
// check block, finalized on light client
|
||||
assert_eq!(
|
||||
net.lock().peer(3).client().info().finalized_number,
|
||||
if FORCE_CHANGE { 0 } else { 10 },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn voter_catches_up_to_latest_round_when_behind() {
|
||||
sp_tracing::try_init_simple();
|
||||
@@ -1540,7 +1372,6 @@ where
|
||||
{
|
||||
let PersistentData {
|
||||
ref authority_set,
|
||||
ref consensus_changes,
|
||||
ref set_state,
|
||||
..
|
||||
} = link.persistent_data;
|
||||
@@ -1564,7 +1395,6 @@ where
|
||||
Environment {
|
||||
authority_set: authority_set.clone(),
|
||||
config: config.clone(),
|
||||
consensus_changes: consensus_changes.clone(),
|
||||
client: link.client.clone(),
|
||||
select_chain: link.select_chain.clone(),
|
||||
set_id: authority_set.set_id(),
|
||||
|
||||
Reference in New Issue
Block a user