From 2a7862b27bbbc0cd88087f270856c5fc26fc3fe9 Mon Sep 17 00:00:00 2001
From: Romain Ruetschi <romain@informal.systems>
Date: Wed, 22 Nov 2023 11:12:12 +0100
Subject: [PATCH] fix(votekeeper): Compute threshold directly in `VoteKeeper`
 to account for `Skip` threshold (#70)

* Failing test for skip round

* Fix expected_output and add test for skip with quorum

* fix(votekeeper): Compute threshold directly in `VoteKeeper` to account for `Skip` threshold (#73)

* fix(votekeeper): Compute threshold directly in `VoteKeeper` to account for `Skip` threshold

* test: Re-enable VoteCount tests

* test: Re-enable RoundVotes tests

* test: Remove duplicate tests

* test: Re-enable VoteKeeper tests

* fix(votekeeper): Do not count validator weights twice if they prevoted and precommited when checking for Skip threshold

* Fix skip tests

---------

Co-authored-by: Anca Zamfir <anca@informal.systems>
---
 Code/common/src/vote.rs        |   2 +-
 Code/driver/src/driver.rs      |  31 ++--
 Code/test/src/vote.rs          |   4 +-
 Code/test/tests/driver.rs      | 267 +++++++++++++++++++++++++++++++++
 Code/test/tests/round_votes.rs |  76 ++++------
 Code/test/tests/vote_count.rs  | 155 -------------------
 Code/test/tests/vote_keeper.rs | 199 +++++++++++++++++++-----
 Code/vote/src/count.rs         | 252 ++++++++++++++-----------------
 Code/vote/src/keeper.rs        | 109 ++++++++++----
 Code/vote/src/round_votes.rs   |  58 ++++++-
 10 files changed, 720 insertions(+), 433 deletions(-)
 delete mode 100644 Code/test/tests/vote_count.rs

diff --git a/Code/common/src/vote.rs b/Code/common/src/vote.rs
index dd5d41f70..84bd6969b 100644
--- a/Code/common/src/vote.rs
+++ b/Code/common/src/vote.rs
@@ -28,7 +28,7 @@ where
     fn round(&self) -> Round;
 
     /// Get a reference to the value being voted for.
-    fn value(&self) -> Option<&<Ctx::Value as Value>::Id>;
+    fn value(&self) -> &Option<<Ctx::Value as Value>::Id>;
 
     /// Take ownership of the value being voted for.
     fn take_value(self) -> Option<<Ctx::Value as Value>::Id>;
diff --git a/Code/driver/src/driver.rs b/Code/driver/src/driver.rs
index 574dd903f..7b7cbc484 100644
--- a/Code/driver/src/driver.rs
+++ b/Code/driver/src/driver.rs
@@ -10,6 +10,7 @@ use malachite_round::state::State as RoundState;
 use malachite_vote::keeper::Message as VoteMessage;
 use malachite_vote::keeper::VoteKeeper;
 use malachite_vote::Threshold;
+use malachite_vote::ThresholdParams;
 
 use crate::env::Env as DriverEnv;
 use crate::event::Event;
@@ -50,7 +51,10 @@ where
         validator_set: Ctx::ValidatorSet,
         address: Ctx::Address,
     ) -> Self {
-        let votes = VoteKeeper::new(validator_set.total_voting_power());
+        let votes = VoteKeeper::new(
+            validator_set.total_voting_power(),
+            ThresholdParams::default(), // TODO: Make this configurable
+        );
 
         Self {
             ctx,
@@ -63,9 +67,17 @@ where
         }
     }
 
+    pub fn height(&self) -> &Ctx::Height {
+        &self.round_state.height
+    }
+
+    pub fn round(&self) -> Round {
+        self.round_state.round
+    }
+
     async fn get_value(&self) -> Option<Ctx::Value> {
         self.env
-            .get_value(self.round_state.height.clone(), self.round_state.round)
+            .get_value(self.height().clone(), self.round())
             .await
     }
 
@@ -76,9 +88,7 @@ where
         };
 
         let msg = match round_msg {
-            RoundMessage::NewRound(round) => {
-                Message::NewRound(self.round_state.height.clone(), round)
-            }
+            RoundMessage::NewRound(round) => Message::NewRound(self.height().clone(), round),
 
             RoundMessage::Proposal(proposal) => {
                 // sign the proposal
@@ -228,11 +238,12 @@ where
             ));
         }
 
-        let round = signed_vote.vote.round();
+        let vote_round = signed_vote.vote.round();
+        let current_round = self.round();
 
-        let Some(vote_msg) = self
-            .votes
-            .apply_vote(signed_vote.vote, validator.voting_power())
+        let Some(vote_msg) =
+            self.votes
+                .apply_vote(signed_vote.vote, validator.voting_power(), current_round)
         else {
             return Ok(None);
         };
@@ -246,7 +257,7 @@ where
             VoteMessage::SkipRound(r) => RoundEvent::SkipRound(r),
         };
 
