Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

executor: Sign votes and verify votes signatures #16

Merged
merged 10 commits into from
Oct 27, 2023
1 change: 0 additions & 1 deletion Code/QUESTIONS.md
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
- How do we deal with errors?
- How do we parametrize over values, id(v), etc.
4 changes: 1 addition & 3 deletions Code/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,4 @@ if complete proposal from a past round => to current one
if we have some threshold (f+1) of votes for a future round => skip to that round

context (get proposer, get value)
signing contextt

abstract over values and validator sets
signing context
2 changes: 2 additions & 0 deletions Code/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ edition = "2021"
publish = false

[dependencies]
secrecy = "0.8.0"
signature = "2.1.0"
28 changes: 13 additions & 15 deletions Code/common/src/consensus.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
Address, Height, Proposal, PublicKey, Round, Validator, ValidatorSet, Value, ValueId, Vote,
Address, Height, PrivateKey, Proposal, PublicKey, Round, Signature, SignedVote, SigningScheme,
Validator, ValidatorSet, Value, ValueId, Vote,
};

/// This trait allows to abstract over the various datatypes
Expand All @@ -11,18 +12,23 @@ where
type Address: Address;
type Height: Height;
type Proposal: Proposal<Self>;
type PublicKey: PublicKey;
type Validator: Validator<Self>;
type ValidatorSet: ValidatorSet<Self>;
type Value: Value;
type Vote: Vote<Self>;

// FIXME: Remove this and thread it through where necessary
const DUMMY_ADDRESS: Self::Address;
type SigningScheme: SigningScheme; // TODO: Do we need to support multiple signing schemes?

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

/// Sign the given vote using the given private key.
/// TODO: Maybe move this as concrete methods in `SignedVote`?
fn sign_vote(vote: &Self::Vote, private_key: &PrivateKey<Self>) -> Signature<Self>;

/// Verify the given vote's signature using the given public key.
/// TODO: Maybe move this as concrete methods in `SignedVote`?
fn verify_signed_vote(signed_vote: &SignedVote<Self>, public_key: &PublicKey<Self>) -> bool;

/// Build a new proposal for the given value at the given height, round and POL round.
fn new_proposal(
height: Self::Height,
Expand All @@ -33,17 +39,9 @@ where

/// Build a new prevote vote by the validator with the given address,
/// for the value identified by the given value id, at the given round.
fn new_prevote(
round: Round,
value_id: Option<ValueId<Self>>,
address: Self::Address,
) -> Self::Vote;
fn new_prevote(round: Round, value_id: Option<ValueId<Self>>) -> Self::Vote;

/// Build a new precommit vote by the validator with the given address,
/// for the value identified by the given value id, at the given round.
fn new_precommit(
round: Round,
value_id: Option<ValueId<Self>>,
address: Self::Address,
) -> Self::Vote;
fn new_precommit(round: Round, value_id: Option<ValueId<Self>>) -> Self::Vote;
}
13 changes: 12 additions & 1 deletion Code/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Common data types and abstractions for the consensus engine.

#![no_std]
#![forbid(unsafe_code)]
#![deny(unused_crate_dependencies, trivial_casts, trivial_numeric_casts)]
#![warn(
Expand All @@ -14,19 +15,29 @@ mod consensus;
mod height;
mod proposal;
mod round;
mod signed_vote;
mod signing;
mod timeout;
mod validator_set;
mod value;
mod vote;

// Re-export `signature` crate for convenience
pub use ::signature;

/// Type alias to make it easier to refer the `ValueId` type of a given `Consensus` engine.
pub type ValueId<C> = <<C as Consensus>::Value as Value>::Id;
pub type PublicKey<C> = <<C as Consensus>::SigningScheme as SigningScheme>::PublicKey;
pub type PrivateKey<C> = <<C as Consensus>::SigningScheme as SigningScheme>::PrivateKey;
pub type Signature<C> = <<C as Consensus>::SigningScheme as SigningScheme>::Signature;

