Skip to content

Commit

Permalink
chore(code): Move signing capabilities out of Context and into `Sig…
Browse files Browse the repository at this point in the history
…ningProvider` trait (#680)
  • Loading branch information
romac authored Dec 13, 2024
1 parent ca1c2fe commit d52a793
Show file tree
Hide file tree
Showing 23 changed files with 461 additions and 281 deletions.
32 changes: 19 additions & 13 deletions code/crates/consensus/src/effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,6 @@ where
/// Resume with: [`Resume::ValidatorSet`]
GetValidatorSet(Ctx::Height),

/// Sign a vote with this node's private key
/// Resume with: [`Resume::SignedVote`]
SignVote(Ctx::Vote),

/// Sign a proposal with this node's private key
/// Resume with: [`Resume::SignedProposal`]
SignProposal(Ctx::Proposal),

/// Verify a signature
/// Resume with: [`Resume::SignatureValidity`]
VerifySignature(SignedMessage<Ctx, ConsensusMsg<Ctx>>, PublicKey<Ctx>),

/// Consensus has decided on a value
/// Resume with: [`Resume::Continue`]
Decide { certificate: CommitCertificate<Ctx> },
Expand All @@ -83,6 +71,21 @@ where

/// Persist a timeout in the Write-Ahead Log for crash recovery
PersistTimeout(Timeout),

/// Sign a vote with this node's private key
/// Resume with: [`Resume::SignedVote`]
SignVote(Ctx::Vote),

/// Sign a proposal with this node's private key
/// Resume with: [`Resume::SignedProposal`]
SignProposal(Ctx::Proposal),

/// Verify a signature
/// Resume with: [`Resume::SignatureValidity`]
VerifySignature(SignedMessage<Ctx, ConsensusMsg<Ctx>>, PublicKey<Ctx>),

/// Verify a commit certificate
VerifyCertificate(CommitCertificate<Ctx>, Ctx::ValidatorSet, ThresholdParams),
}

/// A value with which the consensus process can be resumed after yielding an [`Effect`].
Expand All @@ -103,12 +106,15 @@ where
/// Resume execution with an optional validator set at the given height
ValidatorSet(Ctx::Height, Option<Ctx::ValidatorSet>),

/// Resume execution with the validity of the signature just verified
/// Resume execution with the validity of the signature
SignatureValidity(bool),

/// Resume execution with the signed vote
SignedVote(SignedMessage<Ctx, Ctx::Vote>),

/// Resume execution with the signed proposal
SignedProposal(SignedMessage<Ctx, Ctx::Proposal>),

/// Resume execution with the result of the verification of the [`CommitCertificate`]
CertificateValidity(Result<(), CertificateError<Ctx>>),
}
9 changes: 6 additions & 3 deletions code/crates/consensus/src/handle/proposed_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::prelude::*;
use crate::handle::driver::apply_driver_input;
use crate::types::ProposedValue;

use super::signature::sign_proposal;

#[tracing::instrument(
skip_all,
fields(
Expand Down Expand Up @@ -52,9 +54,10 @@ where
proposed_value.validator_address.clone(),
);

// TODO - keep unsigned proposals in keeper. For now we keep all happy
// by signing all "implicit" proposals with this node's key
let signed_proposal = Ctx::sign_proposal(&state.ctx, proposal);
// TODO: Keep unsigned proposals in keeper.
// For now we keep all happy by signing all "implicit" proposals with this node's key
let signed_proposal = sign_proposal(co, proposal).await?;

state.store_proposal(signed_proposal);
}

Expand Down
17 changes: 17 additions & 0 deletions code/crates/consensus/src/handle/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,20 @@ where

Ok(signed_proposal)
}

pub async fn verify_certificate<Ctx>(
co: &Co<Ctx>,
certificate: CommitCertificate<Ctx>,
validator_set: Ctx::ValidatorSet,
threshold_params: ThresholdParams,
) -> Result<Result<(), CertificateError<Ctx>>, Error<Ctx>>
where
Ctx: Context,
{
let result = perform!(co,
Effect::VerifyCertificate(certificate, validator_set, threshold_params),
Resume::CertificateValidity(result) => result
);

Ok(result)
}
14 changes: 8 additions & 6 deletions code/crates/consensus/src/handle/sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::borrow::Borrow;

use crate::handle::driver::apply_driver_input;
use crate::handle::signature::verify_certificate;
use crate::handle::validator_set::get_validator_set;
use crate::prelude::*;

Expand All @@ -23,11 +22,14 @@ where
return Err(Error::ValidatorSetNotFound(certificate.height));
};

