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<&::Id>; + fn value(&self) -> &Option<::Id>; /// Take ownership of the value being voted for. fn take_value(self) -> Option<::Id>; diff --git a/Code/driver/src/driver.rs b/Code/driver/src/driver.rs index 966a1948a..0a6419d99 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::event::Event; use crate::message::Message; @@ -45,7 +46,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, @@ -57,6 +61,14 @@ where } } + pub fn height(&self) -> &Ctx::Height { + &self.round_state.height + } + + pub fn round(&self) -> Round { + self.round_state.round + } + pub fn get_proposer(&self, round: Round) -> Result<&Ctx::Validator, Error> { let address = self .proposer_selector @@ -77,9 +89,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 @@ -215,11 +225,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); }; @@ -233,7 +244,7 @@ where VoteMessage::SkipRound(r) => RoundEvent::SkipRound(r), }; - self.apply_event(round, round_event) + self.apply_event(vote_round, round_event) } fn apply_timeout(&mut self, timeout: Timeout) -> Result>, Error> { 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 for Vote { self.round } - fn value(&self) -> Option<&ValueId> { - self.value.as_ref() + fn value(&self) -> &Option { + &self.value } fn take_value(self) -> Option { diff --git a/Code/test/tests/driver.rs b/Code/test/tests/driver.rs index 5ea1a7a8a..40396e986 100644 --- a/Code/test/tests/driver.rs +++ b/Code/test/tests/driver.rs @@ -1061,3 +1061,268 @@ 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 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, 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 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, 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 = VoteKeeper::new(3); + let mut keeper: VoteKeeper = 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 = VoteKeeper::new(3); + let mut keeper: VoteKeeper = 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 = VoteKeeper::new(4); + let mut keeper: VoteKeeper = 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 = VoteKeeper::new(4); + let mut keeper: VoteKeeper = 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 = 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 = 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 = 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 = 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 = 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 { - /// 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>, @@ -21,10 +15,8 @@ pub struct VoteCount { } impl VoteCount { - 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 VoteCount { /// 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, - weight: Weight, - ) -> Threshold + pub fn add(&mut self, address: Address, value: Option, weight: Weight) -> Weight where Address: Clone + Ord, Value: Clone + Ord, @@ -45,77 +32,52 @@ impl VoteCount { 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) -> Threshold + pub fn get(&self, value: &Option) -> 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) -> bool + pub fn is_threshold_met( + &self, + threshold: Threshold, + 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) -> 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 PerRound 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 where Ctx: Context, { - threshold_params: ThresholdParams, total_weight: Weight, + threshold_params: ThresholdParams, per_round: BTreeMap>, } @@ -55,27 +55,30 @@ impl VoteKeeper 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>> { + pub fn apply_vote( + &mut self, + vote: Ctx::Vote, + weight: Weight, + current_round: Round, + ) -> Option>> { 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>, ) -> 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, - 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( + vote_type: VoteType, + round: &PerRound, + value: &Option>, + quorum: ThresholdParam, + total_weight: Weight, +) -> Threshold> +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 { } impl RoundVotes { - 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 RoundVotes { address: Address, value: Option, weight: Weight, - ) -> Threshold + ) -> Weight where Address: Clone + Ord, Value: Clone + Ord, @@ -35,13 +35,55 @@ impl RoundVotes { } } - pub fn is_threshold_met(&self, vote_type: VoteType, threshold: Threshold) -> bool + pub fn get_weight(&self, vote_type: VoteType, value: &Option) -> 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) -> 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, + 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 Default for RoundVotes { + fn default() -> Self { + Self::new() + } }