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

Add crypto provider #1214

Closed
wants to merge 13 commits into from
1 change: 1 addition & 0 deletions light-client-verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ flex-error = { version = "0.4.4", default-features = false }

[dev-dependencies]
tendermint-testgen = { path = "../testgen", default-features = false }
sha2 = { version = "0.9", default-features = false }
mzabaluev marked this conversation as resolved.
Show resolved Hide resolved
79 changes: 43 additions & 36 deletions light-client-verifier/src/operations/commit_validator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Provides an interface and default implementation for the `CommitValidator` operation

use core::marker::PhantomData;

use tendermint::block::CommitSig;

use crate::{
Expand All @@ -8,45 +10,14 @@ use crate::{
types::{SignedHeader, ValidatorSet},
};

/// Validates the commit associated with a header against a validator set
pub trait CommitValidator: Send + Sync {
/// Perform basic validation
fn validate(
&self,
signed_header: &SignedHeader,
validators: &ValidatorSet,
) -> Result<(), VerificationError>;

/// Perform full validation, only necessary if we do full verification (2/3)
fn validate_full(
&self,
signed_header: &SignedHeader,
validator_set: &ValidatorSet,
) -> Result<(), VerificationError>;
}

/// Production-ready implementation of a commit validator
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProdCommitValidator {
pub struct CommitValidator<C> {
hasher: ProdHasher,
_c: PhantomData<C>,
}

impl ProdCommitValidator {
/// Create a new commit validator using the given [`Hasher`]
/// to compute the hash of headers and validator sets.
pub fn new(hasher: ProdHasher) -> Self {
Self { hasher }
}
}

impl Default for ProdCommitValidator {
fn default() -> Self {
Self::new(ProdHasher::default())
}
}

impl CommitValidator for ProdCommitValidator {
fn validate(
impl<C> CommitValidator<C> {
pub fn validate(
&self,
signed_header: &SignedHeader,
validator_set: &ValidatorSet,
Expand Down Expand Up @@ -77,7 +48,7 @@ impl CommitValidator for ProdCommitValidator {
//
// It returns `ImplementationSpecific` error if it detects a signer
// that is not present in the validator set
fn validate_full(
pub fn validate_full(
&self,
signed_header: &SignedHeader,
validator_set: &ValidatorSet,
Expand All @@ -104,3 +75,39 @@ impl CommitValidator for ProdCommitValidator {
Ok(())
}
}

/// The batteries-included validator, for when you don't mind the dependencies on
/// the full rust-crypto stack.
#[cfg(feature = "rust-crypto")]
pub type ProdCommitValidator = CommitValidator<RustCryptoProvider>;

#[cfg(not(feature = "rust-crypto"))]
/// Production-ready implementation of a commit validator
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProdCommitValidator<C> {
inner: CommitValidator<C>,
}

impl<C> AsRef<CommitValidator<C>> for ProdCommitValidator<C> {
fn as_ref(&self) -> &CommitValidator<C> {
&self.inner
}
}
impl<C> ProdCommitValidator<C> {
/// Create a new commit validator using the given [`Hasher`]
/// to compute the hash of headers and validator sets.
pub fn new(hasher: ProdHasher) -> Self {
Self {
inner: CommitValidator {
hasher,
_c: PhantomData::default(),
},
}
}
}

impl<C> Default for ProdCommitValidator<C> {
fn default() -> Self {
Self::new(ProdHasher::default())
}
}
23 changes: 15 additions & 8 deletions light-client-verifier/src/predicates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

use core::time::Duration;

use tendermint::{block::Height, hash::Hash};
use tendermint::{
block::Height,
crypto::{CryptoProvider, DefaultHostFunctionsManager},
hash::Hash,
};

use crate::{
errors::VerificationError,
Expand All @@ -15,7 +19,9 @@ use crate::{
/// of the `VerificationPredicates` trait.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ProdPredicates;
impl VerificationPredicates for ProdPredicates {}
impl VerificationPredicates for ProdPredicates {
type CryptoProvider = DefaultHostFunctionsManager;
}

/// Defines the various predicates used to validate and verify light blocks.
///
Expand All @@ -24,6 +30,7 @@ impl VerificationPredicates for ProdPredicates {}
/// This enables test implementations to only override a single method rather than
/// have to re-define every predicate.
pub trait VerificationPredicates: Send + Sync {
type CryptoProvider: CryptoProvider;
/// Compare the provided validator_set_hash against the hash produced from hashing the validator
/// set.
fn validator_sets_match(
Expand Down Expand Up @@ -84,7 +91,7 @@ pub trait VerificationPredicates: Send + Sync {
&self,
signed_header: &SignedHeader,
validators: &ValidatorSet,
commit_validator: &dyn CommitValidator,
commit_validator: &CommitValidator<Self::CryptoProvider>,
) -> Result<(), VerificationError> {
commit_validator.validate(signed_header, validators)?;
commit_validator.validate_full(signed_header, validators)?;
Expand Down Expand Up @@ -471,15 +478,15 @@ mod tests {

// Test scenarios -->
// 1. valid commit - must result "Ok"
let mut result_ok = vp.valid_commit(&signed_header, &val_set, &commit_validator);
let mut result_ok = vp.valid_commit(&signed_header, &val_set, commit_validator.as_ref());

assert!(result_ok.is_ok());

// 2. no commit signatures - must return error
let signatures = signed_header.commit.signatures.clone();
signed_header.commit.signatures = vec![];

let mut result_err = vp.valid_commit(&signed_header, &val_set, &commit_validator);
let mut result_err = vp.valid_commit(&signed_header, &val_set, commit_validator.as_ref());

match result_err {
Err(VerificationError(VerificationErrorDetail::NoSignatureForCommit(_), _)) => {},
Expand All @@ -491,7 +498,7 @@ mod tests {
let mut bad_sigs = vec![signatures.clone().swap_remove(1)];
signed_header.commit.signatures = bad_sigs.clone();

result_err = vp.valid_commit(&signed_header, &val_set, &commit_validator);
result_err = vp.valid_commit(&signed_header, &val_set, commit_validator.as_ref());

match result_err {
Err(VerificationError(VerificationErrorDetail::MismatchPreCommitLength(e), _)) => {
Expand All @@ -504,7 +511,7 @@ mod tests {
// 4. commit.BlockIdFlagAbsent - should be "Ok"
bad_sigs.push(CommitSig::BlockIdFlagAbsent);
signed_header.commit.signatures = bad_sigs;
result_ok = vp.valid_commit(&signed_header, &val_set, &commit_validator);
result_ok = vp.valid_commit(&signed_header, &val_set, commit_validator.as_ref());
assert!(result_ok.is_ok());

// 5. faulty signer - must return error
Expand All @@ -523,7 +530,7 @@ mod tests {
result_err = vp.valid_commit(
&signed_header,
&val_set_with_faulty_signer,
&commit_validator,
commit_validator.as_ref(),
);

match result_err {
Expand Down
42 changes: 26 additions & 16 deletions light-client-verifier/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

use preds::{ProdPredicates, VerificationPredicates};
use serde::{Deserialize, Serialize};
use tendermint::crypto::{CryptoProvider, DefaultHostFunctionsManager};

use crate::{
errors::{ErrorExt, VerificationError, VerificationErrorDetail},
operations::{
voting_power::VotingPowerTally, CommitValidator, Hasher, ProdCommitValidator, ProdHasher,
voting_power::VotingPowerTally, Hasher, ProdCommitValidator, ProdHasher,
ProdVotingPowerCalculator, VotingPowerCalculator,
},
options::Options,
Expand Down Expand Up @@ -71,54 +72,59 @@ macro_rules! verdict {
/// Predicate verifier encapsulating components necessary to facilitate
/// verification.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PredicateVerifier<P, C, V, H> {
pub struct PredicateVerifier<P, C, H, CP> {
predicates: P,
voting_power_calculator: C,
commit_validator: V,
commit_validator: ProdCommitValidator<CP>,
hasher: H,
}

impl<P, C, V, H> Default for PredicateVerifier<P, C, V, H>
impl<P, C, H, CP> Default for PredicateVerifier<P, C, H, CP>
where
P: Default,
C: Default,
V: Default,
H: Default,
CP: CryptoProvider,
{
fn default() -> Self {
Self {
predicates: P::default(),
voting_power_calculator: C::default(),
commit_validator: V::default(),
commit_validator: ProdCommitValidator::default(),
hasher: H::default(),
}
}
}

impl<P, C, V, H> PredicateVerifier<P, C, V, H>
impl<P, C, H, CP> PredicateVerifier<P, C, H, CP>
where
P: VerificationPredicates,
C: VotingPowerCalculator,
V: CommitValidator,
H: Hasher,
CP: CryptoProvider,
{
/// Constructor.
pub fn new(predicates: P, voting_power_calculator: C, commit_validator: V, hasher: H) -> Self {
pub fn new(
predicates: P,
voting_power_calculator: C,
commit_validator: ProdCommitValidator<CP>,
hasher: H,
) -> Self {
Self {
predicates,
voting_power_calculator,
commit_validator,
hasher,
commit_validator,
}
}
}

impl<P, C, V, H> Verifier for PredicateVerifier<P, C, V, H>
impl<P, C, H, CP> Verifier for PredicateVerifier<P, C, H, CP>
where
P: VerificationPredicates,
P: VerificationPredicates + VerificationPredicates<CryptoProvider = CP>,
C: VotingPowerCalculator,
V: CommitValidator,
H: Hasher,
CP: CryptoProvider + Sync + Send,
{
/// Validate the given light block state.
///
Expand Down Expand Up @@ -183,7 +189,7 @@ where
verdict!(self.predicates.valid_commit(
untrusted.signed_header,
untrusted.validators,
&self.commit_validator,
self.commit_validator.as_ref(),
));

// Check that the untrusted block is more recent than the trusted state
Expand Down Expand Up @@ -229,5 +235,9 @@ where
}

/// The default production implementation of the [`PredicateVerifier`].
pub type ProdVerifier =
PredicateVerifier<ProdPredicates, ProdVotingPowerCalculator, ProdCommitValidator, ProdHasher>;
pub type ProdVerifier = PredicateVerifier<
ProdPredicates,
ProdVotingPowerCalculator,
ProdHasher,
DefaultHostFunctionsManager,
>;
6 changes: 3 additions & 3 deletions light-client/src/builder/light_client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! DSL for building a light client [`Instance`]

use tendermint::{block::Height, Hash};
use tendermint::{block::Height, crypto::DefaultHostFunctionsManager, Hash};
#[cfg(feature = "rpc-client")]
use {
crate::components::clock::SystemClock,
Expand Down Expand Up @@ -47,7 +47,7 @@ pub struct LightClientBuilder<State> {
hasher: Box<dyn Hasher>,
verifier: Box<dyn Verifier>,
scheduler: Box<dyn Scheduler>,
predicates: Box<dyn VerificationPredicates>,
predicates: Box<dyn VerificationPredicates<CryptoProvider = DefaultHostFunctionsManager>>,
light_store: Box<dyn LightStore>,

#[allow(dead_code)]
Expand Down Expand Up @@ -106,7 +106,7 @@ impl LightClientBuilder<NoTrustedState> {
clock: Box<dyn Clock>,
verifier: Box<dyn Verifier>,
scheduler: Box<dyn Scheduler>,
predicates: Box<dyn VerificationPredicates>,
predicates: Box<dyn VerificationPredicates<CryptoProvider = DefaultHostFunctionsManager>>,
) -> Self {
Self {
peer_id,
Expand Down
5 changes: 3 additions & 2 deletions tendermint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
async-trait = { version = "0.1", default-features = false }
bytes = { version = "1.0", default-features = false, features = ["serde"] }
digest = { version = "0.10", default-features = false }
ed25519 = { version = "1.3", default-features = false }
ed25519-dalek = { version = "1", default-features = false, features = ["u64_backend"] }
futures = { version = "0.3", default-features = false }
Expand All @@ -51,14 +52,14 @@ tendermint-proto = { version = "0.26.0", default-features = false, path = "../pr
time = { version = "0.3", default-features = false, features = ["macros", "parsing"] }
zeroize = { version = "1.1", default-features = false, features = ["zeroize_derive", "alloc"] }
flex-error = { version = "0.4.4", default-features = false }
k256 = { version = "0.11", optional = true, default-features = false, features = ["ecdsa", "sha256"] }
k256 = { version = "0.11", default-features = false, features = ["ecdsa", "sha256"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this one optional depends on whether CryptoProvider will be under a feature flag.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it now, we lock the signature type in CryptoProvider to the one defined by k256. If we cannot abstract this associated type to less specific rust-crypto bounds ("must be fixed array of 256 bits"?) and still keep it usable in generic code, then k256 should become a hard dependency. But I thought dependency management was the whole purpose of this rework.

ripemd160 = { version = "0.9", default-features = false, optional = true }

[features]
default = ["std"]
std = ["flex-error/std", "flex-error/eyre_tracer", "clock"]
clock = ["time/std"]
secp256k1 = ["k256", "ripemd160"]
secp256k1 = ["ripemd160"]

[dev-dependencies]
pretty_assertions = "1.3.0"
Expand Down
Loading