-        Ok(self.apply_event(round, round_event))
+        Ok(self.apply_event(vote_round, round_event))
     }
 
     fn apply_timeout(&mut self, timeout: Timeout) -> Option<RoundMessage<Ctx>> {
diff --git a/Code/test/src/vote.rs b/Code/test/src/vote.rs
index dd33175e9..35ae1117f 100644
--- a/Code/test/src/vote.rs
+++ b/Code/test/src/vote.rs
@@ -82,8 +82,8 @@ impl malachite_common::Vote<TestContext> for Vote {
         self.round
     }
 
-    fn value(&self) -> Option<&ValueId> {
-        self.value.as_ref()
+    fn value(&self) -> &Option<ValueId> {
+        &self.value
     }
 
     fn take_value(self) -> Option<ValueId> {
diff --git a/Code/test/tests/driver.rs b/Code/test/tests/driver.rs
index 93d4006ba..2e031e0a0 100644
--- a/Code/test/tests/driver.rs
+++ b/Code/test/tests/driver.rs
@@ -975,3 +975,270 @@ fn driver_steps_invalid_signature() {
 
     assert!(matches!(output, Err(Error::InvalidVoteSignature(_, _))));
 }
+
+#[test]
+fn driver_steps_skip_round_skip_threshold() {
+    let value = Value::new(9999);
+
+    let sel = RotateProposer::default();
+    let env = TestEnv::new(move |_, _| Some(value));
+
+    let mut rng = StdRng::seed_from_u64(0x42);
+
+    let sk1 = PrivateKey::generate(&mut rng);
+    let sk2 = PrivateKey::generate(&mut rng);
+    let sk3 = PrivateKey::generate(&mut rng);
+
+    let addr1 = Address::from_public_key(&sk1.public_key());
+    let addr2 = Address::from_public_key(&sk2.public_key());
+    let addr3 = Address::from_public_key(&sk3.public_key());
+
+    let v1 = Validator::new(sk1.public_key(), 1);
+    let v2 = Validator::new(sk2.public_key(), 1);
+    let v3 = Validator::new(sk3.public_key(), 1);
+
+    // Proposer is v1, so we, v3, are not the proposer
+    let (my_sk, my_addr) = (sk3, addr3);
+
+    let ctx = TestContext::new(my_sk.clone());
+    let height = Height::new(1);
+
+    let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]);
+    let mut driver = Driver::new(ctx, env, sel, vs, my_addr);
+
+    let steps = vec![
+        // Start round 0, we, v3, are not the proposer
+        TestStep {
+            desc: "Start round 0, we, v3, are not the proposer",
+            input_event: Some(Event::NewRound(height, Round::new(0))),
+            expected_output: Some(Message::ScheduleTimeout(Timeout::propose(Round::new(0)))),
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Propose,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // Receive a propose timeout, prevote for nil (from v3)
+        TestStep {
+            desc: "Receive a propose timeout, prevote for nil (v3)",
+            input_event: Some(Event::TimeoutElapsed(Timeout::propose(Round::new(0)))),
+            expected_output: Some(Message::Vote(
+                Vote::new_prevote(height, Round::new(0), None, my_addr).signed(&my_sk),
+            )),
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Prevote,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // Receive our own prevote v3
+        TestStep {
+            desc: "Receive our own prevote v3",
+            input_event: None,
+            expected_output: None,
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Prevote,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // v1 prevotes for its own proposal
+        TestStep {
+            desc: "v1 prevotes for its own proposal in round 1",
+            input_event: Some(Event::Vote(
+                Vote::new_prevote(height, Round::new(1), Some(value.id()), addr1).signed(&sk1),
+            )),
+            expected_output: None,
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Prevote,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // v2 prevotes for v1 proposal in round 1, expected output is to move to next round
+        TestStep {
+            desc: "v2 prevotes for v1 proposal, we get +1/3 messages from future round",
+            input_event: Some(Event::Vote(
+                Vote::new_prevote(height, Round::new(1), Some(value.id()), addr2).signed(&sk2),
+            )),
+            expected_output: Some(Message::NewRound(height, Round::new(1))),
+            expected_round: Round::new(1),
+            new_state: State {
+                height,
+                round: Round::new(1),
+                step: Step::NewRound,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+    ];
+
+    let mut previous_message = None;
+
+    for step in steps {
+        println!("Step: {}", step.desc);
+
+        let execute_message = step
+            .input_event
+            .unwrap_or_else(|| previous_message.unwrap());
+
+        let output = block_on(driver.execute(execute_message)).expect("execute succeeded");
+        assert_eq!(output, step.expected_output, "expected output message");
+
+        assert_eq!(driver.round(), step.expected_round, "expected round");
+        assert_eq!(driver.round_state, step.new_state, "new state");
+
+        previous_message = output.and_then(to_input_msg);
+    }
+}
+
+#[test]
+fn driver_steps_skip_round_quorum_threshold() {
+    let value = Value::new(9999);
+
+    let sel = RotateProposer::default();
+    let env = TestEnv::new(move |_, _| Some(value));
+
+    let mut rng = StdRng::seed_from_u64(0x42);
+
+    let sk1 = PrivateKey::generate(&mut rng);
+    let sk2 = PrivateKey::generate(&mut rng);
+    let sk3 = PrivateKey::generate(&mut rng);
+
+    let addr1 = Address::from_public_key(&sk1.public_key());
+    let addr2 = Address::from_public_key(&sk2.public_key());
+    let addr3 = Address::from_public_key(&sk3.public_key());
+
+    let v1 = Validator::new(sk1.public_key(), 1);
+    let v2 = Validator::new(sk2.public_key(), 2);
+    let v3 = Validator::new(sk3.public_key(), 1);
+
+    // Proposer is v1, so we, v3, are not the proposer
+    let (my_sk, my_addr) = (sk3, addr3);
+
+    let ctx = TestContext::new(my_sk.clone());
+    let height = Height::new(1);
+
+    let vs = ValidatorSet::new(vec![v1.clone(), v2.clone(), v3.clone()]);
+    let mut driver = Driver::new(ctx, env, sel, vs, my_addr);
+
+    let steps = vec![
+        // Start round 0, we, v3, are not the proposer
+        TestStep {
+            desc: "Start round 0, we, v3, are not the proposer",
+            input_event: Some(Event::NewRound(height, Round::new(0))),
+            expected_output: Some(Message::ScheduleTimeout(Timeout::propose(Round::new(0)))),
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Propose,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // Receive a propose timeout, prevote for nil (from v3)
+        TestStep {
+            desc: "Receive a propose timeout, prevote for nil (v3)",
+            input_event: Some(Event::TimeoutElapsed(Timeout::propose(Round::new(0)))),
+            expected_output: Some(Message::Vote(
+                Vote::new_prevote(height, Round::new(0), None, my_addr).signed(&my_sk),
+            )),
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Prevote,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // Receive our own prevote v3
+        TestStep {
+            desc: "Receive our own prevote v3",
+            input_event: None,
+            expected_output: None,
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Prevote,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // v1 prevotes for its own proposal
+        TestStep {
+            desc: "v1 prevotes for its own proposal in round 1",
+            input_event: Some(Event::Vote(
+                Vote::new_prevote(height, Round::new(1), Some(value.id()), addr1).signed(&sk1),
+            )),
+            expected_output: None,
+            expected_round: Round::new(0),
+            new_state: State {
+                height,
+                round: Round::new(0),
+                step: Step::Prevote,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+        // v2 prevotes for v1 proposal in round 1, expected output is to move to next round
+        TestStep {
+            desc: "v2 prevotes for v1 proposal, we get +1/3 messages from future round",
+            input_event: Some(Event::Vote(
+                Vote::new_prevote(height, Round::new(1), Some(value.id()), addr2).signed(&sk2),
+            )),
+            expected_output: Some(Message::NewRound(height, Round::new(1))),
+            expected_round: Round::new(1),
+            new_state: State {
+                height,
+                round: Round::new(1),
+                step: Step::NewRound,
+                proposal: None,
+                locked: None,
+                valid: None,
+            },
+        },
+    ];
+
+    let mut previous_message = None;
+
+    for step in steps {
+        println!("Step: {}", step.desc);
+
+        let execute_message = step
+            .input_event
+            .unwrap_or_else(|| previous_message.unwrap());
+
+        let output = block_on(driver.execute(execute_message)).expect("execute succeeded");
+        assert_eq!(output, step.expected_output, "expected output message");
+
+        assert_eq!(driver.round(), step.expected_round, "expected round");
+
+        assert_eq!(driver.round_state, step.new_state, "new state");
+
+        previous_message = output.and_then(to_input_msg);
+    }
+}
diff --git a/Code/test/tests/round_votes.rs b/Code/test/tests/round_votes.rs
index 8be3f46e4..6132734c9 100644
--- a/Code/test/tests/round_votes.rs
+++ b/Code/test/tests/round_votes.rs
@@ -1,6 +1,5 @@
 use malachite_common::VoteType;
 use malachite_vote::round_votes::RoundVotes;
-use malachite_vote::Threshold;
 
 use malachite_test::{Address, ValueId};
 
@@ -13,47 +12,41 @@ const ADDRESS6: Address = Address::new([46; 20]);
 
 #[test]
 fn add_votes_nil() {
-    let total = 3;
+    let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new();
 
-    let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new(total, Default::default());
+    let w = round_votes.add_vote(VoteType::Prevote, ADDRESS1, None, 1);
+    assert_eq!(w, 1);
 
-    // add a vote for nil. nothing changes.
-    let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS1, None, 1);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Prevote, ADDRESS2, None, 1);
+    assert_eq!(w, 2);
 
-    // add it again, nothing changes.
-    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(VoteType::Prevote, ADDRESS3, None, 1);
-    assert_eq!(thresh, Threshold::Nil);
+    let w = round_votes.add_vote(VoteType::Prevote, ADDRESS3, None, 1);
+    assert_eq!(w, 3);
 }
 
 #[test]
 fn add_votes_single_value() {
     let v = ValueId::new(1);
     let val = Some(v);
-    let total = 4;
     let weight = 1;
 
-    let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new(total, Default::default());
+    let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new();
 
     // add a vote. nothing changes.
-    let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS1, val, weight);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Prevote, ADDRESS1, val, weight);
+    assert_eq!(w, 1);
 
     // add it again, nothing changes.
-    let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS2, val, weight);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Prevote, ADDRESS2, val, weight);
+    assert_eq!(w, 2);
 
-    // add a vote for nil, get Thresh::Any
-    let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS3, None, weight);
-    assert_eq!(thresh, Threshold::Any);
+    // add a vote for nil, get w::Any
+    let w = round_votes.add_vote(VoteType::Prevote, ADDRESS3, None, weight);
+    assert_eq!(w, 1);
 
-    // add vote for value, get Thresh::Value
-    let thresh = round_votes.add_vote(VoteType::Prevote, ADDRESS4, val, weight);
-    assert_eq!(thresh, Threshold::Value(v));
+    // add vote for value, get w::Value
+    let w = round_votes.add_vote(VoteType::Prevote, ADDRESS4, val, weight);
+    assert_eq!(w, 3);
 }
 
 #[test]
@@ -62,31 +55,24 @@ fn add_votes_multi_values() {
     let v2 = ValueId::new(2);
     let val1 = Some(v1);
     let val2 = Some(v2);
-    let total = 15;
 
-    let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new(total, Default::default());
+    let mut round_votes: RoundVotes<_, ValueId> = RoundVotes::new();
 
-    // add a vote for v1. nothing changes.
-    let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS1, val1, 1);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Precommit, ADDRESS1, val1, 1);
+    assert_eq!(w, 1);
 
-    // add a vote for v2. nothing changes.
-    let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS2, val2, 1);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Precommit, ADDRESS2, val2, 1);
+    assert_eq!(w, 1);
 
-    // add a vote for nil. nothing changes.
-    let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS3, None, 1);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Precommit, ADDRESS3, None, 1);
+    assert_eq!(w, 1);
 
-    // add a vote for v1. nothing changes
-    let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS4, val1, 1);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Precommit, ADDRESS4, val1, 1);
+    assert_eq!(w, 2);
 
-    // add a vote for v2. nothing changes
-    let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS5, val2, 1);
-    assert_eq!(thresh, Threshold::Unreached);
+    let w = round_votes.add_vote(VoteType::Precommit, ADDRESS5, val2, 1);
+    assert_eq!(w, 2);
 
-    // add a big vote for v2. get Value(v2)
-    let thresh = round_votes.add_vote(VoteType::Precommit, ADDRESS6, val2, 10);
-    assert_eq!(thresh, Threshold::Value(v2));
+    let w = round_votes.add_vote(VoteType::Precommit, ADDRESS6, val2, 10);
+    assert_eq!(w, 12);
 }
diff --git a/Code/test/tests/vote_count.rs b/Code/test/tests/vote_count.rs
deleted file mode 100644
index 179222dda..000000000
--- a/Code/test/tests/vote_count.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-#![allow(clippy::bool_assert_comparison)]
-
-use malachite_vote::count::VoteCount;
-use malachite_vote::Threshold;
-
-#[test]
-fn vote_count_nil() {
-    let mut vc = VoteCount::new(4, Default::default());
-
-    let addr1 = [1];
-    let addr2 = [2];
-    let addr3 = [3];
-    let addr4 = [4];
-
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 0);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr1, None, 1), Threshold::Unreached);
-    assert_eq!(vc.get(&None), 1);
-    assert_eq!(vc.get(&Some(1)), 0);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr2, None, 1), Threshold::Unreached);
-    assert_eq!(vc.get(&None), 2);
-    assert_eq!(vc.get(&Some(1)), 0);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    // addr1 votes again, is ignored
-    assert_eq!(vc.add(addr1, None, 1), Threshold::Unreached);
-    assert_eq!(vc.get(&None), 2);
-    assert_eq!(vc.get(&Some(1)), 0);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr3, None, 1), Threshold::Nil);
-    assert_eq!(vc.get(&None), 3);
-    assert_eq!(vc.get(&Some(1)), 0);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr4, Some(1), 1), Threshold::Any);
-    assert_eq!(vc.get(&None), 3);
-    assert_eq!(vc.get(&Some(1)), 1);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-}
-
-#[test]
-fn vote_count_value() {
-    let mut vc = VoteCount::new(4, Default::default());
-
-    let addr1 = [1];
-    let addr2 = [2];
-    let addr3 = [3];
-    let addr4 = [4];
-
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 0);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr1, Some(1), 1), Threshold::Unreached);
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 1);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr2, Some(1), 1), Threshold::Unreached);
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 2);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    // addr1 votes again, for nil this time, is ignored
-    assert_eq!(vc.add(addr1, None, 1), Threshold::Unreached);
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 2);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr3, Some(1), 1), Threshold::Value(1));
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 3);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    // addr2 votes again, for the same value, is ignored
-    assert_eq!(vc.add(addr2, Some(1), 1), Threshold::Value(1));
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 3);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    assert_eq!(vc.add(addr4, Some(2), 1), Threshold::Any);
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 3);
-    assert_eq!(vc.get(&Some(2)), 1);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-
-    // addr4 votes again, for a different value, is ignored
-    assert_eq!(vc.add(addr4, Some(3), 1), Threshold::Any);
-    assert_eq!(vc.get(&None), 0);
-    assert_eq!(vc.get(&Some(1)), 3);
-    assert_eq!(vc.get(&Some(2)), 1);
-    assert_eq!(vc.get(&Some(3)), 0);
-    assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-    assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
-}
diff --git a/Code/test/tests/vote_keeper.rs b/Code/test/tests/vote_keeper.rs
index 47d8f4eca..f1d6d7849 100644
--- a/Code/test/tests/vote_keeper.rs
+++ b/Code/test/tests/vote_keeper.rs
@@ -10,82 +10,205 @@ const ADDRESS4: Address = Address::new([44; 20]);
 
 #[test]
 fn prevote_apply_nil() {
-    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(3);
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(3, Default::default());
+    let height = Height::new(1);
+    let round = Round::new(0);
 
-    let vote = Vote::new_prevote(Height::new(1), Round::new(0), None, ADDRESS1);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_prevote(height, round, None, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote = Vote::new_prevote(Height::new(1), Round::new(0), None, ADDRESS2);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_prevote(height, round, None, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote = Vote::new_prevote(Height::new(1), Round::new(0), None, ADDRESS3);
-    let msg = keeper.apply_vote(vote, 1);
+    let vote = Vote::new_prevote(height, round, None, ADDRESS3);
+    let msg = keeper.apply_vote(vote, 1, round);
     assert_eq!(msg, Some(Message::PolkaNil));
 }
 
 #[test]
 fn precommit_apply_nil() {
-    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(3);
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(3, Default::default());
+    let height = Height::new(1);
+    let round = Round::new(0);
 
-    let vote = Vote::new_precommit(Height::new(1), Round::new(0), None, ADDRESS1);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_precommit(height, round, None, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote = Vote::new_precommit(Height::new(1), Round::new(0), None, ADDRESS2);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_precommit(height, Round::new(0), None, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote = Vote::new_precommit(Height::new(1), Round::new(0), None, ADDRESS3);
-    let msg = keeper.apply_vote(vote, 1);
+    let vote = Vote::new_precommit(height, Round::new(0), None, ADDRESS3);
+    let msg = keeper.apply_vote(vote, 1, round);
     assert_eq!(msg, Some(Message::PrecommitAny));
 }
 
 #[test]
 fn prevote_apply_single_value() {
-    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(4);
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(4, Default::default());
 
-    let v = ValueId::new(1);
-    let val = Some(v);
+    let id = ValueId::new(1);
+    let val = Some(id);
+    let height = Height::new(1);
+    let round = Round::new(0);
 
-    let vote = Vote::new_prevote(Height::new(1), Round::new(0), val, ADDRESS1);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_prevote(height, Round::new(0), val, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote = Vote::new_prevote(Height::new(1), Round::new(0), val, ADDRESS2);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_prevote(height, Round::new(0), val, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote_nil = Vote::new_prevote(Height::new(1), Round::new(0), None, ADDRESS3);
-    let msg = keeper.apply_vote(vote_nil, 1);
+    let vote_nil = Vote::new_prevote(height, Round::new(0), None, ADDRESS3);
+    let msg = keeper.apply_vote(vote_nil, 1, round);
     assert_eq!(msg, Some(Message::PolkaAny));
 
-    let vote = Vote::new_prevote(Height::new(1), Round::new(0), val, ADDRESS4);
-    let msg = keeper.apply_vote(vote, 1);
-    assert_eq!(msg, Some(Message::PolkaValue(v)));
+    let vote = Vote::new_prevote(height, Round::new(0), val, ADDRESS4);
+    let msg = keeper.apply_vote(vote, 1, round);
+    assert_eq!(msg, Some(Message::PolkaValue(id)));
 }
 
 #[test]
 fn precommit_apply_single_value() {
-    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(4);
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(4, Default::default());
 
-    let v = ValueId::new(1);
-    let val = Some(v);
+    let id = ValueId::new(1);
+    let val = Some(id);
+    let height = Height::new(1);
+    let round = Round::new(0);
 
-    let vote = Vote::new_precommit(Height::new(1), Round::new(0), val, ADDRESS1);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_precommit(height, Round::new(0), val, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote = Vote::new_precommit(Height::new(1), Round::new(0), val, ADDRESS2);
-    let msg = keeper.apply_vote(vote.clone(), 1);
+    let vote = Vote::new_precommit(height, Round::new(0), val, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, round);
     assert_eq!(msg, None);
 
-    let vote_nil = Vote::new_precommit(Height::new(1), Round::new(0), None, ADDRESS3);
-    let msg = keeper.apply_vote(vote_nil, 1);
+    let vote_nil = Vote::new_precommit(height, Round::new(0), None, ADDRESS3);
+    let msg = keeper.apply_vote(vote_nil, 1, round);
     assert_eq!(msg, Some(Message::PrecommitAny));
 
-    let vote = Vote::new_precommit(Height::new(1), Round::new(0), val, ADDRESS4);
-    let msg = keeper.apply_vote(vote, 1);
-    assert_eq!(msg, Some(Message::PrecommitValue(v)));
+    let vote = Vote::new_precommit(height, Round::new(0), val, ADDRESS4);
+    let msg = keeper.apply_vote(vote, 1, round);
+    assert_eq!(msg, Some(Message::PrecommitValue(id)));
+}
+
+#[test]
+fn skip_round_small_quorum_prevotes_two_vals() {
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(4, Default::default());
+
+    let id = ValueId::new(1);
+    let val = Some(id);
+    let height = Height::new(1);
+    let cur_round = Round::new(0);
+    let fut_round = Round::new(1);
+
+    let vote = Vote::new_prevote(height, cur_round, val, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_prevote(height, fut_round, val, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_prevote(height, fut_round, val, ADDRESS3);
+    let msg = keeper.apply_vote(vote, 1, cur_round);
+    assert_eq!(msg, Some(Message::SkipRound(Round::new(1))));
+}
+
+#[test]
+fn skip_round_small_quorum_with_prevote_precommit_two_vals() {
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(4, Default::default());
+
+    let id = ValueId::new(1);
+    let val = Some(id);
+    let height = Height::new(1);
+    let cur_round = Round::new(0);
+    let fut_round = Round::new(1);
+
+    let vote = Vote::new_prevote(height, cur_round, val, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_prevote(height, fut_round, val, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_precommit(height, fut_round, val, ADDRESS3);
+    let msg = keeper.apply_vote(vote, 1, cur_round);
+    assert_eq!(msg, Some(Message::SkipRound(Round::new(1))));
+}
+
+#[test]
+fn skip_round_full_quorum_with_prevote_precommit_two_vals() {
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(5, Default::default());
+
+    let id = ValueId::new(1);
+    let val = Some(id);
+    let height = Height::new(1);
+    let cur_round = Round::new(0);
+    let fut_round = Round::new(1);
+
+    let vote = Vote::new_prevote(height, cur_round, val, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_prevote(height, fut_round, val, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_precommit(height, fut_round, val, ADDRESS3);
+    let msg = keeper.apply_vote(vote, 2, cur_round);
+    assert_eq!(msg, Some(Message::SkipRound(Round::new(1))));
+}
+
+#[test]
+fn no_skip_round_small_quorum_with_same_val() {
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(4, Default::default());
+
+    let id = ValueId::new(1);
+    let val = Some(id);
+    let height = Height::new(1);
+    let cur_round = Round::new(0);
+    let fut_round = Round::new(1);
+
+    let vote = Vote::new_prevote(height, cur_round, val, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_prevote(height, fut_round, val, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_precommit(height, fut_round, val, ADDRESS2);
+    let msg = keeper.apply_vote(vote, 1, cur_round);
+    assert_eq!(msg, None);
+}
+
+#[test]
+fn no_skip_round_full_quorum_with_same_val() {
+    let mut keeper: VoteKeeper<TestContext> = VoteKeeper::new(5, Default::default());
+
+    let id = ValueId::new(1);
+    let val = Some(id);
+    let height = Height::new(1);
+    let cur_round = Round::new(0);
+    let fut_round = Round::new(1);
+
+    let vote = Vote::new_prevote(height, cur_round, val, ADDRESS1);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_prevote(height, fut_round, val, ADDRESS2);
+    let msg = keeper.apply_vote(vote.clone(), 1, cur_round);
+    assert_eq!(msg, None);
+
+    let vote = Vote::new_precommit(height, fut_round, val, ADDRESS2);
+    let msg = keeper.apply_vote(vote, 2, cur_round);
+    assert_eq!(msg, None);
 }
diff --git a/Code/vote/src/count.rs b/Code/vote/src/count.rs
index 5c7b66dcc..dfb970773 100644
--- a/Code/vote/src/count.rs
+++ b/Code/vote/src/count.rs
@@ -1,18 +1,12 @@
 use alloc::collections::BTreeSet;
 
 use crate::value_weights::ValuesWeights;
-use crate::{Threshold, ThresholdParams, Weight};
+use crate::{Threshold, ThresholdParam, Weight};
 
 /// VoteCount tallys votes of the same type.
 /// Votes are for nil or for some value.
 #[derive(Clone, Debug, Default, PartialEq, Eq)]
 pub struct VoteCount<Address, Value> {
-    /// Total weight
-    pub total_weight: Weight,
-
-    /// The threshold parameters
-    pub threshold_params: ThresholdParams,
-
     /// Weight of votes for the values, including nil
     pub values_weights: ValuesWeights<Option<Value>>,
 
@@ -21,10 +15,8 @@ pub struct VoteCount<Address, Value> {
 }
 
 impl<Address, Value> VoteCount<Address, Value> {
-    pub fn new(total_weight: Weight, threshold_params: ThresholdParams) -> Self {
+    pub fn new() -> Self {
         VoteCount {
-            total_weight,
-            threshold_params,
             values_weights: ValuesWeights::new(),
             validator_addresses: BTreeSet::new(),
         }
@@ -32,12 +24,7 @@ impl<Address, Value> VoteCount<Address, Value> {
 
     /// Add vote for a value (or nil) to internal counters, but only if we haven't seen
     /// a vote from that particular validator yet.
-    pub fn add(
-        &mut self,
-        address: Address,
-        value: Option<Value>,
-        weight: Weight,
-    ) -> Threshold<Value>
+    pub fn add(&mut self, address: Address, value: Option<Value>, weight: Weight) -> Weight
     where
         Address: Clone + Ord,
         Value: Clone + Ord,
@@ -45,77 +32,52 @@ impl<Address, Value> VoteCount<Address, Value> {
         let already_voted = !self.validator_addresses.insert(address);
 
         if !already_voted {
-            self.values_weights.add(value.clone(), weight);
+            self.values_weights.add(value, weight)
+        } else {
+            self.values_weights.get(&value)
         }
-
-        self.compute_threshold(value)
     }
 
-    /// Compute whether or not we have reached a threshold for the given value,
-    /// and return that threshold.
-    pub fn compute_threshold(&self, value: Option<Value>) -> Threshold<Value>
+    pub fn get(&self, value: &Option<Value>) -> Weight
     where
-        Address: Ord,
         Value: Ord,
     {
-        let weight = self.values_weights.get(&value);
-
-        match value {
-            Some(value) if self.is_quorum(weight, self.total_weight) => Threshold::Value(value),
-
-            None if self.is_quorum(weight, self.total_weight) => Threshold::Nil,
-
-            _ => {
-                let sum_weight = self.values_weights.sum();
+        self.values_weights.get(value)
+    }
 
-                if self.is_quorum(sum_weight, self.total_weight) {
-                    Threshold::Any
-                } else {
-                    Threshold::Unreached
-                }
-            }
-        }
+    pub fn sum(&self) -> Weight {
+        self.values_weights.sum()
     }
 
     /// Return whether or not the threshold is met, ie. if we have a quorum for that threshold.
-    pub fn is_threshold_met(&self, threshold: Threshold<Value>) -> bool
+    pub fn is_threshold_met(
+        &self,
+        threshold: Threshold<Value>,
+        param: ThresholdParam,
+        total_weight: Weight,
+    ) -> bool
     where
         Value: Ord,
     {
         match threshold {
             Threshold::Value(value) => {
                 let weight = self.values_weights.get(&Some(value));
-                self.is_quorum(weight, self.total_weight)
+                param.is_met(weight, total_weight)
             }
 
             Threshold::Nil => {
                 let weight = self.values_weights.get(&None);
-                self.is_quorum(weight, self.total_weight)
+                param.is_met(weight, total_weight)
             }
 
             Threshold::Any => {
                 let sum_weight = self.values_weights.sum();
-                self.is_quorum(sum_weight, self.total_weight)
+                param.is_met(sum_weight, total_weight)
             }
 
             Threshold::Skip | Threshold::Unreached => false,
         }
     }
-
-    pub fn get(&self, value: &Option<Value>) -> Weight
-    where
-        Value: Ord,
-    {
-        self.values_weights.get(value)
-    }
-
-    pub fn total_weight(&self) -> Weight {
-        self.total_weight
-    }
-
-    fn is_quorum(&self, sum: Weight, total: Weight) -> bool {
-        self.threshold_params.quorum.is_met(sum, total)
-    }
 }
 
 #[cfg(test)]
@@ -125,7 +87,10 @@ mod tests {
 
     #[test]
     fn vote_count_nil() {
-        let mut vc = VoteCount::new(4, Default::default());
+        let t = 4;
+        let q = ThresholdParam::TWO_F_PLUS_ONE;
+
+        let mut vc = VoteCount::new();
 
         let addr1 = [1];
         let addr2 = [2];
@@ -134,62 +99,65 @@ mod tests {
 
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 0);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr1, None, 1), Threshold::Unreached);
+        assert_eq!(vc.add(addr1, None, 1), 1);
         assert_eq!(vc.get(&None), 1);
         assert_eq!(vc.get(&Some(1)), 0);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr2, None, 1), Threshold::Unreached);
+        assert_eq!(vc.add(addr2, None, 1), 2);
         assert_eq!(vc.get(&None), 2);
         assert_eq!(vc.get(&Some(1)), 0);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
         // addr1 votes again, is ignored
-        assert_eq!(vc.add(addr1, None, 1), Threshold::Unreached);
+        assert_eq!(vc.add(addr1, None, 1), 2);
         assert_eq!(vc.get(&None), 2);
         assert_eq!(vc.get(&Some(1)), 0);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr3, None, 1), Threshold::Nil);
+        assert_eq!(vc.add(addr3, None, 1), 3);
         assert_eq!(vc.get(&None), 3);
         assert_eq!(vc.get(&Some(1)), 0);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr4, Some(1), 1), Threshold::Any);
+        assert_eq!(vc.add(addr4, Some(1), 1), 1);
         assert_eq!(vc.get(&None), 3);
         assert_eq!(vc.get(&Some(1)), 1);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
     }
 
     #[test]
     fn vote_count_value() {
-        let mut vc = VoteCount::new(4, Default::default());
+        let t = 4;
+        let q = ThresholdParam::TWO_F_PLUS_ONE;
+
+        let mut vc = VoteCount::new();
 
         let addr1 = [1];
         let addr2 = [2];
@@ -198,79 +166,79 @@ mod tests {
 
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 0);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr1, Some(1), 1), Threshold::Unreached);
+        assert_eq!(vc.add(addr1, Some(1), 1), 1);
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 1);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr2, Some(1), 1), Threshold::Unreached);
+        assert_eq!(vc.add(addr2, Some(1), 1), 2);
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 2);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
         // addr1 votes again, for nil this time, is ignored
-        assert_eq!(vc.add(addr1, None, 1), Threshold::Unreached);
+        assert_eq!(vc.add(addr1, None, 1), 0);
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 2);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr3, Some(1), 1), Threshold::Value(1));
+        assert_eq!(vc.add(addr3, Some(1), 1), 3);
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 3);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
         // addr2 votes again, for the same value, is ignored
-        assert_eq!(vc.add(addr2, Some(1), 1), Threshold::Value(1));
+        assert_eq!(vc.add(addr2, Some(1), 1), 3);
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 3);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
-        assert_eq!(vc.add(addr4, Some(2), 1), Threshold::Any);
+        assert_eq!(vc.add(addr4, Some(2), 1), 1);
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 3);
         assert_eq!(vc.get(&Some(2)), 1);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
 
         // addr4 votes again, for a different value, is ignored
-        assert_eq!(vc.add(addr4, Some(3), 1), Threshold::Any);
+        assert_eq!(vc.add(addr4, Some(3), 1), 0);
         assert_eq!(vc.get(&None), 0);
         assert_eq!(vc.get(&Some(1)), 3);
         assert_eq!(vc.get(&Some(2)), 1);
         assert_eq!(vc.get(&Some(3)), 0);
-        assert_eq!(vc.is_threshold_met(Threshold::Unreached), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Any), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Nil), false);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(1)), true);
-        assert_eq!(vc.is_threshold_met(Threshold::Value(2)), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Unreached, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Any, q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Nil, q, t), false);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(1), q, t), true);
+        assert_eq!(vc.is_threshold_met(Threshold::Value(2), q, t), false);
     }
 }
