Skip to content

Commit

Permalink
feat(driver): Move proposal validity into Propose message (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
romac authored Nov 14, 2023
1 parent 01cfa7e commit bde9355
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 37 deletions.
21 changes: 11 additions & 10 deletions Code/driver/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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<Ctx>) -> Result<Option<Message<Ctx>>, Error<Ctx>> {
let round_msg = match self.apply(msg).await? {
Some(msg) => msg,
Expand Down Expand Up @@ -114,7 +111,9 @@ where
async fn apply(&mut self, msg: Event<Ctx>) -> Result<Option<RoundMessage<Ctx>>, Error<Ctx>> {
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)),
}
Expand Down Expand Up @@ -154,7 +153,11 @@ where
Ok(self.apply_event(round, event))
}

async fn apply_proposal(&mut self, proposal: Ctx::Proposal) -> Option<RoundMessage<Ctx>> {
async fn apply_proposal(
&mut self,
proposal: Ctx::Proposal,
validity: Validity,
) -> Option<RoundMessage<Ctx>> {
// Check that there is an ongoing round
let Some(round_state) = self.round_states.get(&self.round) else {
// TODO: Add logging
Expand All @@ -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
Expand All @@ -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
Expand Down
3 changes: 0 additions & 3 deletions Code/driver/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ctx::Value>;

/// Validate a proposal.
async fn validate_proposal(&self, proposal: &Ctx::Proposal) -> bool;
}
4 changes: 3 additions & 1 deletion Code/driver/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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<Ctx>
where
Ctx: Context,
{
NewRound(Round),
Proposal(Ctx::Proposal),
Proposal(Ctx::Proposal, Validity),
Vote(SignedVote<Ctx>),
TimeoutElapsed(Timeout),
}
2 changes: 2 additions & 0 deletions Code/driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ mod error;
mod event;
mod message;
mod proposer;
mod util;

pub use driver::Driver;
pub use env::Env;
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;
15 changes: 15 additions & 0 deletions Code/driver/src/util.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
13 changes: 2 additions & 11 deletions Code/test/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn Fn(Height, Round) -> Option<Value> + Send + Sync>,
is_valid: Box<dyn Fn(&Proposal) -> bool + Send + Sync>,
}

impl TestEnv {
pub fn new(
get_value: impl Fn(Height, Round) -> Option<Value> + Send + Sync + 'static,
is_valid: impl Fn(&Proposal) -> bool + Send + Sync + 'static,
) -> Self {
pub fn new(get_value: impl Fn(Height, Round) -> Option<Value> + Send + Sync + 'static) -> Self {
Self {
get_value: Box::new(get_value),
is_valid: Box::new(is_valid),
}
}
}
Expand All @@ -27,8 +22,4 @@ impl Env<TestContext> for TestEnv {
async fn get_value(&self, height: Height, round: Round) -> Option<Value> {
(self.get_value)(height, round)
}

async fn validate_proposal(&self, proposal: &Proposal) -> bool {
(self.is_valid)(proposal)
}
}
25 changes: 13 additions & 12 deletions Code/test/tests/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -19,7 +19,8 @@ struct TestStep {

fn to_input_msg(output: Message<TestContext>) -> Option<Event<TestContext>> {
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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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),
)),
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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),
)),
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit bde9355

Please sign in to comment.