From ce7c5b2b89922b3db0752d7428e9338d88107b17 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Mon, 23 Oct 2023 18:23:46 +0200 Subject: [PATCH 1/5] Move tests and associated data types into their own crate --- Code/Cargo.toml | 1 + Code/common/src/consensus.rs | 39 ----- Code/common/src/height.rs | 18 --- Code/common/src/lib.rs | 11 +- Code/common/src/proposal.rs | 43 ------ Code/common/src/validator_set.rs | 212 ---------------------------- Code/common/src/value.rs | 45 ------ Code/common/src/vote.rs | 58 -------- Code/consensus/src/executor.rs | 201 +------------------------- Code/round/src/state_machine.rs | 64 --------- Code/test/Cargo.toml | 12 ++ Code/test/src/consensus.rs | 38 +++++ Code/test/src/consensus_executor.rs | 198 ++++++++++++++++++++++++++ Code/test/src/height.rs | 15 ++ Code/test/src/lib.rs | 21 +++ Code/test/src/proposal.rs | 41 ++++++ Code/test/src/round.rs | 63 +++++++++ Code/test/src/validator_set.rs | 209 +++++++++++++++++++++++++++ Code/test/src/value.rs | 42 ++++++ Code/test/src/vote.rs | 54 +++++++ Code/test/src/vote_count.rs | 94 ++++++++++++ Code/test/src/vote_keeper.rs | 81 +++++++++++ Code/vote/src/count.rs | 98 +------------ Code/vote/src/keeper.rs | 87 ------------ 24 files changed, 873 insertions(+), 872 deletions(-) create mode 100644 Code/test/Cargo.toml create mode 100644 Code/test/src/consensus.rs create mode 100644 Code/test/src/consensus_executor.rs create mode 100644 Code/test/src/height.rs create mode 100644 Code/test/src/lib.rs create mode 100644 Code/test/src/proposal.rs create mode 100644 Code/test/src/round.rs create mode 100644 Code/test/src/validator_set.rs create mode 100644 Code/test/src/value.rs create mode 100644 Code/test/src/vote.rs create mode 100644 Code/test/src/vote_count.rs create mode 100644 Code/test/src/vote_keeper.rs diff --git a/Code/Cargo.toml b/Code/Cargo.toml index 63a6d8dbe..d522ca3a4 100644 --- a/Code/Cargo.toml +++ b/Code/Cargo.toml @@ -6,4 +6,5 @@ members = [ "consensus", "round", "vote", + "test", ] diff --git a/Code/common/src/consensus.rs b/Code/common/src/consensus.rs index 8f7a98d96..a62678716 100644 --- a/Code/common/src/consensus.rs +++ b/Code/common/src/consensus.rs @@ -40,42 +40,3 @@ where address: Self::Address, ) -> Self::Vote; } - -pub mod test { - use crate::height::test::*; - use crate::proposal::test::*; - use crate::validator_set::test::*; - use crate::value::test::*; - use crate::vote::test::*; - use crate::Round; - - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub struct TestConsensus; - - impl super::Consensus for TestConsensus { - type Address = Address; - type Height = Height; - type Proposal = Proposal; - type PublicKey = PublicKey; - type ValidatorSet = ValidatorSet; - type Validator = Validator; - type Value = Value; - type Vote = Vote; - - const DUMMY_ADDRESS: Address = Address::new(42); - - const DUMMY_VALUE: Self::Value = Value::new(9999); - - fn new_proposal(height: Height, round: Round, value: Value, pol_round: Round) -> Proposal { - Proposal::new(height, round, value, pol_round) - } - - fn new_prevote(round: Round, value_id: Option, address: Address) -> Vote { - Vote::new_prevote(round, value_id, address) - } - - fn new_precommit(round: Round, value_id: Option, address: Address) -> Vote { - Vote::new_precommit(round, value_id, address) - } - } -} diff --git a/Code/common/src/height.rs b/Code/common/src/height.rs index ab6d211e2..00d511f67 100644 --- a/Code/common/src/height.rs +++ b/Code/common/src/height.rs @@ -7,21 +7,3 @@ where Self: Clone + Debug + PartialEq + Eq + PartialOrd + Ord, { } - -pub mod test { - /// A blockchain height - #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] - pub struct Height(u64); - - impl Height { - pub fn new(height: u64) -> Self { - Self(height) - } - - pub fn as_u64(&self) -> u64 { - self.0 - } - } - - impl super::Height for Height {} -} diff --git a/Code/common/src/lib.rs b/Code/common/src/lib.rs index 457343369..b86f40792 100644 --- a/Code/common/src/lib.rs +++ b/Code/common/src/lib.rs @@ -26,15 +26,6 @@ pub use height::Height; pub use proposal::Proposal; pub use round::Round; pub use timeout::{Timeout, TimeoutStep}; -pub use validator_set::{Address, PublicKey, Validator, ValidatorSet}; +pub use validator_set::{Address, PublicKey, Validator, ValidatorSet, VotingPower}; pub use value::Value; pub use vote::{Vote, VoteType}; - -pub mod test { - pub use crate::consensus::test::*; - pub use crate::height::test::*; - pub use crate::proposal::test::*; - pub use crate::validator_set::test::*; - pub use crate::value::test::*; - pub use crate::vote::test::*; -} diff --git a/Code/common/src/proposal.rs b/Code/common/src/proposal.rs index 190a203a9..0dc3927cd 100644 --- a/Code/common/src/proposal.rs +++ b/Code/common/src/proposal.rs @@ -11,46 +11,3 @@ where fn value(&self) -> &C::Value; fn pol_round(&self) -> Round; } - -pub mod test { - use crate::test::{Height, TestConsensus, Value}; - use crate::Round; - - /// A proposal for a value in a round - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct Proposal { - pub height: Height, - pub round: Round, - pub value: Value, - pub pol_round: Round, - } - - impl Proposal { - pub fn new(height: Height, round: Round, value: Value, pol_round: Round) -> Self { - Self { - height, - round, - value, - pol_round, - } - } - } - - impl super::Proposal for Proposal { - fn height(&self) -> Height { - self.height - } - - fn round(&self) -> Round { - self.round - } - - fn value(&self) -> &Value { - &self.value - } - - fn pol_round(&self) -> Round { - self.pol_round - } - } -} diff --git a/Code/common/src/validator_set.rs b/Code/common/src/validator_set.rs index 5dde5ed81..e0e282135 100644 --- a/Code/common/src/validator_set.rs +++ b/Code/common/src/validator_set.rs @@ -38,215 +38,3 @@ where fn get_by_public_key(&self, public_key: &C::PublicKey) -> Option<&C::Validator>; fn get_by_address(&self, address: &C::Address) -> Option<&C::Validator>; } - -pub mod test { - use std::sync::atomic::{AtomicUsize, Ordering}; - - use crate::test::TestConsensus; - - use super::VotingPower; - - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] - pub struct PublicKey(Vec); - - impl PublicKey { - pub const fn new(value: Vec) -> Self { - Self(value) - } - - pub fn hash(&self) -> u64 { - self.0.iter().fold(0, |acc, x| acc ^ *x as u64) - } - } - - impl super::PublicKey for PublicKey { - fn hash(&self) -> u64 { - self.hash() - } - } - - #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] - pub struct Address(u64); - - impl Address { - pub const fn new(value: u64) -> Self { - Self(value) - } - } - - impl super::Address for Address {} - - /// A validator is a public key and voting power - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct Validator { - pub address: Address, - pub public_key: PublicKey, - pub voting_power: VotingPower, - } - - impl Validator { - pub fn new(public_key: PublicKey, voting_power: VotingPower) -> Self { - Self { - address: Address(public_key.hash()), - public_key, - voting_power, - } - } - - pub fn hash(&self) -> u64 { - self.public_key.hash() // TODO - } - } - - impl super::Validator for Validator { - fn address(&self) -> &Address { - &self.address - } - - fn public_key(&self) -> &PublicKey { - &self.public_key - } - - fn voting_power(&self) -> VotingPower { - self.voting_power - } - } - - /// A validator set contains a list of validators sorted by address. - pub struct ValidatorSet { - validators: Vec, - proposer: AtomicUsize, - } - - impl ValidatorSet { - pub fn new(validators: impl IntoIterator) -> Self { - let mut validators: Vec<_> = validators.into_iter().collect(); - ValidatorSet::sort_validators(&mut validators); - - assert!(!validators.is_empty()); - - Self { - validators, - proposer: AtomicUsize::new(0), - } - } - - /// The total voting power of the validator set - pub fn total_voting_power(&self) -> VotingPower { - // TODO: Cache this? - self.validators.iter().map(|v| v.voting_power).sum() - } - - /// Add a validator to the set - pub fn add(&mut self, validator: Validator) { - self.validators.push(validator); - - ValidatorSet::sort_validators(&mut self.validators); - } - - /// Update the voting power of the given validator - pub fn update(&mut self, val: Validator) { - if let Some(v) = self - .validators - .iter_mut() - .find(|v| v.address == val.address) - { - v.voting_power = val.voting_power; - } - - dbg!(self.total_voting_power()); - Self::sort_validators(&mut self.validators); - dbg!(self.total_voting_power()); - } - - /// Remove a validator from the set - pub fn remove(&mut self, address: &Address) { - self.validators.retain(|v| &v.address != address); - - Self::sort_validators(&mut self.validators); // TODO: Not needed - } - - /// Get a validator by its address - pub fn get_by_address(&self, address: &Address) -> Option<&Validator> { - self.validators.iter().find(|v| &v.address == address) - } - - pub fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { - self.validators.iter().find(|v| &v.public_key == public_key) - } - - /// In place sort and deduplication of a list of validators - fn sort_validators(vals: &mut Vec) { - use core::cmp::Reverse; - - // Sort the validators according to the current Tendermint requirements - // (v. 0.34 -> first by validator power, descending, then by address, ascending) - vals.sort_unstable_by_key(|v| (Reverse(v.voting_power), v.address)); - - vals.dedup(); - } - - pub fn get_proposer(&self) -> Validator { - // TODO: Proper implementation - assert!(!self.validators.is_empty()); - let proposer = self.validators[self.proposer.load(Ordering::Relaxed)].clone(); - self.proposer.fetch_add(1, Ordering::Relaxed); - proposer - } - } - - impl super::ValidatorSet for ValidatorSet { - fn total_voting_power(&self) -> VotingPower { - self.total_voting_power() - } - - fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { - self.get_by_public_key(public_key) - } - - fn get_proposer(&self) -> Validator { - self.get_proposer() - } - - fn get_by_address(&self, address: &Address) -> Option<&Validator> { - self.get_by_address(address) - } - } - - #[cfg(test)] - mod tests { - use super::*; - - #[test] - fn add_update_remove() { - let v1 = Validator::new(PublicKey(vec![1]), 1); - let v2 = Validator::new(PublicKey(vec![2]), 2); - let v3 = Validator::new(PublicKey(vec![3]), 3); - - let mut vs = ValidatorSet::new(vec![v1, v2, v3]); - assert_eq!(vs.total_voting_power(), 6); - - let v4 = Validator::new(PublicKey(vec![4]), 4); - vs.add(v4); - assert_eq!(vs.total_voting_power(), 10); - - let mut v5 = Validator::new(PublicKey(vec![5]), 5); - vs.update(v5.clone()); // no effect - assert_eq!(vs.total_voting_power(), 10); - - vs.add(v5.clone()); - assert_eq!(vs.total_voting_power(), 15); - - v5.voting_power = 100; - vs.update(v5.clone()); - assert_eq!(vs.total_voting_power(), 110); - - vs.remove(&v5.address); - assert_eq!(vs.total_voting_power(), 10); - - let v6 = Validator::new(PublicKey(vec![6]), 6); - vs.remove(&v6.address); // no effect - assert_eq!(vs.total_voting_power(), 10); - } - } -} diff --git a/Code/common/src/value.rs b/Code/common/src/value.rs index a8351853d..576193858 100644 --- a/Code/common/src/value.rs +++ b/Code/common/src/value.rs @@ -10,48 +10,3 @@ where fn valid(&self) -> bool; } - -pub mod test { - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Copy)] - pub struct ValueId(u64); - - impl ValueId { - pub const fn new(id: u64) -> Self { - Self(id) - } - } - - /// The value to decide on - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] - pub struct Value(u64); - - impl Value { - pub const fn new(value: u64) -> Self { - Self(value) - } - - pub const fn as_u64(&self) -> u64 { - self.0 - } - - pub const fn valid(&self) -> bool { - self.0 > 0 - } - - pub const fn id(&self) -> ValueId { - ValueId(self.0) - } - } - - impl super::Value for Value { - type Id = ValueId; - - fn valid(&self) -> bool { - self.valid() - } - - fn id(&self) -> ValueId { - self.id() - } - } -} diff --git a/Code/common/src/vote.rs b/Code/common/src/vote.rs index 4382652c6..60483467f 100644 --- a/Code/common/src/vote.rs +++ b/Code/common/src/vote.rs @@ -20,61 +20,3 @@ where fn address(&self) -> &C::Address; fn set_address(&mut self, address: C::Address); } - -pub mod test { - use crate::test::{Address, TestConsensus, ValueId}; - use crate::Round; - - use super::VoteType; - - /// A vote for a value in a round - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct Vote { - pub typ: VoteType, - pub round: Round, - pub value: Option, - pub address: Address, - } - - impl Vote { - pub fn new_prevote(round: Round, value: Option, address: Address) -> Self { - Self { - typ: VoteType::Prevote, - round, - value, - address, - } - } - - pub fn new_precommit(round: Round, value: Option, address: Address) -> Self { - Self { - typ: VoteType::Precommit, - round, - value, - address, - } - } - } - - impl super::Vote for Vote { - fn round(&self) -> Round { - self.round - } - - fn value(&self) -> Option<&ValueId> { - self.value.as_ref() - } - - fn vote_type(&self) -> VoteType { - self.typ - } - - fn address(&self) -> &Address { - &self.address - } - - fn set_address(&mut self, address: Address) { - self.address = address; - } - } -} diff --git a/Code/consensus/src/executor.rs b/Code/consensus/src/executor.rs index 8a04b65e1..42c401efe 100644 --- a/Code/consensus/src/executor.rs +++ b/Code/consensus/src/executor.rs @@ -209,205 +209,8 @@ where // Return message, if any transition.message } -} - -#[cfg(test)] -mod tests { - use malachite_common::test::{ - Height, Proposal, PublicKey, TestConsensus, Validator, ValidatorSet, Vote, - }; - use malachite_round::state::{RoundValue, State, Step}; - - use super::*; - - #[test] - fn test_executor_steps() { - let value = TestConsensus::DUMMY_VALUE; // TODO: get value from external source - let value_id = value.id(); - let v1 = Validator::new(PublicKey::new(vec![1]), 1); - let v2 = Validator::new(PublicKey::new(vec![2]), 1); - let v3 = Validator::new(PublicKey::new(vec![3]), 1); - let my_address = v1.address; - let key = v1.clone().public_key; // we are proposer - - let vs = ValidatorSet::new(vec![v1, v2.clone(), v3.clone()]); - let mut executor = Executor::new(Height::new(1), vs, key.clone()); - - let proposal = Proposal::new(Height::new(1), Round::new(0), value.clone(), Round::new(-1)); - struct TestStep { - input_message: Option>, - expected_output_message: Option>, - new_state: State, - } - let steps: Vec = vec![ - // Start round 0, we are proposer, propose value - TestStep { - input_message: Some(Message::NewRound(Round::new(0))), - expected_output_message: Some(Message::Proposal(proposal.clone())), - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Propose, - proposal: None, - locked: None, - valid: None, - }, - }, - // Receive our own proposal, prevote for it (v1) - TestStep { - input_message: None, - expected_output_message: Some(Message::Vote(Vote::new_prevote( - Round::new(0), - Some(value_id), - my_address, - ))), - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Prevote, - proposal: Some(proposal.clone()), - locked: None, - valid: None, - }, - }, - // Receive our own prevote v1 - TestStep { - input_message: None, - expected_output_message: None, - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Prevote, - proposal: Some(proposal.clone()), - locked: None, - valid: None, - }, - }, - // v2 prevotes for our proposal - TestStep { - input_message: Some(Message::Vote(Vote::new_prevote( - Round::new(0), - Some(value_id), - v2.address, - ))), - expected_output_message: None, - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Prevote, - proposal: Some(proposal.clone()), - locked: None, - valid: None, - }, - }, - // v3 prevotes for our proposal, we get +2/3 prevotes, precommit for it (v1) - TestStep { - input_message: Some(Message::Vote(Vote::new_prevote( - Round::new(0), - Some(value_id), - v3.address, - ))), - expected_output_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - my_address, - ))), - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Precommit, - proposal: Some(proposal.clone()), - locked: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - valid: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - }, - }, - // v1 receives its own precommit - TestStep { - input_message: None, - expected_output_message: None, - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Precommit, - proposal: Some(proposal.clone()), - locked: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - valid: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - }, - }, - // v2 precommits for our proposal - TestStep { - input_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - v2.address, - ))), - expected_output_message: None, - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Precommit, - proposal: Some(proposal.clone()), - locked: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - valid: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - }, - }, - // v3 precommits for our proposal, we get +2/3 precommits, decide it (v1) - TestStep { - input_message: Some(Message::Vote(Vote::new_precommit( - Round::new(0), - Some(value_id), - v2.address, - ))), - expected_output_message: None, - new_state: State { - height: Height::new(1), - round: Round::new(0), - step: Step::Commit, - proposal: Some(proposal.clone()), - locked: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - valid: Some(RoundValue { - value: value.clone(), - round: Round::new(0), - }), - }, - }, - ]; - - let mut previous_message = None; - for step in steps { - let execute_message = if step.input_message.is_none() { - previous_message.clone() - } else { - step.input_message - } - .unwrap(); - let message = executor.execute(execute_message); - assert_eq!(message, step.expected_output_message); - let new_state = executor.round_states.get(&Round::new(0)).unwrap(); - assert_eq!(new_state, &step.new_state); - previous_message = message; - } + pub fn round_state(&self, round: Round) -> Option<&RoundState> { + self.round_states.get(&round) } } diff --git a/Code/round/src/state_machine.rs b/Code/round/src/state_machine.rs index 1f05d47a9..12c8c6ae6 100644 --- a/Code/round/src/state_machine.rs +++ b/Code/round/src/state_machine.rs @@ -331,67 +331,3 @@ where let message = Message::decision(round, locked.value.clone()); Transition::to(state.commit_step()).with_message(message) } - -#[cfg(test)] -mod tests { - use super::*; - - use malachite_common::test::{Height, Proposal, TestConsensus, Value}; - use malachite_common::{Timeout, TimeoutStep}; - - #[test] - fn test_propose() { - let value = Value::new(42); - let mut state: State = State::new(Height::new(10)); - - let transition = apply_event(state.clone(), Round::new(0), Event::NewRoundProposer(value)); - - state.step = Step::Propose; - assert_eq!(transition.state, state); - - assert_eq!( - transition.message.unwrap(), - Message::proposal(Height::new(10), Round::new(0), Value::new(42), Round::None) - ); - } - - #[test] - fn test_prevote() { - let value = Value::new(42); - let state: State = State::new(Height::new(1)).new_round(Round::new(1)); - - let transition = apply_event(state, Round::new(1), Event::NewRound); - - assert_eq!(transition.state.step, Step::Propose); - assert_eq!( - transition.message.unwrap(), - Message::Timeout(Timeout { - round: Round::new(1), - step: TimeoutStep::Propose - }) - ); - - let state = transition.state; - - let transition = apply_event( - state, - Round::new(1), - Event::Proposal(Proposal::new( - Height::new(1), - Round::new(1), - value.clone(), - Round::None, - )), - ); - - assert_eq!(transition.state.step, Step::Prevote); - assert_eq!( - transition.message.unwrap(), - Message::prevote( - Round::new(1), - Some(value.id()), - TestConsensus::DUMMY_ADDRESS - ) - ); - } -} diff --git a/Code/test/Cargo.toml b/Code/test/Cargo.toml new file mode 100644 index 000000000..43da1d089 --- /dev/null +++ b/Code/test/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "malachite-test" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +malachite-common = { version = "0.1.0", path = "../common" } +malachite-consensus = { version = "0.1.0", path = "../consensus" } +malachite-round = { version = "0.1.0", path = "../round" } +malachite-vote = { version = "0.1.0", path = "../vote" } diff --git a/Code/test/src/consensus.rs b/Code/test/src/consensus.rs new file mode 100644 index 000000000..ebe110c5d --- /dev/null +++ b/Code/test/src/consensus.rs @@ -0,0 +1,38 @@ +use malachite_common::Consensus; +use malachite_common::Round; + +use crate::height::*; +use crate::proposal::*; +use crate::validator_set::*; +use crate::value::*; +use crate::vote::*; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct TestConsensus; + +impl Consensus for TestConsensus { + type Address = Address; + type Height = Height; + type Proposal = Proposal; + type PublicKey = PublicKey; + type ValidatorSet = ValidatorSet; + type Validator = Validator; + type Value = Value; + type Vote = Vote; + + const DUMMY_ADDRESS: Address = Address::new(42); + + const DUMMY_VALUE: Self::Value = Value::new(9999); + + fn new_proposal(height: Height, round: Round, value: Value, pol_round: Round) -> Proposal { + Proposal::new(height, round, value, pol_round) + } + + fn new_prevote(round: Round, value_id: Option, address: Address) -> Vote { + Vote::new_prevote(round, value_id, address) + } + + fn new_precommit(round: Round, value_id: Option, address: Address) -> Vote { + Vote::new_precommit(round, value_id, address) + } +} diff --git a/Code/test/src/consensus_executor.rs b/Code/test/src/consensus_executor.rs new file mode 100644 index 000000000..4be38d4df --- /dev/null +++ b/Code/test/src/consensus_executor.rs @@ -0,0 +1,198 @@ +use malachite_common::{Consensus, Round}; +use malachite_consensus::executor::{Executor, Message}; +use malachite_round::state::{RoundValue, State, Step}; + +use crate::{Height, Proposal, PublicKey, TestConsensus, Validator, ValidatorSet, Vote}; + +#[test] +fn test_executor_steps() { + let value = TestConsensus::DUMMY_VALUE; // TODO: get value from external source + let value_id = value.id(); + let v1 = Validator::new(PublicKey::new(vec![1]), 1); + let v2 = Validator::new(PublicKey::new(vec![2]), 1); + let v3 = Validator::new(PublicKey::new(vec![3]), 1); + let my_address = v1.address; + let key = v1.clone().public_key; // we are proposer + + let vs = ValidatorSet::new(vec![v1, v2.clone(), v3.clone()]); + + let mut executor = Executor::new(Height::new(1), vs, key.clone()); + + let proposal = Proposal::new(Height::new(1), Round::new(0), value.clone(), Round::new(-1)); + + struct TestStep { + input_message: Option>, + expected_output_message: Option>, + new_state: State, + } + + let steps: Vec = vec![ + // Start round 0, we are proposer, propose value + TestStep { + input_message: Some(Message::NewRound(Round::new(0))), + expected_output_message: Some(Message::Proposal(proposal.clone())), + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Propose, + proposal: None, + locked: None, + valid: None, + }, + }, + // Receive our own proposal, prevote for it (v1) + TestStep { + input_message: None, + expected_output_message: Some(Message::Vote(Vote::new_prevote( + Round::new(0), + Some(value_id), + my_address, + ))), + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Prevote, + proposal: Some(proposal.clone()), + locked: None, + valid: None, + }, + }, + // Receive our own prevote v1 + TestStep { + input_message: None, + expected_output_message: None, + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Prevote, + proposal: Some(proposal.clone()), + locked: None, + valid: None, + }, + }, + // v2 prevotes for our proposal + TestStep { + input_message: Some(Message::Vote(Vote::new_prevote( + Round::new(0), + Some(value_id), + v2.address, + ))), + expected_output_message: None, + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Prevote, + proposal: Some(proposal.clone()), + locked: None, + valid: None, + }, + }, + // v3 prevotes for our proposal, we get +2/3 prevotes, precommit for it (v1) + TestStep { + input_message: Some(Message::Vote(Vote::new_prevote( + Round::new(0), + Some(value_id), + v3.address, + ))), + expected_output_message: Some(Message::Vote(Vote::new_precommit( + Round::new(0), + Some(value_id), + my_address, + ))), + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Precommit, + proposal: Some(proposal.clone()), + locked: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + valid: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + }, + }, + // v1 receives its own precommit + TestStep { + input_message: None, + expected_output_message: None, + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Precommit, + proposal: Some(proposal.clone()), + locked: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + valid: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + }, + }, + // v2 precommits for our proposal + TestStep { + input_message: Some(Message::Vote(Vote::new_precommit( + Round::new(0), + Some(value_id), + v2.address, + ))), + expected_output_message: None, + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Precommit, + proposal: Some(proposal.clone()), + locked: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + valid: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + }, + }, + // v3 precommits for our proposal, we get +2/3 precommits, decide it (v1) + TestStep { + input_message: Some(Message::Vote(Vote::new_precommit( + Round::new(0), + Some(value_id), + v2.address, + ))), + expected_output_message: None, + new_state: State { + height: Height::new(1), + round: Round::new(0), + step: Step::Commit, + proposal: Some(proposal.clone()), + locked: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + valid: Some(RoundValue { + value: value.clone(), + round: Round::new(0), + }), + }, + }, + ]; + + let mut previous_message = None; + for step in steps { + let execute_message = if step.input_message.is_none() { + previous_message.clone() + } else { + step.input_message + } + .unwrap(); + let message = executor.execute(execute_message); + assert_eq!(message, step.expected_output_message); + let new_state = executor.round_state(Round::new(0)).unwrap(); + assert_eq!(new_state, &step.new_state); + previous_message = message; + } +} diff --git a/Code/test/src/height.rs b/Code/test/src/height.rs new file mode 100644 index 000000000..3f4a32c3c --- /dev/null +++ b/Code/test/src/height.rs @@ -0,0 +1,15 @@ +/// A blockchain height +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct Height(u64); + +impl Height { + pub fn new(height: u64) -> Self { + Self(height) + } + + pub fn as_u64(&self) -> u64 { + self.0 + } +} + +impl malachite_common::Height for Height {} diff --git a/Code/test/src/lib.rs b/Code/test/src/lib.rs new file mode 100644 index 000000000..5422ad370 --- /dev/null +++ b/Code/test/src/lib.rs @@ -0,0 +1,21 @@ +#![cfg(test)] +#![forbid(unsafe_code)] +#![deny(unused_crate_dependencies, trivial_casts, trivial_numeric_casts)] + +mod consensus; +mod consensus_executor; +mod height; +mod proposal; +mod round; +mod validator_set; +mod value; +mod vote; +mod vote_count; +mod vote_keeper; + +pub use crate::consensus::*; +pub use crate::height::*; +pub use crate::proposal::*; +pub use crate::validator_set::*; +pub use crate::value::*; +pub use crate::vote::*; diff --git a/Code/test/src/proposal.rs b/Code/test/src/proposal.rs new file mode 100644 index 000000000..a0401f0d3 --- /dev/null +++ b/Code/test/src/proposal.rs @@ -0,0 +1,41 @@ +use malachite_common::Round; + +use crate::{Height, TestConsensus, Value}; + +/// A proposal for a value in a round +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Proposal { + pub height: Height, + pub round: Round, + pub value: Value, + pub pol_round: Round, +} + +impl Proposal { + pub fn new(height: Height, round: Round, value: Value, pol_round: Round) -> Self { + Self { + height, + round, + value, + pol_round, + } + } +} + +impl malachite_common::Proposal for Proposal { + fn height(&self) -> Height { + self.height + } + + fn round(&self) -> Round { + self.round + } + + fn value(&self) -> &Value { + &self.value + } + + fn pol_round(&self) -> Round { + self.pol_round + } +} diff --git a/Code/test/src/round.rs b/Code/test/src/round.rs new file mode 100644 index 000000000..32b09af75 --- /dev/null +++ b/Code/test/src/round.rs @@ -0,0 +1,63 @@ +use crate::{Height, Proposal, TestConsensus, Value}; + +use malachite_common::{Consensus, Round, Timeout, TimeoutStep}; +use malachite_round::events::Event; +use malachite_round::message::Message; +use malachite_round::state::{State, Step}; +use malachite_round::state_machine::apply_event; + +#[test] +fn test_propose() { + let value = Value::new(42); + let mut state: State = State::new(Height::new(10)); + + let transition = apply_event(state.clone(), Round::new(0), Event::NewRoundProposer(value)); + + state.step = Step::Propose; + assert_eq!(transition.state, state); + + assert_eq!( + transition.message.unwrap(), + Message::proposal(Height::new(10), Round::new(0), Value::new(42), Round::None) + ); +} + +#[test] +fn test_prevote() { + let value = Value::new(42); + let state: State = State::new(Height::new(1)).new_round(Round::new(1)); + + let transition = apply_event(state, Round::new(1), Event::NewRound); + + assert_eq!(transition.state.step, Step::Propose); + assert_eq!( + transition.message.unwrap(), + Message::Timeout(Timeout { + round: Round::new(1), + step: TimeoutStep::Propose + }) + ); + + let state = transition.state; + + let transition = apply_event( + state, + Round::new(1), + Event::Proposal(Proposal::new( + Height::new(1), + Round::new(1), + value.clone(), + Round::None, + )), + ); + + assert_eq!(transition.state.step, Step::Prevote); + assert_eq!( + transition.message.unwrap(), + Message::prevote( + Round::new(1), + Some(value.id()), + TestConsensus::DUMMY_ADDRESS + ) + ); +} diff --git a/Code/test/src/validator_set.rs b/Code/test/src/validator_set.rs new file mode 100644 index 000000000..bf7f88f5b --- /dev/null +++ b/Code/test/src/validator_set.rs @@ -0,0 +1,209 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; + +use malachite_common::VotingPower; + +use crate::TestConsensus; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct PublicKey(Vec); + +impl PublicKey { + pub const fn new(value: Vec) -> Self { + Self(value) + } + + pub fn hash(&self) -> u64 { + self.0.iter().fold(0, |acc, x| acc ^ *x as u64) + } +} + +impl malachite_common::PublicKey for PublicKey { + fn hash(&self) -> u64 { + self.hash() + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Address(u64); + +impl Address { + pub const fn new(value: u64) -> Self { + Self(value) + } +} + +impl malachite_common::Address for Address {} + +/// A validator is a public key and voting power +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Validator { + pub address: Address, + pub public_key: PublicKey, + pub voting_power: VotingPower, +} + +impl Validator { + pub fn new(public_key: PublicKey, voting_power: VotingPower) -> Self { + Self { + address: Address(public_key.hash()), + public_key, + voting_power, + } + } + + pub fn hash(&self) -> u64 { + self.public_key.hash() // TODO + } +} + +impl malachite_common::Validator for Validator { + fn address(&self) -> &Address { + &self.address + } + + fn public_key(&self) -> &PublicKey { + &self.public_key + } + + fn voting_power(&self) -> VotingPower { + self.voting_power + } +} + +/// A validator set contains a list of validators sorted by address. +pub struct ValidatorSet { + validators: Vec, + proposer: AtomicUsize, +} + +impl ValidatorSet { + pub fn new(validators: impl IntoIterator) -> Self { + let mut validators: Vec<_> = validators.into_iter().collect(); + ValidatorSet::sort_validators(&mut validators); + + assert!(!validators.is_empty()); + + Self { + validators, + proposer: AtomicUsize::new(0), + } + } + + /// The total voting power of the validator set + pub fn total_voting_power(&self) -> VotingPower { + // TODO: Cache this? + self.validators.iter().map(|v| v.voting_power).sum() + } + + /// Add a validator to the set + pub fn add(&mut self, validator: Validator) { + self.validators.push(validator); + + ValidatorSet::sort_validators(&mut self.validators); + } + + /// Update the voting power of the given validator + pub fn update(&mut self, val: Validator) { + if let Some(v) = self + .validators + .iter_mut() + .find(|v| v.address == val.address) + { + v.voting_power = val.voting_power; + } + + dbg!(self.total_voting_power()); + Self::sort_validators(&mut self.validators); + dbg!(self.total_voting_power()); + } + + /// Remove a validator from the set + pub fn remove(&mut self, address: &Address) { + self.validators.retain(|v| &v.address != address); + + Self::sort_validators(&mut self.validators); // TODO: Not needed + } + + /// Get a validator by its address + pub fn get_by_address(&self, address: &Address) -> Option<&Validator> { + self.validators.iter().find(|v| &v.address == address) + } + + pub fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { + self.validators.iter().find(|v| &v.public_key == public_key) + } + + /// In place sort and deduplication of a list of validators + fn sort_validators(vals: &mut Vec) { + use core::cmp::Reverse; + + // Sort the validators according to the current Tendermint requirements + // (v. 0.34 -> first by validator power, descending, then by address, ascending) + vals.sort_unstable_by_key(|v| (Reverse(v.voting_power), v.address)); + + vals.dedup(); + } + + pub fn get_proposer(&self) -> Validator { + // TODO: Proper implementation + assert!(!self.validators.is_empty()); + let proposer = self.validators[self.proposer.load(Ordering::Relaxed)].clone(); + self.proposer.fetch_add(1, Ordering::Relaxed); + proposer + } +} + +impl malachite_common::ValidatorSet for ValidatorSet { + fn total_voting_power(&self) -> VotingPower { + self.total_voting_power() + } + + fn get_by_public_key(&self, public_key: &PublicKey) -> Option<&Validator> { + self.get_by_public_key(public_key) + } + + fn get_proposer(&self) -> Validator { + self.get_proposer() + } + + fn get_by_address(&self, address: &Address) -> Option<&Validator> { + self.get_by_address(address) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn add_update_remove() { + let v1 = Validator::new(PublicKey(vec![1]), 1); + let v2 = Validator::new(PublicKey(vec![2]), 2); + let v3 = Validator::new(PublicKey(vec![3]), 3); + + let mut vs = ValidatorSet::new(vec![v1, v2, v3]); + assert_eq!(vs.total_voting_power(), 6); + + let v4 = Validator::new(PublicKey(vec![4]), 4); + vs.add(v4); + assert_eq!(vs.total_voting_power(), 10); + + let mut v5 = Validator::new(PublicKey(vec![5]), 5); + vs.update(v5.clone()); // no effect + assert_eq!(vs.total_voting_power(), 10); + + vs.add(v5.clone()); + assert_eq!(vs.total_voting_power(), 15); + + v5.voting_power = 100; + vs.update(v5.clone()); + assert_eq!(vs.total_voting_power(), 110); + + vs.remove(&v5.address); + assert_eq!(vs.total_voting_power(), 10); + + let v6 = Validator::new(PublicKey(vec![6]), 6); + vs.remove(&v6.address); // no effect + assert_eq!(vs.total_voting_power(), 10); + } +} diff --git a/Code/test/src/value.rs b/Code/test/src/value.rs new file mode 100644 index 000000000..86cbbd198 --- /dev/null +++ b/Code/test/src/value.rs @@ -0,0 +1,42 @@ +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Copy)] +pub struct ValueId(u64); + +impl ValueId { + pub const fn new(id: u64) -> Self { + Self(id) + } +} + +/// The value to decide on +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Value(u64); + +impl Value { + pub const fn new(value: u64) -> Self { + Self(value) + } + + pub const fn as_u64(&self) -> u64 { + self.0 + } + + pub const fn valid(&self) -> bool { + self.0 > 0 + } + + pub const fn id(&self) -> ValueId { + ValueId(self.0) + } +} + +impl malachite_common::Value for Value { + type Id = ValueId; + + fn valid(&self) -> bool { + self.valid() + } + + fn id(&self) -> ValueId { + self.id() + } +} diff --git a/Code/test/src/vote.rs b/Code/test/src/vote.rs new file mode 100644 index 000000000..761101aeb --- /dev/null +++ b/Code/test/src/vote.rs @@ -0,0 +1,54 @@ +use malachite_common::{Round, VoteType}; + +use crate::{Address, TestConsensus, ValueId}; + +/// A vote for a value in a round +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Vote { + pub typ: VoteType, + pub round: Round, + pub value: Option, + pub address: Address, +} + +impl Vote { + pub fn new_prevote(round: Round, value: Option, address: Address) -> Self { + Self { + typ: VoteType::Prevote, + round, + value, + address, + } + } + + pub fn new_precommit(round: Round, value: Option, address: Address) -> Self { + Self { + typ: VoteType::Precommit, + round, + value, + address, + } + } +} + +impl malachite_common::Vote for Vote { + fn round(&self) -> Round { + self.round + } + + fn value(&self) -> Option<&ValueId> { + self.value.as_ref() + } + + fn vote_type(&self) -> VoteType { + self.typ + } + + fn address(&self) -> &Address { + &self.address + } + + fn set_address(&mut self, address: Address) { + self.address = address; + } +} diff --git a/Code/test/src/vote_count.rs b/Code/test/src/vote_count.rs new file mode 100644 index 000000000..1f6d81daf --- /dev/null +++ b/Code/test/src/vote_count.rs @@ -0,0 +1,94 @@ +use malachite_common::Round; +use malachite_vote::count::Threshold; +use malachite_vote::RoundVotes; + +use crate::{Address, Height, TestConsensus, ValueId, Vote}; + +#[test] +fn add_votes_nil() { + let total = 3; + + let mut round_votes: RoundVotes = + RoundVotes::new(Height::new(1), Round::new(0), total); + + // add a vote for nil. nothing changes. + let vote = Vote::new_prevote(Round::new(0), None, Address::new(1)); + let thresh = round_votes.add_vote(vote.clone(), 1); + assert_eq!(thresh, Threshold::Init); + + // add it again, nothing changes. + let thresh = round_votes.add_vote(vote.clone(), 1); + assert_eq!(thresh, Threshold::Init); + + // add it again, get Nil + let thresh = round_votes.add_vote(vote.clone(), 1); + assert_eq!(thresh, Threshold::Nil); +} + +#[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 = + RoundVotes::new(Height::new(1), Round::new(0), total); + + // add a vote. nothing changes. + let vote = Vote::new_prevote(Round::new(0), val, Address::new(1)); + let thresh = round_votes.add_vote(vote.clone(), weight); + assert_eq!(thresh, Threshold::Init); + + // add it again, nothing changes. + let thresh = round_votes.add_vote(vote.clone(), weight); + assert_eq!(thresh, Threshold::Init); + + // add a vote for nil, get Thresh::Any + let vote_nil = Vote::new_prevote(Round::new(0), None, Address::new(2)); + let thresh = round_votes.add_vote(vote_nil, weight); + assert_eq!(thresh, Threshold::Any); + + // add vote for value, get Thresh::Value + let thresh = round_votes.add_vote(vote, weight); + assert_eq!(thresh, Threshold::Value(v)); +} + +#[test] +fn add_votes_multi_values() { + let v1 = ValueId::new(1); + let v2 = ValueId::new(2); + let val1 = Some(v1); + let val2 = Some(v2); + let total = 15; + + let mut round_votes: RoundVotes = + RoundVotes::new(Height::new(1), Round::new(0), total); + + // add a vote for v1. nothing changes. + let vote1 = Vote::new_precommit(Round::new(0), val1, Address::new(1)); + let thresh = round_votes.add_vote(vote1.clone(), 1); + assert_eq!(thresh, Threshold::Init); + + // add a vote for v2. nothing changes. + let vote2 = Vote::new_precommit(Round::new(0), val2, Address::new(2)); + let thresh = round_votes.add_vote(vote2.clone(), 1); + assert_eq!(thresh, Threshold::Init); + + // add a vote for nil. nothing changes. + let vote_nil = Vote::new_precommit(Round::new(0), None, Address::new(3)); + let thresh = round_votes.add_vote(vote_nil.clone(), 1); + assert_eq!(thresh, Threshold::Init); + + // add a vote for v1. nothing changes + let thresh = round_votes.add_vote(vote1.clone(), 1); + assert_eq!(thresh, Threshold::Init); + + // add a vote for v2. nothing changes + let thresh = round_votes.add_vote(vote2.clone(), 1); + assert_eq!(thresh, Threshold::Init); + + // add a big vote for v2. get Value(v2) + let thresh = round_votes.add_vote(vote2.clone(), 10); + assert_eq!(thresh, Threshold::Value(v2)); +} diff --git a/Code/test/src/vote_keeper.rs b/Code/test/src/vote_keeper.rs new file mode 100644 index 000000000..c28f057c6 --- /dev/null +++ b/Code/test/src/vote_keeper.rs @@ -0,0 +1,81 @@ +use malachite_common::Round; +use malachite_round::events::Event; +use malachite_vote::keeper::VoteKeeper; + +use crate::{Address, Height, TestConsensus, ValueId, Vote}; + +#[test] +fn prevote_apply_nil() { + let mut keeper: VoteKeeper = VoteKeeper::new(Height::new(1), Round::INITIAL, 3); + + let vote = Vote::new_prevote(Round::new(0), None, Address::new(1)); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let event = keeper.apply_vote(vote, 1); + assert_eq!(event, Some(Event::PolkaNil)); +} + +#[test] +fn precommit_apply_nil() { + let mut keeper: VoteKeeper = VoteKeeper::new(Height::new(1), Round::INITIAL, 3); + + let vote = Vote::new_precommit(Round::new(0), None, Address::new(1)); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let event = keeper.apply_vote(vote, 1); + assert_eq!(event, None); +} + +#[test] +fn prevote_apply_single_value() { + let mut keeper: VoteKeeper = VoteKeeper::new(Height::new(1), Round::INITIAL, 4); + + let v = ValueId::new(1); + let val = Some(v); + let vote = Vote::new_prevote(Round::new(0), val, Address::new(1)); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let vote_nil = Vote::new_prevote(Round::new(0), None, Address::new(2)); + let event = keeper.apply_vote(vote_nil, 1); + assert_eq!(event, Some(Event::PolkaAny)); + + let event = keeper.apply_vote(vote, 1); + assert_eq!(event, Some(Event::PolkaValue(v))); +} + +#[test] +fn precommit_apply_single_value() { + let mut keeper: VoteKeeper = VoteKeeper::new(Height::new(1), Round::INITIAL, 4); + + let v = ValueId::new(1); + let val = Some(v); + let vote = Vote::new_precommit(Round::new(0), val, Address::new(1)); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let event = keeper.apply_vote(vote.clone(), 1); + assert_eq!(event, None); + + let vote_nil = Vote::new_precommit(Round::new(0), None, Address::new(2)); + let event = keeper.apply_vote(vote_nil, 1); + assert_eq!(event, Some(Event::PrecommitAny)); + + let event = keeper.apply_vote(vote, 1); + assert_eq!(event, Some(Event::PrecommitValue(v))); +} diff --git a/Code/vote/src/count.rs b/Code/vote/src/count.rs index f2a22c9bf..fb627ca9b 100644 --- a/Code/vote/src/count.rs +++ b/Code/vote/src/count.rs @@ -130,100 +130,4 @@ pub fn is_quorum(value: Weight, total: Weight) -> bool { } #[cfg(test)] -mod tests { - use malachite_common::test::{Address, Height, TestConsensus, ValueId, Vote}; - use malachite_common::Round; - - use crate::RoundVotes; - - use super::*; - - #[test] - fn add_votes_nil() { - let total = 3; - - let mut round_votes: RoundVotes = - RoundVotes::new(Height::new(1), Round::new(0), total); - - // add a vote for nil. nothing changes. - let vote = Vote::new_prevote(Round::new(0), None, Address::new(1)); - let thresh = round_votes.add_vote(vote.clone(), 1); - assert_eq!(thresh, Threshold::Init); - - // add it again, nothing changes. - let thresh = round_votes.add_vote(vote.clone(), 1); - assert_eq!(thresh, Threshold::Init); - - // add it again, get Nil - let thresh = round_votes.add_vote(vote.clone(), 1); - assert_eq!(thresh, Threshold::Nil); - } - - #[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 = - RoundVotes::new(Height::new(1), Round::new(0), total); - - // add a vote. nothing changes. - let vote = Vote::new_prevote(Round::new(0), val, Address::new(1)); - let thresh = round_votes.add_vote(vote.clone(), weight); - assert_eq!(thresh, Threshold::Init); - - // add it again, nothing changes. - let thresh = round_votes.add_vote(vote.clone(), weight); - assert_eq!(thresh, Threshold::Init); - - // add a vote for nil, get Thresh::Any - let vote_nil = Vote::new_prevote(Round::new(0), None, Address::new(2)); - let thresh = round_votes.add_vote(vote_nil, weight); - assert_eq!(thresh, Threshold::Any); - - // add vote for value, get Thresh::Value - let thresh = round_votes.add_vote(vote, weight); - assert_eq!(thresh, Threshold::Value(v)); - } - - #[test] - fn add_votes_multi_values() { - let v1 = ValueId::new(1); - let v2 = ValueId::new(2); - let val1 = Some(v1); - let val2 = Some(v2); - let total = 15; - - let mut round_votes: RoundVotes = - RoundVotes::new(Height::new(1), Round::new(0), total); - - // add a vote for v1. nothing changes. - let vote1 = Vote::new_precommit(Round::new(0), val1, Address::new(1)); - let thresh = round_votes.add_vote(vote1.clone(), 1); - assert_eq!(thresh, Threshold::Init); - - // add a vote for v2. nothing changes. - let vote2 = Vote::new_precommit(Round::new(0), val2, Address::new(2)); - let thresh = round_votes.add_vote(vote2.clone(), 1); - assert_eq!(thresh, Threshold::Init); - - // add a vote for nil. nothing changes. - let vote_nil = Vote::new_precommit(Round::new(0), None, Address::new(3)); - let thresh = round_votes.add_vote(vote_nil.clone(), 1); - assert_eq!(thresh, Threshold::Init); - - // add a vote for v1. nothing changes - let thresh = round_votes.add_vote(vote1.clone(), 1); - assert_eq!(thresh, Threshold::Init); - - // add a vote for v2. nothing changes - let thresh = round_votes.add_vote(vote2.clone(), 1); - assert_eq!(thresh, Threshold::Init); - - // add a big vote for v2. get Value(v2) - let thresh = round_votes.add_vote(vote2.clone(), 10); - assert_eq!(thresh, Threshold::Value(v2)); - } -} +mod tests {} diff --git a/Code/vote/src/keeper.rs b/Code/vote/src/keeper.rs index 964eb1f55..a24f63829 100644 --- a/Code/vote/src/keeper.rs +++ b/Code/vote/src/keeper.rs @@ -79,90 +79,3 @@ where } } } - -#[cfg(test)] -mod tests { - use malachite_common::test::{Address, Height, TestConsensus, ValueId, Vote}; - - use super::*; - - #[test] - fn prevote_apply_nil() { - let mut keeper: VoteKeeper = - VoteKeeper::new(Height::new(1), Round::INITIAL, 3); - - let vote = Vote::new_prevote(Round::new(0), None, Address::new(1)); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let event = keeper.apply_vote(vote, 1); - assert_eq!(event, Some(Event::PolkaNil)); - } - - #[test] - fn precommit_apply_nil() { - let mut keeper: VoteKeeper = - VoteKeeper::new(Height::new(1), Round::INITIAL, 3); - - let vote = Vote::new_precommit(Round::new(0), None, Address::new(1)); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let event = keeper.apply_vote(vote, 1); - assert_eq!(event, None); - } - - #[test] - fn prevote_apply_single_value() { - let mut keeper: VoteKeeper = - VoteKeeper::new(Height::new(1), Round::INITIAL, 4); - - let v = ValueId::new(1); - let val = Some(v); - let vote = Vote::new_prevote(Round::new(0), val, Address::new(1)); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let vote_nil = Vote::new_prevote(Round::new(0), None, Address::new(2)); - let event = keeper.apply_vote(vote_nil, 1); - assert_eq!(event, Some(Event::PolkaAny)); - - let event = keeper.apply_vote(vote, 1); - assert_eq!(event, Some(Event::PolkaValue(v))); - } - - #[test] - fn precommit_apply_single_value() { - let mut keeper: VoteKeeper = - VoteKeeper::new(Height::new(1), Round::INITIAL, 4); - - let v = ValueId::new(1); - let val = Some(v); - let vote = Vote::new_precommit(Round::new(0), val, Address::new(1)); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let event = keeper.apply_vote(vote.clone(), 1); - assert_eq!(event, None); - - let vote_nil = Vote::new_precommit(Round::new(0), None, Address::new(2)); - let event = keeper.apply_vote(vote_nil, 1); - assert_eq!(event, Some(Event::PrecommitAny)); - - let event = keeper.apply_vote(vote, 1); - assert_eq!(event, Some(Event::PrecommitValue(v))); - } -} From 1b31c770c21606c46ffc7b2e22d4df73de303464 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Tue, 24 Oct 2023 09:24:43 +0200 Subject: [PATCH 2/5] Add small threshold to codecov --- codecov.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/codecov.yml b/codecov.yml index ade8d082c..66a0c8cb2 100644 --- a/codecov.yml +++ b/codecov.yml @@ -7,6 +7,17 @@ coverage: range: "50...100" status: - project: true - patch: true + project: + default: + target: auto + threshold: 5% + paths: + - "Code" + patch: + default: + target: auto + threshold: 5% + paths: + - "Code" + changes: true From 2573a6e6736e17ca1987efc1d14b0f328a2048ae Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Tue, 24 Oct 2023 09:37:57 +0200 Subject: [PATCH 3/5] Small cleanup --- Code/test/src/consensus_executor.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Code/test/src/consensus_executor.rs b/Code/test/src/consensus_executor.rs index 4be38d4df..f794e3af4 100644 --- a/Code/test/src/consensus_executor.rs +++ b/Code/test/src/consensus_executor.rs @@ -182,17 +182,18 @@ fn test_executor_steps() { ]; let mut previous_message = None; + for step in steps { - let execute_message = if step.input_message.is_none() { - previous_message.clone() - } else { - step.input_message - } - .unwrap(); + let execute_message = step + .input_message + .unwrap_or_else(|| previous_message.unwrap()); + let message = executor.execute(execute_message); assert_eq!(message, step.expected_output_message); + let new_state = executor.round_state(Round::new(0)).unwrap(); assert_eq!(new_state, &step.new_state); + previous_message = message; } } From a783847b24f2ca9c547e81d0d13969d938d36be0 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Tue, 24 Oct 2023 11:53:10 +0200 Subject: [PATCH 4/5] Move tests into `tests` folder --- .gitignore | 4 ++++ Code/test/src/lib.rs | 7 +------ Code/test/{src => tests}/consensus_executor.rs | 2 +- Code/test/{src => tests}/round.rs | 2 +- Code/test/{src => tests}/vote_count.rs | 2 +- Code/test/{src => tests}/vote_keeper.rs | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) rename Code/test/{src => tests}/consensus_executor.rs (98%) rename Code/test/{src => tests}/round.rs (96%) rename Code/test/{src => tests}/vote_count.rs (97%) rename Code/test/{src => tests}/vote_keeper.rs (97%) diff --git a/.gitignore b/.gitignore index 5166e1ec1..2784a33a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# macOS Finder and Windows Thumbs.db files +.DS_Store +Thumbs.db + # Generated by Cargo # will have compiled files and executables debug/ diff --git a/Code/test/src/lib.rs b/Code/test/src/lib.rs index 5422ad370..4e9cd3aeb 100644 --- a/Code/test/src/lib.rs +++ b/Code/test/src/lib.rs @@ -1,17 +1,12 @@ -#![cfg(test)] #![forbid(unsafe_code)] -#![deny(unused_crate_dependencies, trivial_casts, trivial_numeric_casts)] +#![deny(trivial_casts, trivial_numeric_casts)] mod consensus; -mod consensus_executor; mod height; mod proposal; -mod round; mod validator_set; mod value; mod vote; -mod vote_count; -mod vote_keeper; pub use crate::consensus::*; pub use crate::height::*; diff --git a/Code/test/src/consensus_executor.rs b/Code/test/tests/consensus_executor.rs similarity index 98% rename from Code/test/src/consensus_executor.rs rename to Code/test/tests/consensus_executor.rs index f794e3af4..042a36612 100644 --- a/Code/test/src/consensus_executor.rs +++ b/Code/test/tests/consensus_executor.rs @@ -2,7 +2,7 @@ use malachite_common::{Consensus, Round}; use malachite_consensus::executor::{Executor, Message}; use malachite_round::state::{RoundValue, State, Step}; -use crate::{Height, Proposal, PublicKey, TestConsensus, Validator, ValidatorSet, Vote}; +use malachite_test::{Height, Proposal, PublicKey, TestConsensus, Validator, ValidatorSet, Vote}; #[test] fn test_executor_steps() { diff --git a/Code/test/src/round.rs b/Code/test/tests/round.rs similarity index 96% rename from Code/test/src/round.rs rename to Code/test/tests/round.rs index 32b09af75..89bb8d07c 100644 --- a/Code/test/src/round.rs +++ b/Code/test/tests/round.rs @@ -1,4 +1,4 @@ -use crate::{Height, Proposal, TestConsensus, Value}; +use malachite_test::{Height, Proposal, TestConsensus, Value}; use malachite_common::{Consensus, Round, Timeout, TimeoutStep}; use malachite_round::events::Event; diff --git a/Code/test/src/vote_count.rs b/Code/test/tests/vote_count.rs similarity index 97% rename from Code/test/src/vote_count.rs rename to Code/test/tests/vote_count.rs index 1f6d81daf..e1367e1c4 100644 --- a/Code/test/src/vote_count.rs +++ b/Code/test/tests/vote_count.rs @@ -2,7 +2,7 @@ use malachite_common::Round; use malachite_vote::count::Threshold; use malachite_vote::RoundVotes; -use crate::{Address, Height, TestConsensus, ValueId, Vote}; +use malachite_test::{Address, Height, TestConsensus, ValueId, Vote}; #[test] fn add_votes_nil() { diff --git a/Code/test/src/vote_keeper.rs b/Code/test/tests/vote_keeper.rs similarity index 97% rename from Code/test/src/vote_keeper.rs rename to Code/test/tests/vote_keeper.rs index c28f057c6..c5c207edb 100644 --- a/Code/test/src/vote_keeper.rs +++ b/Code/test/tests/vote_keeper.rs @@ -2,7 +2,7 @@ use malachite_common::Round; use malachite_round::events::Event; use malachite_vote::keeper::VoteKeeper; -use crate::{Address, Height, TestConsensus, ValueId, Vote}; +use malachite_test::{Address, Height, TestConsensus, ValueId, Vote}; #[test] fn prevote_apply_nil() { From 2168413a1c95daf6e582cb1ea08de4e51e96a6f6 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Tue, 24 Oct 2023 14:38:04 +0200 Subject: [PATCH 5/5] Adjust base when computing coverage in the presence of removed code See: https://docs.codecov.com/docs/commit-status#removed_code_behavior --- codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/codecov.yml b/codecov.yml index 66a0c8cb2..97d7c517a 100644 --- a/codecov.yml +++ b/codecov.yml @@ -11,6 +11,7 @@ coverage: default: target: auto threshold: 5% + removed_code_behavior: adjust_base paths: - "Code" patch: