Skip to content

Commit

Permalink
Merge branch 'main' into anca/adr-overall-architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
ancazamfir committed Nov 14, 2023
2 parents e2d551b + bde9355 commit d18e565
Show file tree
Hide file tree
Showing 18 changed files with 363 additions and 128 deletions.
1 change: 0 additions & 1 deletion Code/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ async-trait = "0.1"
futures = "0.3"
ed25519-consensus = "2.1.0"
rand = { version = "0.8.5", features = ["std_rng"] }
secrecy = "0.8.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
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
105 changes: 60 additions & 45 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 @@ -18,7 +16,9 @@ use malachite_vote::Threshold;
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)]
Expand All @@ -33,7 +33,6 @@ where
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 @@ -54,7 +53,6 @@ where
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());
Expand All @@ -64,7 +62,6 @@ where
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.env.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.env.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
12 changes: 6 additions & 6 deletions Code/driver/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use alloc::boxed::Box;

use async_trait::async_trait;

use malachite_common::Context;
use malachite_common::{Context, Round};

/// Environment for use by the [`Driver`](crate::Driver) to ask
/// for a value to propose and validate proposals.
Expand All @@ -11,9 +11,9 @@ pub trait Env<Ctx>
where
Ctx: Context,
{
/// Get the value to propose.
async fn get_value(&self) -> Ctx::Value;

/// Validate a proposal.
async fn validate_proposal(&self, proposal: &Ctx::Proposal) -> bool;
/// 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>;
}
59 changes: 59 additions & 0 deletions Code/driver/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use core::fmt;

use malachite_common::{Context, SignedVote, Validator};

#[derive(Clone, Debug)]
pub enum Error<Ctx>
where
Ctx: Context,
{
/// No value to propose
NoValueToPropose,

/// Proposer not found
ProposerNotFound(Ctx::Address),

/// Validator not found in validator set
ValidatorNotFound(Ctx::Address),

/// Invalid vote signature
InvalidVoteSignature(SignedVote<Ctx>, Ctx::Validator),
}

impl<Ctx> fmt::Display for Error<Ctx>
where
Ctx: Context,
{
#[cfg_attr(coverage_nightly, coverage(off))]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::NoValueToPropose => write!(f, "No value to propose"),
Error::ProposerNotFound(addr) => write!(f, "Proposer not found: {addr}"),
Error::ValidatorNotFound(addr) => write!(f, "Validator not found: {addr}"),
Error::InvalidVoteSignature(vote, validator) => write!(
f,
"Invalid vote signature by {} on vote {vote:?}",
validator.address()
),
}
}
}

impl<Ctx> PartialEq for Error<Ctx>
where
Ctx: Context,
{
#[cfg_attr(coverage_nightly, coverage(off))]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Error::NoValueToPropose, Error::NoValueToPropose) => true,
(Error::ProposerNotFound(addr1), Error::ProposerNotFound(addr2)) => addr1 == addr2,
(Error::ValidatorNotFound(addr1), Error::ValidatorNotFound(addr2)) => addr1 == addr2,
(
Error::InvalidVoteSignature(vote1, validator1),
Error::InvalidVoteSignature(vote2, validator2),
) => vote1 == vote2 && validator1 == validator2,
_ => false,
}
}
}
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),
}
Loading

0 comments on commit d18e565

Please sign in to comment.