Skip to content

Commit

Permalink
vote: Complete vote keeper (#39)
Browse files Browse the repository at this point in the history
* Rename `Threshold::Init` to `Unreached`

* WIP: Pass votes directly to `VoteCount`

* Cleanup

* Split `compute_threshold` out of `add_vote`

* Only count a single vote per validator address

* Emit `PrecommitAny` message when we reach `Nil` threshold for precommits

* Doc comment

* Refactor `VoteCount` implementation

* Fix tests

* Split `vote` crate into more modules

* Add Skip threshold

* Refactor data kept by the `VoteKeeper` per-round

* Move dealing with skip threshold into `VoteKeeper`

* Rename `Event::RoundSkip` to `Event::SkipRound`

* Cleanup

* Include number of round to skip to in `SkipRound` message

* Parametrize over thresholds
  • Loading branch information
romac authored Nov 7, 2023
1 parent f53355f commit 52d51a4
Show file tree
Hide file tree
Showing 17 changed files with 726 additions and 278 deletions.
2 changes: 1 addition & 1 deletion Code/common/src/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::cmp;
/// Can be either:
/// - `Round::Nil` (ie. `-1`)
/// - `Round::Some(r)` where `r >= 0`
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Round {
/// No round, ie. `-1`
Nil,
Expand Down
2 changes: 1 addition & 1 deletion Code/common/src/validator_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub type VotingPower = u64;
/// TODO: Keep this trait or just add the bounds to Consensus::Address?
pub trait Address
where
Self: Clone + Debug + PartialEq + Eq,
Self: Clone + Debug + Eq + Ord,
{
}

Expand Down
9 changes: 3 additions & 6 deletions Code/consensus/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use malachite_common::{
use malachite_round::events::Event as RoundEvent;
use malachite_round::message::Message as RoundMessage;
use malachite_round::state::State as RoundState;
use malachite_vote::count::Threshold;
use malachite_vote::keeper::Message as VoteMessage;
use malachite_vote::keeper::VoteKeeper;
use malachite_vote::Threshold;

/// Messages that can be received and broadcast by the consensus executor.
#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -63,11 +63,7 @@ where
private_key: PrivateKey<Ctx>,
address: Ctx::Address,
) -> Self {
let votes = VoteKeeper::new(
height.clone(),
Round::NIL,
validator_set.total_voting_power(),
);
let votes = VoteKeeper::new(validator_set.total_voting_power());

Self {
height,
Expand Down Expand Up @@ -219,6 +215,7 @@ where
VoteMessage::PolkaValue(v) => RoundEvent::PolkaValue(v),
VoteMessage::PrecommitAny => RoundEvent::PrecommitAny,
VoteMessage::PrecommitValue(v) => RoundEvent::PrecommitValue(v),
VoteMessage::SkipRound(r) => RoundEvent::SkipRound(r),
};

self.apply_event(round, round_event)
Expand Down
8 changes: 4 additions & 4 deletions Code/round/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use malachite_common::{Context, ValueId};
use malachite_common::{Context, Round, ValueId};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event<Ctx>
Expand All @@ -17,8 +17,8 @@ where
PrecommitAny, // Receive +2/3 precommits for anything. L47
ProposalAndPrecommitValue(Ctx::Proposal), // Receive +2/3 precommits for Value. L49
PrecommitValue(ValueId<Ctx>), // Receive +2/3 precommits for ValueId. L51
RoundSkip, // Receive +1/3 messages from a higher round. OneCorrectProcessInHigherRound, L55
TimeoutPropose, // Timeout waiting for proposal. L57
TimeoutPrevote, // Timeout waiting for prevotes. L61
SkipRound(Round), // Receive +1/3 messages from a higher round. OneCorrectProcessInHigherRound, L55
TimeoutPropose, // Timeout waiting for proposal. L57
TimeoutPrevote, // Timeout waiting for prevotes. L61
TimeoutPrecommit, // Timeout waiting for precommits. L65
}
2 changes: 1 addition & 1 deletion Code/round/src/state_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ where
// From all (except Commit). Various round guards.
(_, Event::PrecommitAny) if this_round => schedule_timeout_precommit(state), // L47
(_, Event::TimeoutPrecommit) if this_round => round_skip(state, data.round.increment()), // L65
(_, Event::RoundSkip) if state.round < data.round => round_skip(state, data.round), // L55
(_, Event::SkipRound(round)) if state.round < round => round_skip(state, round), // L55
(_, Event::ProposalAndPrecommitValue(proposal)) => commit(state, data.round, proposal), // L49

// Invalid transition.
Expand Down
12 changes: 12 additions & 0 deletions Code/test/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ impl ValueId {
}
}

impl From<u64> for ValueId {
fn from(value: u64) -> Self {
Self::new(value)
}
}

/// The value to decide on
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Value(u64);
Expand Down Expand Up @@ -44,3 +50,9 @@ impl malachite_common::Value for Value {
self.id()
}
}