if let Err(e) = certificate.verify(
&state.ctx,
validator_set.borrow(),
if let Err(e) = verify_certificate(
co,
certificate.clone(),
validator_set.as_ref().clone(),
state.params.threshold_params,
) {
)
.await?
{
return Err(Error::InvalidCertificate(certificate, e));
}

Expand Down
6 changes: 4 additions & 2 deletions code/crates/consensus/tests/full_proposal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use malachite_consensus::{FullProposal, FullProposalKeeper, Input, ProposedValue};
use malachite_core_types::{Context, Round, SignedProposal, Validity, ValueOrigin};
use malachite_core_types::{
Context, Round, SignedProposal, SigningProvider, Validity, ValueOrigin,
};
use malachite_test::utils::validators::make_validators;
use malachite_test::{Address, Proposal, Value};
use malachite_test::{Height, TestContext};
Expand All @@ -13,7 +15,7 @@ fn signed_proposal_pol(
address: Address,
) -> SignedProposal<TestContext> {
let proposal1 = Proposal::new(height, round, value, pol_round, address);
ctx.sign_proposal(proposal1)
ctx.signing_provider().sign_proposal(proposal1)
}

fn prop(
Expand Down
74 changes: 2 additions & 72 deletions code/crates/core-types/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use derive_where::derive_where;
use thiserror::Error;

use crate::{
Context, NilOrVal, Round, Signature, SignedExtension, SignedVote, ThresholdParams, Validator,
ValidatorSet, ValueId, Vote, VoteType, VotingPower,
Context, NilOrVal, Round, Signature, SignedExtension, SignedVote, ValueId, Vote, VoteType,
VotingPower,
};

/// Represents a signature for a certificate, including the address and the signature itself.
Expand Down Expand Up @@ -94,76 +94,6 @@ impl<Ctx: Context> CommitCertificate<Ctx> {
aggregated_signature,
}
}

/// Verify the certificate against the given validator set.
///
/// - For each commit signature in the certificate:
/// - Reconstruct the signed precommit and verify its signature
/// - Check that we have 2/3+ of voting power has signed the certificate
///
/// If any of those steps fail, return a [`CertificateError`].
///
/// TODO: Move to Context
pub fn verify(
&self,
ctx: &Ctx,
validator_set: &Ctx::ValidatorSet,
thresholds: ThresholdParams,
) -> Result<(), CertificateError<Ctx>> {
let total_voting_power = validator_set.total_voting_power();
let mut signed_voting_power = 0;

// For each commit signature, reconstruct the signed precommit and verify the signature
for commit_sig in &self.aggregated_signature.signatures {
// Abort if validator not in validator set
let Some(validator) = validator_set.get_by_address(&commit_sig.address) else {
return Err(CertificateError::UnknownValidator(commit_sig.clone()));
};

let voting_power = self.verify_commit_signature(ctx, commit_sig, validator)?;
signed_voting_power += voting_power;
}

// Check if we have 2/3+ voting power
if thresholds
.quorum
.is_met(signed_voting_power, total_voting_power)
{
Ok(())
} else {
Err(CertificateError::NotEnoughVotingPower {
signed: signed_voting_power,
total: total_voting_power,
expected: thresholds.quorum.min_expected(total_voting_power),
})
}
}

/// Verify a commit signature against the public key of its validator.
///
/// ## Return
/// Return the voting power of that validator if the signature is valid.
fn verify_commit_signature(
&self,
ctx: &Ctx,
commit_sig: &CommitSignature<Ctx>,
validator: &Ctx::Validator,
) -> Result<VotingPower, CertificateError<Ctx>> {
// Reconstruct the vote that was signed
let vote = Ctx::new_precommit(
self.height,
self.round,
NilOrVal::Val(self.value_id.clone()),
validator.address().clone(),
);

// Verify signature
if !ctx.verify_signed_vote(&vote, &commit_sig.signature, validator.public_key()) {
return Err(CertificateError::InvalidSignature(commit_sig.clone()));
}

Ok(validator.voting_power())
}
}

/// Represents an error that can occur when verifying a certificate.
Expand Down
47 changes: 9 additions & 38 deletions code/crates/core-types/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::signing::SigningProvider;
use crate::{
Address, Height, NilOrVal, Proposal, ProposalPart, PublicKey, Round, Signature, SignedMessage,
SigningScheme, Validator, ValidatorSet, Value, ValueId, Vote,
Address, Height, NilOrVal, Proposal, ProposalPart, Round, SigningScheme, Validator,
ValidatorSet, Value, ValueId, Vote,
};

/// This trait allows to abstract over the various datatypes
Expand Down Expand Up @@ -33,9 +34,12 @@ where
/// The type of votes that can be cast.
type Vote: Vote<Self>;

/// The signing scheme used to sign votes.
/// The signing scheme used to sign consensus messages.
type SigningScheme: SigningScheme;

/// The signing provider used to sign and verify consensus messages.
type SigningProvider: SigningProvider<Self>;

/// Select a proposer in the validator set for the given height and round.
fn select_proposer<'a>(
&self,
Expand All @@ -44,41 +48,8 @@ where
round: Round,
) -> &'a Self::Validator;

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

/// Verify the given vote's signature using the given public key.
fn verify_signed_vote(
&self,
vote: &Self::Vote,
signature: &Signature<Self>,
public_key: &PublicKey<Self>,
) -> bool;

/// Sign the given proposal with our private key.
fn sign_proposal(&self, proposal: Self::Proposal) -> SignedMessage<Self, Self::Proposal>;

/// Verify the given proposal's signature using the given public key.
fn verify_signed_proposal(
&self,
proposal: &Self::Proposal,
signature: &Signature<Self>,
public_key: &PublicKey<Self>,
) -> bool;

/// Sign the proposal part with our private key.
fn sign_proposal_part(
&self,
proposal_part: Self::ProposalPart,
) -> SignedMessage<Self, Self::ProposalPart>;

/// Verify the given proposal part signature using the given public key.
fn verify_signed_proposal_part(
&self,
proposal_part: &Self::ProposalPart,
signature: &Signature<Self>,
public_key: &PublicKey<Self>,
) -> bool;
/// Get the singing provider.
fn signing_provider(&self) -> &Self::SigningProvider;

/// Build a new proposal for the given value at the given height, round and POL round.
fn new_proposal(
Expand Down
2 changes: 1 addition & 1 deletion code/crates/core-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub use proposal::{Proposal, Validity};
pub use proposal_part::ProposalPart;
pub use round::Round;
pub use signed_message::SignedMessage;
pub use signing::SigningScheme;
pub use signing::{SigningProvider, SigningProviderExt, SigningScheme};
pub use threshold::{Threshold, ThresholdParam, ThresholdParams};
pub use timeout::{Timeout, TimeoutKind};
pub use validator_set::{Address, Validator, ValidatorSet, VotingPower};
Expand Down
Loading

0 comments on commit d52a793

Please sign in to comment.