Skip to content
Snippets Groups Projects
Commit 6565e1f8 authored by André Silva's avatar André Silva Committed by Gavin Wood
Browse files

grandpa: track multiple live rounds in voter set state (#3298)

* grandpa: track multiple live rounds in voter set state

* grandpa: don't assume rounds are completed in-order

* grandpa: fix tests

* grandpa: don't assume round is being tracked on Environment::completed

* grandpa: fix missing import in test
parent 0f0df985
Branches
No related merge requests found
......@@ -30,7 +30,9 @@ use fg_primitives::AuthorityId;
use crate::authorities::{AuthoritySet, SharedAuthoritySet, PendingChange, DelayKind};
use crate::consensus_changes::{SharedConsensusChanges, ConsensusChanges};
use crate::environment::{CompletedRound, CompletedRounds, HasVoted, SharedVoterSetState, VoterSetState};
use crate::environment::{
CompletedRound, CompletedRounds, CurrentRounds, HasVoted, SharedVoterSetState, VoterSetState,
};
use crate::NewAuthoritySet;
const VERSION_KEY: &[u8] = b"grandpa_schema_version";
......@@ -155,6 +157,9 @@ fn migrate_from_version0<Block: BlockT, B, G>(
let base = last_round_state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(last_round_number + 1, HasVoted::No);
let set_state = VoterSetState::Live {
completed_rounds: CompletedRounds::new(
CompletedRound {
......@@ -166,7 +171,7 @@ fn migrate_from_version0<Block: BlockT, B, G>(
set_id,
&new_set,
),
current_round: HasVoted::No,
current_rounds,
};
backend.insert_aux(&[(SET_STATE_KEY, set_state.encode().as_slice())], &[])?;
......@@ -223,9 +228,12 @@ fn migrate_from_version1<Block: BlockT, B, G>(
let base = set_state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(last_round_number + 1, HasVoted::No);
VoterSetState::Live {
completed_rounds: completed_rounds(last_round_number, set_state, base),
current_round: HasVoted::No,
current_rounds,
}
},
None => {
......@@ -233,10 +241,11 @@ fn migrate_from_version1<Block: BlockT, B, G>(
let base = set_state.prevote_ghost
.expect("state is for completed round; completed rounds must have a prevote ghost; qed.");
VoterSetState::Live {
completed_rounds: completed_rounds(0, set_state, base),
current_round: HasVoted::No,
}
VoterSetState::live(
set_id,
&set,
base,
)
},
};
......@@ -300,19 +309,11 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
let base = state.prevote_ghost
.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,
},
set.current().0,
&set,
),
current_round: HasVoted::No,
}
VoterSetState::live(
set.current().0,
&set,
base,
)
}
};
......@@ -338,19 +339,12 @@ pub(crate) fn load_persistent<Block: BlockT, B, G>(
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,
},
0,
&genesis_set,
),
current_round: HasVoted::No,
};
let genesis_state = VoterSetState::live(
0,
&genesis_set,
base,
);
backend.insert_aux(
&[
(AUTHORITY_SET_KEY, genesis_set.encode().as_slice()),
......@@ -396,23 +390,11 @@ pub(crate) fn update_authority_set<Block: BlockT, F, R>(
// we also overwrite the "last completed round" entry with a blank slate
// because from the perspective of the finality gadget, the chain has
// reset.
let round_state = RoundState::genesis((
new_set.canon_hash.clone(),
new_set.canon_number.clone(),
));
let set_state = VoterSetState::<Block>::Live {
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 set_state = VoterSetState::<Block>::live(
new_set.set_id,
&set,
(new_set.canon_hash, new_set.canon_number),
);
let encoded = set_state.encode();
write_aux(&[
......@@ -527,6 +509,9 @@ mod test {
},
);
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(round_number + 1, HasVoted::No);
assert_eq!(
&*set_state.read(),
&VoterSetState::Live {
......@@ -540,7 +525,7 @@ mod test {
set_id,
&*authority_set.inner().read(),
),
current_round: HasVoted::No,
current_rounds,
},
);
}
......@@ -614,6 +599,9 @@ mod test {
},
);
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(round_number + 1, HasVoted::No);
assert_eq!(
&*set_state.read(),
&VoterSetState::Live {
......@@ -627,7 +615,7 @@ mod test {
set_id,
&*authority_set.inner().read(),
),
current_round: HasVoted::No,
current_rounds,
},
);
}
......
......@@ -1248,26 +1248,16 @@ mod tests {
// dummy voter set state
fn voter_set_state() -> SharedVoterSetState<Block> {
use crate::authorities::AuthoritySet;
use crate::environment::{CompletedRound, CompletedRounds, HasVoted, VoterSetState};
use grandpa::round::State as RoundState;
use crate::environment::VoterSetState;
use primitives::H256;
let state = RoundState::genesis((H256::zero(), 0));
let base = state.prevote_ghost.unwrap();
let base = (H256::zero(), 0);
let voters = AuthoritySet::genesis(Vec::new());
let set_state = VoterSetState::Live {
completed_rounds: CompletedRounds::new(
CompletedRound {
state,
number: 0,
votes: Vec::new(),
base,
},
0,
&voters,
),
current_round: HasVoted::No,
};
let set_state = VoterSetState::live(
0,
&voters,
base,
);
set_state.into()
}
......@@ -1542,16 +1532,19 @@ mod tests {
let set_state: SharedVoterSetState<Block> = {
let mut completed_rounds = voter_set_state().read().completed_rounds();
assert!(completed_rounds.push(environment::CompletedRound {
completed_rounds.push(environment::CompletedRound {
number: 1,
state: grandpa::round::State::genesis(Default::default()),
base: Default::default(),
votes: Default::default(),
}));
});
let mut current_rounds = environment::CurrentRounds::new();
current_rounds.insert(2, environment::HasVoted::No);
let set_state = environment::VoterSetState::<Block>::Live {
completed_rounds,
current_round: environment::HasVoted::No,
current_rounds,
};
set_state.into()
......
......@@ -141,26 +141,18 @@ fn config() -> crate::Config {
// dummy voter set state
fn voter_set_state() -> SharedVoterSetState<Block> {
use crate::authorities::AuthoritySet;
use crate::environment::{CompletedRound, CompletedRounds, HasVoted, VoterSetState};
use crate::environment::VoterSetState;
use grandpa::round::State as RoundState;
use primitives::H256;
let state = RoundState::genesis((H256::zero(), 0));
let base = state.prevote_ghost.unwrap();
let voters = AuthoritySet::genesis(Vec::new());
let set_state = VoterSetState::Live {
completed_rounds: CompletedRounds::new(
CompletedRound {
state,
number: 0,
votes: Vec::new(),
base,
},
0,
&voters,
),
current_round: HasVoted::No,
};
let set_state = VoterSetState::live(
0,
&voters,
base,
);
set_state.into()
}
......
......@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::collections::VecDeque;
use std::collections::BTreeMap;
use std::iter::FromIterator;
use std::sync::Arc;
use std::time::{Duration, Instant};
......@@ -74,11 +74,12 @@ pub struct CompletedRound<Block: BlockT> {
pub votes: Vec<SignedMessage<Block>>,
}
// 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).
// 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<Block: BlockT> {
rounds: VecDeque<CompletedRound<Block>>,
rounds: Vec<CompletedRound<Block>>,
set_id: u64,
voters: Vec<AuthorityId>,
}
......@@ -117,8 +118,8 @@ impl<Block: BlockT> CompletedRounds<Block> {
)
-> CompletedRounds<Block>
{
let mut rounds = VecDeque::with_capacity(NUM_LAST_COMPLETED_ROUNDS);
rounds.push_back(genesis);
let mut rounds = Vec::with_capacity(NUM_LAST_COMPLETED_ROUNDS);
rounds.push(genesis);
let voters = voters.current().1.iter().map(|(a, _)| a.clone()).collect();
CompletedRounds { rounds, set_id, voters }
......@@ -131,32 +132,38 @@ impl<Block: BlockT> CompletedRounds<Block> {
/// Iterate over all completed rounds.
pub fn iter(&self) -> impl Iterator<Item=&CompletedRound<Block>> {
self.rounds.iter()
self.rounds.iter().rev()
}
/// Returns the last (latest) completed round.
pub fn last(&self) -> &CompletedRound<Block> {
self.rounds.back()
self.rounds.first()
.expect("inner is never empty; always contains at least genesis; qed")
}
/// Push a new completed round, returns false if the given round is older
/// than the last completed round.
pub fn push(&mut self, completed_round: CompletedRound<Block>) -> bool {
if self.last().number >= completed_round.number {
return false;
}
/// Push a new completed round, oldest round is evicted if number of rounds
/// is higher than `NUM_LAST_COMPLETED_ROUNDS`.
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),
) {
Ok(idx) => self.rounds[idx] = completed_round,
Err(idx) => self.rounds.insert(idx, completed_round),
};
if self.rounds.len() == NUM_LAST_COMPLETED_ROUNDS {
self.rounds.pop_front();
if self.rounds.len() > NUM_LAST_COMPLETED_ROUNDS {
self.rounds.pop();
}
self.rounds.push_back(completed_round);
true
}
}
/// A map with voter status information for currently live rounds,
/// which votes have we cast and what are they.
pub type CurrentRounds<Block> = BTreeMap<u64, HasVoted<Block>>;
/// The state of the current voter set, whether it is currently active or not
/// and information related to the previously completed rounds. Current round
/// voting status is used when restarting the voter, i.e. it will re-use the
......@@ -168,8 +175,8 @@ pub enum VoterSetState<Block: BlockT> {
Live {
/// The previously completed rounds.
completed_rounds: CompletedRounds<Block>,
/// Vote status for the current round.
current_round: HasVoted<Block>,
/// Voter status for the currently live rounds.
current_rounds: CurrentRounds<Block>,
},
/// The voter is paused, i.e. not casting or importing any votes.
Paused {
......@@ -179,6 +186,35 @@ pub enum VoterSetState<Block: BlockT> {
}
impl<Block: BlockT> VoterSetState<Block> {
/// Create a new live VoterSetState with round 0 as a completed round using
/// the given genesis state and the given authorities. Round 1 is added as a
/// current round (with state `HasVoted::No`).
pub(crate) fn live(
set_id: u64,
authority_set: &AuthoritySet<Block::Hash, NumberFor<Block>>,
genesis_state: (Block::Hash, NumberFor<Block>),
) -> VoterSetState<Block> {
let state = RoundState::genesis((genesis_state.0, genesis_state.1));
let completed_rounds = CompletedRounds::new(
CompletedRound {
number: 0,
state,
base: (genesis_state.0, genesis_state.1),
votes: Vec::new(),
},
set_id,
authority_set,
);
let mut current_rounds = CurrentRounds::new();
current_rounds.insert(1, HasVoted::No);
VoterSetState::Live {
completed_rounds,
current_rounds,
}
}
/// Returns the last completed rounds.
pub(crate) fn completed_rounds(&self) -> CompletedRounds<Block> {
match self {
......@@ -198,10 +234,28 @@ impl<Block: BlockT> VoterSetState<Block> {
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: u64)
-> Result<(&CompletedRounds<Block>, &CurrentRounds<Block>), Error>
{
if let VoterSetState::Live { completed_rounds, current_rounds } = self {
if current_rounds.contains_key(&round) {
return Ok((completed_rounds, current_rounds));
} else {
let msg = "Voter acting on a live round we are not tracking.";
return Err(Error::Safety(msg.to_string()));
}
} else {
let msg = "Voter acting while in paused state.";
return Err(Error::Safety(msg.to_string()));
}
}
}
/// Whether we've voted already during a prior run of the program.
#[derive(Debug, Decode, Encode, PartialEq)]
#[derive(Clone, Debug, Decode, Encode, PartialEq)]
pub enum HasVoted<Block: BlockT> {
/// Has not voted already in this round.
No,
......@@ -290,10 +344,16 @@ impl<Block: BlockT> SharedVoterSetState<Block> {
}
/// Return vote status information for the current round.
pub(crate) fn has_voted(&self) -> HasVoted<Block> {
pub(crate) fn has_voted(&self, round: u64) -> HasVoted<Block> {
match &*self.inner.read() {
VoterSetState::Live { current_round: HasVoted::Yes(id, vote), .. } =>
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)
},
_ => HasVoted::No,
}
}
......@@ -502,7 +562,7 @@ where
let local_key = crate::is_voter(&self.voters, &self.config.keystore);
let has_voted = match self.voter_set_state.has_voted() {
let has_voted = match self.voter_set_state.has_voted(round) {
HasVoted::Yes(id, vote) => {
if local_key.as_ref().map(|k| k.public() == id).unwrap_or(false) {
HasVoted::Yes(id, vote)
......@@ -541,7 +601,7 @@ where
}
}
fn proposed(&self, _round: u64, propose: PrimaryPropose<Block>) -> Result<(), Self::Error> {
fn proposed(&self, round: u64, propose: PrimaryPropose<Block>) -> Result<(), Self::Error> {
let local_id = crate::is_voter(&self.voters, &self.config.keystore);
let local_id = match local_id {
......@@ -550,23 +610,26 @@ where
};
self.update_voter_set_state(|voter_set_state| {
let completed_rounds = match voter_set_state {
VoterSetState::Live { completed_rounds, current_round: HasVoted::No } => completed_rounds,
VoterSetState::Live { current_round, .. } 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);
},
_ => {
let msg = "Voter proposing after prevote/precommit or while paused.";
return Err(Error::Safety(msg.to_string()));
},
};
let (completed_rounds, current_rounds) = voter_set_state.with_current_round(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);
}
let mut current_rounds = current_rounds.clone();
let current_round = current_rounds.get_mut(&round)
.expect("checked previously that key exists; qed.");
*current_round = HasVoted::Yes(local_id, Vote::Propose(propose));
let set_state = VoterSetState::<Block>::Live {
completed_rounds: completed_rounds.clone(),
current_round: HasVoted::Yes(local_id, Vote::Propose(propose)),
current_rounds,
};
#[allow(deprecated)]
......@@ -578,7 +641,7 @@ where
Ok(())
}
fn prevoted(&self, _round: u64, prevote: Prevote<Block>) -> Result<(), Self::Error> {
fn prevoted(&self, round: u64, prevote: Prevote<Block>) -> Result<(), Self::Error> {
let local_id = crate::is_voter(&self.voters, &self.config.keystore);
let local_id = match local_id {
......@@ -587,26 +650,28 @@ where
};
self.update_voter_set_state(|voter_set_state| {
let (completed_rounds, propose) = match voter_set_state {
VoterSetState::Live { completed_rounds, current_round: HasVoted::No } =>
(completed_rounds, None),
VoterSetState::Live { completed_rounds, current_round: HasVoted::Yes(_, Vote::Propose(propose)) } =>
(completed_rounds, Some(propose)),
VoterSetState::Live { current_round, .. } if !current_round.can_prevote() => {
// 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);
},
_ => {
let msg = "Voter prevoting after precommit or while paused.";
return Err(Error::Safety(msg.to_string()));
},
};
let (completed_rounds, current_rounds) = voter_set_state.with_current_round(round)?;
let current_round = current_rounds.get(&round)
.expect("checked in with_current_round that key exists; qed.");
if !current_round.can_prevote() {
// 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);
}
let propose = current_round.propose();
let mut current_rounds = current_rounds.clone();
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));
let set_state = VoterSetState::<Block>::Live {
completed_rounds: completed_rounds.clone(),
current_round: HasVoted::Yes(local_id, Vote::Prevote(propose.cloned(), prevote)),
current_rounds,
};
#[allow(deprecated)]
......@@ -618,7 +683,7 @@ where
Ok(())
}
fn precommitted(&self, _round: u64, precommit: Precommit<Block>) -> Result<(), Self::Error> {
fn precommitted(&self, round: u64, precommit: Precommit<Block>) -> Result<(), Self::Error> {
let local_id = crate::is_voter(&self.voters, &self.config.keystore);
let local_id = match local_id {
......@@ -627,24 +692,38 @@ where
};
self.update_voter_set_state(|voter_set_state| {
let (completed_rounds, propose, prevote) = match voter_set_state {
VoterSetState::Live { completed_rounds, current_round: HasVoted::Yes(_, Vote::Prevote(propose, prevote)) } =>
(completed_rounds, propose, prevote),
VoterSetState::Live { current_round, .. } if !current_round.can_precommit() => {
// 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);
},
let (completed_rounds, current_rounds) = voter_set_state.with_current_round(round)?;
let current_round = current_rounds.get(&round)
.expect("checked in with_current_round that key exists; qed.");
if !current_round.can_precommit() {
// 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);
}
let propose = current_round.propose();
let prevote = match current_round {
HasVoted::Yes(_, Vote::Prevote(_, prevote)) => prevote,
_ => {
let msg = "Voter precommitting while paused.";
let msg = "Voter precommitting before prevoting.";
return Err(Error::Safety(msg.to_string()));
}
},
};
let mut current_rounds = current_rounds.clone();
let current_round = current_rounds.get_mut(&round)
.expect("checked previously that key exists; qed.");
*current_round = HasVoted::Yes(
local_id,
Vote::Precommit(propose.cloned(), prevote.clone(), precommit),
);
let set_state = VoterSetState::<Block>::Live {
completed_rounds: completed_rounds.clone(),
current_round: HasVoted::Yes(local_id, Vote::Precommit(propose.clone(), prevote.clone(), precommit)),
current_rounds,
};
#[allow(deprecated)]
......@@ -673,25 +752,37 @@ where
);
self.update_voter_set_state(|voter_set_state| {
let mut completed_rounds = voter_set_state.completed_rounds();
// NOTE: we don't use `with_current_round` here, it is possible that
// we are not currently tracking this round if it is a round we
// caught up to.
let (completed_rounds, current_rounds) =
if let VoterSetState::Live { completed_rounds, current_rounds } = voter_set_state {
(completed_rounds, current_rounds)
} else {
let msg = "Voter acting while in paused state.";
return Err(Error::Safety(msg.to_string()));
};
let mut completed_rounds = completed_rounds.clone();
// TODO: Future integration will store the prevote and precommit index. See #2611.
let votes = historical_votes.seen().clone();
// NOTE: the Environment assumes that rounds are *always* completed in-order.
if !completed_rounds.push(CompletedRound {
completed_rounds.push(CompletedRound {
number: round,
state: state.clone(),
base,
votes,
}) {
let msg = "Voter completed round that is older than the last completed round.";
return Err(Error::Safety(msg.to_string()));
};
});
// remove the round from live rounds and start tracking the next round
let mut current_rounds = current_rounds.clone();
current_rounds.remove(&round);
current_rounds.insert(round + 1, HasVoted::No);
let set_state = VoterSetState::<Block>::Live {
completed_rounds,
current_round: HasVoted::No,
current_rounds,
};
#[allow(deprecated)]
......
......@@ -75,7 +75,7 @@ use serde_json;
use srml_finality_tracker;
use grandpa::Error as GrandpaError;
use grandpa::{voter, round::State as RoundState, BlockNumberOps, voter_set::VoterSet};
use grandpa::{voter, BlockNumberOps, voter_set::VoterSet};
use std::fmt;
use std::sync::Arc;
......@@ -103,7 +103,7 @@ pub use light_import::light_block_import;
pub use observer::run_grandpa_observer;
use aux_schema::PersistentData;
use environment::{CompletedRound, CompletedRounds, Environment, HasVoted, SharedVoterSetState, VoterSetState};
use environment::{Environment, SharedVoterSetState, VoterSetState};
use import::GrandpaBlockImport;
use until_imported::UntilGlobalMessageBlocksImported;
use communication::NetworkBridge;
......@@ -635,22 +635,11 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
// start the new authority set using the block where the
// set changed (not where the signal happened!) as the base.
let genesis_state = RoundState::genesis((new.canon_hash, new.canon_number));
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(),
},
new.set_id,
&*authority_set.inner().read(),
),
current_round: HasVoted::No,
};
let set_state = VoterSetState::live(
new.set_id,
&*authority_set.inner().read(),
(new.canon_hash, new.canon_number),
);
#[allow(deprecated)]
aux_schema::write_voter_set_state(&**client.backend(), &set_state)?;
......@@ -763,4 +752,4 @@ fn is_voter(
.find_map(|(p, _)| keystore.read().key_pair::<AuthorityPair>(&p).ok()),
None => None,
}
}
\ No newline at end of file
}
......@@ -20,7 +20,7 @@ use futures::prelude::*;
use futures::future::{self, Loop as FutureLoop};
use grandpa::{
BlockNumberOps, Error as GrandpaError, round::State as RoundState, voter, voter_set::VoterSet
BlockNumberOps, Error as GrandpaError, voter, voter_set::VoterSet
};
use log::{debug, info, warn};
......@@ -36,7 +36,6 @@ use crate::{
use crate::authorities::SharedAuthoritySet;
use crate::communication::NetworkBridge;
use crate::consensus_changes::SharedConsensusChanges;
use crate::environment::{CompletedRound, CompletedRounds, HasVoted};
use fg_primitives::AuthorityId;
struct ObserverChain<'a, Block: BlockT, B, E, RA>(&'a Client<B, E, Block, RA>);
......@@ -238,22 +237,11 @@ pub fn run_grandpa_observer<B, E, Block: BlockT<Hash=H256>, N, RA, SC>(
VoterCommand::ChangeAuthorities(new) => {
// start the new authority set using the block where the
// set changed (not where the signal happened!) as the base.
let genesis_state = RoundState::genesis((new.canon_hash, new.canon_number));
let set_state = VoterSetState::Live::<Block> {
// 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(),
},
new.set_id,
&*authority_set.inner().read(),
),
current_round: HasVoted::No,
};
let set_state = VoterSetState::live(
new.set_id,
&*authority_set.inner().read(),
(new.canon_hash, new.canon_number),
);
#[allow(deprecated)]
crate::aux_schema::write_voter_set_state(&**client.backend(), &set_state)?;
......
......@@ -17,6 +17,7 @@
//! Tests and test helpers for GRANDPA.
use super::*;
use environment::HasVoted;
use network::test::{Block, DummySpecialization, Hash, TestNetFactory, Peer, PeersClient};
use network::test::{PassThroughVerifier};
use network::config::{ProtocolConfig, Roles, BoxFinalityProofRequestBuilder};
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment