mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +00:00
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
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
if self.rounds.len() == NUM_LAST_COMPLETED_ROUNDS {
|
||||
self.rounds.pop_front();
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user