diff --git a/Code/driver/src/driver.rs b/Code/driver/src/driver.rs index a7e9e539a..116862ecb 100644 --- a/Code/driver/src/driver.rs +++ b/Code/driver/src/driver.rs @@ -18,6 +18,7 @@ use crate::event::Event; use crate::message::Message; use crate::Error; use crate::ProposerSelector; +use crate::Validity; /// Driver for the state machine of the Malachite consensus engine at a given height. #[derive(Clone, Debug)] @@ -73,10 +74,6 @@ where self.env.get_value(self.height.clone(), round).await } - async fn validate_proposal(&self, proposal: &Ctx::Proposal) -> bool { - self.env.validate_proposal(proposal).await - } - pub async fn execute(&mut self, msg: Event) -> Result>, Error> { let round_msg = match self.apply(msg).await? { Some(msg) => msg, @@ -114,7 +111,9 @@ where async fn apply(&mut self, msg: Event) -> Result>, Error> { match msg { Event::NewRound(round) => self.apply_new_round(round).await, - Event::Proposal(proposal) => Ok(self.apply_proposal(proposal).await), + Event::Proposal(proposal, validity) => { + Ok(self.apply_proposal(proposal, validity).await) + } Event::Vote(signed_vote) => self.apply_vote(signed_vote), Event::TimeoutElapsed(timeout) => Ok(self.apply_timeout(timeout)), } @@ -154,7 +153,11 @@ where Ok(self.apply_event(round, event)) } - async fn apply_proposal(&mut self, proposal: Ctx::Proposal) -> Option> { + async fn apply_proposal( + &mut self, + proposal: Ctx::Proposal, + validity: Validity, + ) -> Option> { // Check that there is an ongoing round let Some(round_state) = self.round_states.get(&self.round) else { // TODO: Add logging @@ -178,14 +181,12 @@ where // TODO: Verify proposal signature (make some of these checks part of message validation) - let is_valid = self.validate_proposal(&proposal).await; - match proposal.pol_round() { Round::Nil => { // Is it possible to get +2/3 prevotes before the proposal? // Do we wait for our own prevote to check the threshold? let round = proposal.round(); - let event = if is_valid { + let event = if validity.is_valid() { RoundEvent::Proposal(proposal) } else { RoundEvent::ProposalInvalid @@ -201,7 +202,7 @@ where ) => { let round = proposal.round(); - let event = if is_valid { + let event = if validity.is_valid() { RoundEvent::Proposal(proposal) } else { RoundEvent::ProposalInvalid diff --git a/Code/driver/src/env.rs b/Code/driver/src/env.rs index 9eb7d065e..2915da387 100644 --- a/Code/driver/src/env.rs +++ b/Code/driver/src/env.rs @@ -16,7 +16,4 @@ where /// If `None` is returned, the driver will understand this /// as an error and will not propose a value. async fn get_value(&self, height: Ctx::Height, round: Round) -> Option; - - /// Validate a proposal. - async fn validate_proposal(&self, proposal: &Ctx::Proposal) -> bool; } diff --git a/Code/driver/src/event.rs b/Code/driver/src/event.rs index 060e330ce..d75dfab5c 100644 --- a/Code/driver/src/event.rs +++ b/Code/driver/src/event.rs @@ -1,5 +1,7 @@ use malachite_common::{Context, Round, SignedVote, Timeout}; +use crate::Validity; + /// Events that can be received by the [`Driver`](crate::Driver). #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event @@ -7,7 +9,7 @@ where Ctx: Context, { NewRound(Round), - Proposal(Ctx::Proposal), + Proposal(Ctx::Proposal, Validity), Vote(SignedVote), TimeoutElapsed(Timeout), } diff --git a/Code/driver/src/lib.rs b/Code/driver/src/lib.rs index de3ee1d5c..62401f55e 100644 --- a/Code/driver/src/lib.rs +++ b/Code/driver/src/lib.rs @@ -20,6 +20,7 @@ mod error; mod event; mod message; mod proposer; +mod util; pub use driver::Driver; pub use env::Env; @@ -27,6 +28,7 @@ pub use error::Error; pub use event::Event; pub use message::Message; pub use proposer::ProposerSelector; +pub use util::Validity; // Re-export `#[async_trait]` macro for convenience. pub use async_trait::async_trait; diff --git a/Code/driver/src/util.rs b/Code/driver/src/util.rs new file mode 100644 index 000000000..fd71a5915 --- /dev/null +++ b/Code/driver/src/util.rs @@ -0,0 +1,15 @@ +/// Wether or not a proposal is valid. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Validity { + /// The proposal is valid. + Valid, + /// The proposal is invalid. + Invalid, +} + +impl Validity { + /// Returns `true` if the proposal is valid. + pub fn is_valid(self) -> bool { + self == Validity::Valid + } +} diff --git a/Code/test/src/env.rs b/Code/test/src/env.rs index a5b2c22ef..af79543a4 100644 --- a/Code/test/src/env.rs +++ b/Code/test/src/env.rs @@ -3,21 +3,16 @@ use async_trait::async_trait; use malachite_common::Round; use malachite_driver::Env; -use crate::{Height, Proposal, TestContext, Value}; +use crate::{Height, TestContext, Value}; pub struct TestEnv { get_value: Box Option + Send + Sync>, - is_valid: Box bool + Send + Sync>, } impl TestEnv { - pub fn new( - get_value: impl Fn(Height, Round) -> Option + Send + Sync + 'static, - is_valid: impl Fn(&Proposal) -> bool + Send + Sync + 'static, - ) -> Self { + pub fn new(get_value: impl Fn(Height, Round) -> Option + Send + Sync + 'static) -> Self { Self { get_value: Box::new(get_value), - is_valid: Box::new(is_valid), } } } @@ -27,8 +22,4 @@ impl Env for TestEnv { async fn get_value(&self, height: Height, round: Round) -> Option { (self.get_value)(height, round) } - - async fn validate_proposal(&self, proposal: &Proposal) -> bool { - (self.is_valid)(proposal) - } } diff --git a/Code/test/tests/driver.rs b/Code/test/tests/driver.rs index b31cb0a39..a057b1325 100644 --- a/Code/test/tests/driver.rs +++ b/Code/test/tests/driver.rs @@ -3,7 +3,7 @@ use rand::rngs::StdRng; use rand::SeedableRng; use malachite_common::{Context, Round, Timeout}; -use malachite_driver::{Driver, Error, Event, Message, ProposerSelector}; +use malachite_driver::{Driver, Error, Event, Message, ProposerSelector, Validity}; use malachite_round::state::{RoundValue, State, Step}; use malachite_test::{ Address, Height, PrivateKey, Proposal, TestContext, TestEnv, Validator, ValidatorSet, Vote, @@ -19,7 +19,8 @@ struct TestStep { fn to_input_msg(output: Message) -> Option> { match output { - Message::Propose(p) => Some(Event::Proposal(p)), + // Let's consider our own proposal to always be valid + Message::Propose(p) => Some(Event::Proposal(p, Validity::Valid)), Message::Vote(v) => Some(Event::Vote(v)), Message::Decide(_, _) => None, Message::ScheduleTimeout(_) => None, @@ -63,7 +64,7 @@ fn driver_steps_proposer() { let value_id = value.id(); let sel = RotateProposer::default(); - let env = TestEnv::new(move |_, _| Some(value), |_| true); + let env = TestEnv::new(move |_, _| Some(value)); let mut rng = StdRng::seed_from_u64(0x42); @@ -258,7 +259,7 @@ fn driver_steps_not_proposer_valid() { let value_id = value.id(); let sel = RotateProposer::default(); - let env = TestEnv::new(move |_, _| Some(value), |_| true); + let env = TestEnv::new(move |_, _| Some(value)); let mut rng = StdRng::seed_from_u64(0x42); @@ -300,7 +301,7 @@ fn driver_steps_not_proposer_valid() { }, TestStep { desc: "Receive a proposal, prevote for it (v2)", - input_event: Some(Event::Proposal(proposal.clone())), + input_event: Some(Event::Proposal(proposal.clone(), Validity::Valid)), expected_output: Some(Message::Vote( Vote::new_prevote(Round::new(0), Some(value_id), my_addr).signed(&my_sk), )), @@ -454,7 +455,7 @@ fn driver_steps_not_proposer_invalid() { let value_id = value.id(); let sel = RotateProposer::default(); - let env = TestEnv::new(move |_, _| Some(value), |_| false); + let env = TestEnv::new(move |_, _| Some(value)); let mut rng = StdRng::seed_from_u64(0x42); @@ -496,7 +497,7 @@ fn driver_steps_not_proposer_invalid() { }, TestStep { desc: "Receive an invalid proposal, prevote for nil (v2)", - input_event: Some(Event::Proposal(proposal.clone())), + input_event: Some(Event::Proposal(proposal.clone(), Validity::Invalid)), expected_output: Some(Message::Vote( Vote::new_prevote(Round::new(0), None, my_addr).signed(&my_sk), )), @@ -596,7 +597,7 @@ fn driver_steps_not_proposer_timeout_multiple_rounds() { let value_id = value.id(); let sel = RotateProposer::default(); - let env = TestEnv::new(move |_, _| Some(value), |_| true); + let env = TestEnv::new(move |_, _| Some(value)); let mut rng = StdRng::seed_from_u64(0x42); @@ -798,7 +799,7 @@ fn driver_steps_not_proposer_timeout_multiple_rounds() { #[test] fn driver_steps_no_value_to_propose() { // No value to propose - let env = TestEnv::new(|_, _| None, |_| true); + let env = TestEnv::new(|_, _| None); let mut rng = StdRng::seed_from_u64(0x42); @@ -827,7 +828,7 @@ fn driver_steps_no_value_to_propose() { fn driver_steps_proposer_not_found() { let value = TestContext::DUMMY_VALUE; - let env = TestEnv::new(move |_, _| Some(value), |_| true); + let env = TestEnv::new(move |_, _| Some(value)); let mut rng = StdRng::seed_from_u64(0x42); @@ -858,7 +859,7 @@ fn driver_steps_proposer_not_found() { fn driver_steps_validator_not_found() { let value = TestContext::DUMMY_VALUE; - let env = TestEnv::new(move |_, _| Some(value), |_| true); + let env = TestEnv::new(move |_, _| Some(value)); let mut rng = StdRng::seed_from_u64(0x42); @@ -895,7 +896,7 @@ fn driver_steps_validator_not_found() { fn driver_steps_invalid_signature() { let value = TestContext::DUMMY_VALUE; - let env = TestEnv::new(move |_, _| Some(value), |_| true); + let env = TestEnv::new(move |_, _| Some(value)); let mut rng = StdRng::seed_from_u64(0x42);