Skip to content

Commit

Permalink
Merge branch 'main' into romac/itf
Browse files Browse the repository at this point in the history
  • Loading branch information
romac authored Nov 15, 2023
2 parents 4f66a4d + d39e933 commit 9d540e8
Show file tree
Hide file tree
Showing 23 changed files with 417 additions and 187 deletions.
1 change: 0 additions & 1 deletion Code/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ futures = "0.3"
ed25519-consensus = "2.1.0"
itf = "0.1.2"
rand = { version = "0.8.5", features = ["std_rng"] }
secrecy = "0.8.0"
serde = "1.0"
sha2 = "0.10.8"
signature = "2.1.0"
1 change: 0 additions & 1 deletion Code/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ license.workspace = true
publish.workspace = true

[dependencies]
secrecy.workspace = true
signature.workspace = true
3 changes: 0 additions & 3 deletions Code/common/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ where
type Vote: Vote<Self>;
type SigningScheme: SigningScheme; // TODO: Do we need to support multiple signing schemes?

// FIXME: Remove altogether
const DUMMY_VALUE: Self::Value;

/// Sign the given vote our private key.
fn sign_vote(&self, vote: Self::Vote) -> SignedVote<Self>;

Expand Down
8 changes: 1 addition & 7 deletions Code/common/src/signing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use core::fmt::Debug;

use secrecy::{CloneableSecret, DebugSecret, Zeroize};
use signature::{Keypair, Signer, Verifier};

pub trait SigningScheme
Expand All @@ -11,10 +10,5 @@ where

type PublicKey: Clone + Debug + Eq + Verifier<Self::Signature>;

type PrivateKey: Clone
+ Signer<Self::Signature>
+ Keypair<VerifyingKey = Self::PublicKey>
+ Zeroize
+ DebugSecret
+ CloneableSecret;
type PrivateKey: Clone + Signer<Self::Signature> + Keypair<VerifyingKey = Self::PublicKey>;
}
4 changes: 2 additions & 2 deletions Code/common/src/validator_set.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::fmt::Debug;
use core::fmt::{Debug, Display};

use crate::{Context, PublicKey};

Expand All @@ -12,7 +12,7 @@ pub type VotingPower = u64;
/// TODO: Keep this trait or just add the bounds to Consensus::Address?
pub trait Address
where
Self: Clone + Debug + Eq + Ord,
Self: Clone + Debug + Display + Eq + Ord,
{
}

Expand Down
1 change: 0 additions & 1 deletion Code/driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ malachite-round = { version = "0.1.0", path = "../round" }
malachite-vote = { version = "0.1.0", path = "../vote" }

async-trait.workspace = true
secrecy.workspace = true
19 changes: 0 additions & 19 deletions Code/driver/src/client.rs

This file was deleted.

121 changes: 68 additions & 53 deletions Code/driver/src/driver.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use alloc::collections::BTreeMap;

use malachite_round::state_machine::RoundData;
use secrecy::{ExposeSecret, Secret};

use malachite_common::signature::Keypair;
use malachite_common::{
Context, PrivateKey, Proposal, Round, SignedVote, Timeout, TimeoutStep, Validator,
ValidatorSet, Value, Vote, VoteType,
Context, Proposal, Round, SignedVote, Timeout, TimeoutStep, Validator, ValidatorSet, Value,
Vote, VoteType,
};
use malachite_round::events::Event as RoundEvent;
use malachite_round::message::Message as RoundMessage;
Expand All @@ -15,25 +13,26 @@ use malachite_vote::keeper::Message as VoteMessage;
use malachite_vote::keeper::VoteKeeper;
use malachite_vote::Threshold;

use crate::client::Client as EnvClient;
use crate::env::Env as DriverEnv;
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)]
pub struct Driver<Ctx, Client, PSel>
pub struct Driver<Ctx, Env, PSel>
where
Ctx: Context,
Client: EnvClient<Ctx>,
Env: DriverEnv<Ctx>,
PSel: ProposerSelector<Ctx>,
{
pub ctx: Ctx,
pub client: Client,
pub env: Env,
pub proposer_selector: PSel,

pub height: Ctx::Height,
pub private_key: Secret<PrivateKey<Ctx>>,
pub address: Ctx::Address,
pub validator_set: Ctx::ValidatorSet,

Expand All @@ -42,29 +41,27 @@ where
pub round_states: BTreeMap<Round, RoundState<Ctx>>,
}

