Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Answer queries #65

Merged
merged 22 commits into from
May 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,71 @@ pub fn process_commit_validation_result<H, N>(
}
}

/// Historical votes seen in a round.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "derive-codec", derive(Encode, Decode))]
pub struct HistoricalVotes<H, N, S, Id> {
seen: Vec<SignedMessage<H, N, S, Id>>,
prevote_idx: Option<usize>,
precommit_idx: Option<usize>,
}

impl<H, N, S, Id> HistoricalVotes<H, N, S, Id> {
/// Create a new HistoricalVotes.
pub fn new() -> Self {
HistoricalVotes {
seen: Vec::new(),
prevote_idx: None,
precommit_idx: None,
}
}

/// Create a new HistoricalVotes initialized from the parameters.
pub fn new_with(
seen: Vec<SignedMessage<H, N, S, Id>>,
prevote_idx: Option<usize>,
precommit_idx: Option<usize>
) -> Self {
HistoricalVotes {
seen,
prevote_idx,
precommit_idx,
}
}

/// Push a vote into the list.
pub fn push_vote(&mut self, msg: SignedMessage<H, N, S, Id>) {
self.seen.push(msg)
}

/// Return the messages seen so far.
pub fn seen(&self) -> &Vec<SignedMessage<H, N, S, Id>> {
&self.seen
}

/// Return the number of messages seen before prevoting.
/// None in case we didn't prevote yet.
pub fn prevote_idx(&self) -> Option<usize> {
self.prevote_idx
}

/// Return the number of messages seen before precommiting.
/// None in case we didn't precommit yet.
pub fn precommit_idx(&self) -> Option<usize> {
self.precommit_idx
}

/// Set the number of messages seen before prevoting.
pub fn set_prevoted_idx(&mut self) {
self.prevote_idx = Some(self.seen.len())
}

/// Set the number of messages seen before precommiting.
pub fn set_precommited_idx(&mut self) {
self.precommit_idx = Some(self.seen.len())
}
}

