mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 12:51:02 +00:00
Run cargo fmt on the whole code base (#9394)
* Run cargo fmt on the whole code base * Second run * Add CI check * Fix compilation * More unnecessary braces * Handle weights * Use --all * Use correct attributes... * Fix UI tests * AHHHHHHHHH * 🤦 * Docs * Fix compilation * 🤷 * Please stop * 🤦 x 2 * More * make rustfmt.toml consistent with polkadot Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use sc_finality_grandpa::FinalityProofProvider;
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
@@ -44,7 +44,6 @@ where
|
||||
&self,
|
||||
block: NumberFor<Block>,
|
||||
) -> Result<Option<EncodedFinalityProof>, sc_finality_grandpa::FinalityProofError> {
|
||||
self.prove_finality(block)
|
||||
.map(|x| x.map(|y| EncodedFinalityProof(y.into())))
|
||||
self.prove_finality(block).map(|x| x.map(|y| EncodedFinalityProof(y.into())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,17 +19,16 @@
|
||||
//! RPC API for GRANDPA.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use std::sync::Arc;
|
||||
use futures::{FutureExt, TryFutureExt, TryStreamExt, StreamExt};
|
||||
use log::warn;
|
||||
use jsonrpc_derive::rpc;
|
||||
use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId, manager::SubscriptionManager};
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt};
|
||||
use jsonrpc_core::futures::{
|
||||
future::{Executor as Executor01, Future as Future01},
|
||||
sink::Sink as Sink01,
|
||||
stream::Stream as Stream01,
|
||||
future::Future as Future01,
|
||||
future::Executor as Executor01,
|
||||
};
|
||||
use jsonrpc_derive::rpc;
|
||||
use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
|
||||
use log::warn;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod error;
|
||||
mod finality;
|
||||
@@ -40,8 +39,8 @@ use sc_finality_grandpa::GrandpaJustificationStream;
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
|
||||
use finality::{EncodedFinalityProof, RpcFinalityProofProvider};
|
||||
use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates};
|
||||
use notification::JustificationNotification;
|
||||
use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates};
|
||||
|
||||
type FutureResult<T> =
|
||||
Box<dyn jsonrpc_core::futures::Future<Item = T, Error = jsonrpc_core::Error> + Send>;
|
||||
@@ -67,7 +66,7 @@ pub trait GrandpaApi<Notification, Hash, Number> {
|
||||
fn subscribe_justifications(
|
||||
&self,
|
||||
metadata: Self::Metadata,
|
||||
subscriber: Subscriber<Notification>
|
||||
subscriber: Subscriber<Notification>,
|
||||
);
|
||||
|
||||
/// Unsubscribe from receiving notifications about recently finalized blocks.
|
||||
@@ -79,16 +78,13 @@ pub trait GrandpaApi<Notification, Hash, Number> {
|
||||
fn unsubscribe_justifications(
|
||||
&self,
|
||||
metadata: Option<Self::Metadata>,
|
||||
id: SubscriptionId
|
||||
id: SubscriptionId,
|
||||
) -> jsonrpc_core::Result<bool>;
|
||||
|
||||
/// Prove finality for the given block number by returning the Justification for the last block
|
||||
/// in the set and all the intermediary headers to link them together.
|
||||
#[rpc(name = "grandpa_proveFinality")]
|
||||
fn prove_finality(
|
||||
&self,
|
||||
block: Number,
|
||||
) -> FutureResult<Option<EncodedFinalityProof>>;
|
||||
fn prove_finality(&self, block: Number) -> FutureResult<Option<EncodedFinalityProof>>;
|
||||
}
|
||||
|
||||
/// Implements the GrandpaApi RPC trait for interacting with GRANDPA.
|
||||
@@ -115,13 +111,7 @@ impl<AuthoritySet, VoterState, Block: BlockT, ProofProvider>
|
||||
E: Executor01<Box<dyn Future01<Item = (), Error = ()> + Send>> + Send + Sync + 'static,
|
||||
{
|
||||
let manager = SubscriptionManager::new(Arc::new(executor));
|
||||
Self {
|
||||
authority_set,
|
||||
voter_state,
|
||||
justification_stream,
|
||||
manager,
|
||||
finality_proof_provider,
|
||||
}
|
||||
Self { authority_set, voter_state, justification_stream, manager, finality_proof_provider }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,10 +135,12 @@ where
|
||||
fn subscribe_justifications(
|
||||
&self,
|
||||
_metadata: Self::Metadata,
|
||||
subscriber: Subscriber<JustificationNotification>
|
||||
subscriber: Subscriber<JustificationNotification>,
|
||||
) {
|
||||
let stream = self.justification_stream.subscribe()
|
||||
.map(|x| Ok::<_,()>(JustificationNotification::from(x)))
|
||||
let stream = self
|
||||
.justification_stream
|
||||
.subscribe()
|
||||
.map(|x| Ok::<_, ()>(JustificationNotification::from(x)))
|
||||
.map_err(|e| warn!("Notification stream error: {:?}", e))
|
||||
.compat();
|
||||
|
||||
@@ -163,7 +155,7 @@ where
|
||||
fn unsubscribe_justifications(
|
||||
&self,
|
||||
_metadata: Option<Self::Metadata>,
|
||||
id: SubscriptionId
|
||||
id: SubscriptionId,
|
||||
) -> jsonrpc_core::Result<bool> {
|
||||
Ok(self.manager.cancel(id))
|
||||
}
|
||||
@@ -181,7 +173,7 @@ where
|
||||
error::Error::ProveFinalityFailed(e)
|
||||
})
|
||||
.map_err(jsonrpc_core::Error::from)
|
||||
.compat()
|
||||
.compat(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -189,14 +181,13 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use jsonrpc_core::{types::Params, Notification, Output};
|
||||
use std::{collections::HashSet, convert::TryInto, sync::Arc};
|
||||
use jsonrpc_core::{Notification, Output, types::Params};
|
||||
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use sc_block_builder::{BlockBuilder, RecordProof};
|
||||
use sc_finality_grandpa::{
|
||||
report, AuthorityId, GrandpaJustificationSender, GrandpaJustification,
|
||||
FinalityProof,
|
||||
report, AuthorityId, FinalityProof, GrandpaJustification, GrandpaJustificationSender,
|
||||
};
|
||||
use sp_blockchain::HeaderBackend;
|
||||
use sp_core::crypto::Public;
|
||||
@@ -204,9 +195,7 @@ mod tests {
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
||||
use substrate_test_runtime_client::{
|
||||
runtime::{Block, Header, H256},
|
||||
DefaultTestClientBuilderExt,
|
||||
TestClientBuilderExt,
|
||||
TestClientBuilder,
|
||||
DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
struct TestAuthoritySet;
|
||||
@@ -253,14 +242,14 @@ mod tests {
|
||||
impl<Block: BlockT> RpcFinalityProofProvider<Block> for TestFinalityProofProvider {
|
||||
fn rpc_prove_finality(
|
||||
&self,
|
||||
_block: NumberFor<Block>
|
||||
_block: NumberFor<Block>,
|
||||
) -> Result<Option<EncodedFinalityProof>, sc_finality_grandpa::FinalityProofError> {
|
||||
Ok(Some(EncodedFinalityProof(
|
||||
self.finality_proof
|
||||
.as_ref()
|
||||
.expect("Don't call rpc_prove_finality without setting the FinalityProof")
|
||||
.encode()
|
||||
.into()
|
||||
.into(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
@@ -290,17 +279,14 @@ mod tests {
|
||||
|
||||
let background_rounds = vec![(1, past_round_state)].into_iter().collect();
|
||||
|
||||
Some(report::VoterState {
|
||||
background_rounds,
|
||||
best_round: (2, best_round_state),
|
||||
})
|
||||
Some(report::VoterState { background_rounds, best_round: (2, best_round_state) })
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_io_handler<VoterState>(voter_state: VoterState) -> (
|
||||
jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>,
|
||||
GrandpaJustificationSender<Block>,
|
||||
) where
|
||||
fn setup_io_handler<VoterState>(
|
||||
voter_state: VoterState,
|
||||
) -> (jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>, GrandpaJustificationSender<Block>)
|
||||
where
|
||||
VoterState: ReportVoterState + Send + Sync + 'static,
|
||||
{
|
||||
setup_io_handler_with_finality_proofs(voter_state, None)
|
||||
@@ -309,10 +295,8 @@ mod tests {
|
||||
fn setup_io_handler_with_finality_proofs<VoterState>(
|
||||
voter_state: VoterState,
|
||||
finality_proof: Option<FinalityProof<Header>>,
|
||||
) -> (
|
||||
jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>,
|
||||
GrandpaJustificationSender<Block>,
|
||||
) where
|
||||
) -> (jsonrpc_core::MetaIoHandler<sc_rpc::Metadata>, GrandpaJustificationSender<Block>)
|
||||
where
|
||||
VoterState: ReportVoterState + Send + Sync + 'static,
|
||||
{
|
||||
let (justification_sender, justification_stream) = GrandpaJustificationStream::channel();
|
||||
@@ -345,7 +329,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn working_rpc_handler() {
|
||||
let (io, _) = setup_io_handler(TestVoterState);
|
||||
let (io, _) = setup_io_handler(TestVoterState);
|
||||
|
||||
let request = r#"{"jsonrpc":"2.0","method":"grandpa_roundState","params":[],"id":1}"#;
|
||||
let response = "{\"jsonrpc\":\"2.0\",\"result\":{\
|
||||
@@ -378,7 +362,8 @@ mod tests {
|
||||
let (meta, _) = setup_session();
|
||||
|
||||
// Subscribe
|
||||
let sub_request = r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
|
||||
let sub_request =
|
||||
r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
|
||||
let resp = io.handle_request_sync(sub_request, meta.clone());
|
||||
let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
|
||||
|
||||
@@ -410,7 +395,8 @@ mod tests {
|
||||
let (meta, _) = setup_session();
|
||||
|
||||
// Subscribe
|
||||
let sub_request = r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
|
||||
let sub_request =
|
||||
r#"{"jsonrpc":"2.0","method":"grandpa_subscribeJustifications","params":[],"id":1}"#;
|
||||
let resp = io.handle_request_sync(sub_request, meta.clone());
|
||||
let resp: Output = serde_json::from_str(&resp.unwrap()).unwrap();
|
||||
assert!(matches!(resp, Output::Success(_)));
|
||||
@@ -440,7 +426,10 @@ mod tests {
|
||||
RecordProof::No,
|
||||
Default::default(),
|
||||
&*backend,
|
||||
).unwrap().build().unwrap();
|
||||
)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let block = built_block.block;
|
||||
let block_hash = block.hash();
|
||||
@@ -501,8 +490,7 @@ mod tests {
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
let recv_sub_id: String =
|
||||
serde_json::from_value(json_map["subscription"].take()).unwrap();
|
||||
let recv_sub_id: String = serde_json::from_value(json_map["subscription"].take()).unwrap();
|
||||
let recv_justification: sp_core::Bytes =
|
||||
serde_json::from_value(json_map["result"].take()).unwrap();
|
||||
let recv_justification: GrandpaJustification<Block> =
|
||||
@@ -520,10 +508,8 @@ mod tests {
|
||||
justification: create_justification().encode(),
|
||||
unknown_headers: vec![header(2)],
|
||||
};
|
||||
let (io, _) = setup_io_handler_with_finality_proofs(
|
||||
TestVoterState,
|
||||
Some(finality_proof.clone()),
|
||||
);
|
||||
let (io, _) =
|
||||
setup_io_handler_with_finality_proofs(TestVoterState, Some(finality_proof.clone()));
|
||||
|
||||
let request =
|
||||
"{\"jsonrpc\":\"2.0\",\"method\":\"grandpa_proveFinality\",\"params\":[42],\"id\":1}";
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use parity_scale_codec::Encode;
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use sc_finality_grandpa::GrandpaJustification;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
|
||||
/// An encoded justification proving that the given header has been finalized
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -44,11 +44,8 @@ where
|
||||
H: Clone + Debug + Eq,
|
||||
{
|
||||
fn get(&self) -> (u64, HashSet<AuthorityId>) {
|
||||
let current_voters: HashSet<AuthorityId> = self
|
||||
.current_authorities()
|
||||
.iter()
|
||||
.map(|p| p.0.clone())
|
||||
.collect();
|
||||
let current_voters: HashSet<AuthorityId> =
|
||||
self.current_authorities().iter().map(|p| p.0.clone()).collect();
|
||||
|
||||
(self.set_id(), current_voters)
|
||||
}
|
||||
@@ -152,10 +149,6 @@ impl ReportedRoundStates {
|
||||
.map(|(round, round_state)| RoundState::from(*round, round_state, ¤t_voters))
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
|
||||
Ok(Self {
|
||||
set_id,
|
||||
best,
|
||||
background,
|
||||
})
|
||||
Ok(Self { set_id, best, background })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,16 @@
|
||||
|
||||
//! Utilities for dealing with authorities, authority sets, and handoffs.
|
||||
|
||||
use std::cmp::Ord;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Add;
|
||||
use std::{cmp::Ord, fmt::Debug, ops::Add};
|
||||
|
||||
use fork_tree::ForkTree;
|
||||
use parking_lot::MappedMutexGuard;
|
||||
use finality_grandpa::voter_set::VoterSet;
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use fork_tree::ForkTree;
|
||||
use log::debug;
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use parking_lot::MappedMutexGuard;
|
||||
use sc_consensus::shared_data::{SharedData, SharedDataLocked};
|
||||
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_INFO};
|
||||
use sp_finality_grandpa::{AuthorityId, AuthorityList};
|
||||
use sc_consensus::shared_data::{SharedData, SharedDataLocked};
|
||||
|
||||
use crate::SetId;
|
||||
|
||||
@@ -77,9 +75,7 @@ pub struct SharedAuthoritySet<H, N> {
|
||||
|
||||
impl<H, N> Clone for SharedAuthoritySet<H, N> {
|
||||
fn clone(&self) -> Self {
|
||||
SharedAuthoritySet {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
SharedAuthoritySet { inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,16 +88,15 @@ impl<H, N> SharedAuthoritySet<H, N> {
|
||||
/// Returns access to the [`AuthoritySet`] and locks it.
|
||||
///
|
||||
/// For more information see [`SharedDataLocked`].
|
||||
pub(crate) fn inner_locked(
|
||||
&self,
|
||||
) -> SharedDataLocked<AuthoritySet<H, N>> {
|
||||
pub(crate) fn inner_locked(&self) -> SharedDataLocked<AuthoritySet<H, N>> {
|
||||
self.inner.shared_data_locked()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Eq, N> SharedAuthoritySet<H, N>
|
||||
where N: Add<Output=N> + Ord + Clone + Debug,
|
||||
H: Clone + Debug
|
||||
where
|
||||
N: Add<Output = N> + Ord + Clone + Debug,
|
||||
H: Clone + Debug,
|
||||
{
|
||||
/// Get the earliest limit-block number that's higher or equal to the given
|
||||
/// min number, if any.
|
||||
@@ -136,9 +131,7 @@ where N: Add<Output=N> + Ord + Clone + Debug,
|
||||
|
||||
impl<H, N> From<AuthoritySet<H, N>> for SharedAuthoritySet<H, N> {
|
||||
fn from(set: AuthoritySet<H, N>) -> Self {
|
||||
SharedAuthoritySet {
|
||||
inner: SharedData::new(set),
|
||||
}
|
||||
SharedAuthoritySet { inner: SharedData::new(set) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +184,7 @@ where
|
||||
/// Get a genesis set with given authorities.
|
||||
pub(crate) fn genesis(initial: AuthorityList) -> Option<Self> {
|
||||
if Self::invalid_authority_list(&initial) {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
Some(AuthoritySet {
|
||||
@@ -212,7 +205,7 @@ where
|
||||
authority_set_changes: AuthoritySetChanges<N>,
|
||||
) -> Option<Self> {
|
||||
if Self::invalid_authority_list(&authorities) {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
Some(AuthoritySet {
|
||||
@@ -255,7 +248,7 @@ where
|
||||
for change in &self.pending_forced_changes {
|
||||
if is_descendent_of(&change.canon_hash, best_hash)? {
|
||||
forced = Some((change.canon_hash.clone(), change.canon_height.clone()));
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,16 +256,13 @@ where
|
||||
for (_, _, change) in self.pending_standard_changes.roots() {
|
||||
if is_descendent_of(&change.canon_hash, best_hash)? {
|
||||
standard = Some((change.canon_hash.clone(), change.canon_height.clone()));
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let earliest = match (forced, standard) {
|
||||
(Some(forced), Some(standard)) => Some(if forced.1 < standard.1 {
|
||||
forced
|
||||
} else {
|
||||
standard
|
||||
}),
|
||||
(Some(forced), Some(standard)) =>
|
||||
Some(if forced.1 < standard.1 { forced } else { standard }),
|
||||
(Some(forced), None) => Some(forced),
|
||||
(None, Some(standard)) => Some(standard),
|
||||
(None, None) => None,
|
||||
@@ -300,12 +290,7 @@ where
|
||||
pending.delay,
|
||||
);
|
||||
|
||||
self.pending_standard_changes.import(
|
||||
hash,
|
||||
number,
|
||||
pending,
|
||||
is_descendent_of,
|
||||
)?;
|
||||
self.pending_standard_changes.import(hash, number, pending, is_descendent_of)?;
|
||||
|
||||
debug!(
|
||||
target: "afg",
|
||||
@@ -329,21 +314,21 @@ where
|
||||
{
|
||||
for change in &self.pending_forced_changes {
|
||||
if change.canon_hash == pending.canon_hash {
|
||||
return Err(Error::DuplicateAuthoritySetChange);
|
||||
return Err(Error::DuplicateAuthoritySetChange)
|
||||
}
|
||||
|
||||
if is_descendent_of(&change.canon_hash, &pending.canon_hash)? {
|
||||
return Err(Error::MultiplePendingForcedAuthoritySetChanges);
|
||||
return Err(Error::MultiplePendingForcedAuthoritySetChanges)
|
||||
}
|
||||
}
|
||||
|
||||
// ordered first by effective number and then by signal-block number.
|
||||
let key = (pending.effective_number(), pending.canon_height.clone());
|
||||
let idx = self.pending_forced_changes
|
||||
.binary_search_by_key(&key, |change| (
|
||||
change.effective_number(),
|
||||
change.canon_height.clone(),
|
||||
))
|
||||
let idx = self
|
||||
.pending_forced_changes
|
||||
.binary_search_by_key(&key, |change| {
|
||||
(change.effective_number(), change.canon_height.clone())
|
||||
})
|
||||
.unwrap_or_else(|i| i);
|
||||
|
||||
debug!(
|
||||
@@ -376,24 +361,22 @@ where
|
||||
E: std::error::Error,
|
||||
{
|
||||
if Self::invalid_authority_list(&pending.next_authorities) {
|
||||
return Err(Error::InvalidAuthoritySet);
|
||||
return Err(Error::InvalidAuthoritySet)
|
||||
}
|
||||
|
||||
match pending.delay_kind {
|
||||
DelayKind::Best { .. } => {
|
||||
self.add_forced_change(pending, is_descendent_of)
|
||||
},
|
||||
DelayKind::Finalized => {
|
||||
self.add_standard_change(pending, is_descendent_of)
|
||||
},
|
||||
DelayKind::Best { .. } => self.add_forced_change(pending, is_descendent_of),
|
||||
DelayKind::Finalized => self.add_standard_change(pending, is_descendent_of),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inspect pending changes. Standard pending changes are iterated first,
|
||||
/// and the changes in the tree are traversed in pre-order, afterwards all
|
||||
/// forced changes are iterated.
|
||||
pub(crate) fn pending_changes(&self) -> impl Iterator<Item=&PendingChange<H, N>> {
|
||||
self.pending_standard_changes.iter().map(|(_, _, c)| c)
|
||||
pub(crate) fn pending_changes(&self) -> impl Iterator<Item = &PendingChange<H, N>> {
|
||||
self.pending_standard_changes
|
||||
.iter()
|
||||
.map(|(_, _, c)| c)
|
||||
.chain(self.pending_forced_changes.iter())
|
||||
}
|
||||
|
||||
@@ -404,7 +387,8 @@ where
|
||||
/// Only standard changes are taken into account for the current
|
||||
/// limit, since any existing forced change should preclude the voter from voting.
|
||||
pub(crate) fn current_limit(&self, min: N) -> Option<N> {
|
||||
self.pending_standard_changes.roots()
|
||||
self.pending_standard_changes
|
||||
.roots()
|
||||
.filter(|&(_, _, c)| c.effective_number() >= min)
|
||||
.min_by_key(|&(_, _, c)| c.effective_number())
|
||||
.map(|(_, _, c)| c.effective_number())
|
||||
@@ -450,9 +434,7 @@ where
|
||||
// the block that signaled the change.
|
||||
if change.canon_hash == best_hash || is_descendent_of(&change.canon_hash, &best_hash)? {
|
||||
let median_last_finalized = match change.delay_kind {
|
||||
DelayKind::Best {
|
||||
ref median_last_finalized,
|
||||
} => median_last_finalized.clone(),
|
||||
DelayKind::Best { ref median_last_finalized } => median_last_finalized.clone(),
|
||||
_ => unreachable!(
|
||||
"pending_forced_changes only contains forced changes; forced changes have delay kind Best; qed."
|
||||
),
|
||||
@@ -460,8 +442,8 @@ where
|
||||
|
||||
// check if there's any pending standard change that we depend on
|
||||
for (_, _, standard_change) in self.pending_standard_changes.roots() {
|
||||
if standard_change.effective_number() <= median_last_finalized
|
||||
&& is_descendent_of(&standard_change.canon_hash, &change.canon_hash)?
|
||||
if standard_change.effective_number() <= median_last_finalized &&
|
||||
is_descendent_of(&standard_change.canon_hash, &change.canon_hash)?
|
||||
{
|
||||
log::info!(target: "afg",
|
||||
"Not applying authority set change forced at block #{:?}, due to pending standard change at block #{:?}",
|
||||
@@ -469,11 +451,9 @@ where
|
||||
standard_change.effective_number(),
|
||||
);
|
||||
|
||||
return Err(
|
||||
Error::ForcedAuthoritySetChangeDependencyUnsatisfied(
|
||||
standard_change.effective_number()
|
||||
)
|
||||
);
|
||||
return Err(Error::ForcedAuthoritySetChangeDependencyUnsatisfied(
|
||||
standard_change.effective_number(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,7 +485,7 @@ where
|
||||
},
|
||||
));
|
||||
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,24 +516,19 @@ where
|
||||
F: Fn(&H, &H) -> Result<bool, E>,
|
||||
E: std::error::Error,
|
||||
{
|
||||
let mut status = Status {
|
||||
changed: false,
|
||||
new_set_block: None,
|
||||
};
|
||||
let mut status = Status { changed: false, new_set_block: None };
|
||||
|
||||
match self.pending_standard_changes.finalize_with_descendent_if(
|
||||
&finalized_hash,
|
||||
finalized_number.clone(),
|
||||
is_descendent_of,
|
||||
|change| change.effective_number() <= finalized_number
|
||||
|change| change.effective_number() <= finalized_number,
|
||||
)? {
|
||||
fork_tree::FinalizationResult::Changed(change) => {
|
||||
status.changed = true;
|
||||
|
||||
let pending_forced_changes = std::mem::replace(
|
||||
&mut self.pending_forced_changes,
|
||||
Vec::new(),
|
||||
);
|
||||
let pending_forced_changes =
|
||||
std::mem::replace(&mut self.pending_forced_changes, Vec::new());
|
||||
|
||||
// we will keep all forced changes for any later blocks and that are a
|
||||
// descendent of the finalized block (i.e. they are part of this branch).
|
||||
@@ -566,7 +541,8 @@ where
|
||||
}
|
||||
|
||||
if let Some(change) = change {
|
||||
afg_log!(initial_sync,
|
||||
afg_log!(
|
||||
initial_sync,
|
||||
"👴 Applying authority set change scheduled at block #{:?}",
|
||||
change.canon_height,
|
||||
);
|
||||
@@ -583,10 +559,7 @@ where
|
||||
self.current_authorities = change.next_authorities;
|
||||
self.set_id += 1;
|
||||
|
||||
status.new_set_block = Some((
|
||||
finalized_hash,
|
||||
finalized_number,
|
||||
));
|
||||
status.new_set_block = Some((finalized_hash, finalized_number));
|
||||
}
|
||||
},
|
||||
fork_tree::FinalizationResult::Unchanged => {},
|
||||
@@ -615,12 +588,14 @@ where
|
||||
F: Fn(&H, &H) -> Result<bool, E>,
|
||||
E: std::error::Error,
|
||||
{
|
||||
self.pending_standard_changes.finalizes_any_with_descendent_if(
|
||||
&finalized_hash,
|
||||
finalized_number.clone(),
|
||||
is_descendent_of,
|
||||
|change| change.effective_number() == finalized_number
|
||||
).map_err(Error::ForkTree)
|
||||
self.pending_standard_changes
|
||||
.finalizes_any_with_descendent_if(
|
||||
&finalized_hash,
|
||||
finalized_number.clone(),
|
||||
is_descendent_of,
|
||||
|change| change.effective_number() == finalized_number,
|
||||
)
|
||||
.map_err(Error::ForkTree)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,7 +629,9 @@ pub struct PendingChange<H, N> {
|
||||
}
|
||||
|
||||
impl<H: Decode, N: Decode> Decode for PendingChange<H, N> {
|
||||
fn decode<I: parity_scale_codec::Input>(value: &mut I) -> Result<Self, parity_scale_codec::Error> {
|
||||
fn decode<I: parity_scale_codec::Input>(
|
||||
value: &mut I,
|
||||
) -> Result<Self, parity_scale_codec::Error> {
|
||||
let next_authorities = Decode::decode(value)?;
|
||||
let delay = Decode::decode(value)?;
|
||||
let canon_height = Decode::decode(value)?;
|
||||
@@ -662,17 +639,11 @@ impl<H: Decode, N: Decode> Decode for PendingChange<H, N> {
|
||||
|
||||
let delay_kind = DelayKind::decode(value).unwrap_or(DelayKind::Finalized);
|
||||
|
||||
Ok(PendingChange {
|
||||
next_authorities,
|
||||
delay,
|
||||
canon_height,
|
||||
canon_hash,
|
||||
delay_kind,
|
||||
})
|
||||
Ok(PendingChange { next_authorities, delay, canon_height, canon_hash, delay_kind })
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, N: Add<Output=N> + Clone> PendingChange<H, N> {
|
||||
impl<H, N: Add<Output = N> + Clone> PendingChange<H, N> {
|
||||
/// Returns the effective number this change will be applied at.
|
||||
pub fn effective_number(&self) -> N {
|
||||
self.canon_height.clone() + self.delay.clone()
|
||||
@@ -715,15 +686,17 @@ impl<N: Ord + Clone> AuthoritySetChanges<N> {
|
||||
}
|
||||
|
||||
pub(crate) fn get_set_id(&self, block_number: N) -> AuthoritySetChangeId<N> {
|
||||
if self.0
|
||||
if self
|
||||
.0
|
||||
.last()
|
||||
.map(|last_auth_change| last_auth_change.1 < block_number)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return AuthoritySetChangeId::Latest;
|
||||
return AuthoritySetChangeId::Latest
|
||||
}
|
||||
|
||||
let idx = self.0
|
||||
let idx = self
|
||||
.0
|
||||
.binary_search_by_key(&block_number, |(_, n)| n.clone())
|
||||
.unwrap_or_else(|b| b);
|
||||
|
||||
@@ -732,7 +705,7 @@ impl<N: Ord + Clone> AuthoritySetChanges<N> {
|
||||
|
||||
// if this is the first index but not the first set id then we are missing data.
|
||||
if idx == 0 && set_id != 0 {
|
||||
return AuthoritySetChangeId::Unknown;
|
||||
return AuthoritySetChangeId::Unknown
|
||||
}
|
||||
|
||||
AuthoritySetChangeId::Set(set_id, block_number)
|
||||
@@ -745,7 +718,9 @@ impl<N: Ord + Clone> AuthoritySetChanges<N> {
|
||||
/// number (excluded). The iterator yields a tuple representing the set id and the block number
|
||||
/// of the last block in that set.
|
||||
pub fn iter_from(&self, block_number: N) -> Option<impl Iterator<Item = &(u64, N)>> {
|
||||
let idx = self.0.binary_search_by_key(&block_number, |(_, n)| n.clone())
|
||||
let idx = self
|
||||
.0
|
||||
.binary_search_by_key(&block_number, |(_, n)| n.clone())
|
||||
// if there was a change at the given block number then we should start on the next
|
||||
// index since we want to exclude the current block number
|
||||
.map(|n| n + 1)
|
||||
@@ -756,7 +731,7 @@ impl<N: Ord + Clone> AuthoritySetChanges<N> {
|
||||
|
||||
// if this is the first index but not the first set id then we are missing data.
|
||||
if idx == 0 && set_id != 0 {
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,14 +744,13 @@ mod tests {
|
||||
use super::*;
|
||||
use sp_core::crypto::Public;
|
||||
|
||||
fn static_is_descendent_of<A>(value: bool)
|
||||
-> impl Fn(&A, &A) -> Result<bool, std::io::Error>
|
||||
{
|
||||
fn static_is_descendent_of<A>(value: bool) -> impl Fn(&A, &A) -> Result<bool, std::io::Error> {
|
||||
move |_, _| Ok(value)
|
||||
}
|
||||
|
||||
fn is_descendent_of<A, F>(f: F) -> impl Fn(&A, &A) -> Result<bool, std::io::Error>
|
||||
where F: Fn(&A, &A) -> bool
|
||||
where
|
||||
F: Fn(&A, &A) -> bool,
|
||||
{
|
||||
move |base, hash| Ok(f(base, hash))
|
||||
}
|
||||
@@ -793,14 +767,12 @@ mod tests {
|
||||
authority_set_changes: AuthoritySetChanges::empty(),
|
||||
};
|
||||
|
||||
let change = |height| {
|
||||
PendingChange {
|
||||
next_authorities: current_authorities.clone(),
|
||||
delay: 0,
|
||||
canon_height: height,
|
||||
canon_hash: height.to_string(),
|
||||
delay_kind: DelayKind::Finalized,
|
||||
}
|
||||
let change = |height| PendingChange {
|
||||
next_authorities: current_authorities.clone(),
|
||||
delay: 0,
|
||||
canon_height: height,
|
||||
canon_hash: height.to_string(),
|
||||
delay_kind: DelayKind::Finalized,
|
||||
};
|
||||
|
||||
let is_descendent_of = static_is_descendent_of(false);
|
||||
@@ -808,25 +780,13 @@ mod tests {
|
||||
authorities.add_pending_change(change(1), &is_descendent_of).unwrap();
|
||||
authorities.add_pending_change(change(2), &is_descendent_of).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
authorities.current_limit(0),
|
||||
Some(1),
|
||||
);
|
||||
assert_eq!(authorities.current_limit(0), Some(1),);
|
||||
|
||||
assert_eq!(
|
||||
authorities.current_limit(1),
|
||||
Some(1),
|
||||
);
|
||||
assert_eq!(authorities.current_limit(1), Some(1),);
|
||||
|
||||
assert_eq!(
|
||||
authorities.current_limit(2),
|
||||
Some(2),
|
||||
);
|
||||
assert_eq!(authorities.current_limit(2), Some(2),);
|
||||
|
||||
assert_eq!(
|
||||
authorities.current_limit(3),
|
||||
None,
|
||||
);
|
||||
assert_eq!(authorities.current_limit(3), None,);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -865,13 +825,22 @@ mod tests {
|
||||
delay_kind: DelayKind::Finalized,
|
||||
};
|
||||
|
||||
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(false)).unwrap();
|
||||
authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(false)).unwrap();
|
||||
authorities.add_pending_change(change_c.clone(), &is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_c") => true,
|
||||
("hash_b", "hash_c") => false,
|
||||
_ => unreachable!(),
|
||||
})).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a.clone(), &static_is_descendent_of(false))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_b.clone(), &static_is_descendent_of(false))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(
|
||||
change_c.clone(),
|
||||
&is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_c") => true,
|
||||
("hash_b", "hash_c") => false,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// forced changes are iterated last
|
||||
let change_d = PendingChange {
|
||||
@@ -890,8 +859,12 @@ mod tests {
|
||||
delay_kind: DelayKind::Best { median_last_finalized: 0 },
|
||||
};
|
||||
|
||||
authorities.add_pending_change(change_d.clone(), &static_is_descendent_of(false)).unwrap();
|
||||
authorities.add_pending_change(change_e.clone(), &static_is_descendent_of(false)).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_d.clone(), &static_is_descendent_of(false))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_e.clone(), &static_is_descendent_of(false))
|
||||
.unwrap();
|
||||
|
||||
// ordered by subtree depth
|
||||
assert_eq!(
|
||||
@@ -930,46 +903,48 @@ mod tests {
|
||||
delay_kind: DelayKind::Finalized,
|
||||
};
|
||||
|
||||
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(true)).unwrap();
|
||||
authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(true)).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a.clone(), &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_b.clone(), &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
authorities.pending_changes().collect::<Vec<_>>(),
|
||||
vec![&change_a, &change_b],
|
||||
);
|
||||
assert_eq!(authorities.pending_changes().collect::<Vec<_>>(), vec![&change_a, &change_b],);
|
||||
|
||||
// finalizing "hash_c" won't enact the change signaled at "hash_a" but it will prune out "hash_b"
|
||||
let status = authorities.apply_standard_changes(
|
||||
"hash_c",
|
||||
11,
|
||||
&is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_c") => true,
|
||||
("hash_b", "hash_c") => false,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
false,
|
||||
None,
|
||||
).unwrap();
|
||||
let status = authorities
|
||||
.apply_standard_changes(
|
||||
"hash_c",
|
||||
11,
|
||||
&is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_c") => true,
|
||||
("hash_b", "hash_c") => false,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(status.changed);
|
||||
assert_eq!(status.new_set_block, None);
|
||||
assert_eq!(
|
||||
authorities.pending_changes().collect::<Vec<_>>(),
|
||||
vec![&change_a],
|
||||
);
|
||||
assert_eq!(authorities.pending_changes().collect::<Vec<_>>(), vec![&change_a],);
|
||||
assert_eq!(authorities.authority_set_changes, AuthoritySetChanges::empty());
|
||||
|
||||
// finalizing "hash_d" will enact the change signaled at "hash_a"
|
||||
let status = authorities.apply_standard_changes(
|
||||
"hash_d",
|
||||
15,
|
||||
&is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_d") => true,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
false,
|
||||
None,
|
||||
).unwrap();
|
||||
let status = authorities
|
||||
.apply_standard_changes(
|
||||
"hash_d",
|
||||
15,
|
||||
&is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_d") => true,
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(status.changed);
|
||||
assert_eq!(status.new_set_block, Some(("hash_d", 15)));
|
||||
@@ -1010,8 +985,12 @@ mod tests {
|
||||
delay_kind: DelayKind::Finalized,
|
||||
};
|
||||
|
||||
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(true)).unwrap();
|
||||
authorities.add_pending_change(change_c.clone(), &static_is_descendent_of(true)).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a.clone(), &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_c.clone(), &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
|
||||
let is_descendent_of = is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_b") => true,
|
||||
@@ -1032,13 +1011,9 @@ mod tests {
|
||||
));
|
||||
assert_eq!(authorities.authority_set_changes, AuthoritySetChanges::empty());
|
||||
|
||||
let status = authorities.apply_standard_changes(
|
||||
"hash_b",
|
||||
15,
|
||||
&is_descendent_of,
|
||||
false,
|
||||
None,
|
||||
).unwrap();
|
||||
let status = authorities
|
||||
.apply_standard_changes("hash_b", 15, &is_descendent_of, false, None)
|
||||
.unwrap();
|
||||
|
||||
assert!(status.changed);
|
||||
assert_eq!(status.new_set_block, Some(("hash_b", 15)));
|
||||
@@ -1048,13 +1023,9 @@ mod tests {
|
||||
assert_eq!(authorities.authority_set_changes, AuthoritySetChanges(vec![(0, 15)]));
|
||||
|
||||
// after finalizing `change_a` it should be possible to finalize `change_c`
|
||||
let status = authorities.apply_standard_changes(
|
||||
"hash_d",
|
||||
40,
|
||||
&is_descendent_of,
|
||||
false,
|
||||
None,
|
||||
).unwrap();
|
||||
let status = authorities
|
||||
.apply_standard_changes("hash_d", 40, &is_descendent_of, false, None)
|
||||
.unwrap();
|
||||
|
||||
assert!(status.changed);
|
||||
assert_eq!(status.new_set_block, Some(("hash_d", 40)));
|
||||
@@ -1092,8 +1063,12 @@ mod tests {
|
||||
delay_kind: DelayKind::Finalized,
|
||||
};
|
||||
|
||||
authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(false)).unwrap();
|
||||
authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(true)).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a.clone(), &static_is_descendent_of(false))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_b.clone(), &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
|
||||
let is_descendent_of = is_descendent_of(|base, hash| match (*base, *hash) {
|
||||
("hash_a", "hash_d") => true,
|
||||
@@ -1160,8 +1135,12 @@ mod tests {
|
||||
delay_kind: DelayKind::Best { median_last_finalized: 0 },
|
||||
};
|
||||
|
||||
authorities.add_pending_change(change_a, &static_is_descendent_of(false)).unwrap();
|
||||
authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(false)).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a, &static_is_descendent_of(false))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_b.clone(), &static_is_descendent_of(false))
|
||||
.unwrap();
|
||||
|
||||
// no duplicates are allowed
|
||||
assert!(matches!(
|
||||
@@ -1172,7 +1151,9 @@ mod tests {
|
||||
// there's an effective change triggered at block 15 but not a standard one.
|
||||
// so this should do nothing.
|
||||
assert_eq!(
|
||||
authorities.enacts_standard_change("hash_c", 15, &static_is_descendent_of(true)).unwrap(),
|
||||
authorities
|
||||
.enacts_standard_change("hash_c", 15, &static_is_descendent_of(true))
|
||||
.unwrap(),
|
||||
None,
|
||||
);
|
||||
|
||||
@@ -1194,20 +1175,16 @@ mod tests {
|
||||
|
||||
// let's try and apply the forced changes.
|
||||
// too early and there's no forced changes to apply.
|
||||
assert!(
|
||||
authorities
|
||||
.apply_forced_changes("hash_a10", 10, &static_is_descendent_of(true), false, None)
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert!(authorities
|
||||
.apply_forced_changes("hash_a10", 10, &static_is_descendent_of(true), false, None)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
// too late.
|
||||
assert!(
|
||||
authorities
|
||||
.apply_forced_changes("hash_a16", 16, &is_descendent_of_a, false, None)
|
||||
.unwrap()
|
||||
.is_none()
|
||||
);
|
||||
assert!(authorities
|
||||
.apply_forced_changes("hash_a16", 16, &is_descendent_of_a, false, None)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
// on time -- chooses the right change for this fork.
|
||||
assert_eq!(
|
||||
@@ -1247,9 +1224,7 @@ mod tests {
|
||||
delay: 0,
|
||||
canon_height: 5,
|
||||
canon_hash: "hash_a",
|
||||
delay_kind: DelayKind::Best {
|
||||
median_last_finalized: 0,
|
||||
},
|
||||
delay_kind: DelayKind::Best { median_last_finalized: 0 },
|
||||
};
|
||||
|
||||
// and import it
|
||||
@@ -1258,12 +1233,10 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// it should be enacted at the same block that signaled it
|
||||
assert!(
|
||||
authorities
|
||||
.apply_forced_changes("hash_a", 5, &static_is_descendent_of(false), false, None)
|
||||
.unwrap()
|
||||
.is_some()
|
||||
);
|
||||
assert!(authorities
|
||||
.apply_forced_changes("hash_a", 5, &static_is_descendent_of(false), false, None)
|
||||
.unwrap()
|
||||
.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1306,9 +1279,15 @@ mod tests {
|
||||
};
|
||||
|
||||
// add some pending standard changes all on the same fork
|
||||
authorities.add_pending_change(change_a, &static_is_descendent_of(true)).unwrap();
|
||||
authorities.add_pending_change(change_b, &static_is_descendent_of(true)).unwrap();
|
||||
authorities.add_pending_change(change_c, &static_is_descendent_of(true)).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a, &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_b, &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_c, &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
|
||||
// effective at #45
|
||||
let change_d = PendingChange {
|
||||
@@ -1316,18 +1295,24 @@ mod tests {
|
||||
delay: 5,
|
||||
canon_height: 40,
|
||||
canon_hash: "hash_d",
|
||||
delay_kind: DelayKind::Best {
|
||||
median_last_finalized: 31,
|
||||
},
|
||||
delay_kind: DelayKind::Best { median_last_finalized: 31 },
|
||||
};
|
||||
|
||||
// now add a forced change on the same fork
|
||||
authorities.add_pending_change(change_d, &static_is_descendent_of(true)).unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_d, &static_is_descendent_of(true))
|
||||
.unwrap();
|
||||
|
||||
// the forced change cannot be applied since the pending changes it depends on
|
||||
// have not been applied yet.
|
||||
assert!(matches!(
|
||||
authorities.apply_forced_changes("hash_d45", 45, &static_is_descendent_of(true), false, None),
|
||||
authorities.apply_forced_changes(
|
||||
"hash_d45",
|
||||
45,
|
||||
&static_is_descendent_of(true),
|
||||
false,
|
||||
None
|
||||
),
|
||||
Err(Error::ForcedAuthoritySetChangeDependencyUnsatisfied(15))
|
||||
));
|
||||
assert_eq!(authorities.authority_set_changes, AuthoritySetChanges::empty());
|
||||
@@ -1340,7 +1325,13 @@ mod tests {
|
||||
|
||||
// but the forced change still depends on the next standard change
|
||||
assert!(matches!(
|
||||
authorities.apply_forced_changes("hash_d", 45, &static_is_descendent_of(true), false, None),
|
||||
authorities.apply_forced_changes(
|
||||
"hash_d",
|
||||
45,
|
||||
&static_is_descendent_of(true),
|
||||
false,
|
||||
None
|
||||
),
|
||||
Err(Error::ForcedAuthoritySetChangeDependencyUnsatisfied(20))
|
||||
));
|
||||
assert_eq!(authorities.authority_set_changes, AuthoritySetChanges(vec![(0, 15)]));
|
||||
@@ -1425,29 +1416,19 @@ mod tests {
|
||||
});
|
||||
|
||||
// add the three pending changes
|
||||
authorities
|
||||
.add_pending_change(change_b, &is_descendent_of)
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a0, &is_descendent_of)
|
||||
.unwrap();
|
||||
authorities
|
||||
.add_pending_change(change_a1, &is_descendent_of)
|
||||
.unwrap();
|
||||
authorities.add_pending_change(change_b, &is_descendent_of).unwrap();
|
||||
authorities.add_pending_change(change_a0, &is_descendent_of).unwrap();
|
||||
authorities.add_pending_change(change_a1, &is_descendent_of).unwrap();
|
||||
|
||||
// the earliest change at block `best_a` should be the change at A0 (#5)
|
||||
assert_eq!(
|
||||
authorities
|
||||
.next_change(&"best_a", &is_descendent_of)
|
||||
.unwrap(),
|
||||
authorities.next_change(&"best_a", &is_descendent_of).unwrap(),
|
||||
Some(("hash_a0", 5)),
|
||||
);
|
||||
|
||||
// the earliest change at block `best_b` should be the change at B (#4)
|
||||
assert_eq!(
|
||||
authorities
|
||||
.next_change(&"best_b", &is_descendent_of)
|
||||
.unwrap(),
|
||||
authorities.next_change(&"best_b", &is_descendent_of).unwrap(),
|
||||
Some(("hash_b", 4)),
|
||||
);
|
||||
|
||||
@@ -1458,19 +1439,12 @@ mod tests {
|
||||
|
||||
// the next change is now at A1 (#10)
|
||||
assert_eq!(
|
||||
authorities
|
||||
.next_change(&"best_a", &is_descendent_of)
|
||||
.unwrap(),
|
||||
authorities.next_change(&"best_a", &is_descendent_of).unwrap(),
|
||||
Some(("hash_a1", 10)),
|
||||
);
|
||||
|
||||
// there's no longer any pending change at `best_b` fork
|
||||
assert_eq!(
|
||||
authorities
|
||||
.next_change(&"best_b", &is_descendent_of)
|
||||
.unwrap(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(authorities.next_change(&"best_b", &is_descendent_of).unwrap(), None,);
|
||||
|
||||
// we a forced change at A10 (#8)
|
||||
let change_a10 = PendingChange {
|
||||
@@ -1478,9 +1452,7 @@ mod tests {
|
||||
delay: 0,
|
||||
canon_height: 8,
|
||||
canon_hash: "hash_a10",
|
||||
delay_kind: DelayKind::Best {
|
||||
median_last_finalized: 0,
|
||||
},
|
||||
delay_kind: DelayKind::Best { median_last_finalized: 0 },
|
||||
};
|
||||
|
||||
authorities
|
||||
@@ -1489,9 +1461,7 @@ mod tests {
|
||||
|
||||
// it should take precedence over the change at A1 (#10)
|
||||
assert_eq!(
|
||||
authorities
|
||||
.next_change(&"best_a", &is_descendent_of)
|
||||
.unwrap(),
|
||||
authorities.next_change(&"best_a", &is_descendent_of).unwrap(),
|
||||
Some(("hash_a10", 8)),
|
||||
);
|
||||
}
|
||||
@@ -1511,16 +1481,11 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
let invalid_authorities_weight = vec![
|
||||
(AuthorityId::from_slice(&[1; 32]), 5),
|
||||
(AuthorityId::from_slice(&[2; 32]), 0),
|
||||
];
|
||||
let invalid_authorities_weight =
|
||||
vec![(AuthorityId::from_slice(&[1; 32]), 5), (AuthorityId::from_slice(&[2; 32]), 0)];
|
||||
|
||||
// authority weight of zero is invalid
|
||||
assert_eq!(
|
||||
AuthoritySet::<(), ()>::genesis(invalid_authorities_weight.clone()),
|
||||
None
|
||||
);
|
||||
assert_eq!(AuthoritySet::<(), ()>::genesis(invalid_authorities_weight.clone()), None);
|
||||
assert_eq!(
|
||||
AuthoritySet::<(), ()>::new(
|
||||
invalid_authorities_weight.clone(),
|
||||
@@ -1557,9 +1522,7 @@ mod tests {
|
||||
delay: 10,
|
||||
canon_height: 5,
|
||||
canon_hash: (),
|
||||
delay_kind: DelayKind::Best {
|
||||
median_last_finalized: 0,
|
||||
},
|
||||
delay_kind: DelayKind::Best { median_last_finalized: 0 },
|
||||
};
|
||||
|
||||
// pending change contains an an authority set
|
||||
@@ -1617,17 +1580,13 @@ mod tests {
|
||||
canon_height,
|
||||
canon_hash,
|
||||
delay_kind: if forced {
|
||||
DelayKind::Best {
|
||||
median_last_finalized: 0,
|
||||
}
|
||||
DelayKind::Best { median_last_finalized: 0 }
|
||||
} else {
|
||||
DelayKind::Finalized
|
||||
},
|
||||
};
|
||||
|
||||
authorities
|
||||
.add_pending_change(change, &is_descendent_of)
|
||||
.unwrap();
|
||||
authorities.add_pending_change(change, &is_descendent_of).unwrap();
|
||||
};
|
||||
|
||||
add_pending_change(5, "A", false);
|
||||
@@ -1669,14 +1628,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(authorities.pending_forced_changes.len(), 1);
|
||||
assert_eq!(
|
||||
authorities
|
||||
.pending_forced_changes
|
||||
.first()
|
||||
.unwrap()
|
||||
.canon_hash,
|
||||
"D"
|
||||
);
|
||||
assert_eq!(authorities.pending_forced_changes.first().unwrap().canon_hash, "D");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1714,10 +1666,7 @@ mod tests {
|
||||
authority_set_changes.append(2, 81);
|
||||
|
||||
// we are missing the data for the first set, therefore we should return `None`
|
||||
assert_eq!(
|
||||
None,
|
||||
authority_set_changes.iter_from(40).map(|it| it.collect::<Vec<_>>()),
|
||||
);
|
||||
assert_eq!(None, authority_set_changes.iter_from(40).map(|it| it.collect::<Vec<_>>()),);
|
||||
|
||||
// after adding the data for the first set the same query should work
|
||||
let mut authority_set_changes = AuthoritySetChanges::empty();
|
||||
@@ -1736,14 +1685,8 @@ mod tests {
|
||||
authority_set_changes.iter_from(41).map(|it| it.cloned().collect::<Vec<_>>()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
0,
|
||||
authority_set_changes.iter_from(121).unwrap().count(),
|
||||
);
|
||||
assert_eq!(0, authority_set_changes.iter_from(121).unwrap().count(),);
|
||||
|
||||
assert_eq!(
|
||||
0,
|
||||
authority_set_changes.iter_from(200).unwrap().count(),
|
||||
);
|
||||
assert_eq!(0, authority_set_changes.iter_from(200).unwrap().count(),);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +30,16 @@ use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
||||
use sp_finality_grandpa::{AuthorityList, RoundNumber, SetId};
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
|
||||
use crate::authorities::{
|
||||
AuthoritySet, AuthoritySetChanges, DelayKind, PendingChange, SharedAuthoritySet,
|
||||
use crate::{
|
||||
authorities::{
|
||||
AuthoritySet, AuthoritySetChanges, DelayKind, PendingChange, SharedAuthoritySet,
|
||||
},
|
||||
environment::{
|
||||
CompletedRound, CompletedRounds, CurrentRounds, HasVoted, SharedVoterSetState,
|
||||
VoterSetState,
|
||||
},
|
||||
GrandpaJustification, NewAuthoritySet,
|
||||
};
|
||||
use crate::environment::{
|
||||
CompletedRound, CompletedRounds, CurrentRounds, HasVoted, SharedVoterSetState, VoterSetState,
|
||||
};
|
||||
use crate::{GrandpaJustification, NewAuthoritySet};
|
||||
|
||||
const VERSION_KEY: &[u8] = b"grandpa_schema_version";
|
||||
const SET_STATE_KEY: &[u8] = b"grandpa_completed_round";
|
||||
@@ -141,13 +144,13 @@ struct V2AuthoritySet<H, N> {
|
||||
|
||||
pub(crate) fn load_decode<B: AuxStore, T: Decode>(
|
||||
backend: &B,
|
||||
key: &[u8]
|
||||
key: &[u8],
|
||||
) -> ClientResult<Option<T>> {
|
||||
match backend.get_aux(key)? {
|
||||
None => Ok(None),
|
||||
Some(t) => T::decode(&mut &t[..])
|
||||
.map_err(|e| ClientError::Backend(format!("GRANDPA DB is corrupted: {}", e)))
|
||||
.map(Some)
|
||||
.map(Some),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,24 +163,16 @@ pub(crate) struct PersistentData<Block: BlockT> {
|
||||
fn migrate_from_version0<Block: BlockT, B, G>(
|
||||
backend: &B,
|
||||
genesis_round: &G,
|
||||
) -> ClientResult<
|
||||
Option<(
|
||||
AuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
VoterSetState<Block>,
|
||||
)>,
|
||||
>
|
||||
) -> ClientResult<Option<(AuthoritySet<Block::Hash, NumberFor<Block>>, VoterSetState<Block>)>>
|
||||
where
|
||||
B: AuxStore,
|
||||
G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
|
||||
{
|
||||
CURRENT_VERSION.using_encoded(|s|
|
||||
backend.insert_aux(&[(VERSION_KEY, s)], &[])
|
||||
)?;
|
||||
CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?;
|
||||
|
||||
if let Some(old_set) = load_decode::<_, V0AuthoritySet<Block::Hash, NumberFor<Block>>>(
|
||||
backend,
|
||||
AUTHORITY_SET_KEY,
|
||||
)? {
|
||||
if let Some(old_set) =
|
||||
load_decode::<_, V0AuthoritySet<Block::Hash, NumberFor<Block>>>(backend, AUTHORITY_SET_KEY)?
|
||||
{
|
||||
let new_set: AuthoritySet<Block::Hash, NumberFor<Block>> = old_set.into();
|
||||
backend.insert_aux(&[(AUTHORITY_SET_KEY, new_set.encode().as_slice())], &[])?;
|
||||
|
||||
@@ -193,7 +188,7 @@ where
|
||||
let set_id = new_set.set_id;
|
||||
|
||||
let base = last_round_state.prevote_ghost.expect(
|
||||
"state is for completed round; completed rounds must have a prevote ghost; qed."
|
||||
"state is for completed round; completed rounds must have a prevote ghost; qed.",
|
||||
);
|
||||
|
||||
let mut current_rounds = CurrentRounds::new();
|
||||
@@ -215,7 +210,7 @@ where
|
||||
|
||||
backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?;
|
||||
|
||||
return Ok(Some((new_set, set_state)));
|
||||
return Ok(Some((new_set, set_state)))
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
@@ -224,36 +219,25 @@ where
|
||||
fn migrate_from_version1<Block: BlockT, B, G>(
|
||||
backend: &B,
|
||||
genesis_round: &G,
|
||||
) -> ClientResult<
|
||||
Option<(
|
||||
AuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
VoterSetState<Block>,
|
||||
)>,
|
||||
>
|
||||
) -> ClientResult<Option<(AuthoritySet<Block::Hash, NumberFor<Block>>, VoterSetState<Block>)>>
|
||||
where
|
||||
B: AuxStore,
|
||||
G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
|
||||
{
|
||||
CURRENT_VERSION.using_encoded(|s|
|
||||
backend.insert_aux(&[(VERSION_KEY, s)], &[])
|
||||
)?;
|
||||
CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?;
|
||||
|
||||
if let Some(set) = load_decode::<_, AuthoritySet<Block::Hash, NumberFor<Block>>>(
|
||||
backend,
|
||||
AUTHORITY_SET_KEY,
|
||||
)? {
|
||||
if let Some(set) =
|
||||
load_decode::<_, AuthoritySet<Block::Hash, NumberFor<Block>>>(backend, AUTHORITY_SET_KEY)?
|
||||
{
|
||||
let set_id = set.set_id;
|
||||
|
||||
let completed_rounds = |number, state, base| CompletedRounds::new(
|
||||
CompletedRound {
|
||||
number,
|
||||
state,
|
||||
votes: Vec::new(),
|
||||
base,
|
||||
},
|
||||
set_id,
|
||||
&set,
|
||||
);
|
||||
let completed_rounds = |number, state, base| {
|
||||
CompletedRounds::new(
|
||||
CompletedRound { number, state, votes: Vec::new(), base },
|
||||
set_id,
|
||||
&set,
|
||||
)
|
||||
};
|
||||
|
||||
let set_state = match load_decode::<_, V1VoterSetState<Block::Hash, NumberFor<Block>>>(
|
||||
backend,
|
||||
@@ -284,17 +268,13 @@ where
|
||||
let base = set_state.prevote_ghost
|
||||
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
|
||||
|
||||
VoterSetState::live(
|
||||
set_id,
|
||||
&set,
|
||||
base,
|
||||
)
|
||||
VoterSetState::live(set_id, &set, base)
|
||||
},
|
||||
};
|
||||
|
||||
backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?;
|
||||
|
||||
return Ok(Some((set, set_state)));
|
||||
return Ok(Some((set, set_state)))
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
@@ -303,46 +283,31 @@ where
|
||||
fn migrate_from_version2<Block: BlockT, B, G>(
|
||||
backend: &B,
|
||||
genesis_round: &G,
|
||||
) -> ClientResult<
|
||||
Option<(
|
||||
AuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
VoterSetState<Block>,
|
||||
)>,
|
||||
>
|
||||
) -> ClientResult<Option<(AuthoritySet<Block::Hash, NumberFor<Block>>, VoterSetState<Block>)>>
|
||||
where
|
||||
B: AuxStore,
|
||||
G: Fn() -> RoundState<Block::Hash, NumberFor<Block>>,
|
||||
{
|
||||
CURRENT_VERSION.using_encoded(|s|
|
||||
backend.insert_aux(&[(VERSION_KEY, s)], &[])
|
||||
)?;
|
||||
CURRENT_VERSION.using_encoded(|s| backend.insert_aux(&[(VERSION_KEY, s)], &[]))?;
|
||||
|
||||
if let Some(old_set) = load_decode::<_, V2AuthoritySet<Block::Hash, NumberFor<Block>>>(
|
||||
backend,
|
||||
AUTHORITY_SET_KEY,
|
||||
)? {
|
||||
if let Some(old_set) =
|
||||
load_decode::<_, V2AuthoritySet<Block::Hash, NumberFor<Block>>>(backend, AUTHORITY_SET_KEY)?
|
||||
{
|
||||
let new_set: AuthoritySet<Block::Hash, NumberFor<Block>> = old_set.into();
|
||||
backend.insert_aux(&[(AUTHORITY_SET_KEY, new_set.encode().as_slice())], &[])?;
|
||||
|
||||
let set_state = match load_decode::<_, VoterSetState<Block>>(
|
||||
backend,
|
||||
SET_STATE_KEY,
|
||||
)? {
|
||||
let set_state = match load_decode::<_, VoterSetState<Block>>(backend, SET_STATE_KEY)? {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
let state = genesis_round();
|
||||
let base = state.prevote_ghost
|
||||
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
|
||||
|
||||
VoterSetState::live(
|
||||
new_set.set_id,
|
||||
&new_set,
|
||||
base,
|
||||
)
|
||||
}
|
||||
VoterSetState::live(new_set.set_id, &new_set, base)
|
||||
},
|
||||
};
|
||||
|
||||
return Ok(Some((new_set, set_state)));
|
||||
return Ok(Some((new_set, set_state)))
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
@@ -371,7 +336,7 @@ where
|
||||
return Ok(PersistentData {
|
||||
authority_set: new_set.into(),
|
||||
set_state: set_state.into(),
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
Some(1) => {
|
||||
@@ -381,7 +346,7 @@ where
|
||||
return Ok(PersistentData {
|
||||
authority_set: new_set.into(),
|
||||
set_state: set_state.into(),
|
||||
});
|
||||
})
|
||||
}
|
||||
},
|
||||
Some(2) => {
|
||||
@@ -391,41 +356,31 @@ where
|
||||
return Ok(PersistentData {
|
||||
authority_set: new_set.into(),
|
||||
set_state: set_state.into(),
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(3) => {
|
||||
if let Some(set) = load_decode::<_, AuthoritySet<Block::Hash, NumberFor<Block>>>(
|
||||
backend,
|
||||
AUTHORITY_SET_KEY,
|
||||
)? {
|
||||
let set_state = match load_decode::<_, VoterSetState<Block>>(
|
||||
backend,
|
||||
SET_STATE_KEY,
|
||||
)? {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
let state = make_genesis_round();
|
||||
let base = state.prevote_ghost
|
||||
let set_state =
|
||||
match load_decode::<_, VoterSetState<Block>>(backend, SET_STATE_KEY)? {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
let state = make_genesis_round();
|
||||
let base = state.prevote_ghost
|
||||
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
|
||||
|
||||
VoterSetState::live(
|
||||
set.set_id,
|
||||
&set,
|
||||
base,
|
||||
)
|
||||
}
|
||||
};
|
||||
VoterSetState::live(set.set_id, &set, base)
|
||||
},
|
||||
};
|
||||
|
||||
return Ok(PersistentData {
|
||||
authority_set: set.into(),
|
||||
set_state: set_state.into(),
|
||||
});
|
||||
return Ok(PersistentData { authority_set: set.into(), set_state: set_state.into() })
|
||||
}
|
||||
}
|
||||
Some(other) => return Err(ClientError::Backend(
|
||||
format!("Unsupported GRANDPA DB version: {:?}", other)
|
||||
)),
|
||||
},
|
||||
Some(other) =>
|
||||
return Err(ClientError::Backend(format!("Unsupported GRANDPA DB version: {:?}", other))),
|
||||
}
|
||||
|
||||
// genesis.
|
||||
@@ -436,14 +391,11 @@ where
|
||||
let genesis_set = AuthoritySet::genesis(genesis_authorities)
|
||||
.expect("genesis authorities is non-empty; all weights are non-zero; qed.");
|
||||
let state = make_genesis_round();
|
||||
let base = state.prevote_ghost
|
||||
let base = state
|
||||
.prevote_ghost
|
||||
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
|
||||
|
||||
let genesis_state = VoterSetState::live(
|
||||
0,
|
||||
&genesis_set,
|
||||
base,
|
||||
);
|
||||
let genesis_state = VoterSetState::live(0, &genesis_set, base);
|
||||
|
||||
backend.insert_aux(
|
||||
&[
|
||||
@@ -453,10 +405,7 @@ where
|
||||
&[],
|
||||
)?;
|
||||
|
||||
Ok(PersistentData {
|
||||
authority_set: genesis_set.into(),
|
||||
set_state: genesis_state.into(),
|
||||
})
|
||||
Ok(PersistentData { authority_set: genesis_set.into(), set_state: genesis_state.into() })
|
||||
}
|
||||
|
||||
/// Update the authority set on disk after a change.
|
||||
@@ -486,10 +435,7 @@ where
|
||||
);
|
||||
let encoded = set_state.encode();
|
||||
|
||||
write_aux(&[
|
||||
(AUTHORITY_SET_KEY, &encoded_set[..]),
|
||||
(SET_STATE_KEY, &encoded[..]),
|
||||
])
|
||||
write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..]), (SET_STATE_KEY, &encoded[..])])
|
||||
} else {
|
||||
write_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..])])
|
||||
}
|
||||
@@ -527,10 +473,7 @@ pub(crate) fn write_voter_set_state<Block: BlockT, B: AuxStore>(
|
||||
backend: &B,
|
||||
state: &VoterSetState<Block>,
|
||||
) -> ClientResult<()> {
|
||||
backend.insert_aux(
|
||||
&[(SET_STATE_KEY, state.encode().as_slice())],
|
||||
&[]
|
||||
)
|
||||
backend.insert_aux(&[(SET_STATE_KEY, state.encode().as_slice())], &[])
|
||||
}
|
||||
|
||||
/// Write concluded round.
|
||||
@@ -554,10 +497,10 @@ pub(crate) fn load_authorities<B: AuxStore, H: Decode, N: Decode + Clone + Ord>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
use sp_core::H256;
|
||||
use substrate_test_runtime_client;
|
||||
use super::*;
|
||||
use sp_core::H256;
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
use substrate_test_runtime_client;
|
||||
|
||||
#[test]
|
||||
fn load_decode_from_v0_migrates_data_format() {
|
||||
@@ -582,19 +525,18 @@ mod test {
|
||||
|
||||
let voter_set_state = (round_number, round_state.clone());
|
||||
|
||||
client.insert_aux(
|
||||
&[
|
||||
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
|
||||
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
|
||||
],
|
||||
&[],
|
||||
).unwrap();
|
||||
client
|
||||
.insert_aux(
|
||||
&[
|
||||
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
|
||||
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
|
||||
],
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), None,);
|
||||
|
||||
// should perform the migration
|
||||
load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
@@ -602,23 +544,19 @@ mod test {
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
|
||||
Some(3),
|
||||
);
|
||||
assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3),);
|
||||
|
||||
let PersistentData {
|
||||
authority_set,
|
||||
set_state,
|
||||
..
|
||||
} = load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
&client,
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
).unwrap();
|
||||
let PersistentData { authority_set, set_state, .. } =
|
||||
load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
&client,
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*authority_set.inner(),
|
||||
@@ -628,7 +566,8 @@ mod test {
|
||||
ForkTree::new(),
|
||||
Vec::new(),
|
||||
AuthoritySetChanges::empty(),
|
||||
).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let mut current_rounds = CurrentRounds::new();
|
||||
@@ -673,24 +612,24 @@ mod test {
|
||||
ForkTree::new(),
|
||||
Vec::new(),
|
||||
AuthoritySetChanges::empty(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let voter_set_state = V1VoterSetState::Live(round_number, round_state.clone());
|
||||
|
||||
client.insert_aux(
|
||||
&[
|
||||
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
|
||||
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
|
||||
(VERSION_KEY, 1u32.encode().as_slice()),
|
||||
],
|
||||
&[],
|
||||
).unwrap();
|
||||
client
|
||||
.insert_aux(
|
||||
&[
|
||||
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
|
||||
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
|
||||
(VERSION_KEY, 1u32.encode().as_slice()),
|
||||
],
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
|
||||
Some(1),
|
||||
);
|
||||
assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(1),);
|
||||
|
||||
// should perform the migration
|
||||
load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
@@ -698,23 +637,19 @@ mod test {
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
|
||||
Some(3),
|
||||
);
|
||||
assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3),);
|
||||
|
||||
let PersistentData {
|
||||
authority_set,
|
||||
set_state,
|
||||
..
|
||||
} = load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
&client,
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
).unwrap();
|
||||
let PersistentData { authority_set, set_state, .. } =
|
||||
load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
&client,
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*authority_set.inner(),
|
||||
@@ -724,7 +659,8 @@ mod test {
|
||||
ForkTree::new(),
|
||||
Vec::new(),
|
||||
AuthoritySetChanges::empty(),
|
||||
).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let mut current_rounds = CurrentRounds::new();
|
||||
@@ -768,23 +704,22 @@ mod test {
|
||||
VoterSetState::live(
|
||||
set_id,
|
||||
&authority_set.clone().into(), // Note the conversion!
|
||||
genesis_state
|
||||
genesis_state,
|
||||
);
|
||||
|
||||
client.insert_aux(
|
||||
&[
|
||||
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
|
||||
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
|
||||
(VERSION_KEY, 2u32.encode().as_slice()),
|
||||
],
|
||||
&[],
|
||||
).unwrap();
|
||||
client
|
||||
.insert_aux(
|
||||
&[
|
||||
(AUTHORITY_SET_KEY, authority_set.encode().as_slice()),
|
||||
(SET_STATE_KEY, voter_set_state.encode().as_slice()),
|
||||
(VERSION_KEY, 2u32.encode().as_slice()),
|
||||
],
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
|
||||
Some(2),
|
||||
);
|
||||
assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(2),);
|
||||
|
||||
// should perform the migration
|
||||
load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
@@ -792,22 +727,17 @@ mod test {
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
load_decode::<_, u32>(&client, VERSION_KEY).unwrap(),
|
||||
Some(3),
|
||||
);
|
||||
assert_eq!(load_decode::<_, u32>(&client, VERSION_KEY).unwrap(), Some(3),);
|
||||
|
||||
let PersistentData {
|
||||
authority_set,
|
||||
..
|
||||
} = load_persistent::<substrate_test_runtime_client::runtime::Block, _, _>(
|
||||
&client,
|
||||
H256::random(),
|
||||
0,
|
||||
|| unreachable!(),
|
||||
).unwrap();
|
||||
let PersistentData { authority_set, .. } = load_persistent::<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
_,
|
||||
_,
|
||||
>(&client, H256::random(), 0, || unreachable!())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*authority_set.inner(),
|
||||
@@ -817,7 +747,8 @@ mod test {
|
||||
ForkTree::new(),
|
||||
Vec::new(),
|
||||
AuthoritySetChanges::empty(),
|
||||
).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -843,7 +774,8 @@ mod test {
|
||||
assert_eq!(
|
||||
load_decode::<_, CompletedRound::<substrate_test_runtime_client::runtime::Block>>(
|
||||
&client, &key
|
||||
).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
Some(completed_round),
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,37 +29,36 @@
|
||||
//! In the future, there will be a fallback for allowing sending the same message
|
||||
//! under certain conditions that are used to un-stick the protocol.
|
||||
|
||||
use futures::{prelude::*, channel::mpsc};
|
||||
use futures::{channel::mpsc, prelude::*};
|
||||
use log::{debug, trace};
|
||||
use parking_lot::Mutex;
|
||||
use prometheus_endpoint::Registry;
|
||||
use std::{pin::Pin, sync::Arc, task::{Context, Poll}};
|
||||
use std::{
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use sp_keystore::SyncCryptoStorePtr;
|
||||
use finality_grandpa::Message::{Prevote, Precommit, PrimaryPropose};
|
||||
use finality_grandpa::{voter, voter_set::VoterSet};
|
||||
use finality_grandpa::{
|
||||
voter,
|
||||
voter_set::VoterSet,
|
||||
Message::{Precommit, Prevote, PrimaryPropose},
|
||||
};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use sc_network::{NetworkService, ReputationChange};
|
||||
use sc_network_gossip::{GossipEngine, Network as GossipNetwork};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, NumberFor};
|
||||
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO};
|
||||
use sp_keystore::SyncCryptoStorePtr;
|
||||
use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, NumberFor};
|
||||
|
||||
use crate::{
|
||||
CatchUp, Commit, CommunicationIn, CommunicationOutH,
|
||||
CompactCommit, Error, Message, SignedMessage,
|
||||
environment::HasVoted, CatchUp, Commit, CommunicationIn, CommunicationOutH, CompactCommit,
|
||||
Error, Message, SignedMessage,
|
||||
};
|
||||
use crate::environment::HasVoted;
|
||||
use gossip::{
|
||||
FullCatchUpMessage,
|
||||
FullCommitMessage,
|
||||
GossipMessage,
|
||||
GossipValidator,
|
||||
PeerReport,
|
||||
VoteMessage,
|
||||
};
|
||||
use sp_finality_grandpa::{
|
||||
AuthorityId, AuthoritySignature, SetId as SetIdNumber, RoundNumber,
|
||||
FullCatchUpMessage, FullCommitMessage, GossipMessage, GossipValidator, PeerReport, VoteMessage,
|
||||
};
|
||||
use sp_finality_grandpa::{AuthorityId, AuthoritySignature, RoundNumber, SetId as SetIdNumber};
|
||||
use sp_utils::mpsc::TracingUnboundedReceiver;
|
||||
|
||||
pub mod gossip;
|
||||
@@ -89,11 +88,13 @@ mod cost {
|
||||
pub(super) const INVALID_CATCH_UP: Rep = Rep::new(-5000, "Grandpa: Invalid catch-up");
|
||||
pub(super) const INVALID_COMMIT: Rep = Rep::new(-5000, "Grandpa: Invalid commit");
|
||||
pub(super) const OUT_OF_SCOPE_MESSAGE: Rep = Rep::new(-500, "Grandpa: Out-of-scope message");
|
||||
pub(super) const CATCH_UP_REQUEST_TIMEOUT: Rep = Rep::new(-200, "Grandpa: Catch-up request timeout");
|
||||
pub(super) const CATCH_UP_REQUEST_TIMEOUT: Rep =
|
||||
Rep::new(-200, "Grandpa: Catch-up request timeout");
|
||||
|
||||
// cost of answering a catch up request
|
||||
pub(super) const CATCH_UP_REPLY: Rep = Rep::new(-200, "Grandpa: Catch-up reply");
|
||||
pub(super) const HONEST_OUT_OF_SCOPE_CATCH_UP: Rep = Rep::new(-200, "Grandpa: Out-of-scope catch-up");
|
||||
pub(super) const HONEST_OUT_OF_SCOPE_CATCH_UP: Rep =
|
||||
Rep::new(-200, "Grandpa: Out-of-scope catch-up");
|
||||
}
|
||||
|
||||
// benefit scalars for reporting peers.
|
||||
@@ -144,14 +145,25 @@ pub trait Network<Block: BlockT>: GossipNetwork<Block> + Clone + Send + 'static
|
||||
/// If the given vector of peers is empty then the underlying implementation
|
||||
/// should make a best effort to fetch the block from any peers it is
|
||||
/// connected to (NOTE: this assumption will change in the future #3629).
|
||||
fn set_sync_fork_request(&self, peers: Vec<sc_network::PeerId>, hash: Block::Hash, number: NumberFor<Block>);
|
||||
fn set_sync_fork_request(
|
||||
&self,
|
||||
peers: Vec<sc_network::PeerId>,
|
||||
hash: Block::Hash,
|
||||
number: NumberFor<Block>,
|
||||
);
|
||||
}
|
||||
|
||||
impl<B, H> Network<B> for Arc<NetworkService<B, H>> where
|
||||
impl<B, H> Network<B> for Arc<NetworkService<B, H>>
|
||||
where
|
||||
B: BlockT,
|
||||
H: sc_network::ExHashT,
|
||||
{
|
||||
fn set_sync_fork_request(&self, peers: Vec<sc_network::PeerId>, hash: B::Hash, number: NumberFor<B>) {
|
||||
fn set_sync_fork_request(
|
||||
&self,
|
||||
peers: Vec<sc_network::PeerId>,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>,
|
||||
) {
|
||||
NetworkService::set_sync_fork_request(self, peers, hash, number)
|
||||
}
|
||||
}
|
||||
@@ -179,14 +191,12 @@ pub(crate) struct NetworkBridge<B: BlockT, N: Network<B>> {
|
||||
neighbor_sender: periodic::NeighborPacketSender<B>,
|
||||
|
||||
/// `NeighborPacketWorker` processing packets sent through the `NeighborPacketSender`.
|
||||
//
|
||||
// `NetworkBridge` is required to be cloneable, thus one needs to be able to clone its children,
|
||||
// thus one has to wrap `neighbor_packet_worker` with an `Arc` `Mutex`.
|
||||
neighbor_packet_worker: Arc<Mutex<periodic::NeighborPacketWorker<B>>>,
|
||||
|
||||
/// Receiver side of the peer report stream populated by the gossip validator, forwarded to the
|
||||
/// gossip engine.
|
||||
//
|
||||
// `NetworkBridge` is required to be cloneable, thus one needs to be able to clone its children,
|
||||
// thus one has to wrap gossip_validator_report_stream with an `Arc` `Mutex`. Given that it is
|
||||
// just an `UnboundedReceiver`, one could also switch to a multi-producer-*multi*-consumer
|
||||
@@ -210,12 +220,8 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
prometheus_registry: Option<&Registry>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
) -> Self {
|
||||
let (validator, report_stream) = GossipValidator::new(
|
||||
config,
|
||||
set_state.clone(),
|
||||
prometheus_registry,
|
||||
telemetry.clone(),
|
||||
);
|
||||
let (validator, report_stream) =
|
||||
GossipValidator::new(config, set_state.clone(), prometheus_registry, telemetry.clone());
|
||||
|
||||
let validator = Arc::new(validator);
|
||||
let gossip_engine = Arc::new(Mutex::new(GossipEngine::new(
|
||||
@@ -239,18 +245,13 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
validator.note_round(Round(round.number), |_, _| {});
|
||||
|
||||
for signed in round.votes.iter() {
|
||||
let message = gossip::GossipMessage::Vote(
|
||||
gossip::VoteMessage::<B> {
|
||||
message: signed.clone(),
|
||||
round: Round(round.number),
|
||||
set_id: SetId(set_id),
|
||||
}
|
||||
);
|
||||
let message = gossip::GossipMessage::Vote(gossip::VoteMessage::<B> {
|
||||
message: signed.clone(),
|
||||
round: Round(round.number),
|
||||
set_id: SetId(set_id),
|
||||
});
|
||||
|
||||
gossip_engine.lock().register_gossip_message(
|
||||
topic,
|
||||
message.encode(),
|
||||
);
|
||||
gossip_engine.lock().register_gossip_message(topic, message.encode());
|
||||
}
|
||||
|
||||
trace!(target: "afg",
|
||||
@@ -263,7 +264,8 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
}
|
||||
}
|
||||
|
||||
let (neighbor_packet_worker, neighbor_packet_sender) = periodic::NeighborPacketWorker::new();
|
||||
let (neighbor_packet_worker, neighbor_packet_sender) =
|
||||
periodic::NeighborPacketWorker::new();
|
||||
|
||||
NetworkBridge {
|
||||
service,
|
||||
@@ -277,12 +279,7 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
}
|
||||
|
||||
/// Note the beginning of a new round to the `GossipValidator`.
|
||||
pub(crate) fn note_round(
|
||||
&self,
|
||||
round: Round,
|
||||
set_id: SetId,
|
||||
voters: &VoterSet<AuthorityId>,
|
||||
) {
|
||||
pub(crate) fn note_round(&self, round: Round, set_id: SetId, voters: &VoterSet<AuthorityId>) {
|
||||
// is a no-op if currently in that set.
|
||||
self.validator.note_set(
|
||||
set_id,
|
||||
@@ -290,10 +287,8 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
|to, neighbor| self.neighbor_sender.send(to, neighbor),
|
||||
);
|
||||
|
||||
self.validator.note_round(
|
||||
round,
|
||||
|to, neighbor| self.neighbor_sender.send(to, neighbor),
|
||||
);
|
||||
self.validator
|
||||
.note_round(round, |to, neighbor| self.neighbor_sender.send(to, neighbor));
|
||||
}
|
||||
|
||||
/// Get a stream of signature-checked round messages from the network as well as a sink for round messages to the
|
||||
@@ -305,15 +300,8 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
set_id: SetId,
|
||||
voters: Arc<VoterSet<AuthorityId>>,
|
||||
has_voted: HasVoted<B>,
|
||||
) -> (
|
||||
impl Stream<Item = SignedMessage<B>> + Unpin,
|
||||
OutgoingMessages<B>,
|
||||
) {
|
||||
self.note_round(
|
||||
round,
|
||||
set_id,
|
||||
&*voters,
|
||||
);
|
||||
) -> (impl Stream<Item = SignedMessage<B>> + Unpin, OutgoingMessages<B>) {
|
||||
self.note_round(round, set_id, &*voters);
|
||||
|
||||
let keystore = keystore.and_then(|ks| {
|
||||
let id = ks.local_id();
|
||||
@@ -326,20 +314,20 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
|
||||
let topic = round_topic::<B>(round.0, set_id.0);
|
||||
let telemetry = self.telemetry.clone();
|
||||
let incoming = self.gossip_engine.lock().messages_for(topic)
|
||||
.filter_map(move |notification| {
|
||||
let incoming =
|
||||
self.gossip_engine.lock().messages_for(topic).filter_map(move |notification| {
|
||||
let decoded = GossipMessage::<B>::decode(&mut ¬ification.message[..]);
|
||||
|
||||
match decoded {
|
||||
Err(ref e) => {
|
||||
debug!(target: "afg", "Skipping malformed message {:?}: {}", notification, e);
|
||||
future::ready(None)
|
||||
}
|
||||
},
|
||||
Ok(GossipMessage::Vote(msg)) => {
|
||||
// check signature.
|
||||
if !voters.contains(&msg.message.id) {
|
||||
debug!(target: "afg", "Skipping message from unknown voter {}", msg.message.id);
|
||||
return future::ready(None);
|
||||
return future::ready(None)
|
||||
}
|
||||
|
||||
if voters.len().get() <= TELEMETRY_VOTERS_LIMIT {
|
||||
@@ -378,11 +366,11 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
}
|
||||
|
||||
future::ready(Some(msg.message))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
debug!(target: "afg", "Skipping unknown message type");
|
||||
future::ready(None)
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -458,7 +446,7 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
|
||||
&self,
|
||||
peers: Vec<sc_network::PeerId>,
|
||||
hash: B::Hash,
|
||||
number: NumberFor<B>
|
||||
number: NumberFor<B>,
|
||||
) {
|
||||
Network::set_sync_fork_request(&self.service, peers, hash, number)
|
||||
}
|
||||
@@ -473,9 +461,10 @@ impl<B: BlockT, N: Network<B>> Future for NetworkBridge<B, N> {
|
||||
Poll::Ready(Some((to, packet))) => {
|
||||
self.gossip_engine.lock().send_message(to, packet.encode());
|
||||
},
|
||||
Poll::Ready(None) => return Poll::Ready(
|
||||
Err(Error::Network("Neighbor packet worker stream closed.".into()))
|
||||
),
|
||||
Poll::Ready(None) =>
|
||||
return Poll::Ready(Err(Error::Network(
|
||||
"Neighbor packet worker stream closed.".into(),
|
||||
))),
|
||||
Poll::Pending => break,
|
||||
}
|
||||
}
|
||||
@@ -485,17 +474,17 @@ impl<B: BlockT, N: Network<B>> Future for NetworkBridge<B, N> {
|
||||
Poll::Ready(Some(PeerReport { who, cost_benefit })) => {
|
||||
self.gossip_engine.lock().report(who, cost_benefit);
|
||||
},
|
||||
Poll::Ready(None) => return Poll::Ready(
|
||||
Err(Error::Network("Gossip validator report stream closed.".into()))
|
||||
),
|
||||
Poll::Ready(None) =>
|
||||
return Poll::Ready(Err(Error::Network(
|
||||
"Gossip validator report stream closed.".into(),
|
||||
))),
|
||||
Poll::Pending => break,
|
||||
}
|
||||
}
|
||||
|
||||
match self.gossip_engine.lock().poll_unpin(cx) {
|
||||
Poll::Ready(()) => return Poll::Ready(
|
||||
Err(Error::Network("Gossip engine future finished.".into()))
|
||||
),
|
||||
Poll::Ready(()) =>
|
||||
return Poll::Ready(Err(Error::Network("Gossip engine future finished.".into()))),
|
||||
Poll::Pending => {},
|
||||
}
|
||||
|
||||
@@ -513,18 +502,14 @@ fn incoming_global<B: BlockT>(
|
||||
) -> impl Stream<Item = CommunicationIn<B>> {
|
||||
let process_commit = {
|
||||
let telemetry = telemetry.clone();
|
||||
move |
|
||||
msg: FullCommitMessage<B>,
|
||||
mut notification: sc_network_gossip::TopicNotification,
|
||||
gossip_engine: &Arc<Mutex<GossipEngine<B>>>,
|
||||
gossip_validator: &Arc<GossipValidator<B>>,
|
||||
voters: &VoterSet<AuthorityId>,
|
||||
| {
|
||||
move |msg: FullCommitMessage<B>,
|
||||
mut notification: sc_network_gossip::TopicNotification,
|
||||
gossip_engine: &Arc<Mutex<GossipEngine<B>>>,
|
||||
gossip_validator: &Arc<GossipValidator<B>>,
|
||||
voters: &VoterSet<AuthorityId>| {
|
||||
if voters.len().get() <= TELEMETRY_VOTERS_LIMIT {
|
||||
let precommits_signed_by: Vec<String> =
|
||||
msg.message.auth_data.iter().map(move |(_, a)| {
|
||||
format!("{}", a)
|
||||
}).collect();
|
||||
msg.message.auth_data.iter().map(move |(_, a)| format!("{}", a)).collect();
|
||||
|
||||
telemetry!(
|
||||
telemetry;
|
||||
@@ -547,7 +532,7 @@ fn incoming_global<B: BlockT>(
|
||||
gossip_engine.lock().report(who, cost);
|
||||
}
|
||||
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
let round = msg.round;
|
||||
@@ -570,13 +555,13 @@ fn incoming_global<B: BlockT>(
|
||||
);
|
||||
|
||||
gossip_engine.lock().gossip_message(topic, notification.message.clone(), false);
|
||||
}
|
||||
},
|
||||
voter::CommitProcessingOutcome::Bad(_) => {
|
||||
// report peer and do not gossip.
|
||||
if let Some(who) = notification.sender.take() {
|
||||
gossip_engine.lock().report(who, cost::INVALID_COMMIT);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let cb = voter::Callback::Work(Box::new(cb));
|
||||
@@ -585,27 +570,21 @@ fn incoming_global<B: BlockT>(
|
||||
}
|
||||
};
|
||||
|
||||
let process_catch_up = move |
|
||||
msg: FullCatchUpMessage<B>,
|
||||
mut notification: sc_network_gossip::TopicNotification,
|
||||
gossip_engine: &Arc<Mutex<GossipEngine<B>>>,
|
||||
gossip_validator: &Arc<GossipValidator<B>>,
|
||||
voters: &VoterSet<AuthorityId>,
|
||||
| {
|
||||
let process_catch_up = move |msg: FullCatchUpMessage<B>,
|
||||
mut notification: sc_network_gossip::TopicNotification,
|
||||
gossip_engine: &Arc<Mutex<GossipEngine<B>>>,
|
||||
gossip_validator: &Arc<GossipValidator<B>>,
|
||||
voters: &VoterSet<AuthorityId>| {
|
||||
let gossip_validator = gossip_validator.clone();
|
||||
let gossip_engine = gossip_engine.clone();
|
||||
|
||||
if let Err(cost) = check_catch_up::<B>(
|
||||
&msg.message,
|
||||
voters,
|
||||
msg.set_id,
|
||||
telemetry.clone(),
|
||||
) {
|
||||
if let Err(cost) = check_catch_up::<B>(&msg.message, voters, msg.set_id, telemetry.clone())
|
||||
{
|
||||
if let Some(who) = notification.sender {
|
||||
gossip_engine.lock().report(who, cost);
|
||||
}
|
||||
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
let cb = move |outcome| {
|
||||
@@ -624,7 +603,10 @@ fn incoming_global<B: BlockT>(
|
||||
Some(voter::CommunicationIn::CatchUp(msg.message, cb))
|
||||
};
|
||||
|
||||
gossip_engine.clone().lock().messages_for(topic)
|
||||
gossip_engine
|
||||
.clone()
|
||||
.lock()
|
||||
.messages_for(topic)
|
||||
.filter_map(|notification| {
|
||||
// this could be optimized by decoding piecewise.
|
||||
let decoded = GossipMessage::<B>::decode(&mut ¬ification.message[..]);
|
||||
@@ -642,7 +624,7 @@ fn incoming_global<B: BlockT>(
|
||||
_ => {
|
||||
debug!(target: "afg", "Skipping unknown message type");
|
||||
None
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -688,15 +670,15 @@ pub(crate) struct OutgoingMessages<Block: BlockT> {
|
||||
|
||||
impl<B: BlockT> Unpin for OutgoingMessages<B> {}
|
||||
|
||||
impl<Block: BlockT> Sink<Message<Block>> for OutgoingMessages<Block>
|
||||
{
|
||||
impl<Block: BlockT> Sink<Message<Block>> for OutgoingMessages<Block> {
|
||||
type Error = Error;
|
||||
|
||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
Sink::poll_ready(Pin::new(&mut self.sender), cx)
|
||||
.map(|elem| { elem.map_err(|e| {
|
||||
Sink::poll_ready(Pin::new(&mut self.sender), cx).map(|elem| {
|
||||
elem.map_err(|e| {
|
||||
Error::Network(format!("Failed to poll_ready channel sender: {:?}", e))
|
||||
})})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn start_send(mut self: Pin<&mut Self>, mut msg: Message<Block>) -> Result<(), Self::Error> {
|
||||
@@ -725,11 +707,13 @@ impl<Block: BlockT> Sink<Message<Block>> for OutgoingMessages<Block>
|
||||
keystore.local_id().clone(),
|
||||
self.round,
|
||||
self.set_id,
|
||||
).ok_or_else(
|
||||
|| Error::Signing(format!(
|
||||
"Failed to sign GRANDPA vote for round {} targetting {:?}", self.round, target_hash
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
Error::Signing(format!(
|
||||
"Failed to sign GRANDPA vote for round {} targetting {:?}",
|
||||
self.round, target_hash
|
||||
))
|
||||
)?;
|
||||
})?;
|
||||
|
||||
let message = GossipMessage::Vote(VoteMessage::<Block> {
|
||||
message: signed.clone(),
|
||||
@@ -762,7 +746,7 @@ impl<Block: BlockT> Sink<Message<Block>> for OutgoingMessages<Block>
|
||||
// forward the message to the inner sender.
|
||||
return self.sender.start_send(signed).map_err(|e| {
|
||||
Error::Network(format!("Failed to start_send on channel sender: {:?}", e))
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
Ok(())
|
||||
@@ -773,10 +757,11 @@ impl<Block: BlockT> Sink<Message<Block>> for OutgoingMessages<Block>
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
Sink::poll_close(Pin::new(&mut self.sender), cx)
|
||||
.map(|elem| { elem.map_err(|e| {
|
||||
Sink::poll_close(Pin::new(&mut self.sender), cx).map(|elem| {
|
||||
elem.map_err(|e| {
|
||||
Error::Network(format!("Failed to poll_close channel sender: {:?}", e))
|
||||
})})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,23 +784,22 @@ fn check_compact_commit<Block: BlockT>(
|
||||
if let Some(weight) = voters.get(id).map(|info| info.weight()) {
|
||||
total_weight += weight.get();
|
||||
if total_weight > full_threshold {
|
||||
return Err(cost::MALFORMED_COMMIT);
|
||||
return Err(cost::MALFORMED_COMMIT)
|
||||
}
|
||||
} else {
|
||||
debug!(target: "afg", "Skipping commit containing unknown voter {}", id);
|
||||
return Err(cost::MALFORMED_COMMIT);
|
||||
return Err(cost::MALFORMED_COMMIT)
|
||||
}
|
||||
}
|
||||
|
||||
if total_weight < voters.threshold().get() {
|
||||
return Err(cost::MALFORMED_COMMIT);
|
||||
return Err(cost::MALFORMED_COMMIT)
|
||||
}
|
||||
|
||||
// check signatures on all contained precommits.
|
||||
let mut buf = Vec::new();
|
||||
for (i, (precommit, &(ref sig, ref id))) in msg.precommits.iter()
|
||||
.zip(&msg.auth_data)
|
||||
.enumerate()
|
||||
for (i, (precommit, &(ref sig, ref id))) in
|
||||
msg.precommits.iter().zip(&msg.auth_data).enumerate()
|
||||
{
|
||||
use crate::communication::gossip::Misbehavior;
|
||||
use finality_grandpa::Message as GrandpaMessage;
|
||||
@@ -839,9 +823,10 @@ fn check_compact_commit<Block: BlockT>(
|
||||
signatures_checked: i as i32,
|
||||
blocks_loaded: 0,
|
||||
equivocations_caught: 0,
|
||||
}.cost();
|
||||
}
|
||||
.cost();
|
||||
|
||||
return Err(cost);
|
||||
return Err(cost)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -863,7 +848,7 @@ fn check_catch_up<Block: BlockT>(
|
||||
// check total weight is not out of range for a set of votes.
|
||||
fn check_weight<'a>(
|
||||
voters: &'a VoterSet<AuthorityId>,
|
||||
votes: impl Iterator<Item=&'a AuthorityId>,
|
||||
votes: impl Iterator<Item = &'a AuthorityId>,
|
||||
full_threshold: u64,
|
||||
) -> Result<(), ReputationChange> {
|
||||
let mut total_weight = 0;
|
||||
@@ -872,32 +857,24 @@ fn check_catch_up<Block: BlockT>(
|
||||
if let Some(weight) = voters.get(&id).map(|info| info.weight()) {
|
||||
total_weight += weight.get();
|
||||
if total_weight > full_threshold {
|
||||
return Err(cost::MALFORMED_CATCH_UP);
|
||||
return Err(cost::MALFORMED_CATCH_UP)
|
||||
}
|
||||
} else {
|
||||
debug!(target: "afg", "Skipping catch up message containing unknown voter {}", id);
|
||||
return Err(cost::MALFORMED_CATCH_UP);
|
||||
return Err(cost::MALFORMED_CATCH_UP)
|
||||
}
|
||||
}
|
||||
|
||||
if total_weight < voters.threshold().get() {
|
||||
return Err(cost::MALFORMED_CATCH_UP);
|
||||
return Err(cost::MALFORMED_CATCH_UP)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
check_weight(
|
||||
voters,
|
||||
msg.prevotes.iter().map(|vote| &vote.id),
|
||||
full_threshold,
|
||||
)?;
|
||||
check_weight(voters, msg.prevotes.iter().map(|vote| &vote.id), full_threshold)?;
|
||||
|
||||
check_weight(
|
||||
voters,
|
||||
msg.precommits.iter().map(|vote| &vote.id),
|
||||
full_threshold,
|
||||
)?;
|
||||
check_weight(voters, msg.precommits.iter().map(|vote| &vote.id), full_threshold)?;
|
||||
|
||||
fn check_signatures<'a, B, I>(
|
||||
messages: I,
|
||||
@@ -906,9 +883,10 @@ fn check_catch_up<Block: BlockT>(
|
||||
mut signatures_checked: usize,
|
||||
buf: &mut Vec<u8>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
) -> Result<usize, ReputationChange> where
|
||||
) -> Result<usize, ReputationChange>
|
||||
where
|
||||
B: BlockT,
|
||||
I: Iterator<Item=(Message<B>, &'a AuthorityId, &'a AuthoritySignature)>,
|
||||
I: Iterator<Item = (Message<B>, &'a AuthorityId, &'a AuthoritySignature)>,
|
||||
{
|
||||
use crate::communication::gossip::Misbehavior;
|
||||
|
||||
@@ -916,12 +894,7 @@ fn check_catch_up<Block: BlockT>(
|
||||
signatures_checked += 1;
|
||||
|
||||
if !sp_finality_grandpa::check_message_signature_with_buffer(
|
||||
&msg,
|
||||
id,
|
||||
sig,
|
||||
round,
|
||||
set_id,
|
||||
buf,
|
||||
&msg, id, sig, round, set_id, buf,
|
||||
) {
|
||||
debug!(target: "afg", "Bad catch up message signature {}", id);
|
||||
telemetry!(
|
||||
@@ -933,9 +906,10 @@ fn check_catch_up<Block: BlockT>(
|
||||
|
||||
let cost = Misbehavior::BadCatchUpMessage {
|
||||
signatures_checked: signatures_checked as i32,
|
||||
}.cost();
|
||||
}
|
||||
.cost();
|
||||
|
||||
return Err(cost);
|
||||
return Err(cost)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -959,7 +933,11 @@ fn check_catch_up<Block: BlockT>(
|
||||
// check signatures on all contained precommits.
|
||||
let _ = check_signatures::<Block, _>(
|
||||
msg.precommits.iter().map(|vote| {
|
||||
(finality_grandpa::Message::Precommit(vote.precommit.clone()), &vote.id, &vote.signature)
|
||||
(
|
||||
finality_grandpa::Message::Precommit(vote.precommit.clone()),
|
||||
&vote.id,
|
||||
&vote.signature,
|
||||
)
|
||||
}),
|
||||
msg.round_number,
|
||||
set_id.0,
|
||||
@@ -1009,9 +987,12 @@ impl<Block: BlockT> Sink<(RoundNumber, Commit<Block>)> for CommitsOut<Block> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, input: (RoundNumber, Commit<Block>)) -> Result<(), Self::Error> {
|
||||
fn start_send(
|
||||
self: Pin<&mut Self>,
|
||||
input: (RoundNumber, Commit<Block>),
|
||||
) -> Result<(), Self::Error> {
|
||||
if !self.is_voter {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let (round, commit) = input;
|
||||
@@ -1024,7 +1005,9 @@ impl<Block: BlockT> Sink<(RoundNumber, Commit<Block>)> for CommitsOut<Block> {
|
||||
"target_number" => ?commit.target_number,
|
||||
"target_hash" => ?commit.target_hash,
|
||||
);
|
||||
let (precommits, auth_data) = commit.precommits.into_iter()
|
||||
let (precommits, auth_data) = commit
|
||||
.precommits
|
||||
.into_iter()
|
||||
.map(|signed| (signed.precommit, (signed.signature, signed.id)))
|
||||
.unzip();
|
||||
|
||||
@@ -1032,7 +1015,7 @@ impl<Block: BlockT> Sink<(RoundNumber, Commit<Block>)> for CommitsOut<Block> {
|
||||
target_hash: commit.target_hash,
|
||||
target_number: commit.target_number,
|
||||
precommits,
|
||||
auth_data
|
||||
auth_data,
|
||||
};
|
||||
|
||||
let message = GossipMessage::Commit(FullCommitMessage::<Block> {
|
||||
|
||||
@@ -18,15 +18,19 @@
|
||||
|
||||
//! Periodic rebroadcast of neighbor packets.
|
||||
|
||||
use futures::{future::FutureExt as _, prelude::*, ready, stream::Stream};
|
||||
use futures_timer::Delay;
|
||||
use futures::{future::{FutureExt as _}, prelude::*, ready, stream::Stream};
|
||||
use log::debug;
|
||||
use std::{pin::Pin, task::{Context, Poll}, time::Duration};
|
||||
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
||||
use std::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use super::gossip::{GossipMessage, NeighborPacket};
|
||||
use sc_network::PeerId;
|
||||
use sp_runtime::traits::{NumberFor, Block as BlockT};
|
||||
use super::gossip::{NeighborPacket, GossipMessage};
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
|
||||
// How often to rebroadcast, in cases where no new packets are created.
|
||||
const REBROADCAST_AFTER: Duration = Duration::from_secs(2 * 60);
|
||||
@@ -34,7 +38,7 @@ const REBROADCAST_AFTER: Duration = Duration::from_secs(2 * 60);
|
||||
/// A sender used to send neighbor packets to a background job.
|
||||
#[derive(Clone)]
|
||||
pub(super) struct NeighborPacketSender<B: BlockT>(
|
||||
TracingUnboundedSender<(Vec<PeerId>, NeighborPacket<NumberFor<B>>)>
|
||||
TracingUnboundedSender<(Vec<PeerId>, NeighborPacket<NumberFor<B>>)>,
|
||||
);
|
||||
|
||||
impl<B: BlockT> NeighborPacketSender<B> {
|
||||
@@ -63,24 +67,20 @@ pub(super) struct NeighborPacketWorker<B: BlockT> {
|
||||
impl<B: BlockT> Unpin for NeighborPacketWorker<B> {}
|
||||
|
||||
impl<B: BlockT> NeighborPacketWorker<B> {
|
||||
pub(super) fn new() -> (Self, NeighborPacketSender<B>){
|
||||
let (tx, rx) = tracing_unbounded::<(Vec<PeerId>, NeighborPacket<NumberFor<B>>)>
|
||||
("mpsc_grandpa_neighbor_packet_worker");
|
||||
pub(super) fn new() -> (Self, NeighborPacketSender<B>) {
|
||||
let (tx, rx) = tracing_unbounded::<(Vec<PeerId>, NeighborPacket<NumberFor<B>>)>(
|
||||
"mpsc_grandpa_neighbor_packet_worker",
|
||||
);
|
||||
let delay = Delay::new(REBROADCAST_AFTER);
|
||||
|
||||
(NeighborPacketWorker {
|
||||
last: None,
|
||||
delay,
|
||||
rx,
|
||||
}, NeighborPacketSender(tx))
|
||||
(NeighborPacketWorker { last: None, delay, rx }, NeighborPacketSender(tx))
|
||||
}
|
||||
}
|
||||
|
||||
impl <B: BlockT> Stream for NeighborPacketWorker<B> {
|
||||
impl<B: BlockT> Stream for NeighborPacketWorker<B> {
|
||||
type Item = (Vec<PeerId>, GossipMessage<B>);
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>>
|
||||
{
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||
let this = &mut *self;
|
||||
match this.rx.poll_next_unpin(cx) {
|
||||
Poll::Ready(None) => return Poll::Ready(None),
|
||||
@@ -88,8 +88,8 @@ impl <B: BlockT> Stream for NeighborPacketWorker<B> {
|
||||
this.delay.reset(REBROADCAST_AFTER);
|
||||
this.last = Some((to.clone(), packet.clone()));
|
||||
|
||||
return Poll::Ready(Some((to, GossipMessage::<B>::from(packet))));
|
||||
}
|
||||
return Poll::Ready(Some((to, GossipMessage::<B>::from(packet))))
|
||||
},
|
||||
// Don't return yet, maybe the timer fired.
|
||||
Poll::Pending => {},
|
||||
};
|
||||
@@ -104,10 +104,10 @@ impl <B: BlockT> Stream for NeighborPacketWorker<B> {
|
||||
//
|
||||
// Note: In case poll_unpin is called after the resetted delay fires again, this
|
||||
// will drop one tick. Deemed as very unlikely and also not critical.
|
||||
while let Poll::Ready(()) = this.delay.poll_unpin(cx) {};
|
||||
while let Poll::Ready(()) = this.delay.poll_unpin(cx) {}
|
||||
|
||||
if let Some((ref to, ref packet)) = this.last {
|
||||
return Poll::Ready(Some((to.clone(), GossipMessage::<B>::from(packet.clone()))));
|
||||
return Poll::Ready(Some((to.clone(), GossipMessage::<B>::from(packet.clone()))))
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
|
||||
@@ -18,21 +18,26 @@
|
||||
|
||||
//! Tests for the communication portion of the GRANDPA crate.
|
||||
|
||||
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
||||
use super::{
|
||||
gossip::{self, GossipValidator},
|
||||
Round, SetId, VoterSet,
|
||||
};
|
||||
use crate::{communication::GRANDPA_PROTOCOL_NAME, environment::SharedVoterSetState};
|
||||
use futures::prelude::*;
|
||||
use sc_network::{config::Role, Event as NetworkEvent, ObservedRole, PeerId};
|
||||
use sc_network_test::{Block, Hash};
|
||||
use sc_network_gossip::Validator;
|
||||
use std::sync::Arc;
|
||||
use sp_keyring::Ed25519Keyring;
|
||||
use parity_scale_codec::Encode;
|
||||
use sp_runtime::traits::NumberFor;
|
||||
use std::{borrow::Cow, pin::Pin, task::{Context, Poll}};
|
||||
use crate::communication::GRANDPA_PROTOCOL_NAME;
|
||||
use crate::environment::SharedVoterSetState;
|
||||
use sc_network::{config::Role, Event as NetworkEvent, ObservedRole, PeerId};
|
||||
use sc_network_gossip::Validator;
|
||||
use sc_network_test::{Block, Hash};
|
||||
use sp_finality_grandpa::AuthorityList;
|
||||
use super::gossip::{self, GossipValidator};
|
||||
use super::{VoterSet, Round, SetId};
|
||||
use sp_keyring::Ed25519Keyring;
|
||||
use sp_runtime::traits::NumberFor;
|
||||
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Event {
|
||||
@@ -79,13 +84,14 @@ impl super::Network<Block> for TestNetwork {
|
||||
_peers: Vec<sc_network::PeerId>,
|
||||
_hash: Hash,
|
||||
_number: NumberFor<Block>,
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl sc_network_gossip::ValidatorContext<Block> for TestNetwork {
|
||||
fn broadcast_topic(&mut self, _: Hash, _: bool) { }
|
||||
fn broadcast_topic(&mut self, _: Hash, _: bool) {}
|
||||
|
||||
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) { }
|
||||
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) {}
|
||||
|
||||
fn send_message(&mut self, who: &sc_network::PeerId, data: Vec<u8>) {
|
||||
<Self as sc_network_gossip::Network<Block>>::write_notification(
|
||||
@@ -96,7 +102,7 @@ impl sc_network_gossip::ValidatorContext<Block> for TestNetwork {
|
||||
);
|
||||
}
|
||||
|
||||
fn send_topic(&mut self, _: &sc_network::PeerId, _: Hash, _: bool) { }
|
||||
fn send_topic(&mut self, _: &sc_network::PeerId, _: Hash, _: bool) {}
|
||||
}
|
||||
|
||||
pub(crate) struct Tester {
|
||||
@@ -107,15 +113,17 @@ pub(crate) struct Tester {
|
||||
|
||||
impl Tester {
|
||||
fn filter_network_events<F>(self, mut pred: F) -> impl Future<Output = Self>
|
||||
where F: FnMut(Event) -> bool
|
||||
where
|
||||
F: FnMut(Event) -> bool,
|
||||
{
|
||||
let mut s = Some(self);
|
||||
futures::future::poll_fn(move |cx| loop {
|
||||
match Stream::poll_next(Pin::new(&mut s.as_mut().unwrap().events), cx) {
|
||||
Poll::Ready(None) => panic!("concluded early"),
|
||||
Poll::Ready(Some(item)) => if pred(item) {
|
||||
return Poll::Ready(s.take().unwrap())
|
||||
},
|
||||
Poll::Ready(Some(item)) =>
|
||||
if pred(item) {
|
||||
return Poll::Ready(s.take().unwrap())
|
||||
},
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
})
|
||||
@@ -145,8 +153,7 @@ fn config() -> crate::Config {
|
||||
|
||||
// dummy voter set state
|
||||
fn voter_set_state() -> SharedVoterSetState<Block> {
|
||||
use crate::authorities::AuthoritySet;
|
||||
use crate::environment::VoterSetState;
|
||||
use crate::{authorities::AuthoritySet, environment::VoterSetState};
|
||||
use finality_grandpa::round::State as RoundState;
|
||||
use sp_core::{crypto::Public, H256};
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
@@ -157,20 +164,13 @@ fn voter_set_state() -> SharedVoterSetState<Block> {
|
||||
let voters = vec![(AuthorityId::from_slice(&[1; 32]), 1)];
|
||||
let voters = AuthoritySet::genesis(voters).unwrap();
|
||||
|
||||
let set_state = VoterSetState::live(
|
||||
0,
|
||||
&voters,
|
||||
base,
|
||||
);
|
||||
let set_state = VoterSetState::live(0, &voters, base);
|
||||
|
||||
set_state.into()
|
||||
}
|
||||
|
||||
// needs to run in a tokio runtime.
|
||||
pub(crate) fn make_test_network() -> (
|
||||
impl Future<Output = Tester>,
|
||||
TestNetwork,
|
||||
) {
|
||||
pub(crate) fn make_test_network() -> (impl Future<Output = Tester>, TestNetwork) {
|
||||
let (tx, rx) = tracing_unbounded("test");
|
||||
let net = TestNetwork { sender: tx };
|
||||
|
||||
@@ -185,13 +185,7 @@ pub(crate) fn make_test_network() -> (
|
||||
}
|
||||
}
|
||||
|
||||
let bridge = super::NetworkBridge::new(
|
||||
net.clone(),
|
||||
config(),
|
||||
voter_set_state(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let bridge = super::NetworkBridge::new(net.clone(), config(), voter_set_state(), None, None);
|
||||
|
||||
(
|
||||
futures::future::ready(Tester {
|
||||
@@ -204,19 +198,16 @@ pub(crate) fn make_test_network() -> (
|
||||
}
|
||||
|
||||
fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList {
|
||||
keys.iter()
|
||||
.map(|key| key.clone().public().into())
|
||||
.map(|id| (id, 1))
|
||||
.collect()
|
||||
keys.iter().map(|key| key.clone().public().into()).map(|id| (id, 1)).collect()
|
||||
}
|
||||
|
||||
struct NoopContext;
|
||||
|
||||
impl sc_network_gossip::ValidatorContext<Block> for NoopContext {
|
||||
fn broadcast_topic(&mut self, _: Hash, _: bool) { }
|
||||
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) { }
|
||||
fn send_message(&mut self, _: &sc_network::PeerId, _: Vec<u8>) { }
|
||||
fn send_topic(&mut self, _: &sc_network::PeerId, _: Hash, _: bool) { }
|
||||
fn broadcast_topic(&mut self, _: Hash, _: bool) {}
|
||||
fn broadcast_message(&mut self, _: Hash, _: Vec<u8>, _: bool) {}
|
||||
fn send_message(&mut self, _: &sc_network::PeerId, _: Vec<u8>) {}
|
||||
fn send_topic(&mut self, _: &sc_network::PeerId, _: Hash, _: bool) {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -232,9 +223,12 @@ fn good_commit_leads_to_relay() {
|
||||
let target_hash: Hash = [1; 32].into();
|
||||
let target_number = 500;
|
||||
|
||||
let precommit = finality_grandpa::Precommit { target_hash: target_hash.clone(), target_number };
|
||||
let precommit =
|
||||
finality_grandpa::Precommit { target_hash: target_hash.clone(), target_number };
|
||||
let payload = sp_finality_grandpa::localized_payload(
|
||||
round, set_id, &finality_grandpa::Message::Precommit(precommit.clone())
|
||||
round,
|
||||
set_id,
|
||||
&finality_grandpa::Message::Precommit(precommit.clone()),
|
||||
);
|
||||
|
||||
let mut precommits = Vec::new();
|
||||
@@ -247,24 +241,21 @@ fn good_commit_leads_to_relay() {
|
||||
auth_data.push((signature, public[i].0.clone()))
|
||||
}
|
||||
|
||||
finality_grandpa::CompactCommit {
|
||||
target_hash,
|
||||
target_number,
|
||||
precommits,
|
||||
auth_data,
|
||||
}
|
||||
finality_grandpa::CompactCommit { target_hash, target_number, precommits, auth_data }
|
||||
};
|
||||
|
||||
let encoded_commit = gossip::GossipMessage::<Block>::Commit(gossip::FullCommitMessage {
|
||||
round: Round(round),
|
||||
set_id: SetId(set_id),
|
||||
message: commit,
|
||||
}).encode();
|
||||
})
|
||||
.encode();
|
||||
|
||||
let id = sc_network::PeerId::random();
|
||||
let global_topic = super::global_topic::<Block>(set_id);
|
||||
|
||||
let test = make_test_network().0
|
||||
let test = make_test_network()
|
||||
.0
|
||||
.then(move |tester| {
|
||||
// register a peer.
|
||||
tester.gossip_validator.new_peer(&mut NoopContext, &id, ObservedRole::Full);
|
||||
@@ -272,7 +263,8 @@ fn good_commit_leads_to_relay() {
|
||||
})
|
||||
.then(move |(tester, id)| {
|
||||
// start round, dispatch commit, and wait for broadcast.
|
||||
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
|
||||
let (commits_in, _) =
|
||||
tester.net_handle.global_communication(SetId(1), voter_set, false);
|
||||
|
||||
{
|
||||
let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
|
||||
@@ -301,7 +293,10 @@ fn good_commit_leads_to_relay() {
|
||||
|
||||
let _ = sender.unbounded_send(NetworkEvent::NotificationsReceived {
|
||||
remote: sender_id.clone(),
|
||||
messages: vec![(GRANDPA_PROTOCOL_NAME.into(), commit_to_send.clone().into())],
|
||||
messages: vec![(
|
||||
GRANDPA_PROTOCOL_NAME.into(),
|
||||
commit_to_send.clone().into(),
|
||||
)],
|
||||
});
|
||||
|
||||
// Add a random peer which will be the recipient of this message
|
||||
@@ -316,13 +311,11 @@ fn good_commit_leads_to_relay() {
|
||||
// Announce its local set has being on the current set id through a neighbor
|
||||
// packet, otherwise it won't be eligible to receive the commit
|
||||
let _ = {
|
||||
let update = gossip::VersionedNeighborPacket::V1(
|
||||
gossip::NeighborPacket {
|
||||
round: Round(round),
|
||||
set_id: SetId(set_id),
|
||||
commit_finalized_height: 1,
|
||||
}
|
||||
);
|
||||
let update = gossip::VersionedNeighborPacket::V1(gossip::NeighborPacket {
|
||||
round: Round(round),
|
||||
set_id: SetId(set_id),
|
||||
commit_finalized_height: 1,
|
||||
});
|
||||
|
||||
let msg = gossip::GossipMessage::<Block>::Neighbor(update);
|
||||
|
||||
@@ -333,31 +326,27 @@ fn good_commit_leads_to_relay() {
|
||||
};
|
||||
|
||||
true
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
});
|
||||
|
||||
// when the commit comes in, we'll tell the callback it was good.
|
||||
let handle_commit = commits_in.into_future()
|
||||
.map(|(item, _)| {
|
||||
match item.unwrap() {
|
||||
finality_grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
|
||||
callback.run(finality_grandpa::voter::CommitProcessingOutcome::good());
|
||||
},
|
||||
_ => panic!("commit expected"),
|
||||
}
|
||||
});
|
||||
let handle_commit = commits_in.into_future().map(|(item, _)| match item.unwrap() {
|
||||
finality_grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
|
||||
callback.run(finality_grandpa::voter::CommitProcessingOutcome::good());
|
||||
},
|
||||
_ => panic!("commit expected"),
|
||||
});
|
||||
|
||||
// once the message is sent and commit is "handled" we should have
|
||||
// a repropagation event coming from the network.
|
||||
let fut = future::join(send_message, handle_commit).then(move |(tester, ())| {
|
||||
tester.filter_network_events(move |event| match event {
|
||||
Event::WriteNotification(_, data) => {
|
||||
data == encoded_commit
|
||||
}
|
||||
_ => false,
|
||||
let fut = future::join(send_message, handle_commit)
|
||||
.then(move |(tester, ())| {
|
||||
tester.filter_network_events(move |event| match event {
|
||||
Event::WriteNotification(_, data) => data == encoded_commit,
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
})
|
||||
.map(|_| ());
|
||||
|
||||
// Poll both the future sending and handling the commit, as well as the underlying
|
||||
@@ -382,9 +371,12 @@ fn bad_commit_leads_to_report() {
|
||||
let target_hash: Hash = [1; 32].into();
|
||||
let target_number = 500;
|
||||
|
||||
let precommit = finality_grandpa::Precommit { target_hash: target_hash.clone(), target_number };
|
||||
let precommit =
|
||||
finality_grandpa::Precommit { target_hash: target_hash.clone(), target_number };
|
||||
let payload = sp_finality_grandpa::localized_payload(
|
||||
round, set_id, &finality_grandpa::Message::Precommit(precommit.clone())
|
||||
round,
|
||||
set_id,
|
||||
&finality_grandpa::Message::Precommit(precommit.clone()),
|
||||
);
|
||||
|
||||
let mut precommits = Vec::new();
|
||||
@@ -397,24 +389,21 @@ fn bad_commit_leads_to_report() {
|
||||
auth_data.push((signature, public[i].0.clone()))
|
||||
}
|
||||
|
||||
finality_grandpa::CompactCommit {
|
||||
target_hash,
|
||||
target_number,
|
||||
precommits,
|
||||
auth_data,
|
||||
}
|
||||
finality_grandpa::CompactCommit { target_hash, target_number, precommits, auth_data }
|
||||
};
|
||||
|
||||
let encoded_commit = gossip::GossipMessage::<Block>::Commit(gossip::FullCommitMessage {
|
||||
round: Round(round),
|
||||
set_id: SetId(set_id),
|
||||
message: commit,
|
||||
}).encode();
|
||||
})
|
||||
.encode();
|
||||
|
||||
let id = sc_network::PeerId::random();
|
||||
let global_topic = super::global_topic::<Block>(set_id);
|
||||
|
||||
let test = make_test_network().0
|
||||
let test = make_test_network()
|
||||
.0
|
||||
.map(move |tester| {
|
||||
// register a peer.
|
||||
tester.gossip_validator.new_peer(&mut NoopContext, &id, ObservedRole::Full);
|
||||
@@ -422,7 +411,8 @@ fn bad_commit_leads_to_report() {
|
||||
})
|
||||
.then(move |(tester, id)| {
|
||||
// start round, dispatch commit, and wait for broadcast.
|
||||
let (commits_in, _) = tester.net_handle.global_communication(SetId(1), voter_set, false);
|
||||
let (commits_in, _) =
|
||||
tester.net_handle.global_communication(SetId(1), voter_set, false);
|
||||
|
||||
{
|
||||
let (action, ..) = tester.gossip_validator.do_validate(&id, &encoded_commit[..]);
|
||||
@@ -449,35 +439,35 @@ fn bad_commit_leads_to_report() {
|
||||
});
|
||||
let _ = sender.unbounded_send(NetworkEvent::NotificationsReceived {
|
||||
remote: sender_id.clone(),
|
||||
messages: vec![(GRANDPA_PROTOCOL_NAME.into(), commit_to_send.clone().into())],
|
||||
messages: vec![(
|
||||
GRANDPA_PROTOCOL_NAME.into(),
|
||||
commit_to_send.clone().into(),
|
||||
)],
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
});
|
||||
|
||||
// when the commit comes in, we'll tell the callback it was bad.
|
||||
let handle_commit = commits_in.into_future()
|
||||
.map(|(item, _)| {
|
||||
match item.unwrap() {
|
||||
finality_grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
|
||||
callback.run(finality_grandpa::voter::CommitProcessingOutcome::bad());
|
||||
},
|
||||
_ => panic!("commit expected"),
|
||||
}
|
||||
});
|
||||
let handle_commit = commits_in.into_future().map(|(item, _)| match item.unwrap() {
|
||||
finality_grandpa::voter::CommunicationIn::Commit(_, _, mut callback) => {
|
||||
callback.run(finality_grandpa::voter::CommitProcessingOutcome::bad());
|
||||
},
|
||||
_ => panic!("commit expected"),
|
||||
});
|
||||
|
||||
// once the message is sent and commit is "handled" we should have
|
||||
// a report event coming from the network.
|
||||
let fut = future::join(send_message, handle_commit).then(move |(tester, ())| {
|
||||
tester.filter_network_events(move |event| match event {
|
||||
Event::Report(who, cost_benefit) => {
|
||||
who == id && cost_benefit == super::cost::INVALID_COMMIT
|
||||
}
|
||||
_ => false,
|
||||
let fut = future::join(send_message, handle_commit)
|
||||
.then(move |(tester, ())| {
|
||||
tester.filter_network_events(move |event| match event {
|
||||
Event::Report(who, cost_benefit) =>
|
||||
who == id && cost_benefit == super::cost::INVALID_COMMIT,
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
})
|
||||
.map(|_| ());
|
||||
|
||||
// Poll both the future sending and handling the commit, as well as the underlying
|
||||
@@ -508,7 +498,8 @@ fn peer_with_higher_view_leads_to_catch_up_request() {
|
||||
set_id: SetId(0),
|
||||
round: Round(10),
|
||||
commit_finalized_height: 50,
|
||||
}).encode(),
|
||||
})
|
||||
.encode(),
|
||||
);
|
||||
|
||||
// neighbor packets are always discard
|
||||
@@ -518,27 +509,23 @@ fn peer_with_higher_view_leads_to_catch_up_request() {
|
||||
}
|
||||
|
||||
// a catch up request should be sent to the peer for round - 1
|
||||
tester.filter_network_events(move |event| match event {
|
||||
Event::WriteNotification(peer, message) => {
|
||||
assert_eq!(
|
||||
peer,
|
||||
id,
|
||||
);
|
||||
tester
|
||||
.filter_network_events(move |event| match event {
|
||||
Event::WriteNotification(peer, message) => {
|
||||
assert_eq!(peer, id,);
|
||||
|
||||
assert_eq!(
|
||||
message,
|
||||
gossip::GossipMessage::<Block>::CatchUpRequest(
|
||||
gossip::CatchUpRequestMessage {
|
||||
set_id: SetId(0),
|
||||
round: Round(9),
|
||||
}
|
||||
).encode(),
|
||||
);
|
||||
assert_eq!(
|
||||
message,
|
||||
gossip::GossipMessage::<Block>::CatchUpRequest(
|
||||
gossip::CatchUpRequestMessage { set_id: SetId(0), round: Round(9) }
|
||||
)
|
||||
.encode(),
|
||||
);
|
||||
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
})
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
})
|
||||
.map(|_| ())
|
||||
});
|
||||
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::iter::FromIterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
iter::FromIterator,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use finality_grandpa::{
|
||||
round::State as RoundState, voter, voter_set::VoterSet, BlockNumberOps, Error as GrandpaError,
|
||||
@@ -44,8 +46,10 @@ use sp_finality_grandpa::{
|
||||
AuthorityId, AuthoritySignature, Equivocation, EquivocationProof, GrandpaApi, RoundNumber,
|
||||
SetId, GRANDPA_ENGINE_ID,
|
||||
};
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
authorities::{AuthoritySet, SharedAuthoritySet},
|
||||
@@ -105,13 +109,11 @@ impl<Block: BlockT> Encode for CompletedRounds<Block> {
|
||||
impl<Block: BlockT> parity_scale_codec::EncodeLike for CompletedRounds<Block> {}
|
||||
|
||||
impl<Block: BlockT> Decode for CompletedRounds<Block> {
|
||||
fn decode<I: parity_scale_codec::Input>(value: &mut I) -> Result<Self, parity_scale_codec::Error> {
|
||||
fn decode<I: parity_scale_codec::Input>(
|
||||
value: &mut I,
|
||||
) -> Result<Self, parity_scale_codec::Error> {
|
||||
<(Vec<CompletedRound<Block>>, SetId, Vec<AuthorityId>)>::decode(value)
|
||||
.map(|(rounds, set_id, voters)| CompletedRounds {
|
||||
rounds,
|
||||
set_id,
|
||||
voters,
|
||||
})
|
||||
.map(|(rounds, set_id, voters)| CompletedRounds { rounds, set_id, voters })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,9 +123,7 @@ impl<Block: BlockT> CompletedRounds<Block> {
|
||||
genesis: CompletedRound<Block>,
|
||||
set_id: SetId,
|
||||
voters: &AuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
)
|
||||
-> CompletedRounds<Block>
|
||||
{
|
||||
) -> CompletedRounds<Block> {
|
||||
let mut rounds = Vec::with_capacity(NUM_LAST_COMPLETED_ROUNDS);
|
||||
rounds.push(genesis);
|
||||
|
||||
@@ -137,13 +137,14 @@ impl<Block: BlockT> CompletedRounds<Block> {
|
||||
}
|
||||
|
||||
/// Iterate over all completed rounds.
|
||||
pub fn iter(&self) -> impl Iterator<Item=&CompletedRound<Block>> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &CompletedRound<Block>> {
|
||||
self.rounds.iter().rev()
|
||||
}
|
||||
|
||||
/// Returns the last (latest) completed round.
|
||||
pub fn last(&self) -> &CompletedRound<Block> {
|
||||
self.rounds.first()
|
||||
self.rounds
|
||||
.first()
|
||||
.expect("inner is never empty; always contains at least genesis; qed")
|
||||
}
|
||||
|
||||
@@ -152,10 +153,11 @@ impl<Block: BlockT> CompletedRounds<Block> {
|
||||
pub fn push(&mut self, completed_round: CompletedRound<Block>) {
|
||||
use std::cmp::Reverse;
|
||||
|
||||
match self.rounds.binary_search_by_key(
|
||||
&Reverse(completed_round.number),
|
||||
|completed_round| Reverse(completed_round.number),
|
||||
) {
|
||||
match self
|
||||
.rounds
|
||||
.binary_search_by_key(&Reverse(completed_round.number), |completed_round| {
|
||||
Reverse(completed_round.number)
|
||||
}) {
|
||||
Ok(idx) => self.rounds[idx] = completed_round,
|
||||
Err(idx) => self.rounds.insert(idx, completed_round),
|
||||
};
|
||||
@@ -215,37 +217,31 @@ impl<Block: BlockT> VoterSetState<Block> {
|
||||
let mut current_rounds = CurrentRounds::new();
|
||||
current_rounds.insert(1, HasVoted::No);
|
||||
|
||||
VoterSetState::Live {
|
||||
completed_rounds,
|
||||
current_rounds,
|
||||
}
|
||||
VoterSetState::Live { completed_rounds, current_rounds }
|
||||
}
|
||||
|
||||
/// Returns the last completed rounds.
|
||||
pub(crate) fn completed_rounds(&self) -> CompletedRounds<Block> {
|
||||
match self {
|
||||
VoterSetState::Live { completed_rounds, .. } =>
|
||||
completed_rounds.clone(),
|
||||
VoterSetState::Paused { completed_rounds } =>
|
||||
completed_rounds.clone(),
|
||||
VoterSetState::Live { completed_rounds, .. } => completed_rounds.clone(),
|
||||
VoterSetState::Paused { completed_rounds } => completed_rounds.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the last completed round.
|
||||
pub(crate) fn last_completed_round(&self) -> CompletedRound<Block> {
|
||||
match self {
|
||||
VoterSetState::Live { completed_rounds, .. } =>
|
||||
completed_rounds.last().clone(),
|
||||
VoterSetState::Paused { completed_rounds } =>
|
||||
completed_rounds.last().clone(),
|
||||
VoterSetState::Live { completed_rounds, .. } => completed_rounds.last().clone(),
|
||||
VoterSetState::Paused { completed_rounds } => completed_rounds.last().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the voter set state validating that it includes the given round
|
||||
/// in current rounds and that the voter isn't paused.
|
||||
pub fn with_current_round(&self, round: RoundNumber)
|
||||
-> Result<(&CompletedRounds<Block>, &CurrentRounds<Block>), Error>
|
||||
{
|
||||
pub fn with_current_round(
|
||||
&self,
|
||||
round: RoundNumber,
|
||||
) -> Result<(&CompletedRounds<Block>, &CurrentRounds<Block>), Error> {
|
||||
if let VoterSetState::Live { completed_rounds, current_rounds } = self {
|
||||
if current_rounds.contains_key(&round) {
|
||||
Ok((completed_rounds, current_rounds))
|
||||
@@ -284,10 +280,9 @@ impl<Block: BlockT> HasVoted<Block> {
|
||||
/// Returns the proposal we should vote with (if any.)
|
||||
pub fn propose(&self) -> Option<&PrimaryPropose<Block>> {
|
||||
match self {
|
||||
HasVoted::Yes(_, Vote::Propose(propose)) =>
|
||||
Some(propose),
|
||||
HasVoted::Yes(_, Vote::Prevote(propose, _)) | HasVoted::Yes(_, Vote::Precommit(propose, _, _)) =>
|
||||
propose.as_ref(),
|
||||
HasVoted::Yes(_, Vote::Propose(propose)) => Some(propose),
|
||||
HasVoted::Yes(_, Vote::Prevote(propose, _)) |
|
||||
HasVoted::Yes(_, Vote::Precommit(propose, _, _)) => propose.as_ref(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -295,8 +290,8 @@ impl<Block: BlockT> HasVoted<Block> {
|
||||
/// Returns the prevote we should vote with (if any.)
|
||||
pub fn prevote(&self) -> Option<&Prevote<Block>> {
|
||||
match self {
|
||||
HasVoted::Yes(_, Vote::Prevote(_, prevote)) | HasVoted::Yes(_, Vote::Precommit(_, prevote, _)) =>
|
||||
Some(prevote),
|
||||
HasVoted::Yes(_, Vote::Prevote(_, prevote)) |
|
||||
HasVoted::Yes(_, Vote::Precommit(_, prevote, _)) => Some(prevote),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -304,8 +299,7 @@ impl<Block: BlockT> HasVoted<Block> {
|
||||
/// Returns the precommit we should vote with (if any.)
|
||||
pub fn precommit(&self) -> Option<&Precommit<Block>> {
|
||||
match self {
|
||||
HasVoted::Yes(_, Vote::Precommit(_, _, precommit)) =>
|
||||
Some(precommit),
|
||||
HasVoted::Yes(_, Vote::Precommit(_, _, precommit)) => Some(precommit),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -376,21 +370,21 @@ impl<Block: BlockT> SharedVoterSetState<Block> {
|
||||
/// Return vote status information for the current round.
|
||||
pub(crate) fn has_voted(&self, round: RoundNumber) -> HasVoted<Block> {
|
||||
match &*self.inner.read() {
|
||||
VoterSetState::Live { current_rounds, .. } => {
|
||||
current_rounds.get(&round).and_then(|has_voted| match has_voted {
|
||||
HasVoted::Yes(id, vote) =>
|
||||
Some(HasVoted::Yes(id.clone(), vote.clone())),
|
||||
VoterSetState::Live { current_rounds, .. } => current_rounds
|
||||
.get(&round)
|
||||
.and_then(|has_voted| match has_voted {
|
||||
HasVoted::Yes(id, vote) => Some(HasVoted::Yes(id.clone(), vote.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(HasVoted::No)
|
||||
},
|
||||
.unwrap_or(HasVoted::No),
|
||||
_ => HasVoted::No,
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: not exposed outside of this module intentionally.
|
||||
fn with<F, R>(&self, f: F) -> R
|
||||
where F: FnOnce(&mut VoterSetState<Block>) -> R
|
||||
where
|
||||
F: FnOnce(&mut VoterSetState<Block>) -> R,
|
||||
{
|
||||
f(&mut *self.inner.write())
|
||||
}
|
||||
@@ -452,8 +446,9 @@ impl<BE, Block: BlockT, C, N: NetworkT<Block>, SC, VR> Environment<BE, Block, C,
|
||||
/// Updates the voter set state using the given closure. The write lock is
|
||||
/// held during evaluation of the closure and the environment's voter set
|
||||
/// state is set to its result if successful.
|
||||
pub(crate) fn update_voter_set_state<F>(&self, f: F) -> Result<(), Error> where
|
||||
F: FnOnce(&VoterSetState<Block>) -> Result<Option<VoterSetState<Block>>, Error>
|
||||
pub(crate) fn update_voter_set_state<F>(&self, f: F) -> Result<(), Error>
|
||||
where
|
||||
F: FnOnce(&VoterSetState<Block>) -> Result<Option<VoterSetState<Block>>, Error>,
|
||||
{
|
||||
self.voter_set_state.with(|voter_set_state| {
|
||||
if let Some(set_state) = f(&voter_set_state)? {
|
||||
@@ -461,7 +456,9 @@ impl<BE, Block: BlockT, C, N: NetworkT<Block>, SC, VR> Environment<BE, Block, C,
|
||||
|
||||
if let Some(metrics) = self.metrics.as_ref() {
|
||||
if let VoterSetState::Live { completed_rounds, .. } = voter_set_state {
|
||||
let highest = completed_rounds.rounds.iter()
|
||||
let highest = completed_rounds
|
||||
.rounds
|
||||
.iter()
|
||||
.map(|round| round.number)
|
||||
.max()
|
||||
.expect("There is always one completed round (genesis); qed");
|
||||
@@ -497,7 +494,7 @@ where
|
||||
if *equivocation.offender() == local_id {
|
||||
return Err(Error::Safety(
|
||||
"Refraining from sending equivocation report for our own equivocation.".into(),
|
||||
));
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,11 +517,8 @@ where
|
||||
|
||||
// find the hash of the latest block in the current set
|
||||
let current_set_latest_hash = match next_change {
|
||||
Some((_, n)) if n.is_zero() => {
|
||||
return Err(Error::Safety(
|
||||
"Authority set change signalled at genesis.".to_string(),
|
||||
))
|
||||
}
|
||||
Some((_, n)) if n.is_zero() =>
|
||||
return Err(Error::Safety("Authority set change signalled at genesis.".to_string())),
|
||||
// the next set starts at `n` so the current one lasts until `n - 1`. if
|
||||
// `n` is later than the best block, then the current set is still live
|
||||
// at best block.
|
||||
@@ -538,14 +532,15 @@ where
|
||||
|
||||
// its parent block is the last block in the current set
|
||||
*header.parent_hash()
|
||||
}
|
||||
},
|
||||
// there is no pending change, the latest block for the current set is
|
||||
// the best block.
|
||||
None => best_block_hash,
|
||||
};
|
||||
|
||||
// generate key ownership proof at that block
|
||||
let key_owner_proof = match self.client
|
||||
let key_owner_proof = match self
|
||||
.client
|
||||
.runtime_api()
|
||||
.generate_key_ownership_proof(
|
||||
&BlockId::Hash(current_set_latest_hash),
|
||||
@@ -557,15 +552,12 @@ where
|
||||
Some(proof) => proof,
|
||||
None => {
|
||||
debug!(target: "afg", "Equivocation offender is not part of the authority set.");
|
||||
return Ok(());
|
||||
}
|
||||
return Ok(())
|
||||
},
|
||||
};
|
||||
|
||||
// submit equivocation report at **best** block
|
||||
let equivocation_proof = EquivocationProof::new(
|
||||
authority_set.set_id,
|
||||
equivocation,
|
||||
);
|
||||
let equivocation_proof = EquivocationProof::new(authority_set.set_id, equivocation);
|
||||
|
||||
self.client
|
||||
.runtime_api()
|
||||
@@ -608,7 +600,9 @@ pub(crate) fn ancestry<Block: BlockT, Client>(
|
||||
where
|
||||
Client: HeaderMetadata<Block, Error = sp_blockchain::Error>,
|
||||
{
|
||||
if base == block { return Err(GrandpaError::NotDescendent) }
|
||||
if base == block {
|
||||
return Err(GrandpaError::NotDescendent)
|
||||
}
|
||||
|
||||
let tree_route_res = sp_blockchain::tree_route(&**client, block, base);
|
||||
|
||||
@@ -618,22 +612,17 @@ where
|
||||
debug!(target: "afg", "Encountered error computing ancestry between block {:?} and base {:?}: {:?}",
|
||||
block, base, e);
|
||||
|
||||
return Err(GrandpaError::NotDescendent);
|
||||
}
|
||||
return Err(GrandpaError::NotDescendent)
|
||||
},
|
||||
};
|
||||
|
||||
if tree_route.common_block().hash != base {
|
||||
return Err(GrandpaError::NotDescendent);
|
||||
return Err(GrandpaError::NotDescendent)
|
||||
}
|
||||
|
||||
// skip one because our ancestry is meant to start from the parent of `block`,
|
||||
// and `tree_route` includes it.
|
||||
Ok(tree_route
|
||||
.retracted()
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|e| e.hash)
|
||||
.collect())
|
||||
Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect())
|
||||
}
|
||||
|
||||
impl<B, Block, C, N, SC, VR> voter::Environment<Block::Hash, NumberFor<Block>>
|
||||
@@ -699,7 +688,7 @@ where
|
||||
// before activating the new set. the `authority_set` is updated immediately thus
|
||||
// we restrict the voter based on that.
|
||||
if set_id != authority_set.set_id() {
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
best_chain_containing(block, client, authority_set, select_chain, voting_rule)
|
||||
@@ -718,13 +707,12 @@ where
|
||||
let local_id = local_authority_id(&self.voters, self.config.keystore.as_ref());
|
||||
|
||||
let has_voted = match self.voter_set_state.has_voted(round) {
|
||||
HasVoted::Yes(id, vote) => {
|
||||
HasVoted::Yes(id, vote) =>
|
||||
if local_id.as_ref().map(|k| k == &id).unwrap_or(false) {
|
||||
HasVoted::Yes(id, vote)
|
||||
} else {
|
||||
HasVoted::No
|
||||
}
|
||||
},
|
||||
},
|
||||
HasVoted::No => HasVoted::No,
|
||||
};
|
||||
|
||||
@@ -756,14 +744,17 @@ where
|
||||
|
||||
// schedule incoming messages from the network to be held until
|
||||
// corresponding blocks are imported.
|
||||
let incoming = Box::pin(UntilVoteTargetImported::new(
|
||||
self.client.import_notification_stream(),
|
||||
self.network.clone(),
|
||||
self.client.clone(),
|
||||
incoming,
|
||||
"round",
|
||||
None,
|
||||
).map_err(Into::into));
|
||||
let incoming = Box::pin(
|
||||
UntilVoteTargetImported::new(
|
||||
self.client.import_notification_stream(),
|
||||
self.network.clone(),
|
||||
self.client.clone(),
|
||||
incoming,
|
||||
"round",
|
||||
None,
|
||||
)
|
||||
.map_err(Into::into),
|
||||
);
|
||||
|
||||
// schedule network message cleanup when sink drops.
|
||||
let outgoing = Box::pin(outgoing.sink_err_into());
|
||||
@@ -789,18 +780,20 @@ where
|
||||
|
||||
self.update_voter_set_state(|voter_set_state| {
|
||||
let (completed_rounds, current_rounds) = voter_set_state.with_current_round(round)?;
|
||||
let current_round = current_rounds.get(&round)
|
||||
let current_round = current_rounds
|
||||
.get(&round)
|
||||
.expect("checked in with_current_round that key exists; qed.");
|
||||
|
||||
if !current_round.can_propose() {
|
||||
// we've already proposed in this round (in a previous run),
|
||||
// ignore the given vote and don't update the voter set
|
||||
// state
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
let mut current_rounds = current_rounds.clone();
|
||||
let current_round = current_rounds.get_mut(&round)
|
||||
let current_round = current_rounds
|
||||
.get_mut(&round)
|
||||
.expect("checked previously that key exists; qed.");
|
||||
|
||||
*current_round = HasVoted::Yes(local_id, Vote::Propose(propose));
|
||||
@@ -849,7 +842,7 @@ where
|
||||
// we've already prevoted in this round (in a previous run),
|
||||
// ignore the given vote and don't update the voter set
|
||||
// state
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// report to telemetry and prometheus
|
||||
@@ -858,7 +851,8 @@ where
|
||||
let propose = current_round.propose();
|
||||
|
||||
let mut current_rounds = current_rounds.clone();
|
||||
let current_round = current_rounds.get_mut(&round)
|
||||
let current_round = current_rounds
|
||||
.get_mut(&round)
|
||||
.expect("checked previously that key exists; qed.");
|
||||
|
||||
*current_round = HasVoted::Yes(local_id, Vote::Prevote(propose.cloned(), prevote));
|
||||
@@ -911,7 +905,7 @@ where
|
||||
// we've already precommitted in this round (in a previous run),
|
||||
// ignore the given vote and don't update the voter set
|
||||
// state
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// report to telemetry and prometheus
|
||||
@@ -922,12 +916,13 @@ where
|
||||
HasVoted::Yes(_, Vote::Prevote(_, prevote)) => prevote,
|
||||
_ => {
|
||||
let msg = "Voter precommitting before prevoting.";
|
||||
return Err(Error::Safety(msg.to_string()));
|
||||
}
|
||||
return Err(Error::Safety(msg.to_string()))
|
||||
},
|
||||
};
|
||||
|
||||
let mut current_rounds = current_rounds.clone();
|
||||
let current_round = current_rounds.get_mut(&round)
|
||||
let current_round = current_rounds
|
||||
.get_mut(&round)
|
||||
.expect("checked previously that key exists; qed.");
|
||||
|
||||
*current_round = HasVoted::Yes(
|
||||
@@ -973,7 +968,7 @@ where
|
||||
(completed_rounds, current_rounds)
|
||||
} else {
|
||||
let msg = "Voter acting while in paused state.";
|
||||
return Err(Error::Safety(msg.to_string()));
|
||||
return Err(Error::Safety(msg.to_string()))
|
||||
};
|
||||
|
||||
let mut completed_rounds = completed_rounds.clone();
|
||||
@@ -998,10 +993,7 @@ where
|
||||
current_rounds.insert(round + 1, HasVoted::No);
|
||||
}
|
||||
|
||||
let set_state = VoterSetState::<Block>::Live {
|
||||
completed_rounds,
|
||||
current_rounds,
|
||||
};
|
||||
let set_state = VoterSetState::<Block>::Live { completed_rounds, current_rounds };
|
||||
|
||||
crate::aux_schema::write_voter_set_state(&*self.client, &set_state)?;
|
||||
|
||||
@@ -1038,21 +1030,21 @@ where
|
||||
(completed_rounds, current_rounds)
|
||||
} else {
|
||||
let msg = "Voter acting while in paused state.";
|
||||
return Err(Error::Safety(msg.to_string()));
|
||||
return Err(Error::Safety(msg.to_string()))
|
||||
};
|
||||
|
||||
let mut completed_rounds = completed_rounds.clone();
|
||||
|
||||
if let Some(already_completed) = completed_rounds.rounds
|
||||
.iter_mut().find(|r| r.number == round)
|
||||
if let Some(already_completed) =
|
||||
completed_rounds.rounds.iter_mut().find(|r| r.number == round)
|
||||
{
|
||||
let n_existing_votes = already_completed.votes.len();
|
||||
|
||||
// the interface of Environment guarantees that the previous `historical_votes`
|
||||
// from `completable` is a prefix of what is passed to `concluded`.
|
||||
already_completed.votes.extend(
|
||||
historical_votes.seen().iter().skip(n_existing_votes).cloned()
|
||||
);
|
||||
already_completed
|
||||
.votes
|
||||
.extend(historical_votes.seen().iter().skip(n_existing_votes).cloned());
|
||||
already_completed.state = state;
|
||||
crate::aux_schema::write_concluded_round(&*self.client, &already_completed)?;
|
||||
}
|
||||
@@ -1161,8 +1153,8 @@ where
|
||||
block,
|
||||
);
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
return Ok(None)
|
||||
},
|
||||
};
|
||||
|
||||
// we refuse to vote beyond the current limit number where transitions are scheduled to occur.
|
||||
@@ -1195,7 +1187,7 @@ where
|
||||
}
|
||||
|
||||
if *target_header.number() == target_number {
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
target_header = client
|
||||
@@ -1230,15 +1222,15 @@ where
|
||||
restricted_number < target_header.number()
|
||||
})
|
||||
.or_else(|| Some((target_header.hash(), *target_header.number())))
|
||||
}
|
||||
},
|
||||
Ok(None) => {
|
||||
debug!(target: "afg", "Encountered error finding best chain containing {:?}: couldn't find target block", block);
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
debug!(target: "afg", "Encountered error finding best chain containing {:?}: {:?}", block, e);
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
@@ -1281,20 +1273,22 @@ where
|
||||
status.finalized_number,
|
||||
);
|
||||
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// FIXME #1483: clone only when changed
|
||||
let old_authority_set = authority_set.clone();
|
||||
|
||||
let update_res: Result<_, Error> = client.lock_import_and_run(|import_op| {
|
||||
let status = authority_set.apply_standard_changes(
|
||||
hash,
|
||||
number,
|
||||
&is_descendent_of::<Block, _>(&*client, None),
|
||||
initial_sync,
|
||||
None,
|
||||
).map_err(|e| Error::Safety(e.to_string()))?;
|
||||
let status = authority_set
|
||||
.apply_standard_changes(
|
||||
hash,
|
||||
number,
|
||||
&is_descendent_of::<Block, _>(&*client, None),
|
||||
initial_sync,
|
||||
None,
|
||||
)
|
||||
.map_err(|e| Error::Safety(e.to_string()))?;
|
||||
|
||||
// send a justification notification if a sender exists and in case of error log it.
|
||||
fn notify_justification<Block: BlockT>(
|
||||
@@ -1327,17 +1321,15 @@ where
|
||||
if !justification_required {
|
||||
if let Some(justification_period) = justification_period {
|
||||
let last_finalized_number = client.info().finalized_number;
|
||||
justification_required =
|
||||
(!last_finalized_number.is_zero() || number - last_finalized_number == justification_period) &&
|
||||
(last_finalized_number / justification_period != number / justification_period);
|
||||
justification_required = (!last_finalized_number.is_zero() ||
|
||||
number - last_finalized_number == justification_period) &&
|
||||
(last_finalized_number / justification_period !=
|
||||
number / justification_period);
|
||||
}
|
||||
}
|
||||
|
||||
let justification = GrandpaJustification::from_commit(
|
||||
&client,
|
||||
round_number,
|
||||
commit,
|
||||
)?;
|
||||
let justification =
|
||||
GrandpaJustification::from_commit(&client, round_number, commit)?;
|
||||
|
||||
(justification_required, justification)
|
||||
},
|
||||
@@ -1369,25 +1361,22 @@ where
|
||||
"number" => ?number, "hash" => ?hash,
|
||||
);
|
||||
|
||||
crate::aux_schema::update_best_justification(
|
||||
&justification,
|
||||
|insert| apply_aux(import_op, insert, &[]),
|
||||
)?;
|
||||
crate::aux_schema::update_best_justification(&justification, |insert| {
|
||||
apply_aux(import_op, insert, &[])
|
||||
})?;
|
||||
|
||||
let new_authorities = if let Some((canon_hash, canon_number)) = status.new_set_block {
|
||||
// the authority set has changed.
|
||||
let (new_id, set_ref) = authority_set.current();
|
||||
|
||||
if set_ref.len() > 16 {
|
||||
afg_log!(initial_sync,
|
||||
afg_log!(
|
||||
initial_sync,
|
||||
"👴 Applying GRANDPA set change to new set with {} authorities",
|
||||
set_ref.len(),
|
||||
);
|
||||
} else {
|
||||
afg_log!(initial_sync,
|
||||
"👴 Applying GRANDPA set change to new set {:?}",
|
||||
set_ref,
|
||||
);
|
||||
afg_log!(initial_sync, "👴 Applying GRANDPA set change to new set {:?}", set_ref,);
|
||||
}
|
||||
|
||||
telemetry!(
|
||||
@@ -1419,7 +1408,7 @@ where
|
||||
warn!(target: "afg", "Failed to write updated authority set to disk. Bailing.");
|
||||
warn!(target: "afg", "Node is in a potentially inconsistent state.");
|
||||
|
||||
return Err(e.into());
|
||||
return Err(e.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1433,6 +1422,6 @@ where
|
||||
*authority_set = old_authority_set;
|
||||
|
||||
Err(CommandOrError::Error(e))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,19 +39,20 @@
|
||||
use log::{trace, warn};
|
||||
use std::sync::Arc;
|
||||
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use sc_client_api::backend::Backend;
|
||||
use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend};
|
||||
use sp_finality_grandpa::GRANDPA_ENGINE_ID;
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{NumberFor, Block as BlockT, Header as HeaderT, One},
|
||||
traits::{Block as BlockT, Header as HeaderT, NumberFor, One},
|
||||
};
|
||||
use sc_client_api::backend::Backend;
|
||||
|
||||
use crate::{
|
||||
SharedAuthoritySet, best_justification,
|
||||
authorities::{AuthoritySetChangeId, AuthoritySetChanges},
|
||||
best_justification,
|
||||
justification::GrandpaJustification,
|
||||
SharedAuthoritySet,
|
||||
};
|
||||
|
||||
const MAX_UNKNOWN_HEADERS: usize = 100_000;
|
||||
@@ -76,10 +77,7 @@ where
|
||||
backend: Arc<B>,
|
||||
shared_authority_set: Option<SharedAuthoritySet<Block::Hash, NumberFor<Block>>>,
|
||||
) -> Self {
|
||||
FinalityProofProvider {
|
||||
backend,
|
||||
shared_authority_set,
|
||||
}
|
||||
FinalityProofProvider { backend, shared_authority_set }
|
||||
}
|
||||
|
||||
/// Create new finality proof provider for the service using:
|
||||
@@ -113,14 +111,10 @@ where
|
||||
{
|
||||
changes
|
||||
} else {
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
};
|
||||
|
||||
prove_finality(
|
||||
&*self.backend,
|
||||
authority_set_changes,
|
||||
block,
|
||||
)
|
||||
prove_finality(&*self.backend, authority_set_changes, block)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,11 +160,10 @@ where
|
||||
if info.finalized_number < block {
|
||||
let err = format!(
|
||||
"Requested finality proof for descendant of #{} while we only have finalized #{}.",
|
||||
block,
|
||||
info.finalized_number,
|
||||
block, info.finalized_number,
|
||||
);
|
||||
trace!(target: "afg", "{}", &err);
|
||||
return Err(FinalityProofError::BlockNotYetFinalized);
|
||||
return Err(FinalityProofError::BlockNotYetFinalized)
|
||||
}
|
||||
|
||||
let (justification, just_block) = match authority_set_changes.get_set_id(block) {
|
||||
@@ -185,9 +178,9 @@ where
|
||||
"No justification found for the latest finalized block. \
|
||||
Returning empty proof.",
|
||||
);
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
}
|
||||
}
|
||||
},
|
||||
AuthoritySetChangeId::Set(_, last_block_for_set) => {
|
||||
let last_block_for_set_id = BlockId::Number(last_block_for_set);
|
||||
let justification = if let Some(grandpa_justification) = backend
|
||||
@@ -203,10 +196,10 @@ where
|
||||
Returning empty proof.",
|
||||
block,
|
||||
);
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
};
|
||||
(justification, last_block_for_set)
|
||||
}
|
||||
},
|
||||
AuthoritySetChangeId::Unknown => {
|
||||
warn!(
|
||||
target: "afg",
|
||||
@@ -214,8 +207,8 @@ where
|
||||
You need to resync to populate AuthoritySetChanges properly.",
|
||||
block,
|
||||
);
|
||||
return Err(FinalityProofError::BlockNotInAuthoritySetChanges);
|
||||
}
|
||||
return Err(FinalityProofError::BlockNotInAuthoritySetChanges)
|
||||
},
|
||||
};
|
||||
|
||||
// Collect all headers from the requested block until the last block of the set
|
||||
@@ -224,7 +217,7 @@ where
|
||||
let mut current = block + One::one();
|
||||
loop {
|
||||
if current > just_block || headers.len() >= MAX_UNKNOWN_HEADERS {
|
||||
break;
|
||||
break
|
||||
}
|
||||
headers.push(backend.blockchain().expect_header(BlockId::Number(current))?);
|
||||
current += One::one();
|
||||
@@ -245,9 +238,7 @@ where
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
authorities::AuthoritySetChanges, BlockNumberOps, ClientError, SetId,
|
||||
};
|
||||
use crate::{authorities::AuthoritySetChanges, BlockNumberOps, ClientError, SetId};
|
||||
use futures::executor::block_on;
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_client_api::{apply_aux, LockImportRun};
|
||||
@@ -276,8 +267,9 @@ pub(crate) mod tests {
|
||||
let proof = super::FinalityProof::<Block::Header>::decode(&mut &remote_proof[..])
|
||||
.map_err(|_| ClientError::BadJustification("failed to decode finality proof".into()))?;
|
||||
|
||||
let justification: GrandpaJustification<Block> = Decode::decode(&mut &proof.justification[..])
|
||||
.map_err(|_| ClientError::JustificationDecode)?;
|
||||
let justification: GrandpaJustification<Block> =
|
||||
Decode::decode(&mut &proof.justification[..])
|
||||
.map_err(|_| ClientError::JustificationDecode)?;
|
||||
justification.verify(current_set_id, ¤t_authorities)?;
|
||||
|
||||
Ok(proof)
|
||||
@@ -321,13 +313,13 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
fn store_best_justification(client: &TestClient, just: &GrandpaJustification<Block>) {
|
||||
client.lock_import_and_run(|import_op| {
|
||||
crate::aux_schema::update_best_justification(
|
||||
just,
|
||||
|insert| apply_aux(import_op, insert, &[]),
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
client
|
||||
.lock_import_and_run(|import_op| {
|
||||
crate::aux_schema::update_best_justification(just, |insert| {
|
||||
apply_aux(import_op, insert, &[])
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -336,11 +328,7 @@ pub(crate) mod tests {
|
||||
let authority_set_changes = AuthoritySetChanges::empty();
|
||||
|
||||
// The last finalized block is 4, so we cannot provide further justifications.
|
||||
let proof_of_5 = prove_finality(
|
||||
&*backend,
|
||||
authority_set_changes,
|
||||
5,
|
||||
);
|
||||
let proof_of_5 = prove_finality(&*backend, authority_set_changes, 5);
|
||||
assert!(matches!(proof_of_5, Err(FinalityProofError::BlockNotYetFinalized)));
|
||||
}
|
||||
|
||||
@@ -353,12 +341,7 @@ pub(crate) mod tests {
|
||||
|
||||
// Block 4 is finalized without justification
|
||||
// => we can't prove finality of 3
|
||||
let proof_of_3 = prove_finality(
|
||||
&*backend,
|
||||
authority_set_changes,
|
||||
3,
|
||||
)
|
||||
.unwrap();
|
||||
let proof_of_3 = prove_finality(&*backend, authority_set_changes, 3).unwrap();
|
||||
assert_eq!(proof_of_3, None);
|
||||
}
|
||||
|
||||
@@ -406,14 +389,15 @@ pub(crate) mod tests {
|
||||
1,
|
||||
vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)],
|
||||
finality_proof.encode(),
|
||||
).unwrap_err();
|
||||
)
|
||||
.unwrap_err();
|
||||
}
|
||||
|
||||
fn create_commit<S, Id>(
|
||||
block: Block,
|
||||
round: u64,
|
||||
set_id: SetId,
|
||||
auth: &[Ed25519Keyring]
|
||||
auth: &[Ed25519Keyring],
|
||||
) -> finality_grandpa::Commit<H256, u64, S, Id>
|
||||
where
|
||||
Id: From<sp_core::ed25519::Public>,
|
||||
@@ -481,11 +465,7 @@ pub(crate) mod tests {
|
||||
let mut authority_set_changes = AuthoritySetChanges::empty();
|
||||
authority_set_changes.append(1, 8);
|
||||
|
||||
let proof_of_6 = prove_finality(
|
||||
&*backend,
|
||||
authority_set_changes,
|
||||
6,
|
||||
);
|
||||
let proof_of_6 = prove_finality(&*backend, authority_set_changes, 6);
|
||||
assert!(matches!(proof_of_6, Err(FinalityProofError::BlockNotInAuthoritySetChanges)));
|
||||
}
|
||||
|
||||
@@ -499,11 +479,9 @@ pub(crate) mod tests {
|
||||
let commit = create_commit(block8.clone(), round, 1, &[Ed25519Keyring::Alice]);
|
||||
let grandpa_just8 = GrandpaJustification::from_commit(&client, round, commit).unwrap();
|
||||
|
||||
client.finalize_block(
|
||||
BlockId::Number(8),
|
||||
Some((ID, grandpa_just8.encode().clone()))
|
||||
)
|
||||
.unwrap();
|
||||
client
|
||||
.finalize_block(BlockId::Number(8), Some((ID, grandpa_just8.encode().clone())))
|
||||
.unwrap();
|
||||
|
||||
// Authority set change at block 8, so the justification stored there will be used in the
|
||||
// FinalityProof for block 6
|
||||
@@ -512,13 +490,7 @@ pub(crate) mod tests {
|
||||
authority_set_changes.append(1, 8);
|
||||
|
||||
let proof_of_6: FinalityProof = Decode::decode(
|
||||
&mut &prove_finality(
|
||||
&*backend,
|
||||
authority_set_changes.clone(),
|
||||
6,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap()[..],
|
||||
&mut &prove_finality(&*backend, authority_set_changes.clone(), 6).unwrap().unwrap()[..],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -540,10 +512,7 @@ pub(crate) mod tests {
|
||||
let mut authority_set_changes = AuthoritySetChanges::empty();
|
||||
authority_set_changes.append(0, 5);
|
||||
|
||||
assert!(matches!(
|
||||
prove_finality(&*backend, authority_set_changes, 6),
|
||||
Ok(None),
|
||||
));
|
||||
assert!(matches!(prove_finality(&*backend, authority_set_changes, 6), Ok(None),));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -563,13 +532,7 @@ pub(crate) mod tests {
|
||||
authority_set_changes.append(0, 5);
|
||||
|
||||
let proof_of_6: FinalityProof = Decode::decode(
|
||||
&mut &prove_finality(
|
||||
&*backend,
|
||||
authority_set_changes,
|
||||
6,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap()[..],
|
||||
&mut &prove_finality(&*backend, authority_set_changes, 6).unwrap().unwrap()[..],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
|
||||
@@ -31,9 +31,11 @@ use sp_consensus::{
|
||||
ImportResult, JustificationImport, SelectChain,
|
||||
};
|
||||
use sp_finality_grandpa::{ConsensusLog, ScheduledChange, SetId, GRANDPA_ENGINE_ID};
|
||||
use sp_runtime::generic::{BlockId, OpaqueDigestItemId};
|
||||
use sp_runtime::traits::{Block as BlockT, DigestFor, Header as HeaderT, NumberFor, Zero};
|
||||
use sp_runtime::Justification;
|
||||
use sp_runtime::{
|
||||
generic::{BlockId, OpaqueDigestItemId},
|
||||
traits::{Block as BlockT, DigestFor, Header as HeaderT, NumberFor, Zero},
|
||||
Justification,
|
||||
};
|
||||
use sp_utils::mpsc::TracingUnboundedSender;
|
||||
|
||||
use crate::{
|
||||
@@ -98,12 +100,8 @@ where
|
||||
let chain_info = self.inner.info();
|
||||
|
||||
// request justifications for all pending changes for which change blocks have already been imported
|
||||
let pending_changes: Vec<_> = self
|
||||
.authority_set
|
||||
.inner()
|
||||
.pending_changes()
|
||||
.cloned()
|
||||
.collect();
|
||||
let pending_changes: Vec<_> =
|
||||
self.authority_set.inner().pending_changes().cloned().collect();
|
||||
|
||||
for pending_change in pending_changes {
|
||||
if pending_change.delay_kind == DelayKind::Finalized &&
|
||||
@@ -241,7 +239,7 @@ where
|
||||
) -> Option<PendingChange<Block::Hash, NumberFor<Block>>> {
|
||||
// check for forced authority set hard forks
|
||||
if let Some(change) = self.authority_set_hard_forks.get(&hash) {
|
||||
return Some(change.clone());
|
||||
return Some(change.clone())
|
||||
}
|
||||
|
||||
// check for forced change.
|
||||
@@ -252,7 +250,7 @@ where
|
||||
canon_height: *header.number(),
|
||||
canon_hash: hash,
|
||||
delay_kind: DelayKind::Best { median_last_finalized },
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// check normal scheduled change.
|
||||
@@ -295,10 +293,9 @@ where
|
||||
fn consume(
|
||||
mut self,
|
||||
) -> Option<(AuthoritySet<H, N>, SharedDataLocked<'a, AuthoritySet<H, N>>)> {
|
||||
self.old.take().map(|old| (
|
||||
old,
|
||||
self.guard.take().expect("only taken on deconstruction; qed"),
|
||||
))
|
||||
self.old
|
||||
.take()
|
||||
.map(|old| (old, self.guard.take().expect("only taken on deconstruction; qed")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,20 +308,14 @@ where
|
||||
}
|
||||
|
||||
let number = *(block.header.number());
|
||||
let maybe_change = self.check_new_change(
|
||||
&block.header,
|
||||
hash,
|
||||
);
|
||||
let maybe_change = self.check_new_change(&block.header, hash);
|
||||
|
||||
// returns a function for checking whether a block is a descendent of another
|
||||
// consistent with querying client directly after importing the block.
|
||||
let parent_hash = *block.header.parent_hash();
|
||||
let is_descendent_of = is_descendent_of(&*self.inner, Some((hash, parent_hash)));
|
||||
|
||||
let mut guard = InnerGuard {
|
||||
guard: Some(self.authority_set.inner_locked()),
|
||||
old: None,
|
||||
};
|
||||
let mut guard = InnerGuard { guard: Some(self.authority_set.inner_locked()), old: None };
|
||||
|
||||
// whether to pause the old authority set -- happens after import
|
||||
// of a forced change block.
|
||||
@@ -339,10 +330,10 @@ where
|
||||
do_pause = true;
|
||||
}
|
||||
|
||||
guard.as_mut().add_pending_change(
|
||||
change,
|
||||
&is_descendent_of,
|
||||
).map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
|
||||
guard
|
||||
.as_mut()
|
||||
.add_pending_change(change, &is_descendent_of)
|
||||
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
|
||||
}
|
||||
|
||||
let applied_changes = {
|
||||
@@ -389,7 +380,9 @@ where
|
||||
|
||||
AppliedChanges::Forced(new_authorities)
|
||||
} else {
|
||||
let did_standard = guard.as_mut().enacts_standard_change(hash, number, &is_descendent_of)
|
||||
let did_standard = guard
|
||||
.as_mut()
|
||||
.enacts_standard_change(hash, number, &is_descendent_of)
|
||||
.map_err(|e| ConsensusError::ClientImport(e.to_string()))
|
||||
.map_err(ConsensusError::from)?;
|
||||
|
||||
@@ -413,19 +406,17 @@ where
|
||||
crate::aux_schema::update_authority_set::<Block, _, _>(
|
||||
authorities,
|
||||
authorities_change,
|
||||
|insert| block.auxiliary.extend(
|
||||
insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec())))
|
||||
)
|
||||
|insert| {
|
||||
block
|
||||
.auxiliary
|
||||
.extend(insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))))
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let just_in_case = just_in_case.map(|(o, i)| (o, i.release_mutex()));
|
||||
|
||||
Ok(PendingSetChanges {
|
||||
just_in_case,
|
||||
applied_changes,
|
||||
do_pause,
|
||||
})
|
||||
Ok(PendingSetChanges { just_in_case, applied_changes, do_pause })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +450,7 @@ where
|
||||
// Strip justifications when re-importing an existing block.
|
||||
let _justifications = block.justifications.take();
|
||||
return (&*self.inner).import_block(block, new_cache).await
|
||||
}
|
||||
},
|
||||
Ok(BlockStatus::Unknown) => {},
|
||||
Err(e) => return Err(ConsensusError::ClientImport(e.to_string())),
|
||||
}
|
||||
@@ -483,7 +474,7 @@ where
|
||||
r,
|
||||
);
|
||||
pending_changes.revert();
|
||||
return Ok(r);
|
||||
return Ok(r)
|
||||
},
|
||||
Err(e) => {
|
||||
debug!(
|
||||
@@ -492,7 +483,7 @@ where
|
||||
e,
|
||||
);
|
||||
pending_changes.revert();
|
||||
return Err(ConsensusError::ClientImport(e.to_string()));
|
||||
return Err(ConsensusError::ClientImport(e.to_string()))
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -501,9 +492,9 @@ where
|
||||
|
||||
// Send the pause signal after import but BEFORE sending a `ChangeAuthorities` message.
|
||||
if do_pause {
|
||||
let _ = self.send_voter_commands.unbounded_send(
|
||||
VoterCommand::Pause("Forced change scheduled after inactivity".to_string())
|
||||
);
|
||||
let _ = self.send_voter_commands.unbounded_send(VoterCommand::Pause(
|
||||
"Forced change scheduled after inactivity".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let needs_justification = applied_changes.needs_justification();
|
||||
@@ -521,7 +512,8 @@ where
|
||||
// they should import the block and discard the justification, and they will
|
||||
// then request a justification from sync if it's necessary (which they should
|
||||
// then be able to successfully validate).
|
||||
let _ = self.send_voter_commands.unbounded_send(VoterCommand::ChangeAuthorities(new));
|
||||
let _ =
|
||||
self.send_voter_commands.unbounded_send(VoterCommand::ChangeAuthorities(new));
|
||||
|
||||
// we must clear all pending justifications requests, presumably they won't be
|
||||
// finalized hence why this forced changes was triggered
|
||||
@@ -537,8 +529,8 @@ where
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let grandpa_justification = justifications
|
||||
.and_then(|just| just.into_justification(GRANDPA_ENGINE_ID));
|
||||
let grandpa_justification =
|
||||
justifications.and_then(|just| just.into_justification(GRANDPA_ENGINE_ID));
|
||||
|
||||
match grandpa_justification {
|
||||
Some(justification) => {
|
||||
@@ -559,7 +551,7 @@ where
|
||||
}
|
||||
});
|
||||
},
|
||||
None => {
|
||||
None =>
|
||||
if needs_justification {
|
||||
debug!(
|
||||
target: "afg",
|
||||
@@ -568,8 +560,7 @@ where
|
||||
);
|
||||
|
||||
imported_aux.needs_justification = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(ImportResult::Imported(imported_aux))
|
||||
@@ -616,14 +607,9 @@ impl<Backend, Block: BlockT, Client, SC> GrandpaBlockImport<Backend, Block, Clie
|
||||
{
|
||||
let mut authority_set = authority_set.inner();
|
||||
|
||||
authority_set.pending_standard_changes = authority_set
|
||||
.pending_standard_changes
|
||||
.clone()
|
||||
.map(&mut |hash, _, original| {
|
||||
authority_set_hard_forks
|
||||
.get(&hash)
|
||||
.cloned()
|
||||
.unwrap_or(original)
|
||||
authority_set.pending_standard_changes =
|
||||
authority_set.pending_standard_changes.clone().map(&mut |hash, _, original| {
|
||||
authority_set_hard_forks.get(&hash).cloned().unwrap_or(original)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -664,7 +650,7 @@ where
|
||||
// justification import pipeline similar to what we do for `BlockImport`. In the
|
||||
// meantime we'll just drop the justification, since this is only used for BEEFY which
|
||||
// is still WIP.
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let justification = GrandpaJustification::decode_and_verify_finalizes(
|
||||
@@ -693,7 +679,8 @@ where
|
||||
|
||||
match result {
|
||||
Err(CommandOrError::VoterCommand(command)) => {
|
||||
afg_log!(initial_sync,
|
||||
afg_log!(
|
||||
initial_sync,
|
||||
"👴 Imported justification for block #{} that triggers \
|
||||
command {}, signaling voter.",
|
||||
number,
|
||||
@@ -703,7 +690,7 @@ where
|
||||
// send the command to the voter
|
||||
let _ = self.send_voter_commands.unbounded_send(command);
|
||||
},
|
||||
Err(CommandOrError::Error(e)) => {
|
||||
Err(CommandOrError::Error(e)) =>
|
||||
return Err(match e {
|
||||
Error::Grandpa(error) => ConsensusError::ClientImport(error.to_string()),
|
||||
Error::Network(error) => ConsensusError::ClientImport(error),
|
||||
@@ -713,10 +700,12 @@ where
|
||||
Error::Signing(error) => ConsensusError::ClientImport(error),
|
||||
Error::Timer(error) => ConsensusError::ClientImport(error.to_string()),
|
||||
Error::RuntimeApi(error) => ConsensusError::ClientImport(error.to_string()),
|
||||
});
|
||||
},
|
||||
}),
|
||||
Ok(_) => {
|
||||
assert!(!enacts_change, "returns Ok when no authority set change should be enacted; qed;");
|
||||
assert!(
|
||||
!enacts_change,
|
||||
"returns Ok when no authority set change should be enacted; qed;"
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use finality_grandpa::{voter_set::VoterSet, Error as GrandpaError};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
@@ -52,7 +54,8 @@ impl<Block: BlockT> GrandpaJustification<Block> {
|
||||
client: &Arc<C>,
|
||||
round: u64,
|
||||
commit: Commit<Block>,
|
||||
) -> Result<GrandpaJustification<Block>, Error> where
|
||||
) -> Result<GrandpaJustification<Block>, Error>
|
||||
where
|
||||
C: HeaderBackend<Block>,
|
||||
{
|
||||
let mut votes_ancestries_hashes = HashSet::new();
|
||||
@@ -66,12 +69,14 @@ impl<Block: BlockT> GrandpaJustification<Block> {
|
||||
for signed in commit.precommits.iter() {
|
||||
let mut current_hash = signed.precommit.target_hash;
|
||||
loop {
|
||||
if current_hash == commit.target_hash { break; }
|
||||
if current_hash == commit.target_hash {
|
||||
break
|
||||
}
|
||||
|
||||
match client.header(BlockId::Hash(current_hash))? {
|
||||
Some(current_header) => {
|
||||
if *current_header.number() <= commit.target_number {
|
||||
return error();
|
||||
return error()
|
||||
}
|
||||
|
||||
let parent_hash = *current_header.parent_hash();
|
||||
@@ -95,20 +100,20 @@ impl<Block: BlockT> GrandpaJustification<Block> {
|
||||
finalized_target: (Block::Hash, NumberFor<Block>),
|
||||
set_id: u64,
|
||||
voters: &VoterSet<AuthorityId>,
|
||||
) -> Result<GrandpaJustification<Block>, ClientError> where
|
||||
) -> Result<GrandpaJustification<Block>, ClientError>
|
||||
where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
{
|
||||
|
||||
let justification = GrandpaJustification::<Block>::decode(&mut &*encoded)
|
||||
.map_err(|_| ClientError::JustificationDecode)?;
|
||||
|
||||
if (justification.commit.target_hash, justification.commit.target_number) != finalized_target {
|
||||
if (justification.commit.target_hash, justification.commit.target_number) !=
|
||||
finalized_target
|
||||
{
|
||||
let msg = "invalid commit target in grandpa justification".to_string();
|
||||
Err(ClientError::BadJustification(msg))
|
||||
} else {
|
||||
justification
|
||||
.verify_with_voter_set(set_id, voters)
|
||||
.map(|_| justification)
|
||||
justification.verify_with_voter_set(set_id, voters).map(|_| justification)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,9 +122,8 @@ impl<Block: BlockT> GrandpaJustification<Block> {
|
||||
where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
{
|
||||
let voters = VoterSet::new(authorities.iter().cloned()).ok_or(ClientError::Consensus(
|
||||
sp_consensus::Error::InvalidAuthoritiesSet,
|
||||
))?;
|
||||
let voters = VoterSet::new(authorities.iter().cloned())
|
||||
.ok_or(ClientError::Consensus(sp_consensus::Error::InvalidAuthoritiesSet))?;
|
||||
|
||||
self.verify_with_voter_set(set_id, &voters)
|
||||
}
|
||||
@@ -137,16 +141,12 @@ impl<Block: BlockT> GrandpaJustification<Block> {
|
||||
|
||||
let ancestry_chain = AncestryChain::<Block>::new(&self.votes_ancestries);
|
||||
|
||||
match finality_grandpa::validate_commit(
|
||||
&self.commit,
|
||||
voters,
|
||||
&ancestry_chain,
|
||||
) {
|
||||
match finality_grandpa::validate_commit(&self.commit, voters, &ancestry_chain) {
|
||||
Ok(ref result) if result.ghost().is_some() => {},
|
||||
_ => {
|
||||
let msg = "invalid commit in grandpa justification".to_string();
|
||||
return Err(ClientError::BadJustification(msg));
|
||||
}
|
||||
return Err(ClientError::BadJustification(msg))
|
||||
},
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
@@ -161,11 +161,12 @@ impl<Block: BlockT> GrandpaJustification<Block> {
|
||||
&mut buf,
|
||||
) {
|
||||
return Err(ClientError::BadJustification(
|
||||
"invalid signature for precommit in grandpa justification".to_string()));
|
||||
"invalid signature for precommit in grandpa justification".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
if self.commit.target_hash == signed.precommit.target_hash {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
match ancestry_chain.ancestry(self.commit.target_hash, signed.precommit.target_hash) {
|
||||
@@ -176,21 +177,21 @@ impl<Block: BlockT> GrandpaJustification<Block> {
|
||||
visited_hashes.insert(hash);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
_ =>
|
||||
return Err(ClientError::BadJustification(
|
||||
"invalid precommit ancestry proof in grandpa justification".to_string()));
|
||||
},
|
||||
"invalid precommit ancestry proof in grandpa justification".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
let ancestry_hashes = self.votes_ancestries
|
||||
.iter()
|
||||
.map(|h: &Block::Header| h.hash())
|
||||
.collect();
|
||||
let ancestry_hashes =
|
||||
self.votes_ancestries.iter().map(|h: &Block::Header| h.hash()).collect();
|
||||
|
||||
if visited_hashes != ancestry_hashes {
|
||||
return Err(ClientError::BadJustification(
|
||||
"invalid precommit ancestries in grandpa justification with unused headers".to_string()));
|
||||
"invalid precommit ancestries in grandpa justification with unused headers"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -211,24 +212,28 @@ struct AncestryChain<Block: BlockT> {
|
||||
|
||||
impl<Block: BlockT> AncestryChain<Block> {
|
||||
fn new(ancestry: &[Block::Header]) -> AncestryChain<Block> {
|
||||
let ancestry: HashMap<_, _> = ancestry
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|h: Block::Header| (h.hash(), h))
|
||||
.collect();
|
||||
let ancestry: HashMap<_, _> =
|
||||
ancestry.iter().cloned().map(|h: Block::Header| (h.hash(), h)).collect();
|
||||
|
||||
AncestryChain { ancestry }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT> finality_grandpa::Chain<Block::Hash, NumberFor<Block>> for AncestryChain<Block> where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps
|
||||
impl<Block: BlockT> finality_grandpa::Chain<Block::Hash, NumberFor<Block>> for AncestryChain<Block>
|
||||
where
|
||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||
{
|
||||
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
|
||||
fn ancestry(
|
||||
&self,
|
||||
base: Block::Hash,
|
||||
block: Block::Hash,
|
||||
) -> Result<Vec<Block::Hash>, GrandpaError> {
|
||||
let mut route = Vec::new();
|
||||
let mut current_hash = block;
|
||||
loop {
|
||||
if current_hash == base { break; }
|
||||
if current_hash == base {
|
||||
break
|
||||
}
|
||||
match self.ancestry.get(¤t_hash) {
|
||||
Some(current_header) => {
|
||||
current_hash = *current_header.parent_hash();
|
||||
|
||||
@@ -56,41 +56,39 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use futures::{
|
||||
prelude::*,
|
||||
StreamExt,
|
||||
};
|
||||
use futures::{prelude::*, StreamExt};
|
||||
use log::{debug, error, info};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use parking_lot::RwLock;
|
||||
use prometheus_endpoint::{PrometheusError, Registry};
|
||||
use sc_client_api::{
|
||||
backend::{AuxStore, Backend},
|
||||
LockImportRun, BlockchainEvents, CallExecutor,
|
||||
ExecutionStrategy, Finalizer, TransactionFor, ExecutorProvider,
|
||||
BlockchainEvents, CallExecutor, ExecutionStrategy, ExecutorProvider, Finalizer, LockImportRun,
|
||||
TransactionFor,
|
||||
};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use prometheus_endpoint::{PrometheusError, Registry};
|
||||
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO};
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
use sp_blockchain::{HeaderBackend, Error as ClientError, HeaderMetadata};
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_runtime::traits::{NumberFor, Block as BlockT, DigestFor, Zero};
|
||||
use sp_consensus::{SelectChain, BlockImport};
|
||||
use sp_core::{
|
||||
crypto::Public,
|
||||
};
|
||||
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
||||
use sp_application_crypto::AppKey;
|
||||
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata};
|
||||
use sp_consensus::{BlockImport, SelectChain};
|
||||
use sp_core::crypto::Public;
|
||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, DigestFor, NumberFor, Zero},
|
||||
};
|
||||
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver};
|
||||
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_INFO, CONSENSUS_DEBUG};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use finality_grandpa::Error as GrandpaError;
|
||||
use finality_grandpa::{voter, voter_set::VoterSet};
|
||||
pub use finality_grandpa::BlockNumberOps;
|
||||
use finality_grandpa::{voter, voter_set::VoterSet, Error as GrandpaError};
|
||||
|
||||
use std::{fmt, io};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Poll, Context};
|
||||
use std::{
|
||||
fmt, io,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
// utility logging macro that takes as first argument a conditional to
|
||||
// decide whether to log under debug or info level (useful to restrict
|
||||
@@ -123,6 +121,7 @@ mod voting_rule;
|
||||
|
||||
pub use authorities::{AuthoritySet, AuthoritySetChanges, SharedAuthoritySet};
|
||||
pub use aux_schema::best_justification;
|
||||
pub use finality_grandpa::voter::report;
|
||||
pub use finality_proof::{FinalityProof, FinalityProofError, FinalityProofProvider};
|
||||
pub use import::{find_forced_change, find_scheduled_change, GrandpaBlockImport};
|
||||
pub use justification::GrandpaJustification;
|
||||
@@ -132,13 +131,12 @@ pub use voting_rule::{
|
||||
BeforeBestBlockBy, ThreeQuartersOfTheUnfinalizedChain, VotingRule, VotingRuleResult,
|
||||
VotingRulesBuilder,
|
||||
};
|
||||
pub use finality_grandpa::voter::report;
|
||||
|
||||
use aux_schema::PersistentData;
|
||||
use communication::{Network as NetworkT, NetworkBridge};
|
||||
use environment::{Environment, VoterSetState};
|
||||
use until_imported::UntilGlobalMessageBlocksImported;
|
||||
use sp_finality_grandpa::{AuthorityList, AuthoritySignature, SetId};
|
||||
use until_imported::UntilGlobalMessageBlocksImported;
|
||||
|
||||
// Re-export these two because it's just so damn convenient.
|
||||
pub use sp_finality_grandpa::{AuthorityId, AuthorityPair, GrandpaApi, ScheduledChange};
|
||||
@@ -159,7 +157,8 @@ pub type SignedMessage<Block> = finality_grandpa::SignedMessage<
|
||||
>;
|
||||
|
||||
/// A primary propose message for this chain's block type.
|
||||
pub type PrimaryPropose<Block> = finality_grandpa::PrimaryPropose<<Block as BlockT>::Hash, NumberFor<Block>>;
|
||||
pub type PrimaryPropose<Block> =
|
||||
finality_grandpa::PrimaryPropose<<Block as BlockT>::Hash, NumberFor<Block>>;
|
||||
/// A prevote message for this chain's block type.
|
||||
pub type Prevote<Block> = finality_grandpa::Prevote<<Block as BlockT>::Hash, NumberFor<Block>>;
|
||||
/// A precommit message for this chain's block type.
|
||||
@@ -198,22 +197,14 @@ type CommunicationIn<Block> = finality_grandpa::voter::CommunicationIn<
|
||||
/// Global communication input stream for commits and catch up messages, with
|
||||
/// the hash type not being derived from the block, useful for forcing the hash
|
||||
/// to some type (e.g. `H256`) when the compiler can't do the inference.
|
||||
type CommunicationInH<Block, H> = finality_grandpa::voter::CommunicationIn<
|
||||
H,
|
||||
NumberFor<Block>,
|
||||
AuthoritySignature,
|
||||
AuthorityId,
|
||||
>;
|
||||
type CommunicationInH<Block, H> =
|
||||
finality_grandpa::voter::CommunicationIn<H, NumberFor<Block>, AuthoritySignature, AuthorityId>;
|
||||
|
||||
/// Global communication sink for commits with the hash type not being derived
|
||||
/// from the block, useful for forcing the hash to some type (e.g. `H256`) when
|
||||
/// the compiler can't do the inference.
|
||||
type CommunicationOutH<Block, H> = finality_grandpa::voter::CommunicationOut<
|
||||
H,
|
||||
NumberFor<Block>,
|
||||
AuthoritySignature,
|
||||
AuthorityId,
|
||||
>;
|
||||
type CommunicationOutH<Block, H> =
|
||||
finality_grandpa::voter::CommunicationOut<H, NumberFor<Block>, AuthoritySignature, AuthorityId>;
|
||||
|
||||
/// Shared voter state for querying.
|
||||
pub struct SharedVoterState {
|
||||
@@ -223,18 +214,14 @@ pub struct SharedVoterState {
|
||||
impl SharedVoterState {
|
||||
/// Create a new empty `SharedVoterState` instance.
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
Self { inner: Arc::new(RwLock::new(None)) }
|
||||
}
|
||||
|
||||
fn reset(
|
||||
&self,
|
||||
voter_state: Box<dyn voter::VoterState<AuthorityId> + Sync + Send>,
|
||||
) -> Option<()> {
|
||||
let mut shared_voter_state = self
|
||||
.inner
|
||||
.try_write_for(Duration::from_secs(1))?;
|
||||
let mut shared_voter_state = self.inner.try_write_for(Duration::from_secs(1))?;
|
||||
|
||||
*shared_voter_state = Some(voter_state);
|
||||
Some(())
|
||||
@@ -323,7 +310,8 @@ pub(crate) trait BlockStatus<Block: BlockT> {
|
||||
fn block_number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, Error>;
|
||||
}
|
||||
|
||||
impl<Block: BlockT, Client> BlockStatus<Block> for Arc<Client> where
|
||||
impl<Block: BlockT, Client> BlockStatus<Block> for Arc<Client>
|
||||
where
|
||||
Client: HeaderBackend<Block>,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
{
|
||||
@@ -337,24 +325,36 @@ impl<Block: BlockT, Client> BlockStatus<Block> for Arc<Client> where
|
||||
/// Ideally this would be a trait alias, we're not there yet.
|
||||
/// tracking issue <https://github.com/rust-lang/rust/issues/41517>
|
||||
pub trait ClientForGrandpa<Block, BE>:
|
||||
LockImportRun<Block, BE> + Finalizer<Block, BE> + AuxStore
|
||||
+ HeaderMetadata<Block, Error = sp_blockchain::Error> + HeaderBackend<Block>
|
||||
+ BlockchainEvents<Block> + ProvideRuntimeApi<Block> + ExecutorProvider<Block>
|
||||
LockImportRun<Block, BE>
|
||||
+ Finalizer<Block, BE>
|
||||
+ AuxStore
|
||||
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
||||
+ HeaderBackend<Block>
|
||||
+ BlockchainEvents<Block>
|
||||
+ ProvideRuntimeApi<Block>
|
||||
+ ExecutorProvider<Block>
|
||||
+ BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = sp_consensus::Error>
|
||||
where
|
||||
BE: Backend<Block>,
|
||||
Block: BlockT,
|
||||
{}
|
||||
where
|
||||
BE: Backend<Block>,
|
||||
Block: BlockT,
|
||||
{
|
||||
}
|
||||
|
||||
impl<Block, BE, T> ClientForGrandpa<Block, BE> for T
|
||||
where
|
||||
BE: Backend<Block>,
|
||||
Block: BlockT,
|
||||
T: LockImportRun<Block, BE> + Finalizer<Block, BE> + AuxStore
|
||||
+ HeaderMetadata<Block, Error = sp_blockchain::Error> + HeaderBackend<Block>
|
||||
+ BlockchainEvents<Block> + ProvideRuntimeApi<Block> + ExecutorProvider<Block>
|
||||
+ BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = sp_consensus::Error>,
|
||||
{}
|
||||
where
|
||||
BE: Backend<Block>,
|
||||
Block: BlockT,
|
||||
T: LockImportRun<Block, BE>
|
||||
+ Finalizer<Block, BE>
|
||||
+ AuxStore
|
||||
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
||||
+ HeaderBackend<Block>
|
||||
+ BlockchainEvents<Block>
|
||||
+ ProvideRuntimeApi<Block>
|
||||
+ ExecutorProvider<Block>
|
||||
+ BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = sp_consensus::Error>,
|
||||
{
|
||||
}
|
||||
|
||||
/// Something that one can ask to do a block sync request.
|
||||
pub(crate) trait BlockSyncRequester<Block: BlockT> {
|
||||
@@ -364,14 +364,25 @@ pub(crate) trait BlockSyncRequester<Block: BlockT> {
|
||||
/// If the given vector of peers is empty then the underlying implementation
|
||||
/// should make a best effort to fetch the block from any peers it is
|
||||
/// connected to (NOTE: this assumption will change in the future #3629).
|
||||
fn set_sync_fork_request(&self, peers: Vec<sc_network::PeerId>, hash: Block::Hash, number: NumberFor<Block>);
|
||||
fn set_sync_fork_request(
|
||||
&self,
|
||||
peers: Vec<sc_network::PeerId>,
|
||||
hash: Block::Hash,
|
||||
number: NumberFor<Block>,
|
||||
);
|
||||
}
|
||||
|
||||
impl<Block, Network> BlockSyncRequester<Block> for NetworkBridge<Block, Network> where
|
||||
impl<Block, Network> BlockSyncRequester<Block> for NetworkBridge<Block, Network>
|
||||
where
|
||||
Block: BlockT,
|
||||
Network: NetworkT<Block>,
|
||||
{
|
||||
fn set_sync_fork_request(&self, peers: Vec<sc_network::PeerId>, hash: Block::Hash, number: NumberFor<Block>) {
|
||||
fn set_sync_fork_request(
|
||||
&self,
|
||||
peers: Vec<sc_network::PeerId>,
|
||||
hash: Block::Hash,
|
||||
number: NumberFor<Block>,
|
||||
) {
|
||||
NetworkBridge::set_sync_fork_request(self, peers, hash, number)
|
||||
}
|
||||
}
|
||||
@@ -391,7 +402,7 @@ pub(crate) enum VoterCommand<H, N> {
|
||||
/// Pause the voter for given reason.
|
||||
Pause(String),
|
||||
/// New authorities.
|
||||
ChangeAuthorities(NewAuthoritySet<H, N>)
|
||||
ChangeAuthorities(NewAuthoritySet<H, N>),
|
||||
}
|
||||
|
||||
impl<H, N> fmt::Display for VoterCommand<H, N> {
|
||||
@@ -436,7 +447,7 @@ impl<H, N> From<VoterCommand<H, N>> for CommandOrError<H, N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: fmt::Debug, N: fmt::Debug> ::std::error::Error for CommandOrError<H, N> { }
|
||||
impl<H: fmt::Debug, N: fmt::Debug> ::std::error::Error for CommandOrError<H, N> {}
|
||||
|
||||
impl<H, N> fmt::Display for CommandOrError<H, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@@ -476,8 +487,10 @@ pub trait GenesisAuthoritySetProvider<Block: BlockT> {
|
||||
fn get(&self) -> Result<AuthorityList, ClientError>;
|
||||
}
|
||||
|
||||
impl<Block: BlockT, E> GenesisAuthoritySetProvider<Block> for Arc<dyn ExecutorProvider<Block, Executor = E>>
|
||||
where E: CallExecutor<Block>,
|
||||
impl<Block: BlockT, E> GenesisAuthoritySetProvider<Block>
|
||||
for Arc<dyn ExecutorProvider<Block, Executor = E>>
|
||||
where
|
||||
E: CallExecutor<Block>,
|
||||
{
|
||||
fn get(&self) -> Result<AuthorityList, ClientError> {
|
||||
// This implementation uses the Grandpa runtime API instead of reading directly from the
|
||||
@@ -492,10 +505,12 @@ impl<Block: BlockT, E> GenesisAuthoritySetProvider<Block> for Arc<dyn ExecutorPr
|
||||
None,
|
||||
)
|
||||
.and_then(|call_result| {
|
||||
Decode::decode(&mut &call_result[..])
|
||||
.map_err(|err| ClientError::CallResultDecode(
|
||||
"failed to decode GRANDPA authorities set proof", err
|
||||
))
|
||||
Decode::decode(&mut &call_result[..]).map_err(|err| {
|
||||
ClientError::CallResultDecode(
|
||||
"failed to decode GRANDPA authorities set proof",
|
||||
err,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -507,13 +522,7 @@ pub fn block_import<BE, Block: BlockT, Client, SC>(
|
||||
genesis_authorities_provider: &dyn GenesisAuthoritySetProvider<Block>,
|
||||
select_chain: SC,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
) -> Result<
|
||||
(
|
||||
GrandpaBlockImport<BE, Block, Client, SC>,
|
||||
LinkHalf<Block, Client, SC>,
|
||||
),
|
||||
ClientError,
|
||||
>
|
||||
) -> Result<(GrandpaBlockImport<BE, Block, Client, SC>, LinkHalf<Block, Client, SC>), ClientError>
|
||||
where
|
||||
SC: SelectChain<Block>,
|
||||
BE: Backend<Block> + 'static,
|
||||
@@ -539,13 +548,7 @@ pub fn block_import_with_authority_set_hard_forks<BE, Block: BlockT, Client, SC>
|
||||
select_chain: SC,
|
||||
authority_set_hard_forks: Vec<(SetId, (Block::Hash, NumberFor<Block>), AuthorityList)>,
|
||||
telemetry: Option<TelemetryHandle>,
|
||||
) -> Result<
|
||||
(
|
||||
GrandpaBlockImport<BE, Block, Client, SC>,
|
||||
LinkHalf<Block, Client, SC>,
|
||||
),
|
||||
ClientError,
|
||||
>
|
||||
) -> Result<(GrandpaBlockImport<BE, Block, Client, SC>, LinkHalf<Block, Client, SC>), ClientError>
|
||||
where
|
||||
SC: SelectChain<Block>,
|
||||
BE: Backend<Block> + 'static,
|
||||
@@ -554,11 +557,8 @@ where
|
||||
let chain_info = client.info();
|
||||
let genesis_hash = chain_info.genesis_hash;
|
||||
|
||||
let persistent_data = aux_schema::load_persistent(
|
||||
&*client,
|
||||
genesis_hash,
|
||||
<NumberFor<Block>>::zero(),
|
||||
{
|
||||
let persistent_data =
|
||||
aux_schema::load_persistent(&*client, genesis_hash, <NumberFor<Block>>::zero(), {
|
||||
let telemetry = telemetry.clone();
|
||||
move || {
|
||||
let authorities = genesis_authorities_provider.get()?;
|
||||
@@ -570,13 +570,11 @@ where
|
||||
);
|
||||
Ok(authorities)
|
||||
}
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
|
||||
let (voter_commands_tx, voter_commands_rx) = tracing_unbounded("mpsc_grandpa_voter_command");
|
||||
|
||||
let (justification_sender, justification_stream) =
|
||||
GrandpaJustificationStream::channel();
|
||||
let (justification_sender, justification_stream) = GrandpaJustificationStream::channel();
|
||||
|
||||
// create pending change objects with 0 delay and enacted on finality
|
||||
// (i.e. standard changes) for each authority set hard fork.
|
||||
@@ -646,11 +644,8 @@ where
|
||||
let is_voter = local_authority_id(voters, keystore).is_some();
|
||||
|
||||
// verification stream
|
||||
let (global_in, global_out) = network.global_communication(
|
||||
communication::SetId(set_id),
|
||||
voters.clone(),
|
||||
is_voter,
|
||||
);
|
||||
let (global_in, global_out) =
|
||||
network.global_communication(communication::SetId(set_id), voters.clone(), is_voter);
|
||||
|
||||
// block commit and catch up messages until relevant blocks are imported.
|
||||
let global_in = UntilGlobalMessageBlocksImported::new(
|
||||
@@ -758,23 +753,18 @@ where
|
||||
);
|
||||
|
||||
let conf = config.clone();
|
||||
let telemetry_task = if let Some(telemetry_on_connect) = telemetry
|
||||
.as_ref()
|
||||
.map(|x| x.on_connect_stream())
|
||||
{
|
||||
let authorities = persistent_data.authority_set.clone();
|
||||
let telemetry = telemetry.clone();
|
||||
let events = telemetry_on_connect
|
||||
.for_each(move |_| {
|
||||
let telemetry_task =
|
||||
if let Some(telemetry_on_connect) = telemetry.as_ref().map(|x| x.on_connect_stream()) {
|
||||
let authorities = persistent_data.authority_set.clone();
|
||||
let telemetry = telemetry.clone();
|
||||
let events = telemetry_on_connect.for_each(move |_| {
|
||||
let current_authorities = authorities.current_authorities();
|
||||
let set_id = authorities.set_id();
|
||||
let authority_id = local_authority_id(¤t_authorities, conf.keystore.as_ref())
|
||||
.unwrap_or_default();
|
||||
|
||||
let authorities = current_authorities
|
||||
.iter()
|
||||
.map(|(id, _)| id.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let authorities =
|
||||
current_authorities.iter().map(|(id, _)| id.to_string()).collect::<Vec<_>>();
|
||||
|
||||
let authorities = serde_json::to_string(&authorities).expect(
|
||||
"authorities is always at least an empty vector; \
|
||||
@@ -792,10 +782,10 @@ where
|
||||
|
||||
future::ready(())
|
||||
});
|
||||
future::Either::Left(events)
|
||||
} else {
|
||||
future::Either::Right(future::pending())
|
||||
};
|
||||
future::Either::Left(events)
|
||||
} else {
|
||||
future::Either::Right(future::pending())
|
||||
};
|
||||
|
||||
let voter_work = VoterWork::new(
|
||||
client,
|
||||
@@ -819,8 +809,7 @@ where
|
||||
});
|
||||
|
||||
// Make sure that `telemetry_task` doesn't accidentally finish and kill grandpa.
|
||||
let telemetry_task = telemetry_task
|
||||
.then(|_| future::pending::<()>());
|
||||
let telemetry_task = telemetry_task.then(|_| future::pending::<()>());
|
||||
|
||||
Ok(future::select(voter_work, telemetry_task).map(drop))
|
||||
}
|
||||
@@ -842,7 +831,9 @@ impl Metrics {
|
||||
/// Future that powers the voter.
|
||||
#[must_use]
|
||||
struct VoterWork<B, Block: BlockT, C, N: NetworkT<Block>, SC, VR> {
|
||||
voter: Pin<Box<dyn Future<Output = Result<(), CommandOrError<Block::Hash, NumberFor<Block>>>> + Send>>,
|
||||
voter: Pin<
|
||||
Box<dyn Future<Output = Result<(), CommandOrError<Block::Hash, NumberFor<Block>>>> + Send>,
|
||||
>,
|
||||
shared_voter_state: SharedVoterState,
|
||||
env: Arc<Environment<B, Block, C, N, SC, VR>>,
|
||||
voter_commands_rx: TracingUnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
|
||||
@@ -881,7 +872,7 @@ where
|
||||
Some(Err(e)) => {
|
||||
debug!(target: "afg", "Failed to register metrics: {:?}", e);
|
||||
None
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
@@ -937,12 +928,7 @@ where
|
||||
|
||||
let chain_info = self.env.client.info();
|
||||
|
||||
let authorities = self
|
||||
.env
|
||||
.voters
|
||||
.iter()
|
||||
.map(|(id, _)| id.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let authorities = self.env.voters.iter().map(|(id, _)| id.to_string()).collect::<Vec<_>>();
|
||||
|
||||
let authorities = serde_json::to_string(&authorities).expect(
|
||||
"authorities is always at least an empty vector; elements are always of type string; qed.",
|
||||
@@ -961,10 +947,7 @@ where
|
||||
|
||||
match &*self.env.voter_set_state.read() {
|
||||
VoterSetState::Live { completed_rounds, .. } => {
|
||||
let last_finalized = (
|
||||
chain_info.finalized_hash,
|
||||
chain_info.finalized_number,
|
||||
);
|
||||
let last_finalized = (chain_info.finalized_hash, chain_info.finalized_number);
|
||||
|
||||
let global_comms = global_communication(
|
||||
self.env.set_id,
|
||||
@@ -997,20 +980,18 @@ where
|
||||
|
||||
self.voter = Box::pin(voter);
|
||||
},
|
||||
VoterSetState::Paused { .. } =>
|
||||
self.voter = Box::pin(future::pending()),
|
||||
VoterSetState::Paused { .. } => self.voter = Box::pin(future::pending()),
|
||||
};
|
||||
}
|
||||
|
||||
fn handle_voter_command(
|
||||
&mut self,
|
||||
command: VoterCommand<Block::Hash, NumberFor<Block>>
|
||||
command: VoterCommand<Block::Hash, NumberFor<Block>>,
|
||||
) -> Result<(), Error> {
|
||||
match command {
|
||||
VoterCommand::ChangeAuthorities(new) => {
|
||||
let voters: Vec<String> = new.authorities.iter().map(move |(a, _)| {
|
||||
format!("{}", a)
|
||||
}).collect();
|
||||
let voters: Vec<String> =
|
||||
new.authorities.iter().map(move |(a, _)| format!("{}", a)).collect();
|
||||
telemetry!(
|
||||
self.telemetry;
|
||||
CONSENSUS_INFO;
|
||||
@@ -1034,14 +1015,12 @@ where
|
||||
Ok(Some(set_state))
|
||||
})?;
|
||||
|
||||
let voters = Arc::new(VoterSet::new(new.authorities.into_iter())
|
||||
.expect(
|
||||
"new authorities come from pending change; \
|
||||
let voters = Arc::new(VoterSet::new(new.authorities.into_iter()).expect(
|
||||
"new authorities come from pending change; \
|
||||
pending change comes from `AuthoritySet`; \
|
||||
`AuthoritySet` validates authorities is non-empty and weights are non-zero; \
|
||||
qed."
|
||||
)
|
||||
);
|
||||
qed.",
|
||||
));
|
||||
|
||||
self.env = Arc::new(Environment {
|
||||
voters,
|
||||
@@ -1061,7 +1040,7 @@ where
|
||||
|
||||
self.rebuild_voter();
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
VoterCommand::Pause(reason) => {
|
||||
info!(target: "afg", "Pausing old validator set: {}", reason);
|
||||
|
||||
@@ -1076,7 +1055,7 @@ where
|
||||
|
||||
self.rebuild_voter();
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1096,37 +1075,35 @@ where
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
match Future::poll(Pin::new(&mut self.voter), cx) {
|
||||
Poll::Pending => {}
|
||||
Poll::Pending => {},
|
||||
Poll::Ready(Ok(())) => {
|
||||
// voters don't conclude naturally
|
||||
return Poll::Ready(
|
||||
Err(Error::Safety("finality-grandpa inner voter has concluded.".into()))
|
||||
)
|
||||
}
|
||||
return Poll::Ready(Err(Error::Safety(
|
||||
"finality-grandpa inner voter has concluded.".into(),
|
||||
)))
|
||||
},
|
||||
Poll::Ready(Err(CommandOrError::Error(e))) => {
|
||||
// return inner observer error
|
||||
return Poll::Ready(Err(e))
|
||||
}
|
||||
},
|
||||
Poll::Ready(Err(CommandOrError::VoterCommand(command))) => {
|
||||
// some command issued internally
|
||||
self.handle_voter_command(command)?;
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
match Stream::poll_next(Pin::new(&mut self.voter_commands_rx), cx) {
|
||||
Poll::Pending => {}
|
||||
Poll::Pending => {},
|
||||
Poll::Ready(None) => {
|
||||
// the `voter_commands_rx` stream should never conclude since it's never closed.
|
||||
return Poll::Ready(
|
||||
Err(Error::Safety("`voter_commands_rx` was closed.".into()))
|
||||
)
|
||||
}
|
||||
return Poll::Ready(Err(Error::Safety("`voter_commands_rx` was closed.".into())))
|
||||
},
|
||||
Poll::Ready(Some(command)) => {
|
||||
// some command issued externally
|
||||
self.handle_voter_command(command)?;
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Future::poll(Pin::new(&mut self.network), cx)
|
||||
@@ -1142,10 +1119,10 @@ fn local_authority_id(
|
||||
) -> Option<AuthorityId> {
|
||||
keystore.and_then(|keystore| {
|
||||
voters
|
||||
.iter()
|
||||
.find(|(p, _)| {
|
||||
SyncCryptoStore::has_keys(&**keystore, &[(p.to_raw_vec(), AuthorityId::ID)])
|
||||
})
|
||||
.map(|(p, _)| p.clone())
|
||||
.iter()
|
||||
.find(|(p, _)| {
|
||||
SyncCryptoStore::has_keys(&**keystore, &[(p.to_raw_vec(), AuthorityId::ID)])
|
||||
})
|
||||
.map(|(p, _)| p.clone())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
||||
|
||||
use crate::justification::GrandpaJustification;
|
||||
use crate::Error;
|
||||
use crate::{justification::GrandpaJustification, Error};
|
||||
|
||||
// Stream of justifications returned when subscribing.
|
||||
type JustificationStream<Block> = TracingUnboundedReceiver<GrandpaJustification<Block>>;
|
||||
@@ -41,16 +40,14 @@ type SharedJustificationSenders<Block> = Arc<Mutex<Vec<JustificationSender<Block
|
||||
/// at the end of a Grandpa round.
|
||||
#[derive(Clone)]
|
||||
pub struct GrandpaJustificationSender<Block: BlockT> {
|
||||
subscribers: SharedJustificationSenders<Block>
|
||||
subscribers: SharedJustificationSenders<Block>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> GrandpaJustificationSender<Block> {
|
||||
/// The `subscribers` should be shared with a corresponding
|
||||
/// `GrandpaJustificationStream`.
|
||||
fn new(subscribers: SharedJustificationSenders<Block>) -> Self {
|
||||
Self {
|
||||
subscribers,
|
||||
}
|
||||
Self { subscribers }
|
||||
}
|
||||
|
||||
/// Send out a notification to all subscribers that a new justification
|
||||
@@ -83,7 +80,7 @@ impl<Block: BlockT> GrandpaJustificationSender<Block> {
|
||||
/// so it can be used to add more subscriptions.
|
||||
#[derive(Clone)]
|
||||
pub struct GrandpaJustificationStream<Block: BlockT> {
|
||||
subscribers: SharedJustificationSenders<Block>
|
||||
subscribers: SharedJustificationSenders<Block>,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> GrandpaJustificationStream<Block> {
|
||||
@@ -100,9 +97,7 @@ impl<Block: BlockT> GrandpaJustificationStream<Block> {
|
||||
/// The `subscribers` should be shared with a corresponding
|
||||
/// `GrandpaJustificationSender`.
|
||||
fn new(subscribers: SharedJustificationSenders<Block>) -> Self {
|
||||
Self {
|
||||
subscribers,
|
||||
}
|
||||
Self { subscribers }
|
||||
}
|
||||
|
||||
/// Subscribe to a channel through which justifications are sent
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::marker::{PhantomData, Unpin};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{
|
||||
marker::{PhantomData, Unpin},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use finality_grandpa::{voter, voter_set::VoterSet, BlockNumberOps, Error as GrandpaError};
|
||||
use futures::prelude::*;
|
||||
@@ -95,14 +97,14 @@ where
|
||||
},
|
||||
voter::CommunicationIn::CatchUp(..) => {
|
||||
// ignore catch up messages
|
||||
return future::ok(last_finalized_number);
|
||||
return future::ok(last_finalized_number)
|
||||
},
|
||||
};
|
||||
|
||||
// if the commit we've received targets a block lower or equal to the last
|
||||
// finalized, ignore it and continue with the current state
|
||||
if commit.target_number <= last_finalized_number {
|
||||
return future::ok(last_finalized_number);
|
||||
return future::ok(last_finalized_number)
|
||||
}
|
||||
|
||||
let validation_result = match finality_grandpa::validate_commit(
|
||||
@@ -201,11 +203,9 @@ where
|
||||
telemetry.clone(),
|
||||
);
|
||||
|
||||
let observer_work = observer_work
|
||||
.map_ok(|_| ())
|
||||
.map_err(|e| {
|
||||
warn!("GRANDPA Observer failed: {:?}", e);
|
||||
});
|
||||
let observer_work = observer_work.map_ok(|_| ()).map_err(|e| {
|
||||
warn!("GRANDPA Observer failed: {:?}", e);
|
||||
});
|
||||
|
||||
Ok(observer_work.map(drop))
|
||||
}
|
||||
@@ -213,7 +213,8 @@ where
|
||||
/// Future that powers the observer.
|
||||
#[must_use]
|
||||
struct ObserverWork<B: BlockT, BE, Client, N: NetworkT<B>> {
|
||||
observer: Pin<Box<dyn Future<Output = Result<(), CommandOrError<B::Hash, NumberFor<B>>>> + Send>>,
|
||||
observer:
|
||||
Pin<Box<dyn Future<Output = Result<(), CommandOrError<B::Hash, NumberFor<B>>>> + Send>>,
|
||||
client: Arc<Client>,
|
||||
network: NetworkBridge<B, N>,
|
||||
persistent_data: PersistentData<B>,
|
||||
@@ -285,11 +286,13 @@ where
|
||||
let network = self.network.clone();
|
||||
let voters = voters.clone();
|
||||
|
||||
move |round| network.note_round(
|
||||
crate::communication::Round(round),
|
||||
crate::communication::SetId(set_id),
|
||||
&*voters,
|
||||
)
|
||||
move |round| {
|
||||
network.note_round(
|
||||
crate::communication::Round(round),
|
||||
crate::communication::SetId(set_id),
|
||||
&*voters,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// create observer for the current set
|
||||
@@ -337,7 +340,8 @@ where
|
||||
|
||||
set_state
|
||||
},
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
self.rebuild_observer();
|
||||
Ok(())
|
||||
@@ -356,33 +360,33 @@ where
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
match Future::poll(Pin::new(&mut self.observer), cx) {
|
||||
Poll::Pending => {}
|
||||
Poll::Pending => {},
|
||||
Poll::Ready(Ok(())) => {
|
||||
// observer commit stream doesn't conclude naturally; this could reasonably be an error.
|
||||
return Poll::Ready(Ok(()))
|
||||
}
|
||||
},
|
||||
Poll::Ready(Err(CommandOrError::Error(e))) => {
|
||||
// return inner observer error
|
||||
return Poll::Ready(Err(e))
|
||||
}
|
||||
},
|
||||
Poll::Ready(Err(CommandOrError::VoterCommand(command))) => {
|
||||
// some command issued internally
|
||||
self.handle_voter_command(command)?;
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
match Stream::poll_next(Pin::new(&mut self.voter_commands_rx), cx) {
|
||||
Poll::Pending => {}
|
||||
Poll::Pending => {},
|
||||
Poll::Ready(None) => {
|
||||
// the `voter_commands_rx` stream should never conclude since it's never closed.
|
||||
return Poll::Ready(Ok(()))
|
||||
}
|
||||
},
|
||||
Poll::Ready(Some(command)) => {
|
||||
// some command issued externally
|
||||
self.handle_voter_command(command)?;
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Future::poll(Pin::new(&mut self.network), cx)
|
||||
@@ -393,12 +397,15 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::{
|
||||
aux_schema,
|
||||
communication::tests::{make_test_network, Event},
|
||||
};
|
||||
use assert_matches::assert_matches;
|
||||
use sp_utils::mpsc::tracing_unbounded;
|
||||
use crate::{aux_schema, communication::tests::{Event, make_test_network}};
|
||||
use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt};
|
||||
use sc_network::PeerId;
|
||||
use sp_blockchain::HeaderBackend as _;
|
||||
use sp_utils::mpsc::tracing_unbounded;
|
||||
use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt};
|
||||
|
||||
use futures::executor;
|
||||
|
||||
@@ -426,12 +433,9 @@ mod tests {
|
||||
|
||||
let voters = vec![(sp_keyring::Ed25519Keyring::Alice.public().into(), 1)];
|
||||
|
||||
let persistent_data = aux_schema::load_persistent(
|
||||
&*backend,
|
||||
client.info().genesis_hash,
|
||||
0,
|
||||
|| Ok(voters),
|
||||
).unwrap();
|
||||
let persistent_data =
|
||||
aux_schema::load_persistent(&*backend, client.info().genesis_hash, 0, || Ok(voters))
|
||||
.unwrap();
|
||||
|
||||
let (_tx, voter_command_rx) = tracing_unbounded("");
|
||||
|
||||
|
||||
@@ -21,31 +21,37 @@
|
||||
use super::*;
|
||||
use assert_matches::assert_matches;
|
||||
use environment::HasVoted;
|
||||
use sc_network_test::{
|
||||
Block, BlockImportAdapter, Hash, PassThroughVerifier, Peer, PeersClient, PeersFullClient,
|
||||
TestClient, TestNetFactory, FullPeerConfig,
|
||||
};
|
||||
use sc_network::config::{ProtocolConfig, Role};
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use futures_timer::Delay;
|
||||
use futures::executor::block_on;
|
||||
use tokio::runtime::{Runtime, Handle};
|
||||
use sp_keyring::Ed25519Keyring;
|
||||
use sp_blockchain::Result;
|
||||
use futures_timer::Delay;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use sc_network::config::{ProtocolConfig, Role};
|
||||
use sc_network_test::{
|
||||
Block, BlockImportAdapter, FullPeerConfig, Hash, PassThroughVerifier, Peer, PeersClient,
|
||||
PeersFullClient, TestClient, TestNetFactory,
|
||||
};
|
||||
use sp_api::{ApiRef, ProvideRuntimeApi};
|
||||
use substrate_test_runtime_client::runtime::BlockNumber;
|
||||
use sp_blockchain::Result;
|
||||
use sp_consensus::{
|
||||
BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, ImportResult, BlockImport,
|
||||
import_queue::BoxJustificationImport,
|
||||
import_queue::BoxJustificationImport, BlockImport, BlockImportParams, BlockOrigin,
|
||||
ForkChoiceStrategy, ImportResult, ImportedAux,
|
||||
};
|
||||
use std::{collections::{HashMap, HashSet}, pin::Pin};
|
||||
use sp_runtime::{Justifications, traits::{Block as BlockT, Header as HeaderT}};
|
||||
use sp_runtime::generic::{BlockId, DigestItem};
|
||||
use sp_core::H256;
|
||||
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
||||
use sp_finality_grandpa::{
|
||||
GRANDPA_ENGINE_ID, AuthorityList, EquivocationProof, GrandpaApi, OpaqueKeyOwnershipProof,
|
||||
AuthorityList, EquivocationProof, GrandpaApi, OpaqueKeyOwnershipProof, GRANDPA_ENGINE_ID,
|
||||
};
|
||||
use sp_keyring::Ed25519Keyring;
|
||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||
use sp_runtime::{
|
||||
generic::{BlockId, DigestItem},
|
||||
traits::{Block as BlockT, Header as HeaderT},
|
||||
Justifications,
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
pin::Pin,
|
||||
};
|
||||
use substrate_test_runtime_client::runtime::BlockNumber;
|
||||
use tokio::runtime::{Handle, Runtime};
|
||||
|
||||
use authorities::AuthoritySet;
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
@@ -61,7 +67,7 @@ type GrandpaBlockImport = crate::GrandpaBlockImport<
|
||||
substrate_test_runtime_client::Backend,
|
||||
Block,
|
||||
PeersFullClient,
|
||||
LongestChain<substrate_test_runtime_client::Backend, Block>
|
||||
LongestChain<substrate_test_runtime_client::Backend, Block>,
|
||||
>;
|
||||
|
||||
struct GrandpaTestNet {
|
||||
@@ -71,10 +77,8 @@ struct GrandpaTestNet {
|
||||
|
||||
impl GrandpaTestNet {
|
||||
fn new(test_config: TestApi, n_authority: usize, n_full: usize) -> Self {
|
||||
let mut net = GrandpaTestNet {
|
||||
peers: Vec::with_capacity(n_authority + n_full),
|
||||
test_config,
|
||||
};
|
||||
let mut net =
|
||||
GrandpaTestNet { peers: Vec::with_capacity(n_authority + n_full), test_config };
|
||||
|
||||
for _ in 0..n_authority {
|
||||
net.add_authority_peer();
|
||||
@@ -105,10 +109,7 @@ impl TestNetFactory for GrandpaTestNet {
|
||||
|
||||
/// Create new test network with peers and given config.
|
||||
fn from_config(_config: &ProtocolConfig) -> Self {
|
||||
GrandpaTestNet {
|
||||
peers: Vec::new(),
|
||||
test_config: Default::default(),
|
||||
}
|
||||
GrandpaTestNet { peers: Vec::new(), test_config: Default::default() }
|
||||
}
|
||||
|
||||
fn default_config() -> ProtocolConfig {
|
||||
@@ -133,13 +134,10 @@ impl TestNetFactory for GrandpaTestNet {
|
||||
PassThroughVerifier::new(false) // use non-instant finality.
|
||||
}
|
||||
|
||||
fn make_block_import(&self, client: PeersClient)
|
||||
-> (
|
||||
BlockImportAdapter<Self::BlockImport>,
|
||||
Option<BoxJustificationImport<Block>>,
|
||||
PeerData,
|
||||
)
|
||||
{
|
||||
fn make_block_import(
|
||||
&self,
|
||||
client: PeersClient,
|
||||
) -> (BlockImportAdapter<Self::BlockImport>, Option<BoxJustificationImport<Block>>, PeerData) {
|
||||
match client {
|
||||
PeersClient::Full(ref client, ref backend) => {
|
||||
let (import, link) = block_import(
|
||||
@@ -147,7 +145,8 @@ impl TestNetFactory for GrandpaTestNet {
|
||||
&self.test_config,
|
||||
LongestChain::new(backend.clone()),
|
||||
None,
|
||||
).expect("Could not create block import for fresh peer.");
|
||||
)
|
||||
.expect("Could not create block import for fresh peer.");
|
||||
let justification_import = Box::new(import.clone());
|
||||
(
|
||||
BlockImportAdapter::new(import),
|
||||
@@ -181,9 +180,7 @@ pub(crate) struct TestApi {
|
||||
|
||||
impl TestApi {
|
||||
pub fn new(genesis_authorities: AuthorityList) -> Self {
|
||||
TestApi {
|
||||
genesis_authorities,
|
||||
}
|
||||
TestApi { genesis_authorities }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,21 +232,24 @@ fn make_ids(keys: &[Ed25519Keyring]) -> AuthorityList {
|
||||
|
||||
fn create_keystore(authority: Ed25519Keyring) -> (SyncCryptoStorePtr, tempfile::TempDir) {
|
||||
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
|
||||
let keystore = Arc::new(LocalKeystore::open(keystore_path.path(), None)
|
||||
.expect("Creates keystore"));
|
||||
let keystore =
|
||||
Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore"));
|
||||
SyncCryptoStore::ed25519_generate_new(&*keystore, GRANDPA, Some(&authority.to_seed()))
|
||||
.expect("Creates authority key");
|
||||
|
||||
(keystore, keystore_path)
|
||||
}
|
||||
|
||||
fn block_until_complete(future: impl Future + Unpin, net: &Arc<Mutex<GrandpaTestNet>>, runtime: &mut Runtime) {
|
||||
fn block_until_complete(
|
||||
future: impl Future + Unpin,
|
||||
net: &Arc<Mutex<GrandpaTestNet>>,
|
||||
runtime: &mut Runtime,
|
||||
) {
|
||||
let drive_to_completion = futures::future::poll_fn(|cx| {
|
||||
net.lock().poll(cx); Poll::<()>::Pending
|
||||
net.lock().poll(cx);
|
||||
Poll::<()>::Pending
|
||||
});
|
||||
runtime.block_on(
|
||||
future::select(future, drive_to_completion)
|
||||
);
|
||||
runtime.block_on(future::select(future, drive_to_completion));
|
||||
}
|
||||
|
||||
// Spawns grandpa voters. Returns a future to spawn on the runtime.
|
||||
@@ -264,11 +264,9 @@ fn initialize_grandpa(
|
||||
|
||||
let (net_service, link) = {
|
||||
// temporary needed for some reason
|
||||
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
|
||||
(
|
||||
net.peers[peer_id].network_service().clone(),
|
||||
link,
|
||||
)
|
||||
let link =
|
||||
net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
|
||||
(net.peers[peer_id].network_service().clone(), link)
|
||||
};
|
||||
|
||||
let grandpa_params = GrandpaParams {
|
||||
@@ -288,9 +286,10 @@ fn initialize_grandpa(
|
||||
shared_voter_state: SharedVoterState::empty(),
|
||||
telemetry: None,
|
||||
};
|
||||
let voter = run_grandpa_voter(grandpa_params).expect("all in order with client and network");
|
||||
let voter =
|
||||
run_grandpa_voter(grandpa_params).expect("all in order with client and network");
|
||||
|
||||
fn assert_send<T: Send>(_: &T) { }
|
||||
fn assert_send<T: Send>(_: &T) {}
|
||||
assert_send(&voter);
|
||||
|
||||
voters.push(voter);
|
||||
@@ -307,8 +306,9 @@ fn run_to_completion_with<F>(
|
||||
net: Arc<Mutex<GrandpaTestNet>>,
|
||||
peers: &[Ed25519Keyring],
|
||||
with: F,
|
||||
) -> u64 where
|
||||
F: FnOnce(Handle) -> Option<Pin<Box<dyn Future<Output = ()>>>>
|
||||
) -> u64
|
||||
where
|
||||
F: FnOnce(Handle) -> Option<Pin<Box<dyn Future<Output = ()>>>>,
|
||||
{
|
||||
let mut wait_for = Vec::new();
|
||||
|
||||
@@ -322,20 +322,19 @@ fn run_to_completion_with<F>(
|
||||
let highest_finalized = highest_finalized.clone();
|
||||
let client = net.lock().peers[peer_id].client().clone();
|
||||
|
||||
wait_for.push(
|
||||
Box::pin(
|
||||
client.finality_notification_stream()
|
||||
.take_while(move |n| {
|
||||
let mut highest_finalized = highest_finalized.write();
|
||||
if *n.header.number() > *highest_finalized {
|
||||
*highest_finalized = *n.header.number();
|
||||
}
|
||||
future::ready(n.header.number() < &blocks)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.map(|_| ())
|
||||
)
|
||||
);
|
||||
wait_for.push(Box::pin(
|
||||
client
|
||||
.finality_notification_stream()
|
||||
.take_while(move |n| {
|
||||
let mut highest_finalized = highest_finalized.write();
|
||||
if *n.header.number() > *highest_finalized {
|
||||
*highest_finalized = *n.header.number();
|
||||
}
|
||||
future::ready(n.header.number() < &blocks)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.map(|_| ()),
|
||||
));
|
||||
}
|
||||
|
||||
// wait for all finalized on each.
|
||||
@@ -350,7 +349,7 @@ fn run_to_completion(
|
||||
runtime: &mut Runtime,
|
||||
blocks: u64,
|
||||
net: Arc<Mutex<GrandpaTestNet>>,
|
||||
peers: &[Ed25519Keyring]
|
||||
peers: &[Ed25519Keyring],
|
||||
) -> u64 {
|
||||
run_to_completion_with(runtime, blocks, net, peers, |_| None)
|
||||
}
|
||||
@@ -386,8 +385,7 @@ fn finalize_3_voters_no_observers() {
|
||||
net.block_until_sync();
|
||||
|
||||
for i in 0..3 {
|
||||
assert_eq!(net.peer(i).client().info().best_number, 20,
|
||||
"Peer #{} failed to sync", i);
|
||||
assert_eq!(net.peer(i).client().info().best_number, 20, "Peer #{} failed to sync", i);
|
||||
}
|
||||
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
@@ -395,7 +393,12 @@ fn finalize_3_voters_no_observers() {
|
||||
|
||||
// normally there's no justification for finalized blocks
|
||||
assert!(
|
||||
net.lock().peer(0).client().justifications(&BlockId::Number(20)).unwrap().is_none(),
|
||||
net.lock()
|
||||
.peer(0)
|
||||
.client()
|
||||
.justifications(&BlockId::Number(20))
|
||||
.unwrap()
|
||||
.is_none(),
|
||||
"Extra justification for block#1",
|
||||
);
|
||||
}
|
||||
@@ -425,7 +428,7 @@ fn finalize_3_voters_1_full_observer() {
|
||||
observer_enabled: true,
|
||||
telemetry: None,
|
||||
},
|
||||
link: link,
|
||||
link,
|
||||
network: net_service,
|
||||
voting_rule: (),
|
||||
prometheus_registry: None,
|
||||
@@ -444,9 +447,10 @@ fn finalize_3_voters_1_full_observer() {
|
||||
for peer_id in 0..4 {
|
||||
let client = net.lock().peers[peer_id].client().clone();
|
||||
finality_notifications.push(
|
||||
client.finality_notification_stream()
|
||||
client
|
||||
.finality_notification_stream()
|
||||
.take_while(|n| future::ready(n.header.number() < &20))
|
||||
.for_each(move |_| future::ready(()))
|
||||
.for_each(move |_| future::ready(())),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -458,9 +462,8 @@ fn finalize_3_voters_1_full_observer() {
|
||||
// all peers should have stored the justification for the best finalized block #20
|
||||
for peer_id in 0..4 {
|
||||
let client = net.lock().peers[peer_id].client().as_full().unwrap();
|
||||
let justification = crate::aux_schema::best_justification::<_, Block>(&*client)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let justification =
|
||||
crate::aux_schema::best_justification::<_, Block>(&*client).unwrap().unwrap();
|
||||
|
||||
assert_eq!(justification.commit.target_number, 20);
|
||||
}
|
||||
@@ -469,27 +472,16 @@ fn finalize_3_voters_1_full_observer() {
|
||||
#[test]
|
||||
fn transition_3_voters_twice_1_full_observer() {
|
||||
sp_tracing::try_init_simple();
|
||||
let peers_a = &[
|
||||
Ed25519Keyring::Alice,
|
||||
Ed25519Keyring::Bob,
|
||||
Ed25519Keyring::Charlie,
|
||||
];
|
||||
let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
|
||||
|
||||
let peers_b = &[
|
||||
Ed25519Keyring::Dave,
|
||||
Ed25519Keyring::Eve,
|
||||
Ed25519Keyring::Ferdie,
|
||||
];
|
||||
let peers_b = &[Ed25519Keyring::Dave, Ed25519Keyring::Eve, Ed25519Keyring::Ferdie];
|
||||
|
||||
let peers_c = &[
|
||||
Ed25519Keyring::Alice,
|
||||
Ed25519Keyring::Eve,
|
||||
Ed25519Keyring::Two,
|
||||
];
|
||||
let peers_c = &[Ed25519Keyring::Alice, Ed25519Keyring::Eve, Ed25519Keyring::Two];
|
||||
|
||||
let observer = &[Ed25519Keyring::One];
|
||||
|
||||
let all_peers = peers_a.iter()
|
||||
let all_peers = peers_a
|
||||
.iter()
|
||||
.chain(peers_b)
|
||||
.chain(peers_c)
|
||||
.chain(observer)
|
||||
@@ -511,11 +503,9 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
|
||||
let (net_service, link) = {
|
||||
let net = net.lock();
|
||||
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
|
||||
(
|
||||
net.peers[peer_id].network_service().clone(),
|
||||
link,
|
||||
)
|
||||
let link =
|
||||
net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
|
||||
(net.peers[peer_id].network_service().clone(), link)
|
||||
};
|
||||
|
||||
let grandpa_params = GrandpaParams {
|
||||
@@ -536,7 +526,8 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
telemetry: None,
|
||||
};
|
||||
|
||||
voters.push(run_grandpa_voter(grandpa_params).expect("all in order with client and network"));
|
||||
voters
|
||||
.push(run_grandpa_voter(grandpa_params).expect("all in order with client and network"));
|
||||
}
|
||||
|
||||
net.lock().peer(0).push_blocks(1, false);
|
||||
@@ -544,10 +535,10 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
|
||||
for (i, peer) in net.lock().peers().iter().enumerate() {
|
||||
let full_client = peer.client().as_full().expect("only full clients are used in test");
|
||||
assert_eq!(full_client.chain_info().best_number, 1,
|
||||
"Peer #{} failed to sync", i);
|
||||
assert_eq!(full_client.chain_info().best_number, 1, "Peer #{} failed to sync", i);
|
||||
|
||||
let set: AuthoritySet<Hash, BlockNumber> = crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||
let set: AuthoritySet<Hash, BlockNumber> =
|
||||
crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||
|
||||
assert_eq!(set.current(), (0, make_ids(peers_a).as_slice()));
|
||||
assert_eq!(set.pending_changes().count(), 0);
|
||||
@@ -559,7 +550,8 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
let peers_c = peers_c.clone();
|
||||
|
||||
// wait for blocks to be finalized before generating new ones
|
||||
let block_production = client.finality_notification_stream()
|
||||
let block_production = client
|
||||
.finality_notification_stream()
|
||||
.take_while(|n| future::ready(n.header.number() < &30))
|
||||
.for_each(move |n| {
|
||||
match n.header.number() {
|
||||
@@ -571,10 +563,10 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
// generate transition at block 15, applied at 20.
|
||||
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
|
||||
let mut block = builder.build().unwrap().block;
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(peers_b),
|
||||
delay: 4,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(peers_b), delay: 4 },
|
||||
);
|
||||
|
||||
block
|
||||
});
|
||||
@@ -585,10 +577,10 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
// add more until we have 30.
|
||||
net.lock().peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
|
||||
let mut block = builder.build().unwrap().block;
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(&peers_c),
|
||||
delay: 0,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(&peers_c), delay: 0 },
|
||||
);
|
||||
|
||||
block
|
||||
});
|
||||
@@ -612,16 +604,18 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
for (peer_id, _) in all_peers.into_iter().enumerate() {
|
||||
let client = net.lock().peers[peer_id].client().clone();
|
||||
finality_notifications.push(
|
||||
client.finality_notification_stream()
|
||||
client
|
||||
.finality_notification_stream()
|
||||
.take_while(|n| future::ready(n.header.number() < &30))
|
||||
.for_each(move |_| future::ready(()))
|
||||
.map(move |()| {
|
||||
let full_client = client.as_full().expect("only full clients are used in test");
|
||||
let set: AuthoritySet<Hash, BlockNumber> = crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||
let set: AuthoritySet<Hash, BlockNumber> =
|
||||
crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||
|
||||
assert_eq!(set.current(), (2, make_ids(peers_c).as_slice()));
|
||||
assert_eq!(set.pending_changes().count(), 0);
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -648,7 +642,13 @@ fn justification_is_generated_periodically() {
|
||||
// when block#32 (justification_period) is finalized, justification
|
||||
// is required => generated
|
||||
for i in 0..3 {
|
||||
assert!(net.lock().peer(i).client().justifications(&BlockId::Number(32)).unwrap().is_some());
|
||||
assert!(net
|
||||
.lock()
|
||||
.peer(i)
|
||||
.client()
|
||||
.justifications(&BlockId::Number(32))
|
||||
.unwrap()
|
||||
.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,10 +670,10 @@ fn sync_justifications_on_change_blocks() {
|
||||
// at block 21 we do add a transition which is instant
|
||||
net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
|
||||
let mut block = builder.build().unwrap().block;
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(peers_b),
|
||||
delay: 0,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
|
||||
);
|
||||
block
|
||||
});
|
||||
|
||||
@@ -682,8 +682,7 @@ fn sync_justifications_on_change_blocks() {
|
||||
net.block_until_sync();
|
||||
|
||||
for i in 0..4 {
|
||||
assert_eq!(net.peer(i).client().info().best_number, 25,
|
||||
"Peer #{} failed to sync", i);
|
||||
assert_eq!(net.peer(i).client().info().best_number, 25, "Peer #{} failed to sync", i);
|
||||
}
|
||||
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
@@ -693,12 +692,25 @@ fn sync_justifications_on_change_blocks() {
|
||||
// the first 3 peers are grandpa voters and therefore have already finalized
|
||||
// block 21 and stored a justification
|
||||
for i in 0..3 {
|
||||
assert!(net.lock().peer(i).client().justifications(&BlockId::Number(21)).unwrap().is_some());
|
||||
assert!(net
|
||||
.lock()
|
||||
.peer(i)
|
||||
.client()
|
||||
.justifications(&BlockId::Number(21))
|
||||
.unwrap()
|
||||
.is_some());
|
||||
}
|
||||
|
||||
// the last peer should get the justification by syncing from other peers
|
||||
futures::executor::block_on(futures::future::poll_fn(move |cx| {
|
||||
if net.lock().peer(3).client().justifications(&BlockId::Number(21)).unwrap().is_none() {
|
||||
if net
|
||||
.lock()
|
||||
.peer(3)
|
||||
.client()
|
||||
.justifications(&BlockId::Number(21))
|
||||
.unwrap()
|
||||
.is_none()
|
||||
{
|
||||
net.lock().poll(cx);
|
||||
Poll::Pending
|
||||
} else {
|
||||
@@ -717,8 +729,12 @@ fn finalizes_multiple_pending_changes_in_order() {
|
||||
let peers_c = &[Ed25519Keyring::Dave, Ed25519Keyring::Alice, Ed25519Keyring::Bob];
|
||||
|
||||
let all_peers = &[
|
||||
Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie,
|
||||
Ed25519Keyring::Dave, Ed25519Keyring::Eve, Ed25519Keyring::Ferdie,
|
||||
Ed25519Keyring::Alice,
|
||||
Ed25519Keyring::Bob,
|
||||
Ed25519Keyring::Charlie,
|
||||
Ed25519Keyring::Dave,
|
||||
Ed25519Keyring::Eve,
|
||||
Ed25519Keyring::Ferdie,
|
||||
];
|
||||
let genesis_voters = make_ids(peers_a);
|
||||
|
||||
@@ -735,10 +751,10 @@ fn finalizes_multiple_pending_changes_in_order() {
|
||||
// at block 21 we do add a transition which is instant
|
||||
net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
|
||||
let mut block = builder.build().unwrap().block;
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(peers_b),
|
||||
delay: 0,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
|
||||
);
|
||||
block
|
||||
});
|
||||
|
||||
@@ -748,10 +764,10 @@ fn finalizes_multiple_pending_changes_in_order() {
|
||||
// at block 26 we add another which is enacted at block 30
|
||||
net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
|
||||
let mut block = builder.build().unwrap().block;
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(peers_c),
|
||||
delay: 4,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(peers_c), delay: 4 },
|
||||
);
|
||||
block
|
||||
});
|
||||
|
||||
@@ -762,8 +778,7 @@ fn finalizes_multiple_pending_changes_in_order() {
|
||||
|
||||
// all peers imported both change blocks
|
||||
for i in 0..6 {
|
||||
assert_eq!(net.peer(i).client().info().best_number, 30,
|
||||
"Peer #{} failed to sync", i);
|
||||
assert_eq!(net.peer(i).client().info().best_number, 30, "Peer #{} failed to sync", i);
|
||||
}
|
||||
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
@@ -794,16 +809,17 @@ fn force_change_to_new_set() {
|
||||
let mut block = builder.build().unwrap().block;
|
||||
|
||||
// add a forced transition at block 12.
|
||||
add_forced_change(&mut block, 0, ScheduledChange {
|
||||
next_authorities: voters.clone(),
|
||||
delay: 10,
|
||||
});
|
||||
add_forced_change(
|
||||
&mut block,
|
||||
0,
|
||||
ScheduledChange { next_authorities: voters.clone(), delay: 10 },
|
||||
);
|
||||
|
||||
// add a normal transition too to ensure that forced changes take priority.
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(genesis_authorities),
|
||||
delay: 5,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(genesis_authorities), delay: 5 },
|
||||
);
|
||||
|
||||
block
|
||||
});
|
||||
@@ -812,11 +828,11 @@ fn force_change_to_new_set() {
|
||||
net.lock().block_until_sync();
|
||||
|
||||
for (i, peer) in net.lock().peers().iter().enumerate() {
|
||||
assert_eq!(peer.client().info().best_number, 26,
|
||||
"Peer #{} failed to sync", i);
|
||||
assert_eq!(peer.client().info().best_number, 26, "Peer #{} failed to sync", i);
|
||||
|
||||
let full_client = peer.client().as_full().expect("only full clients are used in test");
|
||||
let set: AuthoritySet<Hash, BlockNumber> = crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||
let set: AuthoritySet<Hash, BlockNumber> =
|
||||
crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||
|
||||
assert_eq!(set.current(), (1, voters.as_slice()));
|
||||
assert_eq!(set.pending_changes().count(), 0);
|
||||
@@ -841,12 +857,14 @@ fn allows_reimporting_change_blocks() {
|
||||
let (mut block_import, ..) = net.make_block_import(client.clone());
|
||||
|
||||
let full_client = client.as_full().unwrap();
|
||||
let builder = full_client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
|
||||
let builder = full_client
|
||||
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
||||
.unwrap();
|
||||
let mut block = builder.build().unwrap().block;
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(peers_b),
|
||||
delay: 0,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
|
||||
);
|
||||
|
||||
let block = || {
|
||||
let block = block.clone();
|
||||
@@ -886,13 +904,15 @@ fn test_bad_justification() {
|
||||
let (mut block_import, ..) = net.make_block_import(client.clone());
|
||||
|
||||
let full_client = client.as_full().expect("only full clients are used in test");
|
||||
let builder = full_client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
|
||||
let builder = full_client
|
||||
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
||||
.unwrap();
|
||||
let mut block = builder.build().unwrap().block;
|
||||
|
||||
add_scheduled_change(&mut block, ScheduledChange {
|
||||
next_authorities: make_ids(peers_b),
|
||||
delay: 0,
|
||||
});
|
||||
add_scheduled_change(
|
||||
&mut block,
|
||||
ScheduledChange { next_authorities: make_ids(peers_b), delay: 0 },
|
||||
);
|
||||
|
||||
let block = || {
|
||||
let block = block.clone();
|
||||
@@ -923,8 +943,8 @@ fn test_bad_justification() {
|
||||
|
||||
#[test]
|
||||
fn voter_persists_its_votes() {
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use futures::future;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
sp_tracing::try_init_simple();
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
@@ -959,8 +979,7 @@ fn voter_persists_its_votes() {
|
||||
|
||||
let set_state = {
|
||||
let bob_client = net.peer(1).client().clone();
|
||||
let (_, _, link) = net
|
||||
.make_block_import(bob_client);
|
||||
let (_, _, link) = net.make_block_import(bob_client);
|
||||
let LinkHalf { persistent_data, .. } = link.lock().take().unwrap();
|
||||
let PersistentData { set_state, .. } = persistent_data;
|
||||
set_state
|
||||
@@ -983,10 +1002,7 @@ fn voter_persists_its_votes() {
|
||||
let (net_service, link) = {
|
||||
// temporary needed for some reason
|
||||
let link = net.peers[0].data.lock().take().expect("link initialized at startup; qed");
|
||||
(
|
||||
net.peers[0].network_service().clone(),
|
||||
link,
|
||||
)
|
||||
(net.peers[0].network_service().clone(), link)
|
||||
};
|
||||
|
||||
let grandpa_params = GrandpaParams {
|
||||
@@ -1026,8 +1042,7 @@ fn voter_persists_its_votes() {
|
||||
// read the persisted state after aborting alice_voter1.
|
||||
let alice_client = net.peer(0).client().clone();
|
||||
|
||||
let (_block_import, _, link) = net
|
||||
.make_block_import(alice_client);
|
||||
let (_block_import, _, link) = net.make_block_import(alice_client);
|
||||
let link = link.lock().take().unwrap();
|
||||
|
||||
let grandpa_params = GrandpaParams {
|
||||
@@ -1064,8 +1079,7 @@ fn voter_persists_its_votes() {
|
||||
net.peer(0).push_blocks(20, false);
|
||||
net.block_until_sync();
|
||||
|
||||
assert_eq!(net.peer(0).client().info().best_number, 20,
|
||||
"Peer #{} failed to sync", 0);
|
||||
assert_eq!(net.peer(0).client().info().best_number, 20, "Peer #{} failed to sync", 0);
|
||||
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
|
||||
@@ -1113,12 +1127,13 @@ fn voter_persists_its_votes() {
|
||||
// we push 20 more blocks to alice's chain
|
||||
net.lock().peer(0).push_blocks(20, false);
|
||||
|
||||
let interval = futures::stream::unfold(Delay::new(Duration::from_millis(200)), |delay|
|
||||
Box::pin(async move {
|
||||
delay.await;
|
||||
Some(((), Delay::new(Duration::from_millis(200))))
|
||||
})
|
||||
);
|
||||
let interval =
|
||||
futures::stream::unfold(Delay::new(Duration::from_millis(200)), |delay| {
|
||||
Box::pin(async move {
|
||||
delay.await;
|
||||
Some(((), Delay::new(Duration::from_millis(200))))
|
||||
})
|
||||
});
|
||||
|
||||
interval
|
||||
.take_while(move |_| {
|
||||
@@ -1135,17 +1150,19 @@ fn voter_persists_its_votes() {
|
||||
runtime_handle.spawn(alice_voter2(peers, net.clone()));
|
||||
|
||||
// and we push our own prevote for block 30
|
||||
let prevote = finality_grandpa::Prevote {
|
||||
target_number: 30,
|
||||
target_hash: block_30_hash,
|
||||
};
|
||||
let prevote =
|
||||
finality_grandpa::Prevote { target_number: 30, target_hash: block_30_hash };
|
||||
|
||||
// One should either be calling `Sink::send` or `Sink::start_send` followed
|
||||
// by `Sink::poll_complete` to make sure items are being flushed. Given that
|
||||
// we send in a loop including a delay until items are received, this can be
|
||||
// ignored for the sake of reduced complexity.
|
||||
Pin::new(&mut *round_tx.lock()).start_send(finality_grandpa::Message::Prevote(prevote)).unwrap();
|
||||
} else if state.compare_exchange(1, 2, Ordering::SeqCst, Ordering::SeqCst).unwrap() == 1 {
|
||||
Pin::new(&mut *round_tx.lock())
|
||||
.start_send(finality_grandpa::Message::Prevote(prevote))
|
||||
.unwrap();
|
||||
} else if state.compare_exchange(1, 2, Ordering::SeqCst, Ordering::SeqCst).unwrap() ==
|
||||
1
|
||||
{
|
||||
// the next message we receive should be our own prevote
|
||||
let prevote = match signed.message {
|
||||
finality_grandpa::Message::Prevote(prevote) => prevote,
|
||||
@@ -1155,11 +1172,12 @@ fn voter_persists_its_votes() {
|
||||
// targeting block 30
|
||||
assert!(prevote.target_number == 30);
|
||||
|
||||
// after alice restarts it should send its previous prevote
|
||||
// therefore we won't ever receive it again since it will be a
|
||||
// known message on the gossip layer
|
||||
|
||||
} else if state.compare_exchange(2, 3, Ordering::SeqCst, Ordering::SeqCst).unwrap() == 2 {
|
||||
// after alice restarts it should send its previous prevote
|
||||
// therefore we won't ever receive it again since it will be a
|
||||
// known message on the gossip layer
|
||||
} else if state.compare_exchange(2, 3, Ordering::SeqCst, Ordering::SeqCst).unwrap() ==
|
||||
2
|
||||
{
|
||||
// we then receive a precommit from alice for block 15
|
||||
// even though we casted a prevote for block 30
|
||||
let precommit = match signed.message {
|
||||
@@ -1202,13 +1220,13 @@ fn finalize_3_voters_1_light_observer() {
|
||||
},
|
||||
net.peers[3].data.lock().take().expect("link initialized at startup; qed"),
|
||||
net.peers[3].network_service().clone(),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
net.peer(0).push_blocks(20, false);
|
||||
net.block_until_sync();
|
||||
|
||||
for i in 0..4 {
|
||||
assert_eq!(net.peer(i).client().info().best_number, 20,
|
||||
"Peer #{} failed to sync", i);
|
||||
assert_eq!(net.peer(i).client().info().best_number, 20, "Peer #{} failed to sync", i);
|
||||
}
|
||||
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
@@ -1231,7 +1249,11 @@ fn voter_catches_up_to_latest_round_when_behind() {
|
||||
let net = Arc::new(Mutex::new(net));
|
||||
let mut finality_notifications = Vec::new();
|
||||
|
||||
let voter = |keystore, peer_id, link, net: Arc<Mutex<GrandpaTestNet>>| -> Pin<Box<dyn Future<Output = ()> + Send>> {
|
||||
let voter = |keystore,
|
||||
peer_id,
|
||||
link,
|
||||
net: Arc<Mutex<GrandpaTestNet>>|
|
||||
-> Pin<Box<dyn Future<Output = ()> + Send>> {
|
||||
let grandpa_params = GrandpaParams {
|
||||
config: Config {
|
||||
gossip_duration: TEST_GOSSIP_DURATION,
|
||||
@@ -1259,17 +1281,16 @@ fn voter_catches_up_to_latest_round_when_behind() {
|
||||
for (peer_id, key) in peers.iter().enumerate() {
|
||||
let (client, link) = {
|
||||
let net = net.lock();
|
||||
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
|
||||
(
|
||||
net.peers[peer_id].client().clone(),
|
||||
link,
|
||||
)
|
||||
let link =
|
||||
net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
|
||||
(net.peers[peer_id].client().clone(), link)
|
||||
};
|
||||
|
||||
finality_notifications.push(
|
||||
client.finality_notification_stream()
|
||||
client
|
||||
.finality_notification_stream()
|
||||
.take_while(|n| future::ready(n.header.number() < &50))
|
||||
.for_each(move |_| future::ready(()))
|
||||
.for_each(move |_| future::ready(())),
|
||||
);
|
||||
|
||||
let (keystore, keystore_path) = create_keystore(*key);
|
||||
@@ -1324,11 +1345,10 @@ fn voter_catches_up_to_latest_round_when_behind() {
|
||||
};
|
||||
|
||||
let drive_to_completion = futures::future::poll_fn(|cx| {
|
||||
net.lock().poll(cx); Poll::<()>::Pending
|
||||
net.lock().poll(cx);
|
||||
Poll::<()>::Pending
|
||||
});
|
||||
runtime.block_on(
|
||||
future::select(test, drive_to_completion)
|
||||
);
|
||||
runtime.block_on(future::select(test, drive_to_completion));
|
||||
}
|
||||
|
||||
type TestEnvironment<N, VR> = Environment<
|
||||
@@ -1350,11 +1370,7 @@ where
|
||||
N: NetworkT<Block>,
|
||||
VR: VotingRule<Block, TestClient>,
|
||||
{
|
||||
let PersistentData {
|
||||
ref authority_set,
|
||||
ref set_state,
|
||||
..
|
||||
} = link.persistent_data;
|
||||
let PersistentData { ref authority_set, ref set_state, .. } = link.persistent_data;
|
||||
|
||||
let config = Config {
|
||||
gossip_duration: TEST_GOSSIP_DURATION,
|
||||
@@ -1366,13 +1382,8 @@ where
|
||||
telemetry: None,
|
||||
};
|
||||
|
||||
let network = NetworkBridge::new(
|
||||
network_service.clone(),
|
||||
config.clone(),
|
||||
set_state.clone(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let network =
|
||||
NetworkBridge::new(network_service.clone(), config.clone(), set_state.clone(), None, None);
|
||||
|
||||
Environment {
|
||||
authority_set: authority_set.clone(),
|
||||
@@ -1428,25 +1439,28 @@ fn grandpa_environment_respects_voting_rules() {
|
||||
|
||||
// the unrestricted environment should just return the best block
|
||||
assert_eq!(
|
||||
block_on(unrestricted_env.best_chain_containing(
|
||||
peer.client().info().finalized_hash
|
||||
)).unwrap().unwrap().1,
|
||||
block_on(unrestricted_env.best_chain_containing(peer.client().info().finalized_hash))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1,
|
||||
21,
|
||||
);
|
||||
|
||||
// both the other environments should return block 16, which is 3/4 of the
|
||||
// way in the unfinalized chain
|
||||
assert_eq!(
|
||||
block_on(three_quarters_env.best_chain_containing(
|
||||
peer.client().info().finalized_hash
|
||||
)).unwrap().unwrap().1,
|
||||
block_on(three_quarters_env.best_chain_containing(peer.client().info().finalized_hash))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1,
|
||||
16,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
block_on(default_env.best_chain_containing(
|
||||
peer.client().info().finalized_hash
|
||||
)).unwrap().unwrap().1,
|
||||
block_on(default_env.best_chain_containing(peer.client().info().finalized_hash))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1,
|
||||
16,
|
||||
);
|
||||
|
||||
@@ -1455,18 +1469,20 @@ fn grandpa_environment_respects_voting_rules() {
|
||||
|
||||
// the 3/4 environment should propose block 21 for voting
|
||||
assert_eq!(
|
||||
block_on(three_quarters_env.best_chain_containing(
|
||||
peer.client().info().finalized_hash
|
||||
)).unwrap().unwrap().1,
|
||||
block_on(three_quarters_env.best_chain_containing(peer.client().info().finalized_hash))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1,
|
||||
21,
|
||||
);
|
||||
|
||||
// while the default environment will always still make sure we don't vote
|
||||
// on the best block (2 behind)
|
||||
assert_eq!(
|
||||
block_on(default_env.best_chain_containing(
|
||||
peer.client().info().finalized_hash
|
||||
)).unwrap().unwrap().1,
|
||||
block_on(default_env.best_chain_containing(peer.client().info().finalized_hash))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1,
|
||||
19,
|
||||
);
|
||||
|
||||
@@ -1477,9 +1493,10 @@ fn grandpa_environment_respects_voting_rules() {
|
||||
// best block, there's a hard rule that we can't cast any votes lower than
|
||||
// the given base (#21).
|
||||
assert_eq!(
|
||||
block_on(default_env.best_chain_containing(
|
||||
peer.client().info().finalized_hash
|
||||
)).unwrap().unwrap().1,
|
||||
block_on(default_env.best_chain_containing(peer.client().info().finalized_hash))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1,
|
||||
21,
|
||||
);
|
||||
}
|
||||
@@ -1518,9 +1535,7 @@ fn grandpa_environment_never_overwrites_round_voter_state() {
|
||||
assert_eq!(get_current_round(2), None);
|
||||
|
||||
// after completing round 1 we should start tracking round 2
|
||||
environment
|
||||
.completed(1, round_state(), base(), &historical_votes())
|
||||
.unwrap();
|
||||
environment.completed(1, round_state(), base(), &historical_votes()).unwrap();
|
||||
|
||||
assert_eq!(get_current_round(2).unwrap(), HasVoted::No);
|
||||
|
||||
@@ -1530,10 +1545,8 @@ fn grandpa_environment_never_overwrites_round_voter_state() {
|
||||
|
||||
let info = peer.client().info();
|
||||
|
||||
let prevote = finality_grandpa::Prevote {
|
||||
target_hash: info.best_hash,
|
||||
target_number: info.best_number,
|
||||
};
|
||||
let prevote =
|
||||
finality_grandpa::Prevote { target_hash: info.best_hash, target_number: info.best_number };
|
||||
|
||||
// we prevote for round 2 which should lead to us updating the voter state
|
||||
environment.prevoted(2, prevote.clone()).unwrap();
|
||||
@@ -1545,9 +1558,7 @@ fn grandpa_environment_never_overwrites_round_voter_state() {
|
||||
|
||||
// if we report round 1 as completed again we should not overwrite the
|
||||
// voter state for round 2
|
||||
environment
|
||||
.completed(1, round_state(), base(), &historical_votes())
|
||||
.unwrap();
|
||||
environment.completed(1, round_state(), base(), &historical_votes()).unwrap();
|
||||
|
||||
assert_matches!(get_current_round(2).unwrap(), HasVoted::Yes(_, _));
|
||||
}
|
||||
@@ -1566,7 +1577,9 @@ fn imports_justification_for_regular_blocks_on_import() {
|
||||
let (mut block_import, ..) = net.make_block_import(client.clone());
|
||||
|
||||
let full_client = client.as_full().expect("only full clients are used in test");
|
||||
let builder = full_client.new_block_at(&BlockId::Number(0), Default::default(), false).unwrap();
|
||||
let builder = full_client
|
||||
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
||||
.unwrap();
|
||||
let block = builder.build().unwrap().block;
|
||||
|
||||
let block_hash = block.hash();
|
||||
@@ -1597,11 +1610,7 @@ fn imports_justification_for_regular_blocks_on_import() {
|
||||
precommits: vec![precommit],
|
||||
};
|
||||
|
||||
GrandpaJustification::from_commit(
|
||||
&full_client,
|
||||
round,
|
||||
commit,
|
||||
).unwrap()
|
||||
GrandpaJustification::from_commit(&full_client, round, commit).unwrap()
|
||||
};
|
||||
|
||||
// we import the block with justification attached
|
||||
@@ -1622,9 +1631,7 @@ fn imports_justification_for_regular_blocks_on_import() {
|
||||
);
|
||||
|
||||
// the justification should be imported and available from the client
|
||||
assert!(
|
||||
client.justifications(&BlockId::Hash(block_hash)).unwrap().is_some(),
|
||||
);
|
||||
assert!(client.justifications(&BlockId::Hash(block_hash)).unwrap().is_some(),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1644,10 +1651,7 @@ fn grandpa_environment_doesnt_send_equivocation_reports_for_itself() {
|
||||
};
|
||||
|
||||
let signed_prevote = {
|
||||
let prevote = finality_grandpa::Prevote {
|
||||
target_hash: H256::random(),
|
||||
target_number: 1,
|
||||
};
|
||||
let prevote = finality_grandpa::Prevote { target_hash: H256::random(), target_number: 1 };
|
||||
|
||||
let signed = alice.sign(&[]).into();
|
||||
(prevote, signed)
|
||||
@@ -1667,10 +1671,7 @@ fn grandpa_environment_doesnt_send_equivocation_reports_for_itself() {
|
||||
// reporting the equivocation should fail since the offender is a local
|
||||
// authority (i.e. we have keys in our keystore for the given id)
|
||||
let equivocation_proof = sp_finality_grandpa::Equivocation::Prevote(equivocation.clone());
|
||||
assert!(matches!(
|
||||
environment.report_equivocation(equivocation_proof),
|
||||
Err(Error::Safety(_))
|
||||
));
|
||||
assert!(matches!(environment.report_equivocation(equivocation_proof), Err(Error::Safety(_))));
|
||||
|
||||
// if we set the equivocation offender to another id for which we don't have
|
||||
// keys it should work
|
||||
|
||||
@@ -23,32 +23,31 @@
|
||||
//! This is used for votes and commit messages currently.
|
||||
|
||||
use super::{
|
||||
BlockStatus as BlockStatusT,
|
||||
BlockSyncRequester as BlockSyncRequesterT,
|
||||
CommunicationIn,
|
||||
Error,
|
||||
BlockStatus as BlockStatusT, BlockSyncRequester as BlockSyncRequesterT, CommunicationIn, Error,
|
||||
SignedMessage,
|
||||
};
|
||||
|
||||
use log::{debug, warn};
|
||||
use sp_utils::mpsc::TracingUnboundedReceiver;
|
||||
use futures::prelude::*;
|
||||
use futures::stream::{Fuse, StreamExt};
|
||||
use futures_timer::Delay;
|
||||
use finality_grandpa::voter;
|
||||
use parking_lot::Mutex;
|
||||
use prometheus_endpoint::{
|
||||
Gauge, U64, PrometheusError, register, Registry,
|
||||
use futures::{
|
||||
prelude::*,
|
||||
stream::{Fuse, StreamExt},
|
||||
};
|
||||
use futures_timer::Delay;
|
||||
use log::{debug, warn};
|
||||
use parking_lot::Mutex;
|
||||
use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64};
|
||||
use sc_client_api::{BlockImportNotification, ImportNotifications};
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
||||
use sp_utils::mpsc::TracingUnboundedReceiver;
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
use wasm_timer::Instant;
|
||||
|
||||
const LOG_PENDING_INTERVAL: Duration = Duration::from_secs(15);
|
||||
@@ -84,7 +83,6 @@ pub(crate) enum DiscardWaitOrReady<Block: BlockT, W, R> {
|
||||
}
|
||||
|
||||
/// Prometheus metrics for the `UntilImported` queue.
|
||||
//
|
||||
// At a given point in time there can be more than one `UntilImported` queue. One can not register a
|
||||
// metric twice, thus queues need to share the same Prometheus metrics instead of instantiating
|
||||
// their own ones.
|
||||
@@ -101,10 +99,13 @@ pub(crate) struct Metrics {
|
||||
impl Metrics {
|
||||
pub(crate) fn register(registry: &Registry) -> Result<Self, PrometheusError> {
|
||||
Ok(Self {
|
||||
global_waiting_messages: register(Gauge::new(
|
||||
"finality_grandpa_until_imported_waiting_messages_number",
|
||||
"Number of finality grandpa messages waiting within the until imported queue.",
|
||||
)?, registry)?,
|
||||
global_waiting_messages: register(
|
||||
Gauge::new(
|
||||
"finality_grandpa_until_imported_waiting_messages_number",
|
||||
"Number of finality grandpa messages waiting within the until imported queue.",
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
local_waiting_messages: 0,
|
||||
})
|
||||
}
|
||||
@@ -120,7 +121,6 @@ impl Metrics {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Clone for Metrics {
|
||||
fn clone(&self) -> Self {
|
||||
Metrics {
|
||||
@@ -136,8 +136,7 @@ impl Drop for Metrics {
|
||||
fn drop(&mut self) {
|
||||
// Reduce the global counter by the amount of messages that were still left in the dropped
|
||||
// queue.
|
||||
self.global_waiting_messages
|
||||
.sub(self.local_waiting_messages)
|
||||
self.global_waiting_messages.sub(self.local_waiting_messages)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,11 +199,12 @@ where
|
||||
// used in the event of missed import notifications
|
||||
const CHECK_PENDING_INTERVAL: Duration = Duration::from_secs(5);
|
||||
|
||||
let check_pending = futures::stream::unfold(Delay::new(CHECK_PENDING_INTERVAL), |delay|
|
||||
let check_pending = futures::stream::unfold(Delay::new(CHECK_PENDING_INTERVAL), |delay| {
|
||||
Box::pin(async move {
|
||||
delay.await;
|
||||
Some((Ok(()), Delay::new(CHECK_PENDING_INTERVAL)))
|
||||
}));
|
||||
})
|
||||
});
|
||||
|
||||
UntilImported {
|
||||
import_notifications: import_notifications.fuse(),
|
||||
@@ -220,7 +220,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, BStatus, BSyncRequester, I, M> Stream for UntilImported<Block, BStatus, BSyncRequester, I, M> where
|
||||
impl<Block, BStatus, BSyncRequester, I, M> Stream
|
||||
for UntilImported<Block, BStatus, BSyncRequester, I, M>
|
||||
where
|
||||
Block: BlockT,
|
||||
BStatus: BlockStatusT<Block>,
|
||||
BSyncRequester: BlockSyncRequesterT<Block>,
|
||||
@@ -257,7 +259,7 @@ impl<Block, BStatus, BSyncRequester, I, M> Stream for UntilImported<Block, BStat
|
||||
if let Some(metrics) = &mut this.metrics {
|
||||
metrics.waiting_messages_inc();
|
||||
}
|
||||
}
|
||||
},
|
||||
Poll::Pending => break,
|
||||
}
|
||||
}
|
||||
@@ -269,12 +271,12 @@ impl<Block, BStatus, BSyncRequester, I, M> Stream for UntilImported<Block, BStat
|
||||
// new block imported. queue up all messages tied to that hash.
|
||||
if let Some((_, _, messages)) = this.pending.remove(¬ification.hash) {
|
||||
let canon_number = *notification.header.number();
|
||||
let ready_messages = messages.into_iter()
|
||||
.filter_map(|m| m.wait_completed(canon_number));
|
||||
let ready_messages =
|
||||
messages.into_iter().filter_map(|m| m.wait_completed(canon_number));
|
||||
|
||||
this.ready.extend(ready_messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Poll::Pending => break,
|
||||
}
|
||||
}
|
||||
@@ -286,7 +288,9 @@ impl<Block, BStatus, BSyncRequester, I, M> Stream for UntilImported<Block, BStat
|
||||
|
||||
if update_interval {
|
||||
let mut known_keys = Vec::new();
|
||||
for (&block_hash, &mut (block_number, ref mut last_log, ref v)) in this.pending.iter_mut() {
|
||||
for (&block_hash, &mut (block_number, ref mut last_log, ref v)) in
|
||||
this.pending.iter_mut()
|
||||
{
|
||||
if let Some(number) = this.status_check.block_number(block_hash)? {
|
||||
known_keys.push((block_hash, number));
|
||||
} else {
|
||||
@@ -318,8 +322,8 @@ impl<Block, BStatus, BSyncRequester, I, M> Stream for UntilImported<Block, BStat
|
||||
|
||||
for (known_hash, canon_number) in known_keys {
|
||||
if let Some((_, _, pending_messages)) = this.pending.remove(&known_hash) {
|
||||
let ready_messages = pending_messages.into_iter()
|
||||
.filter_map(|m| m.wait_completed(canon_number));
|
||||
let ready_messages =
|
||||
pending_messages.into_iter().filter_map(|m| m.wait_completed(canon_number));
|
||||
|
||||
this.ready.extend(ready_messages);
|
||||
}
|
||||
@@ -363,9 +367,9 @@ impl<Block: BlockT> BlockUntilImported<Block> for SignedMessage<Block> {
|
||||
if let Some(number) = status_check.block_number(target_hash)? {
|
||||
if number != target_number {
|
||||
warn_authority_wrong_target(target_hash, msg.id);
|
||||
return Ok(DiscardWaitOrReady::Discard);
|
||||
return Ok(DiscardWaitOrReady::Discard)
|
||||
} else {
|
||||
return Ok(DiscardWaitOrReady::Ready(msg));
|
||||
return Ok(DiscardWaitOrReady::Ready(msg))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,13 +390,8 @@ impl<Block: BlockT> BlockUntilImported<Block> for SignedMessage<Block> {
|
||||
|
||||
/// Helper type definition for the stream which waits until vote targets for
|
||||
/// signed messages are imported.
|
||||
pub(crate) type UntilVoteTargetImported<Block, BlockStatus, BlockSyncRequester, I> = UntilImported<
|
||||
Block,
|
||||
BlockStatus,
|
||||
BlockSyncRequester,
|
||||
I,
|
||||
SignedMessage<Block>,
|
||||
>;
|
||||
pub(crate) type UntilVoteTargetImported<Block, BlockStatus, BlockSyncRequester, I> =
|
||||
UntilImported<Block, BlockStatus, BlockSyncRequester, I, SignedMessage<Block>>;
|
||||
|
||||
/// This blocks a global message import, i.e. a commit or catch up messages,
|
||||
/// until all blocks referenced in its votes are known.
|
||||
@@ -445,19 +444,18 @@ impl<Block: BlockT> BlockUntilImported<Block> for BlockGlobalMessage<Block> {
|
||||
if let Some(number) = status_check.block_number(target_hash)? {
|
||||
entry.insert(KnownOrUnknown::Known(number));
|
||||
number
|
||||
|
||||
} else {
|
||||
entry.insert(KnownOrUnknown::Unknown(perceived_number));
|
||||
perceived_number
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if canon_number != perceived_number {
|
||||
// invalid global message: messages targeting wrong number
|
||||
// or at least different from other vote in same global
|
||||
// message.
|
||||
return Ok(false);
|
||||
return Ok(false)
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
@@ -466,23 +464,24 @@ impl<Block: BlockT> BlockUntilImported<Block> for BlockGlobalMessage<Block> {
|
||||
match input {
|
||||
voter::CommunicationIn::Commit(_, ref commit, ..) => {
|
||||
// add known hashes from all precommits.
|
||||
let precommit_targets = commit.precommits
|
||||
.iter()
|
||||
.map(|c| (c.target_number, c.target_hash));
|
||||
let precommit_targets =
|
||||
commit.precommits.iter().map(|c| (c.target_number, c.target_hash));
|
||||
|
||||
for (target_number, target_hash) in precommit_targets {
|
||||
if !query_known(target_hash, target_number)? {
|
||||
return Ok(DiscardWaitOrReady::Discard);
|
||||
return Ok(DiscardWaitOrReady::Discard)
|
||||
}
|
||||
}
|
||||
},
|
||||
voter::CommunicationIn::CatchUp(ref catch_up, ..) => {
|
||||
// add known hashes from all prevotes and precommits.
|
||||
let prevote_targets = catch_up.prevotes
|
||||
let prevote_targets = catch_up
|
||||
.prevotes
|
||||
.iter()
|
||||
.map(|s| (s.prevote.target_number, s.prevote.target_hash));
|
||||
|
||||
let precommit_targets = catch_up.precommits
|
||||
let precommit_targets = catch_up
|
||||
.precommits
|
||||
.iter()
|
||||
.map(|s| (s.precommit.target_number, s.precommit.target_hash));
|
||||
|
||||
@@ -490,29 +489,39 @@ impl<Block: BlockT> BlockUntilImported<Block> for BlockGlobalMessage<Block> {
|
||||
|
||||
for (target_number, target_hash) in targets {
|
||||
if !query_known(target_hash, target_number)? {
|
||||
return Ok(DiscardWaitOrReady::Discard);
|
||||
return Ok(DiscardWaitOrReady::Discard)
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let unknown_hashes = checked_hashes.into_iter().filter_map(|(hash, num)| match num {
|
||||
KnownOrUnknown::Unknown(number) => Some((hash, number)),
|
||||
KnownOrUnknown::Known(_) => None,
|
||||
}).collect::<Vec<_>>();
|
||||
let unknown_hashes = checked_hashes
|
||||
.into_iter()
|
||||
.filter_map(|(hash, num)| match num {
|
||||
KnownOrUnknown::Unknown(number) => Some((hash, number)),
|
||||
KnownOrUnknown::Known(_) => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if unknown_hashes.is_empty() {
|
||||
// none of the hashes in the global message were unknown.
|
||||
// we can just return the message directly.
|
||||
return Ok(DiscardWaitOrReady::Ready(input));
|
||||
return Ok(DiscardWaitOrReady::Ready(input))
|
||||
}
|
||||
|
||||
let locked_global = Arc::new(Mutex::new(Some(input)));
|
||||
|
||||
let items_to_await = unknown_hashes.into_iter().map(|(hash, target_number)| {
|
||||
(hash, target_number, BlockGlobalMessage { inner: locked_global.clone(), target_number })
|
||||
}).collect();
|
||||
let items_to_await = unknown_hashes
|
||||
.into_iter()
|
||||
.map(|(hash, target_number)| {
|
||||
(
|
||||
hash,
|
||||
target_number,
|
||||
BlockGlobalMessage { inner: locked_global.clone(), target_number },
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// schedule waits for all unknown messages.
|
||||
// when the last one of these has `wait_completed` called on it,
|
||||
@@ -525,7 +534,7 @@ impl<Block: BlockT> BlockUntilImported<Block> for BlockGlobalMessage<Block> {
|
||||
// Delete the inner message so it won't ever be forwarded. Future calls to
|
||||
// `wait_completed` on the same `inner` will ignore it.
|
||||
*self.inner.lock() = None;
|
||||
return None;
|
||||
return None
|
||||
}
|
||||
|
||||
match Arc::try_unwrap(self.inner) {
|
||||
@@ -542,25 +551,20 @@ impl<Block: BlockT> BlockUntilImported<Block> for BlockGlobalMessage<Block> {
|
||||
|
||||
/// A stream which gates off incoming global messages, i.e. commit and catch up
|
||||
/// messages, until all referenced block hashes have been imported.
|
||||
pub(crate) type UntilGlobalMessageBlocksImported<Block, BlockStatus, BlockSyncRequester, I> = UntilImported<
|
||||
Block,
|
||||
BlockStatus,
|
||||
BlockSyncRequester,
|
||||
I,
|
||||
BlockGlobalMessage<Block>,
|
||||
>;
|
||||
pub(crate) type UntilGlobalMessageBlocksImported<Block, BlockStatus, BlockSyncRequester, I> =
|
||||
UntilImported<Block, BlockStatus, BlockSyncRequester, I, BlockGlobalMessage<Block>>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{CatchUp, CompactCommit};
|
||||
use substrate_test_runtime_client::runtime::{Block, Hash, Header};
|
||||
use sp_consensus::BlockOrigin;
|
||||
use sc_client_api::BlockImportNotification;
|
||||
use finality_grandpa::Precommit;
|
||||
use futures::future::Either;
|
||||
use futures_timer::Delay;
|
||||
use sc_client_api::BlockImportNotification;
|
||||
use sp_consensus::BlockOrigin;
|
||||
use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
|
||||
use finality_grandpa::Precommit;
|
||||
use substrate_test_runtime_client::runtime::{Block, Hash, Header};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TestChainState {
|
||||
@@ -571,10 +575,8 @@ mod tests {
|
||||
impl TestChainState {
|
||||
fn new() -> (Self, ImportNotifications<Block>) {
|
||||
let (tx, rx) = tracing_unbounded("test");
|
||||
let state = TestChainState {
|
||||
sender: tx,
|
||||
known_blocks: Arc::new(Mutex::new(HashMap::new())),
|
||||
};
|
||||
let state =
|
||||
TestChainState { sender: tx, known_blocks: Arc::new(Mutex::new(HashMap::new())) };
|
||||
|
||||
(state, rx)
|
||||
}
|
||||
@@ -588,13 +590,15 @@ mod tests {
|
||||
let number = header.number().clone();
|
||||
|
||||
self.known_blocks.lock().insert(hash, number);
|
||||
self.sender.unbounded_send(BlockImportNotification {
|
||||
hash,
|
||||
origin: BlockOrigin::File,
|
||||
header,
|
||||
is_new_best: false,
|
||||
tree_route: None,
|
||||
}).unwrap();
|
||||
self.sender
|
||||
.unbounded_send(BlockImportNotification {
|
||||
hash,
|
||||
origin: BlockOrigin::File,
|
||||
header,
|
||||
is_new_best: false,
|
||||
tree_route: None,
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,14 +619,17 @@ mod tests {
|
||||
|
||||
impl Default for TestBlockSyncRequester {
|
||||
fn default() -> Self {
|
||||
TestBlockSyncRequester {
|
||||
requests: Arc::new(Mutex::new(Vec::new())),
|
||||
}
|
||||
TestBlockSyncRequester { requests: Arc::new(Mutex::new(Vec::new())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockSyncRequesterT<Block> for TestBlockSyncRequester {
|
||||
fn set_sync_fork_request(&self, _peers: Vec<sc_network::PeerId>, hash: Hash, number: NumberFor<Block>) {
|
||||
fn set_sync_fork_request(
|
||||
&self,
|
||||
_peers: Vec<sc_network::PeerId>,
|
||||
hash: Hash,
|
||||
number: NumberFor<Block>,
|
||||
) {
|
||||
self.requests.lock().push((hash, number));
|
||||
}
|
||||
}
|
||||
@@ -639,7 +646,7 @@ mod tests {
|
||||
|
||||
// unwrap the commit from `CommunicationIn` returning its fields in a tuple,
|
||||
// panics if the given message isn't a commit
|
||||
fn unapply_commit(msg: CommunicationIn<Block>) -> (u64, CompactCommit::<Block>) {
|
||||
fn unapply_commit(msg: CommunicationIn<Block>) -> (u64, CompactCommit<Block>) {
|
||||
match msg {
|
||||
voter::CommunicationIn::Commit(round, commit, ..) => (round, commit),
|
||||
_ => panic!("expected commit"),
|
||||
@@ -658,7 +665,8 @@ mod tests {
|
||||
fn message_all_dependencies_satisfied<F>(
|
||||
msg: CommunicationIn<Block>,
|
||||
enact_dependencies: F,
|
||||
) -> CommunicationIn<Block> where
|
||||
) -> CommunicationIn<Block>
|
||||
where
|
||||
F: FnOnce(&TestChainState),
|
||||
{
|
||||
let (chain_state, import_notifications) = TestChainState::new();
|
||||
@@ -688,7 +696,8 @@ mod tests {
|
||||
fn blocking_message_on_dependencies<F>(
|
||||
msg: CommunicationIn<Block>,
|
||||
enact_dependencies: F,
|
||||
) -> CommunicationIn<Block> where
|
||||
) -> CommunicationIn<Block>
|
||||
where
|
||||
F: FnOnce(&TestChainState),
|
||||
{
|
||||
let (chain_state, import_notifications) = TestChainState::new();
|
||||
@@ -710,16 +719,17 @@ mod tests {
|
||||
// NOTE: needs to be cloned otherwise it is moved to the stream and
|
||||
// dropped too early.
|
||||
let inner_chain_state = chain_state.clone();
|
||||
let work = future::select(until_imported.into_future(), Delay::new(Duration::from_millis(100)))
|
||||
.then(move |res| match res {
|
||||
Either::Left(_) => panic!("timeout should have fired first"),
|
||||
Either::Right((_, until_imported)) => {
|
||||
// timeout fired. push in the headers.
|
||||
enact_dependencies(&inner_chain_state);
|
||||
let work =
|
||||
future::select(until_imported.into_future(), Delay::new(Duration::from_millis(100)))
|
||||
.then(move |res| match res {
|
||||
Either::Left(_) => panic!("timeout should have fired first"),
|
||||
Either::Right((_, until_imported)) => {
|
||||
// timeout fired. push in the headers.
|
||||
enact_dependencies(&inner_chain_state);
|
||||
|
||||
until_imported
|
||||
}
|
||||
});
|
||||
until_imported
|
||||
},
|
||||
});
|
||||
|
||||
futures::executor::block_on(work).0.unwrap().unwrap()
|
||||
}
|
||||
@@ -734,37 +744,22 @@ mod tests {
|
||||
target_hash: h1.hash(),
|
||||
target_number: 5,
|
||||
precommits: vec![
|
||||
Precommit {
|
||||
target_hash: h2.hash(),
|
||||
target_number: 6,
|
||||
},
|
||||
Precommit {
|
||||
target_hash: h3.hash(),
|
||||
target_number: 7,
|
||||
},
|
||||
Precommit { target_hash: h2.hash(), target_number: 6 },
|
||||
Precommit { target_hash: h3.hash(), target_number: 7 },
|
||||
],
|
||||
auth_data: Vec::new(), // not used
|
||||
};
|
||||
|
||||
let unknown_commit = || voter::CommunicationIn::Commit(
|
||||
0,
|
||||
unknown_commit.clone(),
|
||||
voter::Callback::Blank,
|
||||
);
|
||||
let unknown_commit =
|
||||
|| voter::CommunicationIn::Commit(0, unknown_commit.clone(), voter::Callback::Blank);
|
||||
|
||||
let res = blocking_message_on_dependencies(
|
||||
unknown_commit(),
|
||||
|chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
},
|
||||
);
|
||||
let res = blocking_message_on_dependencies(unknown_commit(), |chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
unapply_commit(res),
|
||||
unapply_commit(unknown_commit()),
|
||||
);
|
||||
assert_eq!(unapply_commit(res), unapply_commit(unknown_commit()),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -777,37 +772,22 @@ mod tests {
|
||||
target_hash: h1.hash(),
|
||||
target_number: 5,
|
||||
precommits: vec![
|
||||
Precommit {
|
||||
target_hash: h2.hash(),
|
||||
target_number: 6,
|
||||
},
|
||||
Precommit {
|
||||
target_hash: h3.hash(),
|
||||
target_number: 7,
|
||||
},
|
||||
Precommit { target_hash: h2.hash(), target_number: 6 },
|
||||
Precommit { target_hash: h3.hash(), target_number: 7 },
|
||||
],
|
||||
auth_data: Vec::new(), // not used
|
||||
};
|
||||
|
||||
let known_commit = || voter::CommunicationIn::Commit(
|
||||
0,
|
||||
known_commit.clone(),
|
||||
voter::Callback::Blank,
|
||||
);
|
||||
let known_commit =
|
||||
|| voter::CommunicationIn::Commit(0, known_commit.clone(), voter::Callback::Blank);
|
||||
|
||||
let res = message_all_dependencies_satisfied(
|
||||
known_commit(),
|
||||
|chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
},
|
||||
);
|
||||
let res = message_all_dependencies_satisfied(known_commit(), |chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
unapply_commit(res),
|
||||
unapply_commit(known_commit()),
|
||||
);
|
||||
assert_eq!(unapply_commit(res), unapply_commit(known_commit()),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -816,37 +796,27 @@ mod tests {
|
||||
let h2 = make_header(6);
|
||||
let h3 = make_header(7);
|
||||
|
||||
let signed_prevote = |header: &Header| {
|
||||
finality_grandpa::SignedPrevote {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
prevote: finality_grandpa::Prevote {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
}
|
||||
let signed_prevote = |header: &Header| finality_grandpa::SignedPrevote {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
prevote: finality_grandpa::Prevote {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
};
|
||||
|
||||
let signed_precommit = |header: &Header| {
|
||||
finality_grandpa::SignedPrecommit {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
precommit: finality_grandpa::Precommit {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
}
|
||||
let signed_precommit = |header: &Header| finality_grandpa::SignedPrecommit {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
precommit: finality_grandpa::Precommit {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
};
|
||||
|
||||
let prevotes = vec![
|
||||
signed_prevote(&h1),
|
||||
signed_prevote(&h3),
|
||||
];
|
||||
let prevotes = vec![signed_prevote(&h1), signed_prevote(&h3)];
|
||||
|
||||
let precommits = vec![
|
||||
signed_precommit(&h1),
|
||||
signed_precommit(&h2),
|
||||
];
|
||||
let precommits = vec![signed_precommit(&h1), signed_precommit(&h2)];
|
||||
|
||||
let unknown_catch_up = finality_grandpa::CatchUp {
|
||||
round_number: 1,
|
||||
@@ -856,24 +826,16 @@ mod tests {
|
||||
base_number: *h1.number(),
|
||||
};
|
||||
|
||||
let unknown_catch_up = || voter::CommunicationIn::CatchUp(
|
||||
unknown_catch_up.clone(),
|
||||
voter::Callback::Blank,
|
||||
);
|
||||
let unknown_catch_up =
|
||||
|| voter::CommunicationIn::CatchUp(unknown_catch_up.clone(), voter::Callback::Blank);
|
||||
|
||||
let res = blocking_message_on_dependencies(
|
||||
unknown_catch_up(),
|
||||
|chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
},
|
||||
);
|
||||
let res = blocking_message_on_dependencies(unknown_catch_up(), |chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
unapply_catch_up(res),
|
||||
unapply_catch_up(unknown_catch_up()),
|
||||
);
|
||||
assert_eq!(unapply_catch_up(res), unapply_catch_up(unknown_catch_up()),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -882,37 +844,27 @@ mod tests {
|
||||
let h2 = make_header(6);
|
||||
let h3 = make_header(7);
|
||||
|
||||
let signed_prevote = |header: &Header| {
|
||||
finality_grandpa::SignedPrevote {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
prevote: finality_grandpa::Prevote {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
}
|
||||
let signed_prevote = |header: &Header| finality_grandpa::SignedPrevote {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
prevote: finality_grandpa::Prevote {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
};
|
||||
|
||||
let signed_precommit = |header: &Header| {
|
||||
finality_grandpa::SignedPrecommit {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
precommit: finality_grandpa::Precommit {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
}
|
||||
let signed_precommit = |header: &Header| finality_grandpa::SignedPrecommit {
|
||||
id: Default::default(),
|
||||
signature: Default::default(),
|
||||
precommit: finality_grandpa::Precommit {
|
||||
target_hash: header.hash(),
|
||||
target_number: *header.number(),
|
||||
},
|
||||
};
|
||||
|
||||
let prevotes = vec![
|
||||
signed_prevote(&h1),
|
||||
signed_prevote(&h3),
|
||||
];
|
||||
let prevotes = vec![signed_prevote(&h1), signed_prevote(&h3)];
|
||||
|
||||
let precommits = vec![
|
||||
signed_precommit(&h1),
|
||||
signed_precommit(&h2),
|
||||
];
|
||||
let precommits = vec![signed_precommit(&h1), signed_precommit(&h2)];
|
||||
|
||||
let unknown_catch_up = finality_grandpa::CatchUp {
|
||||
round_number: 1,
|
||||
@@ -922,24 +874,16 @@ mod tests {
|
||||
base_number: *h1.number(),
|
||||
};
|
||||
|
||||
let unknown_catch_up = || voter::CommunicationIn::CatchUp(
|
||||
unknown_catch_up.clone(),
|
||||
voter::Callback::Blank,
|
||||
);
|
||||
let unknown_catch_up =
|
||||
|| voter::CommunicationIn::CatchUp(unknown_catch_up.clone(), voter::Callback::Blank);
|
||||
|
||||
let res = message_all_dependencies_satisfied(
|
||||
unknown_catch_up(),
|
||||
|chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
},
|
||||
);
|
||||
let res = message_all_dependencies_satisfied(unknown_catch_up(), |chain_state| {
|
||||
chain_state.import_header(h1);
|
||||
chain_state.import_header(h2);
|
||||
chain_state.import_header(h3);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
unapply_catch_up(res),
|
||||
unapply_catch_up(unknown_catch_up()),
|
||||
);
|
||||
assert_eq!(unapply_catch_up(res), unapply_catch_up(unknown_catch_up()),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -970,23 +914,14 @@ mod tests {
|
||||
target_hash: h1.hash(),
|
||||
target_number: 5,
|
||||
precommits: vec![
|
||||
Precommit {
|
||||
target_hash: h2.hash(),
|
||||
target_number: 6,
|
||||
},
|
||||
Precommit {
|
||||
target_hash: h3.hash(),
|
||||
target_number: 7,
|
||||
},
|
||||
Precommit { target_hash: h2.hash(), target_number: 6 },
|
||||
Precommit { target_hash: h3.hash(), target_number: 7 },
|
||||
],
|
||||
auth_data: Vec::new(), // not used
|
||||
};
|
||||
|
||||
let unknown_commit = || voter::CommunicationIn::Commit(
|
||||
0,
|
||||
unknown_commit.clone(),
|
||||
voter::Callback::Blank,
|
||||
);
|
||||
let unknown_commit =
|
||||
|| voter::CommunicationIn::Commit(0, unknown_commit.clone(), voter::Callback::Blank);
|
||||
|
||||
// we send the commit message and spawn the until_imported stream
|
||||
global_tx.unbounded_send(unknown_commit()).unwrap();
|
||||
@@ -1002,7 +937,7 @@ mod tests {
|
||||
if block_sync_requests.contains(&(h2.hash(), *h2.number())) &&
|
||||
block_sync_requests.contains(&(h3.hash(), *h3.number()))
|
||||
{
|
||||
return Poll::Ready(());
|
||||
return Poll::Ready(())
|
||||
}
|
||||
|
||||
// NOTE: nothing in this function is future-aware (i.e nothing gets registered to wake
|
||||
@@ -1016,10 +951,12 @@ mod tests {
|
||||
// the `until_imported` stream doesn't request the blocks immediately,
|
||||
// but it should request them after a small timeout
|
||||
let timeout = Delay::new(Duration::from_secs(60));
|
||||
let test = future::select(assert, timeout).map(|res| match res {
|
||||
Either::Left(_) => {},
|
||||
Either::Right(_) => panic!("timed out waiting for block sync request"),
|
||||
}).map(drop);
|
||||
let test = future::select(assert, timeout)
|
||||
.map(|res| match res {
|
||||
Either::Left(_) => {},
|
||||
Either::Right(_) => panic!("timed out waiting for block sync request"),
|
||||
})
|
||||
.map(drop);
|
||||
|
||||
futures::executor::block_on(test);
|
||||
}
|
||||
@@ -1035,10 +972,8 @@ mod tests {
|
||||
base_number: *header.number(),
|
||||
};
|
||||
|
||||
let catch_up = voter::CommunicationIn::CatchUp(
|
||||
unknown_catch_up.clone(),
|
||||
voter::Callback::Blank,
|
||||
);
|
||||
let catch_up =
|
||||
voter::CommunicationIn::CatchUp(unknown_catch_up.clone(), voter::Callback::Blank);
|
||||
|
||||
Arc::new(Mutex::new(Some(catch_up)))
|
||||
}
|
||||
@@ -1047,15 +982,10 @@ mod tests {
|
||||
fn block_global_message_wait_completed_return_when_all_awaited() {
|
||||
let msg_inner = test_catch_up();
|
||||
|
||||
let waiting_block_1 = BlockGlobalMessage::<Block> {
|
||||
inner: msg_inner.clone(),
|
||||
target_number: 1,
|
||||
};
|
||||
let waiting_block_1 =
|
||||
BlockGlobalMessage::<Block> { inner: msg_inner.clone(), target_number: 1 };
|
||||
|
||||
let waiting_block_2 = BlockGlobalMessage::<Block> {
|
||||
inner: msg_inner,
|
||||
target_number: 2,
|
||||
};
|
||||
let waiting_block_2 = BlockGlobalMessage::<Block> { inner: msg_inner, target_number: 2 };
|
||||
|
||||
// waiting_block_2 is still waiting for block 2, thus this should return `None`.
|
||||
assert!(waiting_block_1.wait_completed(1).is_none());
|
||||
@@ -1069,15 +999,10 @@ mod tests {
|
||||
fn block_global_message_wait_completed_return_none_on_block_number_missmatch() {
|
||||
let msg_inner = test_catch_up();
|
||||
|
||||
let waiting_block_1 = BlockGlobalMessage::<Block> {
|
||||
inner: msg_inner.clone(),
|
||||
target_number: 1,
|
||||
};
|
||||
let waiting_block_1 =
|
||||
BlockGlobalMessage::<Block> { inner: msg_inner.clone(), target_number: 1 };
|
||||
|
||||
let waiting_block_2 = BlockGlobalMessage::<Block> {
|
||||
inner: msg_inner,
|
||||
target_number: 2,
|
||||
};
|
||||
let waiting_block_2 = BlockGlobalMessage::<Block> { inner: msg_inner, target_number: 2 };
|
||||
|
||||
// Calling wait_completed with wrong block number should yield None.
|
||||
assert!(waiting_block_1.wait_completed(1234).is_none());
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
//! restrictions that are taken into account by the GRANDPA environment when
|
||||
//! selecting a finality target to vote on.
|
||||
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
use std::pin::Pin;
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
|
||||
use sc_client_api::blockchain::HeaderBackend;
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_runtime::traits::{Block as BlockT, Header, NumberFor, One, Zero};
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Header, NumberFor, One, Zero},
|
||||
};
|
||||
|
||||
/// A future returned by a `VotingRule` to restrict a given vote, if any restriction is necessary.
|
||||
pub type VotingRuleResult<Block> =
|
||||
@@ -63,7 +63,8 @@ where
|
||||
) -> VotingRuleResult<Block>;
|
||||
}
|
||||
|
||||
impl<Block, B> VotingRule<Block, B> for () where
|
||||
impl<Block, B> VotingRule<Block, B> for ()
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block>,
|
||||
{
|
||||
@@ -83,7 +84,8 @@ impl<Block, B> VotingRule<Block, B> for () where
|
||||
/// behind the best block.
|
||||
#[derive(Clone)]
|
||||
pub struct BeforeBestBlockBy<N>(N);
|
||||
impl<Block, B> VotingRule<Block, B> for BeforeBestBlockBy<NumberFor<Block>> where
|
||||
impl<Block, B> VotingRule<Block, B> for BeforeBestBlockBy<NumberFor<Block>>
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block>,
|
||||
{
|
||||
@@ -97,7 +99,7 @@ impl<Block, B> VotingRule<Block, B> for BeforeBestBlockBy<NumberFor<Block>> wher
|
||||
use sp_arithmetic::traits::Saturating;
|
||||
|
||||
if current_target.number().is_zero() {
|
||||
return Box::pin(async { None });
|
||||
return Box::pin(async { None })
|
||||
}
|
||||
|
||||
// find the target number restricted by this rule
|
||||
@@ -105,17 +107,13 @@ impl<Block, B> VotingRule<Block, B> for BeforeBestBlockBy<NumberFor<Block>> wher
|
||||
|
||||
// our current target is already lower than this rule would restrict
|
||||
if target_number >= *current_target.number() {
|
||||
return Box::pin(async { None });
|
||||
return Box::pin(async { None })
|
||||
}
|
||||
|
||||
let current_target = current_target.clone();
|
||||
|
||||
// find the block at the given target height
|
||||
Box::pin(std::future::ready(find_target(
|
||||
&*backend,
|
||||
target_number.clone(),
|
||||
¤t_target,
|
||||
)))
|
||||
Box::pin(std::future::ready(find_target(&*backend, target_number.clone(), ¤t_target)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +123,8 @@ impl<Block, B> VotingRule<Block, B> for BeforeBestBlockBy<NumberFor<Block>> wher
|
||||
#[derive(Clone)]
|
||||
pub struct ThreeQuartersOfTheUnfinalizedChain;
|
||||
|
||||
impl<Block, B> VotingRule<Block, B> for ThreeQuartersOfTheUnfinalizedChain where
|
||||
impl<Block, B> VotingRule<Block, B> for ThreeQuartersOfTheUnfinalizedChain
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block>,
|
||||
{
|
||||
@@ -150,15 +149,11 @@ impl<Block, B> VotingRule<Block, B> for ThreeQuartersOfTheUnfinalizedChain where
|
||||
|
||||
// our current target is already lower than this rule would restrict
|
||||
if target_number >= *current_target.number() {
|
||||
return Box::pin(async { None });
|
||||
return Box::pin(async { None })
|
||||
}
|
||||
|
||||
// find the block at the given target height
|
||||
Box::pin(std::future::ready(find_target(
|
||||
&*backend,
|
||||
target_number,
|
||||
current_target,
|
||||
)))
|
||||
Box::pin(std::future::ready(find_target(&*backend, target_number, current_target)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +162,8 @@ fn find_target<Block, B>(
|
||||
backend: &B,
|
||||
target_number: NumberFor<Block>,
|
||||
current_header: &Block::Header,
|
||||
) -> Option<(Block::Hash, NumberFor<Block>)> where
|
||||
) -> Option<(Block::Hash, NumberFor<Block>)>
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block>,
|
||||
{
|
||||
@@ -184,11 +180,13 @@ fn find_target<Block, B>(
|
||||
}
|
||||
|
||||
if *target_header.number() == target_number {
|
||||
return Some((target_hash, target_number));
|
||||
return Some((target_hash, target_number))
|
||||
}
|
||||
|
||||
target_hash = *target_header.parent_hash();
|
||||
target_header = backend.header(BlockId::Hash(target_hash)).ok()?
|
||||
target_header = backend
|
||||
.header(BlockId::Hash(target_hash))
|
||||
.ok()?
|
||||
.expect("Header known to exist due to the existence of one of its descendents; qed");
|
||||
}
|
||||
}
|
||||
@@ -199,13 +197,12 @@ struct VotingRules<Block, B> {
|
||||
|
||||
impl<B, Block> Clone for VotingRules<B, Block> {
|
||||
fn clone(&self) -> Self {
|
||||
VotingRules {
|
||||
rules: self.rules.clone(),
|
||||
}
|
||||
VotingRules { rules: self.rules.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, B> VotingRule<Block, B> for VotingRules<Block, B> where
|
||||
impl<Block, B> VotingRule<Block, B> for VotingRules<Block, B>
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block> + 'static,
|
||||
{
|
||||
@@ -230,8 +227,8 @@ impl<Block, B> VotingRule<Block, B> for VotingRules<Block, B> where
|
||||
.await
|
||||
.filter(|(_, restricted_number)| {
|
||||
// NOTE: we can only restrict votes within the interval [base, target)
|
||||
restricted_number >= base.number()
|
||||
&& restricted_number < restricted_target.number()
|
||||
restricted_number >= base.number() &&
|
||||
restricted_number < restricted_target.number()
|
||||
})
|
||||
.and_then(|(hash, _)| backend.header(BlockId::Hash(hash)).ok())
|
||||
.and_then(std::convert::identity)
|
||||
@@ -257,7 +254,8 @@ pub struct VotingRulesBuilder<Block, B> {
|
||||
rules: Vec<Box<dyn VotingRule<Block, B>>>,
|
||||
}
|
||||
|
||||
impl<Block, B> Default for VotingRulesBuilder<Block, B> where
|
||||
impl<Block, B> Default for VotingRulesBuilder<Block, B>
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block> + 'static,
|
||||
{
|
||||
@@ -268,19 +266,19 @@ impl<Block, B> Default for VotingRulesBuilder<Block, B> where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, B> VotingRulesBuilder<Block, B> where
|
||||
impl<Block, B> VotingRulesBuilder<Block, B>
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block> + 'static,
|
||||
{
|
||||
/// Return a new voting rule builder using the given backend.
|
||||
pub fn new() -> Self {
|
||||
VotingRulesBuilder {
|
||||
rules: Vec::new(),
|
||||
}
|
||||
VotingRulesBuilder { rules: Vec::new() }
|
||||
}
|
||||
|
||||
/// Add a new voting rule to the builder.
|
||||
pub fn add<R>(mut self, rule: R) -> Self where
|
||||
pub fn add<R>(mut self, rule: R) -> Self
|
||||
where
|
||||
R: VotingRule<Block, B> + 'static,
|
||||
{
|
||||
self.rules.push(Box::new(rule));
|
||||
@@ -288,8 +286,9 @@ impl<Block, B> VotingRulesBuilder<Block, B> where
|
||||
}
|
||||
|
||||
/// Add all given voting rules to the builder.
|
||||
pub fn add_all<I>(mut self, rules: I) -> Self where
|
||||
I: IntoIterator<Item=Box<dyn VotingRule<Block, B>>>,
|
||||
pub fn add_all<I>(mut self, rules: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = Box<dyn VotingRule<Block, B>>>,
|
||||
{
|
||||
self.rules.extend(rules);
|
||||
self
|
||||
@@ -298,13 +297,12 @@ impl<Block, B> VotingRulesBuilder<Block, B> where
|
||||
/// Return a new `VotingRule` that applies all of the previously added
|
||||
/// voting rules in-order.
|
||||
pub fn build(self) -> impl VotingRule<Block, B> + Clone {
|
||||
VotingRules {
|
||||
rules: Arc::new(self.rules),
|
||||
}
|
||||
VotingRules { rules: Arc::new(self.rules) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block, B> VotingRule<Block, B> for Box<dyn VotingRule<Block, B>> where
|
||||
impl<Block, B> VotingRule<Block, B> for Box<dyn VotingRule<Block, B>>
|
||||
where
|
||||
Block: BlockT,
|
||||
B: HeaderBackend<Block>,
|
||||
Self: Clone,
|
||||
@@ -358,33 +356,19 @@ mod tests {
|
||||
fn multiple_voting_rules_cannot_restrict_past_base() {
|
||||
// setup an aggregate voting rule composed of two voting rules
|
||||
// where each subtracts 50 blocks from the current target
|
||||
let rule = VotingRulesBuilder::new()
|
||||
.add(Subtract(50))
|
||||
.add(Subtract(50))
|
||||
.build();
|
||||
let rule = VotingRulesBuilder::new().add(Subtract(50)).add(Subtract(50)).build();
|
||||
|
||||
let mut client = Arc::new(TestClientBuilder::new().build());
|
||||
|
||||
for _ in 0..200 {
|
||||
let block = client
|
||||
.new_block(Default::default())
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block;
|
||||
let block = client.new_block(Default::default()).unwrap().build().unwrap().block;
|
||||
|
||||
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
||||
}
|
||||
|
||||
let genesis = client
|
||||
.header(&BlockId::Number(0u32.into()))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let genesis = client.header(&BlockId::Number(0u32.into())).unwrap().unwrap();
|
||||
|
||||
let best = client
|
||||
.header(&BlockId::Hash(client.info().best_hash))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let best = client.header(&BlockId::Hash(client.info().best_hash)).unwrap().unwrap();
|
||||
|
||||
let (_, number) =
|
||||
futures::executor::block_on(rule.restrict_vote(client.clone(), &genesis, &best, &best))
|
||||
@@ -394,10 +378,7 @@ mod tests {
|
||||
// which means that we should be voting for block #100
|
||||
assert_eq!(number, 100);
|
||||
|
||||
let block110 = client
|
||||
.header(&BlockId::Number(110u32.into()))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let block110 = client.header(&BlockId::Number(110u32.into())).unwrap().unwrap();
|
||||
|
||||
let (_, number) = futures::executor::block_on(rule.restrict_vote(
|
||||
client.clone(),
|
||||
|
||||
Reference in New Issue
Block a user