diff --git a/substrate/core/finality-grandpa/src/aux_schema.rs b/substrate/core/finality-grandpa/src/aux_schema.rs index 4dd6e75dd9..824c65b9e6 100644 --- a/substrate/core/finality-grandpa/src/aux_schema.rs +++ b/substrate/core/finality-grandpa/src/aux_schema.rs @@ -151,16 +151,22 @@ fn migrate_from_version0( None => (0, genesis_round()), }; + let set_id = new_set.current().0; + let base = last_round_state.prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); let set_state = VoterSetState::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: last_round_number, - state: last_round_state, - votes: Vec::new(), - base, - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: last_round_number, + state: last_round_state, + votes: Vec::new(), + base, + }, + set_id, + &new_set, + ), current_round: HasVoted::No, }; @@ -189,6 +195,19 @@ fn migrate_from_version1( backend, AUTHORITY_SET_KEY, )? { + let set_id = set.current().0; + + 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>>( backend, SET_STATE_KEY, @@ -198,12 +217,7 @@ fn migrate_from_version1( .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::Paused { - completed_rounds: CompletedRounds::new(CompletedRound { - number: last_round_number, - state: set_state, - votes: Vec::new(), - base, - }), + completed_rounds: completed_rounds(last_round_number, set_state, base), } }, Some(V1VoterSetState::Live(last_round_number, set_state)) => { @@ -211,12 +225,7 @@ fn migrate_from_version1( .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: last_round_number, - state: set_state, - votes: Vec::new(), - base, - }), + completed_rounds: completed_rounds(last_round_number, set_state, base), current_round: HasVoted::No, } }, @@ -226,12 +235,7 @@ fn migrate_from_version1( .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: 0, - state: set_state, - votes: Vec::new(), - base, - }), + completed_rounds: completed_rounds(0, set_state, base), current_round: HasVoted::No, } }, @@ -298,12 +302,16 @@ pub(crate) fn load_persistent( .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); VoterSetState::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: 0, - votes: Vec::new(), - base, - state, - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: 0, + votes: Vec::new(), + base, + state, + }, + set.current().0, + &set, + ), current_round: HasVoted::No, } } @@ -325,18 +333,23 @@ pub(crate) fn load_persistent( info!(target: "afg", "Loading GRANDPA authority set \ from genesis on what appears to be first startup."); - let genesis_set = AuthoritySet::genesis(genesis_authorities()?); + let genesis_authorities = genesis_authorities()?; + let genesis_set = AuthoritySet::genesis(genesis_authorities.clone()); let state = make_genesis_round(); let base = state.prevote_ghost .expect("state is for completed round; completed rounds must have a prevote ghost; qed."); let genesis_state = VoterSetState::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: 0, - votes: Vec::new(), - state, - base, - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: 0, + votes: Vec::new(), + state, + base, + }, + 0, + &genesis_set, + ), current_round: HasVoted::No, }; backend.insert_aux( @@ -355,6 +368,10 @@ pub(crate) fn load_persistent( } /// Update the authority set on disk after a change. +/// +/// If there has just been a handoff, pass a `new_set` parameter that describes the +/// handoff. `set` in all cases should reflect the current authority set, with all +/// changes and handoffs applied. pub(crate) fn update_authority_set( set: &AuthoritySet>, new_set: Option<&NewAuthoritySet>>, @@ -385,12 +402,16 @@ pub(crate) fn update_authority_set( new_set.canon_number.clone(), )); let set_state = VoterSetState::::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: 0, - state: round_state, - votes: Vec::new(), - base: (new_set.canon_hash, new_set.canon_number), - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: 0, + state: round_state, + votes: Vec::new(), + base: (new_set.canon_hash, new_set.canon_number), + }, + new_set.set_id, + &set, + ), current_round: HasVoted::No, }; let encoded = set_state.encode(); @@ -500,7 +521,7 @@ mod test { assert_eq!( *authority_set.inner().read(), AuthoritySet { - current_authorities: authorities, + current_authorities: authorities.clone(), pending_standard_changes: ForkTree::new(), pending_forced_changes: Vec::new(), set_id, @@ -510,12 +531,16 @@ mod test { assert_eq!( &*set_state.read(), &VoterSetState::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: round_number, - state: round_state.clone(), - base: round_state.prevote_ghost.unwrap(), - votes: vec![], - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: round_number, + state: round_state.clone(), + base: round_state.prevote_ghost.unwrap(), + votes: vec![], + }, + set_id, + &*authority_set.inner().read(), + ), current_round: HasVoted::No, }, ); @@ -583,7 +608,7 @@ mod test { assert_eq!( *authority_set.inner().read(), AuthoritySet { - current_authorities: authorities, + current_authorities: authorities.clone(), pending_standard_changes: ForkTree::new(), pending_forced_changes: Vec::new(), set_id, @@ -593,12 +618,16 @@ mod test { assert_eq!( &*set_state.read(), &VoterSetState::Live { - completed_rounds: CompletedRounds::new(CompletedRound { - number: round_number, - state: round_state.clone(), - base: round_state.prevote_ghost.unwrap(), - votes: vec![], - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: round_number, + state: round_state.clone(), + base: round_state.prevote_ghost.unwrap(), + votes: vec![], + }, + set_id, + &*authority_set.inner().read(), + ), current_round: HasVoted::No, }, ); diff --git a/substrate/core/finality-grandpa/src/communication/gossip.rs b/substrate/core/finality-grandpa/src/communication/gossip.rs index 460998e707..ca529125ec 100644 --- a/substrate/core/finality-grandpa/src/communication/gossip.rs +++ b/substrate/core/finality-grandpa/src/communication/gossip.rs @@ -71,6 +71,7 @@ use runtime_primitives::traits::{NumberFor, Block as BlockT, Zero}; use network::consensus_gossip::{self as network_gossip, MessageIntent, ValidatorContext}; use network::{config::Roles, PeerId}; use parity_codec::{Encode, Decode}; +use crate::ed25519::Public as AuthorityId; use substrate_telemetry::{telemetry, CONSENSUS_DEBUG}; use log::{trace, debug, warn}; @@ -94,6 +95,8 @@ enum Consider { RejectPast, /// Message is from the future. Reject. RejectFuture, + /// Message cannot be evaluated. Reject. + RejectOutOfScope, } /// A view of protocol state. @@ -300,6 +303,10 @@ pub(super) enum Misbehavior { // A message received that's from the future relative to our view. // always misbehavior. FutureMessage, + // A message received that cannot be evaluated relative to our view. + // This happens before we have a view and have sent out neighbor packets. + // always misbehavior. + OutOfScopeMessage, } impl Misbehavior { @@ -319,6 +326,7 @@ impl Misbehavior { (benefit as i32).saturating_add(cost as i32) }, FutureMessage => cost::FUTURE_MESSAGE, + OutOfScopeMessage => cost::OUT_OF_SCOPE_MESSAGE, } } } @@ -407,7 +415,7 @@ impl Peers { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub(super) enum Action { // repropagate under given topic, to the given peers, applying cost/benefit to originator. Keep(H, i32), @@ -418,9 +426,10 @@ pub(super) enum Action { } struct Inner { - local_view: View>, + local_view: Option>>, peers: Peers>, live_topics: KeepTopics, + authorities: Vec, config: crate::Config, next_rebroadcast: Instant, } @@ -430,58 +439,87 @@ type MaybeMessage = Option<(Vec, NeighborPacket> impl Inner { fn new(config: crate::Config) -> Self { Inner { - local_view: View::default(), + local_view: None, peers: Peers::default(), live_topics: KeepTopics::new(), next_rebroadcast: Instant::now() + REBROADCAST_AFTER, + authorities: Vec::new(), config, } } - /// Note a round in a set has started. - fn note_round(&mut self, round: Round, set_id: SetId) -> MaybeMessage { - if self.local_view.round == round && self.local_view.set_id == set_id { - return None; + /// Note a round in the current set has started. + fn note_round(&mut self, round: Round) -> MaybeMessage { + { + let local_view = match self.local_view { + None => return None, + Some(ref mut v) => if v.round == round { + return None + } else { + v + }, + }; + + let set_id = local_view.set_id; + + debug!(target: "afg", "Voter {} noting beginning of round {:?} to network.", + self.config.name(), (round,set_id)); + + local_view.round = round; + + self.live_topics.push(round, set_id); } - - debug!(target: "afg", "Voter {} noting beginning of round {:?} to network.", - self.config.name(), (round, set_id)); - - self.local_view.round = round; - self.local_view.set_id = set_id; - - self.live_topics.push(round, set_id); self.multicast_neighbor_packet() } /// Note that a voter set with given ID has started. Does nothing if the last /// call to the function was with the same `set_id`. - fn note_set(&mut self, set_id: SetId) -> MaybeMessage { - if self.local_view.set_id == set_id { - return None; - } + fn note_set(&mut self, set_id: SetId, authorities: Vec) -> MaybeMessage { + { + let local_view = match self.local_view { + ref mut x @ None => x.get_or_insert(View { + round: Round(0), + set_id, + last_commit: None, + }), + Some(ref mut v) => if v.set_id == set_id { + return None + } else { + v + }, + }; - self.local_view.update_set(set_id); - self.live_topics.push(Round(0), set_id); + local_view.update_set(set_id); + self.live_topics.push(Round(0), set_id); + self.authorities = authorities; + } self.multicast_neighbor_packet() } /// Note that we've imported a commit finalizing a given block. fn note_commit_finalized(&mut self, finalized: NumberFor) -> MaybeMessage { - if self.local_view.last_commit.as_ref() < Some(&finalized) { - self.local_view.last_commit = Some(finalized); - self.multicast_neighbor_packet() - } else { - None + { + match self.local_view { + None => return None, + Some(ref mut v) => if v.last_commit.as_ref() < Some(&finalized) { + v.last_commit = Some(finalized); + } else { + return None + }, + }; } + + self.multicast_neighbor_packet() } fn consider_vote(&self, round: Round, set_id: SetId) -> Consider { - self.local_view.consider_vote(round, set_id) + self.local_view.as_ref().map(|v| v.consider_vote(round, set_id)) + .unwrap_or(Consider::RejectOutOfScope) } fn consider_global(&self, set_id: SetId, number: NumberFor) -> Consider { - self.local_view.consider_global(set_id, number) + self.local_view.as_ref().map(|v| v.consider_global(set_id, number)) + .unwrap_or(Consider::RejectOutOfScope) } fn cost_past_rejection(&self, _who: &PeerId, _round: Round, _set_id: SetId) -> i32 { @@ -494,11 +532,18 @@ impl Inner { { match self.consider_vote(full.round, full.set_id) { Consider::RejectFuture => return Action::Discard(Misbehavior::FutureMessage.cost()), + Consider::RejectOutOfScope => return Action::Discard(Misbehavior::OutOfScopeMessage.cost()), Consider::RejectPast => return Action::Discard(self.cost_past_rejection(who, full.round, full.set_id)), Consider::Accept => {}, } + // ensure authority is part of the set. + if !self.authorities.contains(&full.message.id) { + telemetry!(CONSENSUS_DEBUG; "afg.bad_msg_signature"; "signature" => ?full.message.id); + return Action::Discard(cost::UNKNOWN_VOTER); + } + if let Err(()) = super::check_message_sig::( &full.message.message, &full.message.id, @@ -527,7 +572,9 @@ impl Inner { Consider::RejectFuture => return Action::Discard(Misbehavior::FutureMessage.cost()), Consider::RejectPast => return Action::Discard(self.cost_past_rejection(who, full.round, full.set_id)), + Consider::RejectOutOfScope => return Action::Discard(Misbehavior::OutOfScopeMessage.cost()), Consider::Accept => {}, + } if full.message.precommits.len() != full.message.auth_data.len() || full.message.precommits.is_empty() { @@ -561,14 +608,16 @@ impl Inner { } fn multicast_neighbor_packet(&self) -> MaybeMessage { - let packet = NeighborPacket { - round: self.local_view.round, - set_id: self.local_view.set_id, - commit_finalized_height: self.local_view.last_commit.unwrap_or(Zero::zero()), - }; + self.local_view.as_ref().map(|local_view| { + let packet = NeighborPacket { + round: local_view.round, + set_id: local_view.set_id, + commit_finalized_height: local_view.last_commit.unwrap_or(Zero::zero()), + }; - let peers = self.peers.inner.keys().cloned().collect(); - Some((peers, packet)) + let peers = self.peers.inner.keys().cloned().collect(); + (peers, packet) + }) } } @@ -579,7 +628,7 @@ pub(super) struct GossipValidator { } impl GossipValidator { - /// Create a new gossip-validator. + /// Create a new gossip-validator. This initialized the current set to 0. pub(super) fn new(config: crate::Config) -> (GossipValidator, ReportStream) { let (tx, rx) = mpsc::unbounded(); let val = GossipValidator { @@ -590,21 +639,22 @@ impl GossipValidator { (val, ReportStream { reports: rx }) } - /// Note a round in a set has started. - pub(super) fn note_round(&self, round: Round, set_id: SetId, send_neighbor: F) + /// Note a round in the current set has started. + pub(super) fn note_round(&self, round: Round, send_neighbor: F) where F: FnOnce(Vec, NeighborPacket>) { - let maybe_msg = self.inner.write().note_round(round, set_id); + let maybe_msg = self.inner.write().note_round(round); if let Some((to, msg)) = maybe_msg { send_neighbor(to, msg); } } - /// Note that a voter set with given ID has started. - pub(super) fn note_set(&self, set_id: SetId, send_neighbor: F) + /// Note that a voter set with given ID has started. Updates the current set to given + /// value and initializes the round to 0. + pub(super) fn note_set(&self, set_id: SetId, authorities: Vec, send_neighbor: F) where F: FnOnce(Vec, NeighborPacket>) { - let maybe_msg = self.inner.write().note_set(set_id); + let maybe_msg = self.inner.write().note_set(set_id, authorities); if let Some((to, msg)) = maybe_msg { send_neighbor(to, msg); } @@ -658,19 +708,23 @@ impl GossipValidator { impl network_gossip::Validator for GossipValidator { fn new_peer(&self, context: &mut dyn ValidatorContext, who: &PeerId, _roles: Roles) { - let packet_data = { + let packet = { let mut inner = self.inner.write(); inner.peers.new_peer(who.clone()); - let packet = NeighborPacket { - round: inner.local_view.round, - set_id: inner.local_view.set_id, - commit_finalized_height: inner.local_view.last_commit.unwrap_or(Zero::zero()), - }; - - GossipMessage::::from(packet).encode() + inner.local_view.as_ref().map(|v| { + NeighborPacket { + round: v.round, + set_id: v.set_id, + commit_finalized_height: v.last_commit.unwrap_or(Zero::zero()), + } + }) }; - context.send_message(who, packet_data); + + if let Some(packet) = packet { + let packet_data = GossipMessage::::from(packet).encode(); + context.send_message(who, packet_data); + } } fn peer_disconnected(&self, _context: &mut dyn ValidatorContext, who: &PeerId) { @@ -746,7 +800,12 @@ impl network_gossip::Validator for GossipValidator } // global message. - let our_best_commit = inner.local_view.last_commit; + let local_view = match inner.local_view { + Some(ref v) => v, + None => return false, // cannot evaluate until we have a local view. + }; + + let our_best_commit = local_view.last_commit; let peer_best_commit = peer.view.last_commit; match GossipMessage::::decode(&mut data) { @@ -774,8 +833,13 @@ impl network_gossip::Validator for GossipValidator Some((None, _)) => {}, }; + let local_view = match inner.local_view { + Some(ref v) => v, + None => return true, // no local view means we can't evaluate or hold any topic. + }; + // global messages -- only keep the best commit. - let best_commit = inner.local_view.last_commit; + let best_commit = local_view.last_commit; match GossipMessage::::decode(&mut data) { None => true, @@ -1004,8 +1068,10 @@ mod tests { let set_id = 1; + val.note_set(SetId(set_id), Vec::new(), |_, _| {}); + for round_num in 1u64..10 { - val.note_round(Round(round_num), SetId(set_id), |_, _| {}); + val.note_round(Round(round_num), |_, _| {}); } { @@ -1025,4 +1091,47 @@ mod tests { } } } + + #[test] + fn message_from_unknown_authority_discarded() { + assert!(cost::UNKNOWN_VOTER != cost::BAD_SIGNATURE); + + let (val, _) = GossipValidator::::new(config()); + let set_id = 1; + let auth = AuthorityId::from_raw([1u8; 32]); + let peer = PeerId::random(); + + val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); + val.note_round(Round(0), |_, _| {}); + + let inner = val.inner.read(); + let unknown_voter = inner.validate_round_message(&peer, &VoteOrPrecommitMessage { + round: Round(0), + set_id: SetId(set_id), + message: SignedMessage:: { + message: grandpa::Message::Prevote(grandpa::Prevote { + target_hash: Default::default(), + target_number: 10, + }), + signature: Default::default(), + id: AuthorityId::from_raw([2u8; 32]), + } + }); + + let bad_sig = inner.validate_round_message(&peer, &VoteOrPrecommitMessage { + round: Round(0), + set_id: SetId(set_id), + message: SignedMessage:: { + message: grandpa::Message::Prevote(grandpa::Prevote { + target_hash: Default::default(), + target_number: 10, + }), + signature: Default::default(), + id: auth.clone(), + } + }); + + assert_eq!(unknown_voter, Action::Discard(cost::UNKNOWN_VOTER)); + assert_eq!(bad_sig, Action::Discard(cost::BAD_SIGNATURE)); + } } diff --git a/substrate/core/finality-grandpa/src/communication/mod.rs b/substrate/core/finality-grandpa/src/communication/mod.rs index 05033c2c41..58d9cd6421 100644 --- a/substrate/core/finality-grandpa/src/communication/mod.rs +++ b/substrate/core/finality-grandpa/src/communication/mod.rs @@ -64,12 +64,14 @@ mod cost { pub(super) const BAD_SIGNATURE: i32 = -100; pub(super) const MALFORMED_COMMIT: i32 = -1000; pub(super) const FUTURE_MESSAGE: i32 = -500; + pub(super) const UNKNOWN_VOTER: i32 = -150; pub(super) const INVALID_VIEW_CHANGE: i32 = -500; pub(super) const PER_UNDECODABLE_BYTE: i32 = -5; pub(super) const PER_SIGNATURE_CHECKED: i32 = -25; pub(super) const PER_BLOCK_LOADED: i32 = -10; pub(super) const INVALID_COMMIT: i32 = -5000; + pub(super) const OUT_OF_SCOPE_MESSAGE: i32 = -500; } // benefit scalars for reporting peers. @@ -233,7 +235,7 @@ impl> NetworkBridge { pub(crate) fn new( service: N, config: crate::Config, - set_state: Option<(u64, &crate::environment::VoterSetState)>, + set_state: Option<&crate::environment::VoterSetState>, on_exit: impl Future + Clone + Send + 'static, ) -> ( Self, @@ -244,15 +246,18 @@ impl> NetworkBridge { let validator = Arc::new(validator); service.register_validator(validator.clone()); - if let Some((set_id, set_state)) = set_state { + if let Some(set_state) = set_state { // register all previous votes with the gossip service so that they're // available to peers potentially stuck on a previous round. - for round in set_state.completed_rounds().iter() { + let completed = set_state.completed_rounds(); + let (set_id, voters) = completed.set_info(); + validator.note_set(SetId(set_id), voters.to_vec(), |_, _| {}); + for round in completed.iter() { let topic = round_topic::(round.number, set_id); // we need to note the round with the gossip validator otherwise // messages will be ignored. - validator.note_round(Round(round.number), SetId(set_id), |_, _| {}); + validator.note_round(Round(round.number), |_, _| {}); for signed in round.votes.iter() { let message = gossip::GossipMessage::VoteOrPrecommit( @@ -295,7 +300,7 @@ impl> NetworkBridge { (bridge, startup_work) } - /// Get the round messages for a round in a given set ID. These are signature-checked. + /// Get the round messages for a round in the current set ID. These are signature-checked. pub(crate) fn round_communication( &self, round: Round, @@ -307,9 +312,18 @@ impl> NetworkBridge { impl Stream,Error=Error>, impl Sink,SinkError=Error>, ) { + // is a no-op if currently in that set. + self.validator.note_set( + set_id, + voters.voters().iter().map(|(v, _)| v.clone()).collect(), + |to, neighbor| self.service.send_message( + to, + GossipMessage::::from(neighbor).encode() + ), + ); + self.validator.note_round( round, - set_id, |to, neighbor| self.service.send_message( to, GossipMessage::::from(neighbor).encode() @@ -410,6 +424,7 @@ impl> NetworkBridge { ) { self.validator.note_set( set_id, + voters.voters().iter().map(|(v, _)| v.clone()).collect(), |to, neighbor| self.service.send_message(to, GossipMessage::::from(neighbor).encode()), ); @@ -650,7 +665,7 @@ fn check_compact_commit( let f = voters.total_weight() - voters.threshold(); let full_threshold = voters.total_weight() + f; - // check total weight is not too high. + // check total weight is not out of range. let mut total_weight = 0; for (_, ref id) in &msg.auth_data { if let Some(weight) = voters.info(id).map(|info| info.weight()) { diff --git a/substrate/core/finality-grandpa/src/environment.rs b/substrate/core/finality-grandpa/src/environment.rs index 4aab32be8a..690c1011a7 100644 --- a/substrate/core/finality-grandpa/src/environment.rs +++ b/substrate/core/finality-grandpa/src/environment.rs @@ -46,7 +46,7 @@ use crate::{ use consensus_common::SelectChain; -use crate::authorities::SharedAuthoritySet; +use crate::authorities::{AuthoritySet, SharedAuthoritySet}; use crate::consensus_changes::SharedConsensusChanges; use crate::justification::GrandpaJustification; use crate::until_imported::UntilVoteTargetImported; @@ -66,11 +66,13 @@ pub struct CompletedRound { pub votes: Vec>, } -// Data about last completed rounds. Stores NUM_LAST_COMPLETED_ROUNDS and always +// Data about last completed rounds within a single voter set. Stores NUM_LAST_COMPLETED_ROUNDS and always // contains data about at least one round (genesis). #[derive(Debug, Clone, PartialEq)] pub struct CompletedRounds { - inner: VecDeque>, + rounds: VecDeque>, + set_id: u64, + voters: Vec, } // NOTE: the current strategy for persisting completed rounds is very naive @@ -80,34 +82,51 @@ const NUM_LAST_COMPLETED_ROUNDS: usize = 2; impl Encode for CompletedRounds { fn encode(&self) -> Vec { - Vec::from_iter(&self.inner).encode() + let v = Vec::from_iter(&self.rounds); + (&v, &self.set_id, &self.voters).encode() } } impl Decode for CompletedRounds { fn decode(value: &mut I) -> Option { - Vec::>::decode(value) - .map(|completed_rounds| CompletedRounds { - inner: completed_rounds.into(), + <(Vec>, u64, Vec)>::decode(value) + .map(|(rounds, set_id, voters)| CompletedRounds { + rounds: rounds.into(), + set_id, + voters, }) } } impl CompletedRounds { /// Create a new completed rounds tracker with NUM_LAST_COMPLETED_ROUNDS capacity. - pub fn new(genesis: CompletedRound) -> CompletedRounds { - let mut inner = VecDeque::with_capacity(NUM_LAST_COMPLETED_ROUNDS); - inner.push_back(genesis); - CompletedRounds { inner } + pub(crate) fn new( + genesis: CompletedRound, + set_id: u64, + voters: &AuthoritySet>, + ) + -> CompletedRounds + { + let mut rounds = VecDeque::with_capacity(NUM_LAST_COMPLETED_ROUNDS); + rounds.push_back(genesis); + + let voters = voters.current().1.iter().map(|(a, _)| a.clone()).collect(); + CompletedRounds { rounds, set_id, voters } } + /// Get the set-id and voter set of the completed rounds. + pub fn set_info(&self) -> (u64, &[AuthorityId]) { + (self.set_id, &self.voters[..]) + } + + /// Iterate over all completed rounds. pub fn iter(&self) -> impl Iterator> { - self.inner.iter() + self.rounds.iter() } /// Returns the last (latest) completed round. pub fn last(&self) -> &CompletedRound { - self.inner.back() + self.rounds.back() .expect("inner is never empty; always contains at least genesis; qed") } @@ -118,11 +137,11 @@ impl CompletedRounds { return false; } - if self.inner.len() == NUM_LAST_COMPLETED_ROUNDS { - self.inner.pop_front(); + if self.rounds.len() == NUM_LAST_COMPLETED_ROUNDS { + self.rounds.pop_front(); } - self.inner.push_back(completed_round); + self.rounds.push_back(completed_round); true } diff --git a/substrate/core/finality-grandpa/src/lib.rs b/substrate/core/finality-grandpa/src/lib.rs index 2a1da15499..7e3f19940b 100644 --- a/substrate/core/finality-grandpa/src/lib.rs +++ b/substrate/core/finality-grandpa/src/lib.rs @@ -501,7 +501,7 @@ pub fn run_grandpa_voter, N, RA, SC, X>( let (network, network_startup) = NetworkBridge::new( network, config.clone(), - Some((authority_set.set_id(), &set_state.read())), + Some(&set_state.read()), on_exit.clone(), ); @@ -640,12 +640,16 @@ pub fn run_grandpa_voter, N, RA, SC, X>( let set_state = VoterSetState::Live { // always start at round 0 when changing sets. - completed_rounds: CompletedRounds::new(CompletedRound { - number: 0, - state: genesis_state, - base: (new.canon_hash, new.canon_number), - votes: Vec::new(), - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: 0, + state: genesis_state, + base: (new.canon_hash, new.canon_number), + votes: Vec::new(), + }, + new.set_id, + &*authority_set.inner().read(), + ), current_round: HasVoted::No, }; diff --git a/substrate/core/finality-grandpa/src/observer.rs b/substrate/core/finality-grandpa/src/observer.rs index f172e58ee9..9ed1cc8c62 100644 --- a/substrate/core/finality-grandpa/src/observer.rs +++ b/substrate/core/finality-grandpa/src/observer.rs @@ -225,12 +225,16 @@ pub fn run_grandpa_observer, N, RA, SC>( let set_state = VoterSetState::Live:: { // always start at round 0 when changing sets. - completed_rounds: CompletedRounds::new(CompletedRound { - number: 0, - state: genesis_state, - base: (new.canon_hash, new.canon_number), - votes: Vec::new(), - }), + completed_rounds: CompletedRounds::new( + CompletedRound { + number: 0, + state: genesis_state, + base: (new.canon_hash, new.canon_number), + votes: Vec::new(), + }, + new.set_id, + &*authority_set.inner().read(), + ), current_round: HasVoted::No, };