#[cfg(test)]
mod tests {
use super::threshold;
Expand Down
156 changes: 144 additions & 12 deletions src/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::bitfield::{Shared as BitfieldContext, Bitfield};
use crate::vote_graph::VoteGraph;
use crate::voter_set::VoterSet;

use super::{Equivocation, Prevote, Precommit, Chain, BlockNumberOps};
use super::{Equivocation, Prevote, Precommit, Chain, BlockNumberOps, HistoricalVotes, Message, SignedMessage};

#[derive(Hash, Eq, PartialEq)]
struct Address;
Expand Down Expand Up @@ -131,6 +131,7 @@ impl<Id: Hash + Eq + Clone, Vote: Clone + Eq, Signature: Clone + Eq> VoteTracker
Entry::Vacant(vacant) => {
self.current_weight += weight;
let multiplicity = vacant.insert(VoteMultiplicity::Single(vote, signature));

return AddVoteResult {
multiplicity: Some(multiplicity),
duplicated: false,
Expand Down Expand Up @@ -223,6 +224,7 @@ pub struct Round<Id: Hash + Eq, H: Hash + Eq, N, Signature> {
graph: VoteGraph<H, N, VoteWeight>, // DAG of blocks which have been voted on.
prevote: VoteTracker<Id, Prevote<H, N>, Signature>, // tracks prevotes that have been counted
precommit: VoteTracker<Id, Precommit<H, N>, Signature>, // tracks precommits
historical_votes: HistoricalVotes<H, N, Signature, Id>,
round_number: u64,
voters: VoterSet<Id>,
total_weight: u64,
Expand Down Expand Up @@ -274,6 +276,7 @@ impl<Id, H, N, Signature> Round<Id, H, N, Signature> where
graph: VoteGraph::new(base_hash, base_number),
prevote: VoteTracker::new(),
precommit: VoteTracker::new(),
historical_votes: HistoricalVotes::new(),
bitfield_context: BitfieldContext::new(n_validators),
prevote_ghost: None,
precommit_ghost: None,
Expand Down Expand Up @@ -309,7 +312,7 @@ impl<Id, H, N, Signature> Round<Id, H, N, Signature> where
let weight = info.weight();

let equivocation = {
let multiplicity = match self.prevote.add_vote(signer.clone(), vote, signature, weight) {
let multiplicity = match self.prevote.add_vote(signer.clone(), vote.clone(), signature.clone(), weight) {
AddVoteResult { multiplicity: Some(m), .. } => m,
AddVoteResult { duplicated, .. } => {
import_result.duplicated = duplicated;
Expand All @@ -319,26 +322,36 @@ impl<Id, H, N, Signature> Round<Id, H, N, Signature> where
let round_number = self.round_number;

match multiplicity {
VoteMultiplicity::Single(ref vote, _) => {
VoteMultiplicity::Single(single_vote, _) => {
let vote_weight = VoteWeight {
bitfield: self.bitfield_context.prevote_bitfield(info)
.expect("info is instantiated from same voter set as context; qed"),
};

self.graph.insert(
vote.target_hash.clone(),
vote.target_number,
single_vote.target_hash.clone(),
single_vote.target_number,
vote_weight,
chain,
)?;

// Push the vote into HistoricalVotes.
let message = Message::Prevote(vote);
let signed_message = SignedMessage { id: signer, signature, message };
self.historical_votes.push_vote(signed_message);

None
}
VoteMultiplicity::Equivocated(ref first, ref second) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to store equivocated votes as well?

Copy link
Contributor

@rphmeier rphmeier May 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very least, the first equivocation of a type from a given voter

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// mark the equivocator as such. no need to "undo" the first vote.
self.bitfield_context.equivocated_prevote(info)
.expect("info is instantiated from same voter set as bitfield; qed");

// Push the vote into HistoricalVotes.
let message = Message::Prevote(vote);
let signed_message = SignedMessage { id: signer.clone(), signature, message };
self.historical_votes.push_vote(signed_message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe only add if vote == second? Or maybe if vote == first || vote == second just to be safe wrt ordering.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed since we early-exit after we've already seen an equivocation.


Some(Equivocation {
round_number,
identity: signer,
Expand Down Expand Up @@ -386,7 +399,7 @@ impl<Id, H, N, Signature> Round<Id, H, N, Signature> where
let weight = info.weight();

let equivocation = {
let multiplicity = match self.precommit.add_vote(signer.clone(), vote, signature, weight) {
let multiplicity = match self.precommit.add_vote(signer.clone(), vote.clone(), signature.clone(), weight) {
AddVoteResult { multiplicity: Some(m), .. } => m,
AddVoteResult { duplicated, .. } => {
import_result.duplicated = duplicated;
Expand All @@ -396,33 +409,42 @@ impl<Id, H, N, Signature> Round<Id, H, N, Signature> where
let round_number = self.round_number;

match multiplicity {
VoteMultiplicity::Single(ref vote, _) => {
VoteMultiplicity::Single(single_vote, _) => {
let vote_weight = VoteWeight {
bitfield: self.bitfield_context.precommit_bitfield(info)
.expect("info is instantiated from same voter set as context; qed"),
};

self.graph.insert(
vote.target_hash.clone(),
vote.target_number,
single_vote.target_hash.clone(),
single_vote.target_number,
vote_weight,
chain,
)?;

let message = Message::Precommit(vote);
let signed_message = SignedMessage { id: signer, signature, message };
self.historical_votes.push_vote(signed_message);

None
}
},
VoteMultiplicity::Equivocated(ref first, ref second) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

// mark the equivocator as such. no need to "undo" the first vote.
self.bitfield_context.equivocated_precommit(info)
.expect("info is instantiated from same voter set as bitfield; qed");

// Push the vote into HistoricalVotes.
let message = Message::Precommit(vote);
let signed_message = SignedMessage { id: signer.clone(), signature, message };
self.historical_votes.push_vote(signed_message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.


Some(Equivocation {
round_number,
identity: signer,
first: first.clone(),
second: second.clone(),
})
}
},
}
};

Expand Down Expand Up @@ -472,7 +494,7 @@ impl<Id, H, N, Signature> Round<Id, H, N, Signature> where
type Item = (V, S);

fn next(&mut self) -> Option<(V, S)> {
match *self.multiplicity {
match self.multiplicity {
VoteMultiplicity::Single(ref v, ref s) => {
if self.yielded == 0 {
self.yielded += 1;
Expand Down Expand Up @@ -674,6 +696,35 @@ impl<Id, H, N, Signature> Round<Id, H, N, Signature> where
pub fn precommits(&self) -> Vec<(Id, Precommit<H, N>, Signature)> {
self.precommit.votes()
}

/// Return all votes (prevotes and precommits) by importing order.
pub fn historical_votes(&self) -> &HistoricalVotes<H, N, Signature, Id> {
&self.historical_votes
}

/// Set the number of prevotes and precommits received at the moment of prevoting.
/// It should be called inmediatly after prevoting.
pub fn set_prevoted_index(&mut self) {
self.historical_votes.set_prevoted_idx()
}

/// Set the number of prevotes and precommits received at the moment of precommiting.
/// It should be called inmediatly after precommiting.
pub fn set_precommited_index(&mut self) {
self.historical_votes.set_precommited_idx()
}

/// Get the number of prevotes and precommits received at the moment of prevoting.
/// Returns None if the prevote wasn't realized.
pub fn prevoted_index(&self) -> Option<usize> {
self.historical_votes.prevote_idx
}

/// Get the number of prevotes and precommits received at the moment of precommiting.
/// Returns None if the precommit wasn't realized.
pub fn precommited_index(&self) -> Option<usize> {
self.historical_votes.precommit_idx
}
}

#[cfg(test)]
Expand Down Expand Up @@ -896,4 +947,85 @@ mod tests {
// adding an extra vote by 5 doesn't increase the count.
assert_eq!(vote_weight, TotalWeight { prevote: 1 + 5 + 2 + 3, precommit: 0 });
}

#[test]
fn historical_votes_works() {
let mut chain = DummyChain::new();
chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
chain.push_blocks("E", &["EA", "EB", "EC", "ED"]);
chain.push_blocks("F", &["FA", "FB", "FC"]);

let mut round = Round::new(RoundParams {
round_number: 1,
voters: voters(),
base: ("C", 4),
});

round.import_prevote(
&chain,
Prevote::new("FC", 10),
"Alice",
Signature("Alice"),
).unwrap();

round.set_prevoted_index();

round.import_prevote(
&chain,
Prevote::new("EA", 7),
"Eve",
Signature("Eve"),
).unwrap();

round.import_precommit(
&chain,
Precommit::new("EA", 7),
"Eve",
Signature("Eve"),
).unwrap();

round.import_prevote(
&chain,
Prevote::new("EC", 10),
"Alice",
Signature("Alice"),
).unwrap();

round.set_precommited_index();

assert_eq!(round.historical_votes(), &HistoricalVotes::new_with(
vec![
SignedMessage {
message: Message::Prevote(
Prevote { target_hash: "FC", target_number: 10 }
),
signature: Signature("Alice"),
id: "Alice"
},
SignedMessage {
message: Message::Prevote(
Prevote { target_hash: "EA", target_number: 7 }
),
signature: Signature("Eve"),
id: "Eve"
},
SignedMessage {
message: Message::Precommit(
Precommit { target_hash: "EA", target_number: 7 }
),
signature: Signature("Eve"),
id: "Eve"
},
SignedMessage {
message: Message::Prevote(
Prevote { target_hash: "EC", target_number: 10 }
),
signature: Signature("Alice"),
id: "Alice"
},
],
Some(1),
Some(4),
));
}
}
6 changes: 2 additions & 4 deletions src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use tokio::timer::Delay;
use parking_lot::Mutex;
use futures::prelude::*;
use futures::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use super::{Chain, Commit, Error, Equivocation, Message, Prevote, Precommit, PrimaryPropose, SignedMessage};
use super::{Chain, Commit, Error, Equivocation, Message, Prevote, Precommit, PrimaryPropose, SignedMessage, HistoricalVotes};

pub const GENESIS_HASH: &str = "genesis";
const NULL_HASH: &str = "NULL";
Expand Down Expand Up @@ -53,8 +53,6 @@ impl DummyChain {
}

pub fn push_blocks(&mut self, mut parent: &'static str, blocks: &[&'static str]) {
use std::cmp::Ord;

if blocks.is_empty() { return }

let base_number = self.inner.get(parent).unwrap().number + 1;
Expand Down Expand Up @@ -218,7 +216,7 @@ impl crate::voter::Environment<&'static str, u32> for Environment {
_round: u64,
_state: RoundState<&'static str, u32>,
_base: (&'static str, u32),
_votes: Vec<SignedMessage<&'static str, u32, Signature, Id>>,
_votes: &HistoricalVotes<&'static str, u32, Self::Signature, Self::Id>,
) -> Result<(), Error> {
Ok(())
}
Expand Down
Loading