pub use consensus::Consensus;
pub use height::Height;
pub use proposal::Proposal;
pub use round::Round;
pub use signed_vote::SignedVote;
pub use signing::SigningScheme;
pub use timeout::{Timeout, TimeoutStep};
pub use validator_set::{Address, PublicKey, Validator, ValidatorSet, VotingPower};
pub use validator_set::{Address, Validator, ValidatorSet, VotingPower};
pub use value::Value;
pub use vote::{Vote, VoteType};
6 changes: 4 additions & 2 deletions Code/common/src/round.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::cmp;

/// A round number.
///
/// Can be either:
Expand Down Expand Up @@ -69,13 +71,13 @@ impl Round {
}

impl PartialOrd for Round {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Round {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_i64().cmp(&other.as_i64())
}
}
Expand Down
26 changes: 26 additions & 0 deletions Code/common/src/signed_vote.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::{Consensus, Signature};

// TODO: Do we need to abstract over `SignedVote` as well?

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SignedVote<C>
where
C: Consensus,
{
pub vote: C::Vote,
pub address: C::Address,
pub signature: Signature<C>,
}

impl<C> SignedVote<C>
where
C: Consensus,
{
pub fn new(vote: C::Vote, address: C::Address, signature: Signature<C>) -> Self {
Self {
vote,
address,
signature,
}
}
}
20 changes: 20 additions & 0 deletions Code/common/src/signing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use core::fmt::Debug;

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

pub trait SigningScheme
where
Self: Clone + Debug + Eq,
{
type Signature: Clone + Debug + Eq;

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

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

use crate::Consensus;
use crate::{Consensus, PublicKey};

/// Voting power held by a validator.
///
/// TODO: Do we need to abstract over this as well?
pub type VotingPower = u64;

/// Defines the requirements for a public key type.
pub trait PublicKey
where
Self: Clone + Debug + PartialEq + Eq,
{
}

/// Defines the requirements for an address.
///
/// TODO: Keep this trait or just add the bounds to Consensus::Address?
Expand All @@ -33,7 +26,7 @@ where
fn address(&self) -> &C::Address;

/// The public key of the validator, used to verify signatures.
fn public_key(&self) -> &C::PublicKey;
fn public_key(&self) -> &PublicKey<C>;

/// The voting power held by the validaror.
fn voting_power(&self) -> VotingPower;
Expand All @@ -53,7 +46,7 @@ where
fn get_proposer(&self) -> C::Validator;

/// Get the validator with the given public key.
fn get_by_public_key(&self, public_key: &C::PublicKey) -> Option<&C::Validator>;
fn get_by_public_key(&self, public_key: &PublicKey<C>) -> Option<&C::Validator>;

/// Get the validator with the given address.
fn get_by_address(&self, address: &C::Address) -> Option<&C::Validator>;
Expand Down
4 changes: 0 additions & 4 deletions Code/common/src/vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,4 @@ where

/// The type of vote.
fn vote_type(&self) -> VoteType;

// FIXME: round message votes should not include address
fn address(&self) -> &C::Address;
fn set_address(&mut self, address: C::Address);
}
2 changes: 2 additions & 0 deletions Code/consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ publish = false
malachite-common = { version = "0.1.0", path = "../common" }
malachite-round = { version = "0.1.0", path = "../round" }
malachite-vote = { version = "0.1.0", path = "../vote" }

secrecy = "0.8.0"
64 changes: 29 additions & 35 deletions Code/consensus/src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,40 @@
use std::collections::BTreeMap;

use secrecy::{ExposeSecret, Secret};

use malachite_common::signature::Keypair;
use malachite_common::{
Consensus, Proposal, Round, Timeout, TimeoutStep, Validator, ValidatorSet, Value, Vote,
VoteType,
Consensus, PrivateKey, Proposal, Round, SignedVote, Timeout, TimeoutStep, Validator,
ValidatorSet, Value, Vote, VoteType,
};
use malachite_round::events::Event as RoundEvent;
use malachite_round::message::Message as RoundMessage;
use malachite_round::state::State as RoundState;
use malachite_vote::count::Threshold;
use malachite_vote::keeper::VoteKeeper;