diff --git a/Code/vote/src/keeper.rs b/Code/vote/src/keeper.rs
index 5694ec2d7..b1668f288 100644
--- a/Code/vote/src/keeper.rs
+++ b/Code/vote/src/keeper.rs
@@ -31,9 +31,9 @@ impl<Ctx> PerRound<Ctx>
 where
     Ctx: Context,
 {
-    fn new(total_weight: Weight, threshold_params: ThresholdParams) -> Self {
+    fn new() -> Self {
         Self {
-            votes: RoundVotes::new(total_weight, threshold_params),
+            votes: RoundVotes::new(),
             addresses_weights: RoundWeights::new(),
             emitted_msgs: BTreeSet::new(),
         }
@@ -46,8 +46,8 @@ pub struct VoteKeeper<Ctx>
 where
     Ctx: Context,
 {
-    threshold_params: ThresholdParams,
     total_weight: Weight,
+    threshold_params: ThresholdParams,
     per_round: BTreeMap<Round, PerRound<Ctx>>,
 }
 
@@ -55,27 +55,30 @@ impl<Ctx> VoteKeeper<Ctx>
 where
     Ctx: Context,
 {
-    pub fn new(total_weight: Weight) -> Self {
+    pub fn new(total_weight: Weight, threshold_params: ThresholdParams) -> Self {
         VoteKeeper {
-            // TODO: Make these configurable
-            threshold_params: ThresholdParams::default(),
-
             total_weight,
+            threshold_params,
             per_round: BTreeMap::new(),
         }
     }
 
     /// Apply a vote with a given weight, potentially triggering an event.
-    pub fn apply_vote(&mut self, vote: Ctx::Vote, weight: Weight) -> Option<Message<ValueId<Ctx>>> {
+    pub fn apply_vote(
+        &mut self,
+        vote: Ctx::Vote,
+        weight: Weight,
+        current_round: Round,
+    ) -> Option<Message<ValueId<Ctx>>> {
         let round = self
             .per_round
             .entry(vote.round())
-            .or_insert_with(|| PerRound::new(self.total_weight, self.threshold_params));
+            .or_insert_with(PerRound::new);
 
-        let threshold = round.votes.add_vote(
+        round.votes.add_vote(
             vote.vote_type(),
             vote.validator_address().clone(),
-            vote.value().cloned(),
+            vote.value().clone(),
             weight,
         );
 
@@ -83,21 +86,38 @@ where
             .addresses_weights
             .set_once(vote.validator_address().clone(), weight);
 
-        let msg = threshold_to_message(vote.vote_type(), vote.round(), threshold)?;
+        if vote.round() > current_round {
+            let combined_weight = round.addresses_weights.sum();
 
-        let final_msg = if !round.emitted_msgs.contains(&msg) {
-            Some(msg)
-        } else if Self::skip_round(round, self.total_weight, self.threshold_params.honest) {
-            Some(Message::SkipRound(vote.round()))
-        } else {
-            None
-        };
+            let skip_round = self
+                .threshold_params
+                .honest
+                .is_met(combined_weight, self.total_weight);
 
-        if let Some(final_msg) = &final_msg {
-            round.emitted_msgs.insert(final_msg.clone());
+            if skip_round {
+                let msg = Message::SkipRound(vote.round());
+                round.emitted_msgs.insert(msg.clone());
+                return Some(msg);
+            }
         }
 
-        final_msg
+        let threshold = compute_threshold(
+            vote.vote_type(),
+            round,
+            vote.value(),
+            self.threshold_params.quorum,
+            self.total_weight,
+        );
+
+        let msg = threshold_to_message(vote.vote_type(), vote.round(), threshold);
+
+        match msg {
+            Some(msg) if !round.emitted_msgs.contains(&msg) => {
+                round.emitted_msgs.insert(msg.clone());
+                Some(msg)
+            }
+            _ => None,
+        }
     }
 
     /// Check if a threshold is met, ie. if we have a quorum for that threshold.
@@ -108,19 +128,44 @@ where
         threshold: Threshold<ValueId<Ctx>>,
     ) -> bool {
         self.per_round.get(round).map_or(false, |round| {
-            round.votes.is_threshold_met(vote_type, threshold)
+            round.votes.is_threshold_met(
+                vote_type,
+                threshold,
+                self.threshold_params.quorum,
+                self.total_weight,
+            )
         })
     }
+}
 
-    /// Check whether or not we should skip this round, in case we haven't emitted any messages
-    /// yet, and we have reached an honest threshold for the round.
-    fn skip_round(
-        round: &PerRound<Ctx>,
-        total_weight: Weight,
-        threshold_param: ThresholdParam,
-    ) -> bool {
-        round.emitted_msgs.is_empty()
-            && threshold_param.is_met(round.addresses_weights.sum(), total_weight)
+/// Compute whether or not we have reached a threshold for the given value,
+/// and return that threshold.
+fn compute_threshold<Ctx>(
+    vote_type: VoteType,
+    round: &PerRound<Ctx>,
+    value: &Option<ValueId<Ctx>>,
+    quorum: ThresholdParam,
+    total_weight: Weight,
+) -> Threshold<ValueId<Ctx>>
+where
+    Ctx: Context,
+{
+    let weight = round.votes.get_weight(vote_type, value);
+
+    match value {
+        Some(value) if quorum.is_met(weight, total_weight) => Threshold::Value(value.clone()),
+
+        None if quorum.is_met(weight, total_weight) => Threshold::Nil,
+
+        _ => {
+            let weight_sum = round.votes.weight_sum(vote_type);
+
+            if quorum.is_met(weight_sum, total_weight) {
+                Threshold::Any
+            } else {
+                Threshold::Unreached
+            }
+        }
     }
 }
 
diff --git a/Code/vote/src/round_votes.rs b/Code/vote/src/round_votes.rs
index 21a14f80b..58307b597 100644
--- a/Code/vote/src/round_votes.rs
+++ b/Code/vote/src/round_votes.rs
@@ -1,7 +1,7 @@
 use malachite_common::VoteType;
 
 use crate::count::VoteCount;
-use crate::{Threshold, ThresholdParams, Weight};
+use crate::{Threshold, ThresholdParam, Weight};
 
 /// Tracks all the votes for a single round
 #[derive(Clone, Debug)]
@@ -11,10 +11,10 @@ pub struct RoundVotes<Address, Value> {
 }
 
 impl<Address, Value> RoundVotes<Address, Value> {
-    pub fn new(total_weight: Weight, threshold_params: ThresholdParams) -> Self {
+    pub fn new() -> Self {
         RoundVotes {
-            prevotes: VoteCount::new(total_weight, threshold_params),
-            precommits: VoteCount::new(total_weight, threshold_params),
+            prevotes: VoteCount::new(),
+            precommits: VoteCount::new(),
         }
     }
 
@@ -24,7 +24,7 @@ impl<Address, Value> RoundVotes<Address, Value> {
         address: Address,
         value: Option<Value>,
         weight: Weight,
-    ) -> Threshold<Value>
+    ) -> Weight
     where
         Address: Clone + Ord,
         Value: Clone + Ord,
@@ -35,13 +35,55 @@ impl<Address, Value> RoundVotes<Address, Value> {
         }
     }
 
-    pub fn is_threshold_met(&self, vote_type: VoteType, threshold: Threshold<Value>) -> bool
+    pub fn get_weight(&self, vote_type: VoteType, value: &Option<Value>) -> Weight
     where
         Value: Ord,
     {
         match vote_type {
-            VoteType::Prevote => self.prevotes.is_threshold_met(threshold),
-            VoteType::Precommit => self.precommits.is_threshold_met(threshold),
+            VoteType::Prevote => self.prevotes.get(value),
+            VoteType::Precommit => self.precommits.get(value),
         }
     }
+
+    pub fn weight_sum(&self, vote_type: VoteType) -> Weight {
+        match vote_type {
+            VoteType::Prevote => self.prevotes.sum(),
+            VoteType::Precommit => self.precommits.sum(),
+        }
+    }
+
+    pub fn combined_weight(&self, value: &Option<Value>) -> Weight
+    where
+        Value: Ord,
+    {
+        self.prevotes.get(value) + self.precommits.get(value)
+    }
+
+    /// Return whether or not the threshold is met, ie. if we have a quorum for that threshold.
+    pub fn is_threshold_met(
+        &self,
+        vote_type: VoteType,
+        threshold: Threshold<Value>,
+        param: ThresholdParam,
+        total_weight: Weight,
+    ) -> bool
+    where
+        Value: Ord,
+    {
+        match vote_type {
+            VoteType::Prevote => self
+                .prevotes
+                .is_threshold_met(threshold, param, total_weight),
+
+            VoteType::Precommit => self
+                .precommits
+                .is_threshold_met(threshold, param, total_weight),
+        }
+    }
+}
+
+impl<Address, Value> Default for RoundVotes<Address, Value> {
+    fn default() -> Self {
+        Self::new()
+    }
 }