impl From<u64> for Value {
fn from(value: u64) -> Self {
Self::new(value)
}
}
3 changes: 2 additions & 1 deletion Code/test/tests/consensus_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ fn executor_steps_proposer() {
TestStep {
desc: "v3 precommits for our proposal, we get +2/3 precommits, decide it (v1)",
input_event: Some(Event::Vote(
Vote::new_precommit(Round::new(0), Some(value_id), addr2).signed(&sk2),
Vote::new_precommit(Round::new(0), Some(value_id), addr3).signed(&sk3),
)),
expected_output: Some(Message::Decide(Round::new(0), value.clone())),
new_state: State {
Expand Down Expand Up @@ -571,6 +571,7 @@ fn executor_steps_not_proposer_timeout_multiple_rounds() {
// TODO - add expected executor round to test and assert before the new_state check below
let new_state = executor.round_state(executor.round).unwrap();
assert_eq!(new_state, &step.new_state, "new state");
assert_eq!(output, step.expected_output, "expected output message");

previous_message = output.and_then(to_input_msg);
}
Expand Down
6 changes: 4 additions & 2 deletions Code/test/tests/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ fn test_propose() {
let height = Height::new(10);
let round = Round::new(0);

let mut state: State<TestContext> = State::default();
state.round = round;
let mut state: State<TestContext> = State {
round,
..Default::default()
};

let data = RoundData::new(round, &height, &ADDRESS);

Expand Down
74 changes: 35 additions & 39 deletions Code/test/tests/round_votes.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
use malachite_common::Round;
use malachite_vote::count::Threshold;
use malachite_vote::RoundVotes;
use malachite_common::VoteType;
use malachite_vote::round_votes::RoundVotes;
use malachite_vote::Threshold;

use malachite_test::{Address, Height, TestContext, ValueId, Vote};
use malachite_test::{Address, ValueId};

const ADDRESS: Address = Address::new([42; 20]);
const ADDRESS1: Address = Address::new([41; 20]);
const ADDRESS2: Address = Address::new([42; 20]);
const ADDRESS3: Address = Address::new([43; 20]);
const ADDRESS4: Address = Address::new([44; 20]);
const ADDRESS5: Address = Address::new([45; 20]);
const ADDRESS6: Address = Address::new([46; 20]);

#[test]
fn add_votes_nil() {
let total = 3;

let mut round_votes: RoundVotes<TestContext> =
RoundVotes::new(Height::new(1), Round::new(0), total);
let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new(total, Default::default());

// add a vote for nil. nothing changes.
let vote = Vote::new_prevote(Round::new(0), None, ADDRESS);
let thresh = round_votes.add_vote(vote.clone(), 1);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS1, None, 1);
assert_eq!(thresh, Threshold::Unreached);

// add it again, nothing changes.
let thresh = round_votes.add_vote(vote.clone(), 1);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS2, None, 1);
assert_eq!(thresh, Threshold::Unreached);

// add it again, get Nil
let thresh = round_votes.add_vote(vote.clone(), 1);
let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS3, None, 1);
assert_eq!(thresh, Threshold::Nil);
}

Expand All @@ -34,25 +37,22 @@ fn add_votes_single_value() {
let total = 4;
let weight = 1;

let mut round_votes: RoundVotes<TestContext> =
RoundVotes::new(Height::new(1), Round::new(0), total);
let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new(total, Default::default());

// add a vote. nothing changes.
let vote = Vote::new_prevote(Round::new(0), val, ADDRESS);
let thresh = round_votes.add_vote(vote.clone(), weight);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS1, val, weight);
assert_eq!(thresh, Threshold::Unreached);

// add it again, nothing changes.
let thresh = round_votes.add_vote(vote.clone(), weight);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS2, val, weight);
assert_eq!(thresh, Threshold::Unreached);

// add a vote for nil, get Thresh::Any
let vote_nil = Vote::new_prevote(Round::new(0), None, ADDRESS);
let thresh = round_votes.add_vote(vote_nil, weight);
let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS3, None, weight);
assert_eq!(thresh, Threshold::Any);

// add vote for value, get Thresh::Value
let thresh = round_votes.add_vote(vote, weight);
let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS4, val, weight);
assert_eq!(thresh, Threshold::Value(v));
}

Expand All @@ -64,33 +64,29 @@ fn add_votes_multi_values() {
let val2 = Some(v2);
let total = 15;

let mut round_votes: RoundVotes<TestContext> =
RoundVotes::new(Height::new(1), Round::new(0), total);
let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new(total, Default::default());

// add a vote for v1. nothing changes.
let vote1 = Vote::new_precommit(Round::new(0), val1, ADDRESS);
let thresh = round_votes.add_vote(vote1.clone(), 1);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS1, val1, 1);
assert_eq!(thresh, Threshold::Unreached);

// add a vote for v2. nothing changes.
let vote2 = Vote::new_precommit(Round::new(0), val2, ADDRESS);
let thresh = round_votes.add_vote(vote2.clone(), 1);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS2, val2, 1);
assert_eq!(thresh, Threshold::Unreached);

// add a vote for nil. nothing changes.
let vote_nil = Vote::new_precommit(Round::new(0), None, ADDRESS);
let thresh = round_votes.add_vote(vote_nil.clone(), 1);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS3, None, 1);
assert_eq!(thresh, Threshold::Unreached);

// add a vote for v1. nothing changes
let thresh = round_votes.add_vote(vote1.clone(), 1);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS4, val1, 1);
assert_eq!(thresh, Threshold::Unreached);

// add a vote for v2. nothing changes
let thresh = round_votes.add_vote(vote2.clone(), 1);
assert_eq!(thresh, Threshold::Init);
let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS5, val2, 1);
assert_eq!(thresh, Threshold::Unreached);

// add a big vote for v2. get Value(v2)
let thresh = round_votes.add_vote(vote2.clone(), 10);
let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS6, val2, 10);
assert_eq!(thresh, Threshold::Value(v2));
}
Loading

0 comments on commit 52d51a4

Please sign in to comment.