use crate::message::Message;

#[derive(Clone, Debug)]
pub struct Executor<C>
where
C: Consensus,
{
height: C::Height,
key: C::PublicKey,
key: Secret<PrivateKey<C>>,
validator_set: C::ValidatorSet,
round: Round,
votes: VoteKeeper<C>,
round_states: BTreeMap<Round, RoundState<C>>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Message<C>
where
C: Consensus,
{
NewRound(Round),
Proposal(C::Proposal),
Vote(C::Vote),
Timeout(Timeout),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Output<C>
where
C: Consensus,
{
Propose(C::Proposal),
Vote(C::Vote),
Vote(SignedVote<C>),
Decide(Round, C::Value),
SetTimeout(Timeout),
}
Expand All @@ -49,7 +43,7 @@ impl<C> Executor<C>
where
C: Consensus,
{
pub fn new(height: C::Height, validator_set: C::ValidatorSet, key: C::PublicKey) -> Self {
pub fn new(height: C::Height, validator_set: C::ValidatorSet, key: PrivateKey<C>) -> Self {
let votes = VoteKeeper::new(
height.clone(),
Round::INITIAL,
Expand All @@ -58,7 +52,7 @@ where

Self {
height,
key,
key: Secret::new(key),
validator_set,
round: Round::INITIAL,
votes,
Expand Down Expand Up @@ -92,19 +86,17 @@ where
Some(Output::Propose(proposal))
}

RoundMessage::Vote(mut v) => {
// sign the vote

// FIXME: round message votes should not include address
RoundMessage::Vote(vote) => {
let address = self
.validator_set
.get_by_public_key(&self.key)?
.get_by_public_key(&self.key.expose_secret().verifying_key())?
.address()
.clone();

v.set_address(address);
let signature = C::sign_vote(&vote, self.key.expose_secret());
let signed_vote = SignedVote::new(vote, address, signature);

Some(Output::Vote(v))
Some(Output::Vote(signed_vote))
}

RoundMessage::Timeout(timeout) => Some(Output::SetTimeout(timeout)),
Expand All @@ -120,15 +112,15 @@ where
match msg {
Message::NewRound(round) => self.apply_new_round(round),
Message::Proposal(proposal) => self.apply_proposal(proposal),
Message::Vote(vote) => self.apply_vote(vote),
Message::Vote(signed_vote) => self.apply_vote(signed_vote),
Message::Timeout(timeout) => self.apply_timeout(timeout),
}
}

fn apply_new_round(&mut self, round: Round) -> Option<RoundMessage<C>> {
let proposer = self.validator_set.get_proposer();

let event = if proposer.public_key() == &self.key {
let event = if proposer.public_key() == &self.key.expose_secret().verifying_key() {
let value = self.get_value();
RoundEvent::NewRoundProposer(value)
} else {
Expand Down Expand Up @@ -185,18 +177,20 @@ where
}
}

fn apply_vote(&mut self, vote: C::Vote) -> Option<RoundMessage<C>> {
let Some(validator) = self.validator_set.get_by_address(vote.address()) else {
// TODO: Is this the correct behavior? How to log such "errors"?
fn apply_vote(&mut self, signed_vote: SignedVote<C>) -> Option<RoundMessage<C>> {
// TODO: How to handle missing validator?
let validator = self.validator_set.get_by_address(&signed_vote.address)?;

if !C::verify_signed_vote(&signed_vote, validator.public_key()) {
// TODO: How to handle invalid votes?
return None;
};
}

let round = vote.round();
let round = signed_vote.vote.round();

let event = match self.votes.apply_vote(vote, validator.voting_power()) {
Some(event) => event,
None => return None,
};
let event = self
.votes
.apply_vote(signed_vote.vote, validator.voting_power())?;

self.apply_event(round, event)
}
Expand Down
1 change: 1 addition & 0 deletions Code/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::panic))]

pub mod executor;
pub mod message;
Loading
Loading