impl<Ctx, Client, PSel> Driver<Ctx, Client, PSel>
impl<Ctx, Env, PSel> Driver<Ctx, Env, PSel>
where
Ctx: Context,
Client: EnvClient<Ctx>,
Env: DriverEnv<Ctx>,
PSel: ProposerSelector<Ctx>,
{
pub fn new(
ctx: Ctx,
client: Client,
env: Env,
proposer_selector: PSel,
height: Ctx::Height,
validator_set: Ctx::ValidatorSet,
private_key: PrivateKey<Ctx>,
address: Ctx::Address,
) -> Self {
let votes = VoteKeeper::new(validator_set.total_voting_power());

Self {
ctx,
client,
env,
proposer_selector,
height,
private_key: Secret::new(private_key),
address,
validator_set,
round: Round::NIL,
Expand All @@ -73,68 +70,76 @@ where
}
}

async fn get_value(&self) -> Ctx::Value {
self.client.get_value().await
async fn get_value(&self, round: Round) -> Option<Ctx::Value> {
self.env.get_value(self.height.clone(), round).await
}

async fn validate_proposal(&self, proposal: &Ctx::Proposal) -> bool {
self.client.validate_proposal(proposal).await
}

pub async fn execute(&mut self, msg: Event<Ctx>) -> Option<Message<Ctx>> {
let round_msg = match self.apply(msg).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,
None => return None,
None => return Ok(None),
};

match round_msg {
let msg = match round_msg {
RoundMessage::NewRound(round) => {
// XXX: Check if there is an existing state?
assert!(self.round < round);
Some(Message::NewRound(round))
Message::NewRound(round)
}

RoundMessage::Proposal(proposal) => {
// sign the proposal
Some(Message::Propose(proposal))
Message::Propose(proposal)
}

RoundMessage::Vote(vote) => {
let signed_vote = self.ctx.sign_vote(vote);
Some(Message::Vote(signed_vote))
Message::Vote(signed_vote)
}

RoundMessage::ScheduleTimeout(timeout) => Some(Message::ScheduleTimeout(timeout)),
RoundMessage::ScheduleTimeout(timeout) => Message::ScheduleTimeout(timeout),

RoundMessage::Decision(value) => {
// TODO: update the state
Some(Message::Decide(value.round, value.value))
Message::Decide(value.round, value.value)
}
}
};

Ok(Some(msg))
}

async fn apply(&mut self, msg: Event<Ctx>) -> Option<RoundMessage<Ctx>> {
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) => 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) => self.apply_timeout(timeout),
Event::TimeoutElapsed(timeout) => Ok(self.apply_timeout(timeout)),
}
}

async fn apply_new_round(&mut self, round: Round) -> Option<RoundMessage<Ctx>> {
async fn apply_new_round(
&mut self,
round: Round,
) -> Result<Option<RoundMessage<Ctx>>, Error<Ctx>> {
let proposer_address = self
.proposer_selector
.select_proposer(round, &self.validator_set);

let proposer = self
.validator_set
.get_by_address(&proposer_address)
.expect("proposer not found"); // FIXME: expect
.ok_or_else(|| Error::ProposerNotFound(proposer_address.clone()))?;

let event = if proposer.address() == &self.address {
// We are the proposer
// TODO: Schedule propose timeout

let Some(value) = self.get_value(round).await else {
return Err(Error::NoValueToPropose);
};

// TODO: Write this check differently, maybe just based on the address
let event = if proposer.public_key() == &self.private_key.expose_secret().verifying_key() {
let value = self.get_value().await;
RoundEvent::NewRoundProposer(value)
} else {
RoundEvent::NewRound
Expand All @@ -145,10 +150,14 @@ where
.insert(round, RoundState::default().new_round(round));
self.round = round;

self.apply_event(round, event)
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 @@ -172,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 @@ -195,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 All @@ -207,25 +214,33 @@ where
}
}

fn apply_vote(&mut self, signed_vote: SignedVote<Ctx>) -> Option<RoundMessage<Ctx>> {
// TODO: How to handle missing validator?
fn apply_vote(
&mut self,
signed_vote: SignedVote<Ctx>,
) -> Result<Option<RoundMessage<Ctx>>, Error<Ctx>> {
let validator = self
.validator_set
.get_by_address(signed_vote.validator_address())?;
.get_by_address(signed_vote.validator_address())
.ok_or_else(|| Error::ValidatorNotFound(signed_vote.validator_address().clone()))?;

if !self
.ctx
.verify_signed_vote(&signed_vote, validator.public_key())
{
// TODO: How to handle invalid votes?
return None;
return Err(Error::InvalidVoteSignature(
signed_vote.clone(),
validator.clone(),
));
}

let round = signed_vote.vote.round();

let vote_msg = self
let Some(vote_msg) = self
.votes
.apply_vote(signed_vote.vote, validator.voting_power())?;
.apply_vote(signed_vote.vote, validator.voting_power())
else {
return Ok(None);
};

let round_event = match vote_msg {
VoteMessage::PolkaAny => RoundEvent::PolkaAny,
Expand All @@ -236,7 +251,7 @@ where
VoteMessage::SkipRound(r) => RoundEvent::SkipRound(r),
};

self.apply_event(round, round_event)
Ok(self.apply_event(round, round_event))
}

fn apply_timeout(&mut self, timeout: Timeout) -> Option<RoundMessage<Ctx>> {
Expand Down
19 changes: 19 additions & 0 deletions Code/driver/src/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use alloc::boxed::Box;

use async_trait::async_trait;

use malachite_common::{Context, Round};

/// Environment for use by the [`Driver`](crate::Driver) to ask
/// for a value to propose and validate proposals.
#[async_trait]
pub trait Env<Ctx>
where
Ctx: Context,
{
/// Get the value to propose for the given height and round.
///
/// 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>;
}
Loading

0 comments on commit 9d540e8

Please sign in to comment.