From cc000c81a6526a9eb88e3af28d8993ffc9a76f1d Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Wed, 1 Jun 2022 16:46:36 +0100 Subject: [PATCH 01/10] Introduced HostFunctionsProvider trait to tenderrmint-light-client-verifier --- .gitignore | 2 + light-client-verifier/Cargo.toml | 1 + light-client-verifier/src/host_functions.rs | 7 + light-client-verifier/src/lib.rs | 2 + light-client-verifier/src/merkle.rs | 139 ++++++++++++++++++ light-client-verifier/src/operations.rs | 3 - .../src/operations/commit_validator.rs | 24 ++- .../src/operations/hasher.rs | 24 --- .../src/operations/voting_power.rs | 41 +++++- light-client-verifier/src/predicates.rs | 31 ++-- light-client-verifier/src/verifier.rs | 32 ++-- tendermint/src/abci/response/check_tx.rs | 6 +- tendermint/src/block/header.rs | 13 +- tendermint/src/hash.rs | 5 + tendermint/src/validator.rs | 14 +- 15 files changed, 253 insertions(+), 91 deletions(-) create mode 100644 light-client-verifier/src/host_functions.rs create mode 100644 light-client-verifier/src/merkle.rs delete mode 100644 light-client-verifier/src/operations/hasher.rs diff --git a/.gitignore b/.gitignore index a066462b4..348b09737 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ Cargo.lock # Proptest regressions dumps **/*.proptest-regressions +.idea + # Light Client WASM light-client-js/pkg/ light-client-js/examples/verifier-web/node_modules/ diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 4529a8fe3..e348ef9ef 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -24,6 +24,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["flex-error/std", "flex-error/eyre_tracer"] +secp256k1 = ["tendermint/secp256k1"] [dependencies] tendermint = { version = "0.24.0-pre.2", path = "../tendermint", default-features = false } diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs new file mode 100644 index 000000000..0aba00c40 --- /dev/null +++ b/light-client-verifier/src/host_functions.rs @@ -0,0 +1,7 @@ +pub trait HostFunctionsProvider: Send + Sync { + /// sha256 hash function + fn sha2_256(preimage: &[u8]) -> [u8; 32]; + + /// Verify an ed25519 signature + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool; +} diff --git a/light-client-verifier/src/lib.rs b/light-client-verifier/src/lib.rs index ce413cefd..abb169065 100644 --- a/light-client-verifier/src/lib.rs +++ b/light-client-verifier/src/lib.rs @@ -5,6 +5,8 @@ extern crate alloc; mod prelude; pub mod errors; +pub mod host_functions; +pub mod merkle; pub mod operations; pub mod options; pub mod predicates; diff --git a/light-client-verifier/src/merkle.rs b/light-client-verifier/src/merkle.rs new file mode 100644 index 000000000..eaff4f2f8 --- /dev/null +++ b/light-client-verifier/src/merkle.rs @@ -0,0 +1,139 @@ +//! Merkle tree used in Tendermint networks + +use crate::{host_functions::HostFunctionsProvider, prelude::*}; + +/// Size of Merkle root hash +pub const HASH_SIZE: usize = 32; + +/// Hash is the output of the cryptographic digest function +pub type Hash = [u8; HASH_SIZE]; + +/// Compute a simple Merkle root from vectors of arbitrary byte vectors. +/// The leaves of the tree are the bytes of the given byte vectors in +/// the given order. +pub fn simple_hash_from_byte_vectors(byte_vecs: Vec>) -> Hash { + simple_hash_from_byte_slices_inner::(byte_vecs.as_slice()) +} + +// recurse into subtrees +fn simple_hash_from_byte_slices_inner(byte_slices: &[Vec]) -> Hash { + let length = byte_slices.len(); + match length { + 0 => empty_hash::(), + 1 => leaf_hash::(byte_slices[0].as_slice()), + _ => { + let k = get_split_point(length); + let left = simple_hash_from_byte_slices_inner::(&byte_slices[..k]); + let right = simple_hash_from_byte_slices_inner::(&byte_slices[k..]); + inner_hash::(&left, &right) + }, + } +} + +// returns the largest power of 2 less than length +fn get_split_point(length: usize) -> usize { + match length { + 0 => panic!("tree is empty!"), + 1 => panic!("tree has only one element!"), + 2 => 1, + _ => length.next_power_of_two() / 2, + } +} + +// tmhash({}) +fn empty_hash() -> Hash { + // the empty string / byte slice + let empty = Vec::with_capacity(0); + + // hash it ! + H::sha2_256(&empty) +} + +// tmhash(0x00 || leaf) +fn leaf_hash(bytes: &[u8]) -> Hash { + // make a new array starting with 0 and copy in the bytes + let mut leaf_bytes = Vec::with_capacity(bytes.len() + 1); + leaf_bytes.push(0x00); + leaf_bytes.extend_from_slice(bytes); + + // hash it ! + H::sha2_256(&leaf_bytes) +} + +// tmhash(0x01 || left || right) +fn inner_hash(left: &[u8], right: &[u8]) -> Hash { + // make a new array starting with 0x1 and copy in the bytes + let mut inner_bytes = Vec::with_capacity(left.len() + right.len() + 1); + inner_bytes.push(0x01); + inner_bytes.extend_from_slice(left); + inner_bytes.extend_from_slice(right); + + // hash it ! + H::sha2_256(&inner_bytes) +} + +#[cfg(test)] +mod tests { + use subtle_encoding::hex; + + use super::*; // TODO: use non-subtle ? + + #[test] + fn test_get_split_point() { + assert_eq!(get_split_point(2), 1); + assert_eq!(get_split_point(3), 2); + assert_eq!(get_split_point(4), 2); + assert_eq!(get_split_point(5), 4); + assert_eq!(get_split_point(10), 8); + assert_eq!(get_split_point(20), 16); + assert_eq!(get_split_point(100), 64); + assert_eq!(get_split_point(255), 128); + assert_eq!(get_split_point(256), 128); + assert_eq!(get_split_point(257), 256); + } + + #[test] + fn test_rfc6962_empty_tree() { + let empty_tree_root_hex = + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + let empty_tree_root = &hex::decode(empty_tree_root_hex).unwrap(); + let empty_tree: Vec> = vec![vec![]; 0]; + + let root = simple_hash_from_byte_vectors(empty_tree); + assert_eq!(empty_tree_root, &root); + } + + #[test] + fn test_rfc6962_empty_leaf() { + let empty_leaf_root_hex = + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"; + let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); + let one_empty_leaf: Vec> = vec![vec![]; 1]; + + let root = simple_hash_from_byte_vectors(one_empty_leaf); + assert_eq!(empty_leaf_root, &root); + } + + #[test] + fn test_rfc6962_leaf() { + let leaf_root_hex = "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56"; + let leaf_string = "L123456"; + + let leaf_root = &hex::decode(leaf_root_hex).unwrap(); + let leaf_tree: Vec> = vec![leaf_string.as_bytes().to_vec(); 1]; + + let root = simple_hash_from_byte_vectors(leaf_tree); + assert_eq!(leaf_root, &root); + } + + #[test] + fn test_rfc6962_node() { + let node_hash_hex = "aa217fe888e47007fa15edab33c2b492a722cb106c64667fc2b044444de66bbb"; + let left_string = "N123"; + let right_string = "N456"; + + let node_hash = &hex::decode(node_hash_hex).unwrap(); + let hash = inner_hash(left_string.as_bytes(), right_string.as_bytes()); + assert_eq!(node_hash, &hash); + } +} diff --git a/light-client-verifier/src/operations.rs b/light-client-verifier/src/operations.rs index f7c43429a..e45ad2729 100644 --- a/light-client-verifier/src/operations.rs +++ b/light-client-verifier/src/operations.rs @@ -1,8 +1,5 @@ //! Crypto function traits allowing mocking out during testing -pub mod hasher; -pub use self::hasher::*; - pub mod voting_power; pub use self::voting_power::*; diff --git a/light-client-verifier/src/operations/commit_validator.rs b/light-client-verifier/src/operations/commit_validator.rs index db2f13882..b01f47cee 100644 --- a/light-client-verifier/src/operations/commit_validator.rs +++ b/light-client-verifier/src/operations/commit_validator.rs @@ -1,12 +1,14 @@ //! Provides an interface and default implementation for the `CommitValidator` operation +use core::marker::PhantomData; use tendermint::block::CommitSig; use crate::{ errors::VerificationError, - operations::{Hasher, ProdHasher}, types::{SignedHeader, ValidatorSet}, }; +use crate::host_functions::HostFunctionsProvider; +use crate::merkle::simple_hash_from_byte_vectors; /// Validates the commit associated with a header against a validator set pub trait CommitValidator: Send + Sync { @@ -27,25 +29,16 @@ pub trait CommitValidator: Send + Sync { /// Production-ready implementation of a commit validator #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProdCommitValidator { - hasher: ProdHasher, -} +pub struct ProdCommitValidator(PhantomData); -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 { +impl Default for ProdCommitValidator { fn default() -> Self { - Self::new(ProdHasher::default()) + Self(PhantomData) } } -impl CommitValidator for ProdCommitValidator { +impl CommitValidator for ProdCommitValidator { fn validate( &self, signed_header: &SignedHeader, @@ -94,9 +87,10 @@ impl CommitValidator for ProdCommitValidator { }; if validator_set.validator(*validator_address) == None { + let bytes = validator_set.serialize_to_preimage(); return Err(VerificationError::faulty_signer( *validator_address, - self.hasher.hash_validator_set(validator_set), + simple_hash_from_byte_vectors::(bytes).into() )); } } diff --git a/light-client-verifier/src/operations/hasher.rs b/light-client-verifier/src/operations/hasher.rs deleted file mode 100644 index 285defa2a..000000000 --- a/light-client-verifier/src/operations/hasher.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Provides an interface and default implementation for the `Hasher` operation - -use tendermint::Hash; - -use crate::types::{Header, ValidatorSet}; - -/// Hashing for headers and validator sets -pub trait Hasher: Send + Sync { - /// Hash the given header - fn hash_header(&self, header: &Header) -> Hash { - header.hash() - } - - /// Hash the given validator set - fn hash_validator_set(&self, validator_set: &ValidatorSet) -> Hash { - validator_set.hash() - } -} - -/// Default implementation of a hasher -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct ProdHasher; - -impl Hasher for ProdHasher {} diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 58dbeec65..1bf1787af 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -2,16 +2,19 @@ use alloc::collections::BTreeSet as HashSet; use core::{convert::TryFrom, fmt}; +use core::marker::PhantomData; use serde::{Deserialize, Serialize}; use tendermint::{ block::CommitSig, trust_threshold::TrustThreshold as _, vote::{SignedVote, ValidatorIndex, Vote}, + PublicKey, }; use crate::{ errors::VerificationError, + host_functions::HostFunctionsProvider, prelude::*, types::{Commit, SignedHeader, TrustThreshold, ValidatorSet}, }; @@ -40,7 +43,7 @@ impl fmt::Display for VotingPowerTally { /// Computes the voting power in a commit against a validator set. /// /// This trait provides default implementation of some helper functions. -pub trait VotingPowerCalculator: Send + Sync { +pub trait VotingPowerCalculator: Send + Sync { /// Compute the total voting power in a validator set fn total_power_of(&self, validator_set: &ValidatorSet) -> u64 { validator_set @@ -101,9 +104,9 @@ pub trait VotingPowerCalculator: Send + Sync { /// Default implementation of a `VotingPowerCalculator` #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct ProdVotingPowerCalculator; +pub struct ProdVotingPowerCalculator(PhantomData); -impl VotingPowerCalculator for ProdVotingPowerCalculator { +impl VotingPowerCalculator for ProdVotingPowerCalculator { fn voting_power_in( &self, signed_header: &SignedHeader, @@ -146,10 +149,11 @@ impl VotingPowerCalculator for ProdVotingPowerCalculator { // Check vote is valid let sign_bytes = signed_vote.sign_bytes(); - if validator - .verify_signature(&sign_bytes, signed_vote.signature()) - .is_err() - { + if !verify_signature::( + validator.pub_key, + &sign_bytes, + signed_vote.signature().as_bytes(), + ) { return Err(VerificationError::invalid_signature( signed_vote.signature().as_bytes().to_vec(), Box::new(validator), @@ -179,6 +183,29 @@ impl VotingPowerCalculator for ProdVotingPowerCalculator { } } +fn verify_signature( + pubkey: PublicKey, + message: &[u8], + signature: &[u8], +) -> bool { + match pubkey { + PublicKey::Ed25519(pk) => H::ed25519_verify(signature, message, pk.as_ref()), + /// TODO: secp256k1 + #[cfg(feature = "secp256k1")] + PublicKey::Secp256k1(pk) => match k256::ecdsa::Signature::try_from(signature.as_bytes()) { + Ok(sig) => pk.verify(msg, &sig).map_err(|_| { + Error::signature_invalid("Secp256k1 signature verification failed".to_string()) + }), + Err(e) => Err(Error::signature_invalid(format!( + "invalid Secp256k1 signature: {}", + e + ))), + }, + + _ => unreachable!() + } +} + fn non_absent_vote( commit_sig: &CommitSig, validator_index: ValidatorIndex, diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index 4233efc00..762443ec9 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -1,12 +1,15 @@ //! Predicates for light block validation and verification. +use core::marker::PhantomData; use core::time::Duration; use tendermint::{block::Height, hash::Hash}; use crate::{ errors::VerificationError, - operations::{CommitValidator, Hasher, VotingPowerCalculator}, + host_functions::HostFunctionsProvider, + merkle::simple_hash_from_byte_vectors, + operations::{CommitValidator, VotingPowerCalculator}, prelude::*, types::{Header, SignedHeader, Time, TrustThreshold, ValidatorSet}, }; @@ -14,8 +17,9 @@ use crate::{ /// Production predicates, using the default implementation /// of the `VerificationPredicates` trait. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct ProdPredicates; -impl VerificationPredicates for ProdPredicates {} +pub struct ProdPredicates(PhantomData); + +impl VerificationPredicates for ProdPredicates {} /// Defines the various predicates used to validate and verify light blocks. /// @@ -23,22 +27,22 @@ 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 { +pub trait VerificationPredicates: Send + Sync { /// Compare the provided validator_set_hash against the hash produced from hashing the validator /// set. fn validator_sets_match( &self, validators: &ValidatorSet, header_validators_hash: Hash, - hasher: &dyn Hasher, ) -> Result<(), VerificationError> { - let validators_hash = hasher.hash_validator_set(validators); + let validators_hash = + simple_hash_from_byte_vectors::(validators.serialize_to_preimage()).into(); if header_validators_hash == validators_hash { Ok(()) } else { Err(VerificationError::invalid_validator_set( header_validators_hash, - validators_hash, + validators_hash.into(), )) } } @@ -48,9 +52,9 @@ pub trait VerificationPredicates: Send + Sync { &self, next_validators: &ValidatorSet, header_next_validators_hash: Hash, - hasher: &dyn Hasher, ) -> Result<(), VerificationError> { - let next_validators_hash = hasher.hash_validator_set(next_validators); + let next_validators_hash = + simple_hash_from_byte_vectors::(next_validators.serialize_to_preimage()).into(); if header_next_validators_hash == next_validators_hash { Ok(()) } else { @@ -66,10 +70,9 @@ pub trait VerificationPredicates: Send + Sync { &self, header: &Header, commit_hash: Hash, - hasher: &dyn Hasher, ) -> Result<(), VerificationError> { - let header_hash = hasher.hash_header(header); - if header_hash == commit_hash { + let header_hash = simple_hash_from_byte_vectors::(header.serialize_to_preimage()).into(); + if commit_hash == header_hash { Ok(()) } else { Err(VerificationError::invalid_commit_value( @@ -167,7 +170,7 @@ pub trait VerificationPredicates: Send + Sync { untrusted_sh: &SignedHeader, trusted_validators: &ValidatorSet, trust_threshold: &TrustThreshold, - calculator: &dyn VotingPowerCalculator, + calculator: &dyn VotingPowerCalculator, ) -> Result<(), VerificationError> { calculator.check_enough_trust(untrusted_sh, trusted_validators, *trust_threshold)?; Ok(()) @@ -179,7 +182,7 @@ pub trait VerificationPredicates: Send + Sync { &self, untrusted_sh: &SignedHeader, untrusted_validators: &ValidatorSet, - calculator: &dyn VotingPowerCalculator, + calculator: &dyn VotingPowerCalculator, ) -> Result<(), VerificationError> { calculator.check_signers_overlap(untrusted_sh, untrusted_validators)?; Ok(()) diff --git a/light-client-verifier/src/verifier.rs b/light-client-verifier/src/verifier.rs index 8525cf666..580c5703d 100644 --- a/light-client-verifier/src/verifier.rs +++ b/light-client-verifier/src/verifier.rs @@ -1,12 +1,15 @@ //! Provides an interface and default implementation of the `Verifier` component +use core::marker::PhantomData; + use preds::{ProdPredicates, VerificationPredicates}; use serde::{Deserialize, Serialize}; use crate::{ errors::{ErrorExt, VerificationError, VerificationErrorDetail}, + host_functions::HostFunctionsProvider, operations::{ - voting_power::VotingPowerTally, CommitValidator, Hasher, ProdCommitValidator, ProdHasher, + voting_power::VotingPowerTally, CommitValidator, ProdCommitValidator, ProdVotingPowerCalculator, VotingPowerCalculator, }, options::Options, @@ -75,7 +78,7 @@ pub struct PredicateVerifier { predicates: P, voting_power_calculator: C, commit_validator: V, - hasher: H, + phantom: PhantomData, } impl Default for PredicateVerifier @@ -90,35 +93,35 @@ where predicates: P::default(), voting_power_calculator: C::default(), commit_validator: V::default(), - hasher: H::default(), + phantom: PhantomData::, } } } impl PredicateVerifier where - P: VerificationPredicates, - C: VotingPowerCalculator, + P: VerificationPredicates, + C: VotingPowerCalculator, V: CommitValidator, - H: Hasher, + H: HostFunctionsProvider, { /// 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: V) -> Self { Self { predicates, voting_power_calculator, commit_validator, - hasher, + phantom: PhantomData::, } } } impl Verifier for PredicateVerifier where - P: VerificationPredicates, - C: VotingPowerCalculator, + P: VerificationPredicates, + C: VotingPowerCalculator, V: CommitValidator, - H: Hasher, + H: HostFunctionsProvider, { /// Validate the given light block state. /// @@ -159,7 +162,6 @@ where verdict!(self.predicates.validator_sets_match( untrusted.validators, untrusted.signed_header.header.validators_hash, - &self.hasher, )); // TODO(thane): Is this check necessary for IBC? @@ -168,7 +170,6 @@ where verdict!(self.predicates.next_validators_match( untrusted_next_validators, untrusted.signed_header.header.next_validators_hash, - &self.hasher, )); } @@ -176,7 +177,6 @@ where verdict!(self.predicates.header_matches_commit( &untrusted.signed_header.header, untrusted.signed_header.commit.block_id.hash, - &self.hasher, )); // Additional implementation specific validation @@ -229,5 +229,5 @@ where } /// The default production implementation of the [`PredicateVerifier`]. -pub type ProdVerifier = - PredicateVerifier; +pub type ProdVerifier = + PredicateVerifier, ProdVotingPowerCalculator, ProdCommitValidator, H>; diff --git a/tendermint/src/abci/response/check_tx.rs b/tendermint/src/abci/response/check_tx.rs index 0a1897fcb..30646daed 100644 --- a/tendermint/src/abci/response/check_tx.rs +++ b/tendermint/src/abci/response/check_tx.rs @@ -34,9 +34,9 @@ pub struct CheckTx { pub sender: String, /// The transaction's priority (for mempool ordering). pub priority: i64, - // mempool_error is contained in the proto, but skipped here: - // > mempool_error is set by Tendermint. - // > ABCI applictions creating a ResponseCheckTX should not set mempool_error. + /* mempool_error is contained in the proto, but skipped here: + * > mempool_error is set by Tendermint. + * > ABCI applictions creating a ResponseCheckTX should not set mempool_error. */ } // ============================================================================= diff --git a/tendermint/src/block/header.rs b/tendermint/src/block/header.rs index 2d0b241de..65501fae3 100644 --- a/tendermint/src/block/header.rs +++ b/tendermint/src/block/header.rs @@ -164,14 +164,14 @@ impl From
for RawHeader { } impl Header { - /// Hash this header - pub fn hash(&self) -> Hash { + /// Serialize the header to the preimage bytes + pub fn serialize_to_preimage(&self) -> Vec> { // Note that if there is an encoding problem this will // panic (as the golang code would): // https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/block.go#L393 // https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/encoding_helper.go#L9:6 - let fields_bytes = vec![ + vec![ self.version.encode_vec(), self.chain_id.encode_vec(), self.height.encode_vec(), @@ -186,7 +186,12 @@ impl Header { self.last_results_hash.unwrap_or_default().encode_vec(), self.evidence_hash.unwrap_or_default().encode_vec(), self.proposer_address.encode_vec(), - ]; + ] + } + + /// Hash this header + pub fn hash(&self) -> Hash { + let fields_bytes = self.serialize_to_preimage(); Hash::Sha256(simple_hash_from_byte_vectors(fields_bytes)) } diff --git a/tendermint/src/hash.rs b/tendermint/src/hash.rs index 17de5c533..85358da2e 100644 --- a/tendermint/src/hash.rs +++ b/tendermint/src/hash.rs @@ -53,6 +53,11 @@ impl From for Vec { } } } +impl From<[u8; SHA256_HASH_SIZE]> for Hash { + fn from(value: [u8; SHA256_HASH_SIZE]) -> Self { + Hash::Sha256(value) + } +} impl Hash { /// Create a new `Hash` with the given algorithm type diff --git a/tendermint/src/validator.rs b/tendermint/src/validator.rs index 73f56bc79..b63cc0560 100644 --- a/tendermint/src/validator.rs +++ b/tendermint/src/validator.rs @@ -133,13 +133,17 @@ impl Set { .cloned() } - /// Compute the hash of this validator set - pub fn hash(&self) -> Hash { - let validator_bytes: Vec> = self - .validators() + /// Serialize the validator set to the preimage bytes + pub fn serialize_to_preimage(&self) -> Vec> { + self.validators() .iter() .map(|validator| validator.hash_bytes()) - .collect(); + .collect() + } + + /// Compute the hash of this validator set + pub fn hash(&self) -> Hash { + let validator_bytes = self.serialize_to_preimage(); Hash::Sha256(merkle::simple_hash_from_byte_vectors(validator_bytes)) } From 14e3fcf0f6759882cd9868293ba82e283f8b893e Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Thu, 2 Jun 2022 11:57:13 +0100 Subject: [PATCH 02/10] fixes tests --- light-client-js/Cargo.toml | 2 +- light-client-verifier/Cargo.toml | 9 +++ light-client-verifier/src/host_functions.rs | 31 +++++++++ light-client-verifier/src/merkle.rs | 13 ++-- .../src/operations/commit_validator.rs | 8 +-- .../src/operations/voting_power.rs | 21 +++--- light-client-verifier/src/predicates.rs | 68 ++++++------------- 7 files changed, 84 insertions(+), 68 deletions(-) diff --git a/light-client-js/Cargo.toml b/light-client-js/Cargo.toml index aaa2ba2bc..8cec3c26c 100644 --- a/light-client-js/Cargo.toml +++ b/light-client-js/Cargo.toml @@ -23,7 +23,7 @@ default = ["console_error_panic_hook"] serde = { version = "1.0", default-features = false, features = [ "derive" ] } serde_json = { version = "1.0", default-features = false } # TODO(thane): Remove once https://github.com/rustwasm/wasm-bindgen/issues/2508 is resolved -syn = { version = "=1.0.65", default-features = false } +syn = { version = "1.0.95", default-features = false } tendermint = { version = "0.24.0-pre.2", default-features = false, path = "../tendermint" } tendermint-light-client-verifier = { version = "0.24.0-pre.2", default-features = false, path = "../light-client-verifier" } wasm-bindgen = { version = "0.2.63", default-features = false, features = [ "serde-serialize" ] } diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index e348ef9ef..b8e3284d3 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -23,6 +23,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] +std = [ + "tendermint/std", + "serde/std", + "time/std", + "flex-error/std" +] default = ["flex-error/std", "flex-error/eyre_tracer"] secp256k1 = ["tendermint/secp256k1"] @@ -35,3 +41,6 @@ flex-error = { version = "0.4.4", default-features = false } [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } +hex = "0.4.3" +sp-core = { version = "6.0.0", features = ["full_crypto"] } +#sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" } \ No newline at end of file diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 0aba00c40..1735be152 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -1,3 +1,6 @@ +//! Host function utilities + +/// Host functions that the light client needs for crypto operations. pub trait HostFunctionsProvider: Send + Sync { /// sha256 hash function fn sha2_256(preimage: &[u8]) -> [u8; 32]; @@ -5,3 +8,31 @@ pub trait HostFunctionsProvider: Send + Sync { /// Verify an ed25519 signature fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool; } + +#[cfg(test)] +#[derive(Default)] +pub struct TestHostFunctions; + +#[cfg(test)] +impl HostFunctionsProvider for TestHostFunctions { + fn sha2_256(preimage: &[u8]) -> [u8; 32] { + sp_core::hashing::sha2_256(preimage) + } + + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool { + use sp_core::{ed25519, ByteArray, Pair}; + + let result = ed25519::Signature::from_slice(sig) + .ok_or(()) + .and_then(|sig| { + let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; + Ok((sig, public_key)) + }); + + if let Ok((sig, public_key)) = result { + return ed25519::Pair::verify(&sig, msg, &public_key); + } + + false + } +} diff --git a/light-client-verifier/src/merkle.rs b/light-client-verifier/src/merkle.rs index eaff4f2f8..e4b502317 100644 --- a/light-client-verifier/src/merkle.rs +++ b/light-client-verifier/src/merkle.rs @@ -74,9 +74,8 @@ fn inner_hash(left: &[u8], right: &[u8]) -> Hash { #[cfg(test)] mod tests { - use subtle_encoding::hex; - - use super::*; // TODO: use non-subtle ? + use super::*; + use crate::host_functions::TestHostFunctions; // TODO: use non-subtle ? #[test] fn test_get_split_point() { @@ -99,7 +98,7 @@ mod tests { let empty_tree_root = &hex::decode(empty_tree_root_hex).unwrap(); let empty_tree: Vec> = vec![vec![]; 0]; - let root = simple_hash_from_byte_vectors(empty_tree); + let root = simple_hash_from_byte_vectors::(empty_tree); assert_eq!(empty_tree_root, &root); } @@ -110,7 +109,7 @@ mod tests { let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); let one_empty_leaf: Vec> = vec![vec![]; 1]; - let root = simple_hash_from_byte_vectors(one_empty_leaf); + let root = simple_hash_from_byte_vectors::(one_empty_leaf); assert_eq!(empty_leaf_root, &root); } @@ -122,7 +121,7 @@ mod tests { let leaf_root = &hex::decode(leaf_root_hex).unwrap(); let leaf_tree: Vec> = vec![leaf_string.as_bytes().to_vec(); 1]; - let root = simple_hash_from_byte_vectors(leaf_tree); + let root = simple_hash_from_byte_vectors::(leaf_tree); assert_eq!(leaf_root, &root); } @@ -133,7 +132,7 @@ mod tests { let right_string = "N456"; let node_hash = &hex::decode(node_hash_hex).unwrap(); - let hash = inner_hash(left_string.as_bytes(), right_string.as_bytes()); + let hash = inner_hash::(left_string.as_bytes(), right_string.as_bytes()); assert_eq!(node_hash, &hash); } } diff --git a/light-client-verifier/src/operations/commit_validator.rs b/light-client-verifier/src/operations/commit_validator.rs index b01f47cee..e485908ee 100644 --- a/light-client-verifier/src/operations/commit_validator.rs +++ b/light-client-verifier/src/operations/commit_validator.rs @@ -1,14 +1,15 @@ //! Provides an interface and default implementation for the `CommitValidator` operation use core::marker::PhantomData; + use tendermint::block::CommitSig; use crate::{ errors::VerificationError, + host_functions::HostFunctionsProvider, + merkle::simple_hash_from_byte_vectors, types::{SignedHeader, ValidatorSet}, }; -use crate::host_functions::HostFunctionsProvider; -use crate::merkle::simple_hash_from_byte_vectors; /// Validates the commit associated with a header against a validator set pub trait CommitValidator: Send + Sync { @@ -31,7 +32,6 @@ pub trait CommitValidator: Send + Sync { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProdCommitValidator(PhantomData); - impl Default for ProdCommitValidator { fn default() -> Self { Self(PhantomData) @@ -90,7 +90,7 @@ impl CommitValidator for ProdCommitValidator { let bytes = validator_set.serialize_to_preimage(); return Err(VerificationError::faulty_signer( *validator_address, - simple_hash_from_byte_vectors::(bytes).into() + simple_hash_from_byte_vectors::(bytes).into(), )); } } diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 1bf1787af..56cf1ba16 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -1,8 +1,7 @@ //! Provides an interface and default implementation for the `VotingPower` operation use alloc::collections::BTreeSet as HashSet; -use core::{convert::TryFrom, fmt}; -use core::marker::PhantomData; +use core::{convert::TryFrom, fmt, marker::PhantomData}; use serde::{Deserialize, Serialize}; use tendermint::{ @@ -202,7 +201,7 @@ fn verify_signature( ))), }, - _ => unreachable!() + _ => unreachable!(), } } @@ -255,7 +254,9 @@ mod tests { }; use super::*; - use crate::{errors::VerificationErrorDetail, types::LightBlock}; + use crate::{ + errors::VerificationErrorDetail, host_functions::TestHostFunctions, types::LightBlock, + }; const EXPECTED_RESULT: VotingPowerTally = VotingPowerTally { total: 100, @@ -265,7 +266,7 @@ mod tests { #[test] fn test_empty_signatures() { - let vp_calculator = ProdVotingPowerCalculator::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut light_block: LightBlock = TestgenLightBlock::new_default(10) @@ -286,7 +287,7 @@ mod tests { #[test] fn test_all_signatures_absent() { - let vp_calculator = ProdVotingPowerCalculator::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -308,7 +309,7 @@ mod tests { #[test] fn test_all_signatures_nil() { - let vp_calculator = ProdVotingPowerCalculator::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let validator_set = ValidatorSet::new(vec!["a", "b"]); @@ -330,7 +331,7 @@ mod tests { #[test] fn test_one_invalid_signature() { - let vp_calculator = ProdVotingPowerCalculator::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -358,7 +359,7 @@ mod tests { #[test] fn test_all_signatures_invalid() { - let vp_calculator = ProdVotingPowerCalculator::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -380,7 +381,7 @@ mod tests { #[test] fn test_signatures_from_diff_valset() { - let vp_calculator = ProdVotingPowerCalculator::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut light_block: LightBlock = TestgenLightBlock::new_default(10) diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index 762443ec9..27d314b62 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -1,7 +1,6 @@ //! Predicates for light block validation and verification. -use core::marker::PhantomData; -use core::time::Duration; +use core::{marker::PhantomData, time::Duration}; use tendermint::{block::Height, hash::Hash}; @@ -219,9 +218,8 @@ mod tests { use crate::{ errors::{VerificationError, VerificationErrorDetail}, - operations::{ - Hasher, ProdCommitValidator, ProdHasher, ProdVotingPowerCalculator, VotingPowerTally, - }, + host_functions::TestHostFunctions, + operations::{ProdCommitValidator, ProdVotingPowerCalculator, VotingPowerTally}, predicates::{ProdPredicates, VerificationPredicates}, prelude::*, types::{LightBlock, TrustThreshold}, @@ -244,7 +242,7 @@ mod tests { let header_one = Header::new(&val).generate().unwrap(); let header_two = Header::new(&val).generate().unwrap(); - let vp = ProdPredicates::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let result_ok = vp.is_monotonic_bft_time(header_two.time, header_one.time); @@ -267,7 +265,7 @@ mod tests { let header_one = Header::new(&val).generate().unwrap(); let header_two = Header::new(&val).height(2).generate().unwrap(); - let vp = ProdPredicates::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let result_ok = vp.is_monotonic_height(header_two.height, header_one.height); @@ -290,7 +288,7 @@ mod tests { let val = Validator::new("val-1"); let header = Header::new(&[val]).generate().unwrap(); - let vp = ProdPredicates::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let mut trusting_period = Duration::new(1000, 0); @@ -319,7 +317,7 @@ mod tests { let val = Validator::new("val-1"); let header = Header::new(&[val]).generate().unwrap(); - let vp = ProdPredicates::default(); + let vp = ProdPredicates::::default(); let one_second = Duration::new(1, 0); let now = OffsetDateTime::now_utc().try_into().unwrap(); @@ -350,15 +348,13 @@ mod tests { let bad_validator_set = ValidatorSet::new(vec!["bad-val"]).generate().unwrap(); - let vp = ProdPredicates::default(); - let hasher = ProdHasher::default(); + let vp = ProdPredicates::::default(); // Test positive case // 1. For predicate: validator_sets_match let val_sets_match_ok = vp.validator_sets_match( &light_block.validators, light_block.signed_header.header.validators_hash, - &hasher, ); assert!(val_sets_match_ok.is_ok()); @@ -367,7 +363,6 @@ mod tests { let next_val_sets_match_ok = vp.next_validators_match( &light_block.next_validators, light_block.signed_header.header.next_validators_hash, - &hasher, ); assert!(next_val_sets_match_ok.is_ok()); @@ -379,7 +374,6 @@ mod tests { let val_sets_match_err = vp.validator_sets_match( &light_block.validators, light_block.signed_header.header.validators_hash, - &hasher, ); match val_sets_match_err { @@ -388,10 +382,7 @@ mod tests { e.header_validators_hash, light_block.signed_header.header.validators_hash ); - assert_eq!( - e.validators_hash, - hasher.hash_validator_set(&light_block.validators) - ); + assert_eq!(e.validators_hash, light_block.validators.hash()); }, _ => panic!("expected InvalidValidatorSet error"), } @@ -401,7 +392,6 @@ mod tests { let next_val_sets_match_err = vp.next_validators_match( &light_block.next_validators, light_block.signed_header.header.next_validators_hash, - &hasher, ); match next_val_sets_match_err { @@ -410,10 +400,7 @@ mod tests { e.header_next_validators_hash, light_block.signed_header.header.next_validators_hash ); - assert_eq!( - e.next_validators_hash, - hasher.hash_validator_set(&light_block.next_validators) - ); + assert_eq!(e.next_validators_hash, light_block.next_validators.hash()); }, _ => panic!("expected InvalidNextValidatorSet error"), } @@ -426,15 +413,11 @@ mod tests { .unwrap() .signed_header; - let vp = ProdPredicates::default(); - let hasher = ProdHasher::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid signed header verifies - let result_ok = vp.header_matches_commit( - &signed_header.header, - signed_header.commit.block_id.hash, - &hasher, - ); + let result_ok = + vp.header_matches_commit(&signed_header.header, signed_header.commit.block_id.hash); assert!(result_ok.is_ok()); @@ -443,14 +426,11 @@ mod tests { "15F15EF50BDE2018F4B129A827F90C18222C757770C8295EB8EE7BF50E761BC0" .parse() .unwrap(); - let result_err = vp.header_matches_commit( - &signed_header.header, - signed_header.commit.block_id.hash, - &hasher, - ); + let result_err = + vp.header_matches_commit(&signed_header.header, signed_header.commit.block_id.hash); // 3. ensure it fails with: VerificationVerificationError::InvalidCommitValue - let header_hash = hasher.hash_header(&signed_header.header); + let header_hash = signed_header.header.hash(); match result_err { Err(VerificationError(VerificationErrorDetail::InvalidCommitValue(e), _)) => { @@ -468,9 +448,8 @@ mod tests { let mut signed_header = light_block.signed_header; let val_set = light_block.validators; - let vp = ProdPredicates::default(); - let hasher = ProdHasher::default(); - let commit_validator = ProdCommitValidator::new(hasher); + let vp = ProdPredicates::::default(); + let commit_validator = ProdCommitValidator::::default(); // Test scenarios --> // 1. valid commit - must result "Ok" @@ -543,10 +522,7 @@ mod tests { .unwrap() ); - assert_eq!( - e.validator_set, - hasher.hash_validator_set(&val_set_with_faulty_signer) - ); + assert_eq!(e.validator_set, val_set_with_faulty_signer.hash()); }, _ => panic!("expected FaultySigner error"), } @@ -559,7 +535,7 @@ mod tests { let light_block2: LightBlock = test_lb1.next().generate().unwrap().into(); - let vp = ProdPredicates::default(); + let vp = ProdPredicates::::default(); // Test scenarios --> // 1. next_validator_set hash matches @@ -606,7 +582,7 @@ mod tests { let val_set = light_block.validators; let signed_header = light_block.signed_header; - let vp = ProdPredicates::default(); + let vp = ProdPredicates::::default(); let mut trust_threshold = TrustThreshold::new(1, 3).expect("Cannot make trust threshold"); let voting_power_calculator = ProdVotingPowerCalculator::default(); @@ -660,7 +636,7 @@ mod tests { let mut light_block: LightBlock = TestgenLightBlock::new_default(2).generate().unwrap().into(); - let vp = ProdPredicates::default(); + let vp = ProdPredicates::::default(); let voting_power_calculator = ProdVotingPowerCalculator::default(); // Test scenarios --> From d9cc9193a1f27b8c306070c405dbe7282c38678f Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Thu, 2 Jun 2022 12:21:58 +0100 Subject: [PATCH 03/10] secpk1256 doesn't work, but hey, its optional --- light-client-verifier/Cargo.toml | 2 +- light-client-verifier/src/host_functions.rs | 37 +++++++++++++++++++ .../src/operations/voting_power.rs | 11 +----- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index b8e3284d3..35711f33b 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -43,4 +43,4 @@ flex-error = { version = "0.4.4", default-features = false } tendermint-testgen = { path = "../testgen", default-features = false } hex = "0.4.3" sp-core = { version = "6.0.0", features = ["full_crypto"] } -#sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.22" } \ No newline at end of file +hex-literal = "0.3.4" diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 1735be152..4b177296f 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -7,6 +7,9 @@ pub trait HostFunctionsProvider: Send + Sync { /// Verify an ed25519 signature fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool; + + /// verify secp256k1 signatures + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool; } #[cfg(test)] @@ -35,4 +38,38 @@ impl HostFunctionsProvider for TestHostFunctions { false } + + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool { + use sp_core::{ecdsa, ByteArray, Pair}; + + let result = ecdsa::Signature::from_slice(sig.clone()) + .ok_or(()) + .and_then(|sig| { + let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; + Ok((public, sig)) + }); + + if let Ok((public, signature)) = result { + return ecdsa::Pair::verify_weak(&sig, message, &public); + } + + false + } +} + +#[cfg(test)] +mod tests { + use hex_literal::hex; + use crate::host_functions::{HostFunctionsProvider, TestHostFunctions}; + + #[test] + #[should_panic] + // not super sure what the problem is here but secpk256 is optional so 🤷🏾‍ + fn test_secpk1256_verification() { + let public = hex!("043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a"); + let msg = hex!("313233343030"); + let sig = hex!("304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0"); + + assert!(TestHostFunctions::secp256k1_verify(&sig, &msg, &public)) + } } diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 56cf1ba16..ace36db02 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -191,16 +191,7 @@ fn verify_signature( PublicKey::Ed25519(pk) => H::ed25519_verify(signature, message, pk.as_ref()), /// TODO: secp256k1 #[cfg(feature = "secp256k1")] - PublicKey::Secp256k1(pk) => match k256::ecdsa::Signature::try_from(signature.as_bytes()) { - Ok(sig) => pk.verify(msg, &sig).map_err(|_| { - Error::signature_invalid("Secp256k1 signature verification failed".to_string()) - }), - Err(e) => Err(Error::signature_invalid(format!( - "invalid Secp256k1 signature: {}", - e - ))), - }, - + PublicKey::Secp256k1(pk) => H::secp256k1_verify(signature, message, &pk.to_bytes()[..]), _ => unreachable!(), } } From 3b021da3fbf2e9192b617c4f245f01da62284be5 Mon Sep 17 00:00:00 2001 From: David Salami Date: Mon, 20 Jun 2022 10:17:58 +0100 Subject: [PATCH 04/10] switch k256 to 0.10.4 --- tendermint/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tendermint/Cargo.toml b/tendermint/Cargo.toml index fca8929ca..e36b66768 100644 --- a/tendermint/Cargo.toml +++ b/tendermint/Cargo.toml @@ -51,7 +51,7 @@ tendermint-proto = { version = "0.24.0-pre.2", default-features = false, path = time = { version = "0.3.5", 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.10.4", optional = true, default-features = false, features = ["ecdsa", "sha256"] } ripemd = { version = "0.1", default-features = false, optional = true } [features] From c56e6a7192bce425f477a2e23796a6f8df7b1105 Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Mon, 20 Jun 2022 15:47:05 +0100 Subject: [PATCH 05/10] refactor tendermint-light-client --- light-client-verifier/Cargo.toml | 5 +- light-client-verifier/src/host_functions.rs | 76 ++++++++++--------- light-client-verifier/src/merkle.rs | 11 +-- .../src/operations/voting_power.rs | 15 ++-- light-client-verifier/src/predicates.rs | 24 +++--- light-client/Cargo.toml | 2 + light-client/src/builder/light_client.rs | 58 ++++++++------ light-client/src/builder/supervisor.rs | 43 ++++++----- light-client/src/errors.rs | 2 +- light-client/src/fork_detector.rs | 49 ++++++------ light-client/src/light_client.rs | 44 ++++++----- light-client/src/supervisor.rs | 46 ++++++----- light-client/src/tests.rs | 5 +- light-client/tests/light_client.rs | 6 +- light-client/tests/supervisor.rs | 9 +-- tools/kvstore-test/Cargo.toml | 2 +- tools/kvstore-test/tests/light-client.rs | 5 +- 17 files changed, 220 insertions(+), 182 deletions(-) diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 35711f33b..70d969f5e 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -27,10 +27,11 @@ std = [ "tendermint/std", "serde/std", "time/std", - "flex-error/std" + "flex-error/std", ] default = ["flex-error/std", "flex-error/eyre_tracer"] secp256k1 = ["tendermint/secp256k1"] +test-helpers = [ "sp-core"] [dependencies] tendermint = { version = "0.24.0-pre.2", path = "../tendermint", default-features = false } @@ -38,6 +39,8 @@ derive_more = { version = "0.99.5", default-features = false, features = ["displ serde = { version = "1.0.106", default-features = false } time = { version = "0.3.5", default-features = false } flex-error = { version = "0.4.4", default-features = false } +sp-std = { version = "4.0.0", default-features = false } +sp-core = { version = "6.0.0", features = ["full_crypto"], optional = true } [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 4b177296f..aa5537608 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -1,7 +1,9 @@ //! Host function utilities +use sp_std::fmt::Debug; + /// Host functions that the light client needs for crypto operations. -pub trait HostFunctionsProvider: Send + Sync { +pub trait HostFunctionsProvider: Send + Sync + Default + Debug + 'static { /// sha256 hash function fn sha2_256(preimage: &[u8]) -> [u8; 32]; @@ -12,55 +14,59 @@ pub trait HostFunctionsProvider: Send + Sync { fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool; } -#[cfg(test)] -#[derive(Default)] -pub struct TestHostFunctions; +#[cfg(any(feature = "test-helpers", test))] +pub mod helper { + use crate::host_functions::HostFunctionsProvider; -#[cfg(test)] -impl HostFunctionsProvider for TestHostFunctions { - fn sha2_256(preimage: &[u8]) -> [u8; 32] { - sp_core::hashing::sha2_256(preimage) - } + #[derive(Default, Debug)] + pub struct HostFunctionsManager; + + impl HostFunctionsProvider for HostFunctionsManager { + fn sha2_256(preimage: &[u8]) -> [u8; 32] { + sp_core::hashing::sha2_256(preimage) + } + + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool { + use sp_core::{ed25519, ByteArray, Pair}; - fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool { - use sp_core::{ed25519, ByteArray, Pair}; + let result = ed25519::Signature::from_slice(sig) + .ok_or(()) + .and_then(|sig| { + let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; + Ok((sig, public_key)) + }); - let result = ed25519::Signature::from_slice(sig) - .ok_or(()) - .and_then(|sig| { - let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; - Ok((sig, public_key)) - }); + if let Ok((sig, public_key)) = result { + return ed25519::Pair::verify(&sig, msg, &public_key); + } - if let Ok((sig, public_key)) = result { - return ed25519::Pair::verify(&sig, msg, &public_key); + false } - false - } + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool { + use sp_core::{ecdsa, ByteArray, Pair}; - fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool { - use sp_core::{ecdsa, ByteArray, Pair}; + let result = ecdsa::Signature::from_slice(sig.clone()) + .ok_or(()) + .and_then(|sig| { + let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; + Ok((public, sig)) + }); - let result = ecdsa::Signature::from_slice(sig.clone()) - .ok_or(()) - .and_then(|sig| { - let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; - Ok((public, sig)) - }); + if let Ok((public, signature)) = result { + return ecdsa::Pair::verify_weak(&sig, message, &public); + } - if let Ok((public, signature)) = result { - return ecdsa::Pair::verify_weak(&sig, message, &public); + false } - - false } } #[cfg(test)] mod tests { use hex_literal::hex; - use crate::host_functions::{HostFunctionsProvider, TestHostFunctions}; + + use crate::host_functions::{helper::HostFunctionsManager, HostFunctionsProvider}; #[test] #[should_panic] @@ -70,6 +76,6 @@ mod tests { let msg = hex!("313233343030"); let sig = hex!("304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0"); - assert!(TestHostFunctions::secp256k1_verify(&sig, &msg, &public)) + assert!(HostFunctionsManager::secp256k1_verify(&sig, &msg, &public)) } } diff --git a/light-client-verifier/src/merkle.rs b/light-client-verifier/src/merkle.rs index e4b502317..89572fc6d 100644 --- a/light-client-verifier/src/merkle.rs +++ b/light-client-verifier/src/merkle.rs @@ -75,7 +75,7 @@ fn inner_hash(left: &[u8], right: &[u8]) -> Hash { #[cfg(test)] mod tests { use super::*; - use crate::host_functions::TestHostFunctions; // TODO: use non-subtle ? + use crate::host_functions::helper::HostFunctionsManager; // TODO: use non-subtle ? #[test] fn test_get_split_point() { @@ -98,7 +98,7 @@ mod tests { let empty_tree_root = &hex::decode(empty_tree_root_hex).unwrap(); let empty_tree: Vec> = vec![vec![]; 0]; - let root = simple_hash_from_byte_vectors::(empty_tree); + let root = simple_hash_from_byte_vectors::(empty_tree); assert_eq!(empty_tree_root, &root); } @@ -109,7 +109,7 @@ mod tests { let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); let one_empty_leaf: Vec> = vec![vec![]; 1]; - let root = simple_hash_from_byte_vectors::(one_empty_leaf); + let root = simple_hash_from_byte_vectors::(one_empty_leaf); assert_eq!(empty_leaf_root, &root); } @@ -121,7 +121,7 @@ mod tests { let leaf_root = &hex::decode(leaf_root_hex).unwrap(); let leaf_tree: Vec> = vec![leaf_string.as_bytes().to_vec(); 1]; - let root = simple_hash_from_byte_vectors::(leaf_tree); + let root = simple_hash_from_byte_vectors::(leaf_tree); assert_eq!(leaf_root, &root); } @@ -132,7 +132,8 @@ mod tests { let right_string = "N456"; let node_hash = &hex::decode(node_hash_hex).unwrap(); - let hash = inner_hash::(left_string.as_bytes(), right_string.as_bytes()); + let hash = + inner_hash::(left_string.as_bytes(), right_string.as_bytes()); assert_eq!(node_hash, &hash); } } diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index ace36db02..0cda1746f 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -246,7 +246,8 @@ mod tests { use super::*; use crate::{ - errors::VerificationErrorDetail, host_functions::TestHostFunctions, types::LightBlock, + errors::VerificationErrorDetail, host_functions::helper::HostFunctionsManager, + types::LightBlock, }; const EXPECTED_RESULT: VotingPowerTally = VotingPowerTally { @@ -257,7 +258,7 @@ mod tests { #[test] fn test_empty_signatures() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut light_block: LightBlock = TestgenLightBlock::new_default(10) @@ -278,7 +279,7 @@ mod tests { #[test] fn test_all_signatures_absent() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -300,7 +301,7 @@ mod tests { #[test] fn test_all_signatures_nil() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let validator_set = ValidatorSet::new(vec!["a", "b"]); @@ -322,7 +323,7 @@ mod tests { #[test] fn test_one_invalid_signature() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -350,7 +351,7 @@ mod tests { #[test] fn test_all_signatures_invalid() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -372,7 +373,7 @@ mod tests { #[test] fn test_signatures_from_diff_valset() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut light_block: LightBlock = TestgenLightBlock::new_default(10) diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index 27d314b62..3db4693ee 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -218,7 +218,7 @@ mod tests { use crate::{ errors::{VerificationError, VerificationErrorDetail}, - host_functions::TestHostFunctions, + host_functions::helper::HostFunctionsManager, operations::{ProdCommitValidator, ProdVotingPowerCalculator, VotingPowerTally}, predicates::{ProdPredicates, VerificationPredicates}, prelude::*, @@ -242,7 +242,7 @@ mod tests { let header_one = Header::new(&val).generate().unwrap(); let header_two = Header::new(&val).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let result_ok = vp.is_monotonic_bft_time(header_two.time, header_one.time); @@ -265,7 +265,7 @@ mod tests { let header_one = Header::new(&val).generate().unwrap(); let header_two = Header::new(&val).height(2).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let result_ok = vp.is_monotonic_height(header_two.height, header_one.height); @@ -288,7 +288,7 @@ mod tests { let val = Validator::new("val-1"); let header = Header::new(&[val]).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let mut trusting_period = Duration::new(1000, 0); @@ -317,7 +317,7 @@ mod tests { let val = Validator::new("val-1"); let header = Header::new(&[val]).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); let one_second = Duration::new(1, 0); let now = OffsetDateTime::now_utc().try_into().unwrap(); @@ -348,7 +348,7 @@ mod tests { let bad_validator_set = ValidatorSet::new(vec!["bad-val"]).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // Test positive case // 1. For predicate: validator_sets_match @@ -413,7 +413,7 @@ mod tests { .unwrap() .signed_header; - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid signed header verifies let result_ok = @@ -448,8 +448,8 @@ mod tests { let mut signed_header = light_block.signed_header; let val_set = light_block.validators; - let vp = ProdPredicates::::default(); - let commit_validator = ProdCommitValidator::::default(); + let vp = ProdPredicates::::default(); + let commit_validator = ProdCommitValidator::::default(); // Test scenarios --> // 1. valid commit - must result "Ok" @@ -535,7 +535,7 @@ mod tests { let light_block2: LightBlock = test_lb1.next().generate().unwrap().into(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // Test scenarios --> // 1. next_validator_set hash matches @@ -582,7 +582,7 @@ mod tests { let val_set = light_block.validators; let signed_header = light_block.signed_header; - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); let mut trust_threshold = TrustThreshold::new(1, 3).expect("Cannot make trust threshold"); let voting_power_calculator = ProdVotingPowerCalculator::default(); @@ -636,7 +636,7 @@ mod tests { let mut light_block: LightBlock = TestgenLightBlock::new_default(2).generate().unwrap().into(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); let voting_power_calculator = ProdVotingPowerCalculator::default(); // Test scenarios --> diff --git a/light-client/Cargo.toml b/light-client/Cargo.toml index 495a952a4..9ab8143ce 100644 --- a/light-client/Cargo.toml +++ b/light-client/Cargo.toml @@ -53,9 +53,11 @@ sled = { version = "0.34.3", optional = true, default-features = false } static_assertions = { version = "1.1.0", default-features = false } time = { version = "0.3.5", default-features = false, features = ["std"] } tokio = { version = "1.0", default-features = false, features = ["rt"], optional = true } +sp-std = { version = "4.0.0", default-features = false } [dev-dependencies] tendermint-testgen = { path = "../testgen", default-features = false } +tendermint-light-client-verifier = { version = "0.24.0-pre.2", path = "../light-client-verifier", features = ["test-helpers"] } serde_json = { version = "1.0.51", default-features = false } gumdrop = { version = "0.8.0", default-features = false } diff --git a/light-client/src/builder/light_client.rs b/light-client/src/builder/light_client.rs index e7c4d5fef..63a35a584 100644 --- a/light-client/src/builder/light_client.rs +++ b/light-client/src/builder/light_client.rs @@ -1,12 +1,14 @@ //! DSL for building a light client [`Instance`] +use sp_std::marker::PhantomData; use tendermint::{block::Height, Hash}; +use tendermint_light_client_verifier::merkle::simple_hash_from_byte_vectors; #[cfg(feature = "rpc-client")] use { crate::components::clock::SystemClock, crate::components::io::RpcIo, crate::components::scheduler, - crate::verifier::{operations::ProdHasher, predicates::ProdPredicates, ProdVerifier}, + crate::verifier::{predicates::ProdPredicates, ProdVerifier}, core::time::Duration, tendermint_rpc as rpc, }; @@ -23,7 +25,7 @@ use crate::{ store::LightStore, supervisor::Instance, verifier::{ - operations::Hasher, + host_functions::HostFunctionsProvider, options::Options, predicates::VerificationPredicates, types::{LightBlock, PeerId, Status}, @@ -39,40 +41,43 @@ pub struct HasTrustedState; /// Builder for a light client [`Instance`] #[must_use] -pub struct LightClientBuilder { +pub struct LightClientBuilder { peer_id: PeerId, options: Options, io: Box, clock: Box, - hasher: Box, verifier: Box, scheduler: Box, - predicates: Box, + predicates: Box>, light_store: Box, #[allow(dead_code)] state: State, + _phantom: PhantomData, } -impl LightClientBuilder { +impl LightClientBuilder { /// Private method to move from one state to another - fn with_state(self, state: Next) -> LightClientBuilder { + fn with_state(self, state: Next) -> LightClientBuilder { LightClientBuilder { peer_id: self.peer_id, options: self.options, io: self.io, clock: self.clock, - hasher: self.hasher, verifier: self.verifier, scheduler: self.scheduler, predicates: self.predicates, light_store: self.light_store, + _phantom: PhantomData, state, } } } -impl LightClientBuilder { +impl LightClientBuilder +where + HostFunctions: HostFunctionsProvider, +{ /// Initialize a builder for a production (non-mock) light client. #[cfg(feature = "rpc-client")] pub fn prod( @@ -87,11 +92,10 @@ impl LightClientBuilder { options, light_store, Box::new(RpcIo::new(peer_id, rpc_client, timeout)), - Box::new(ProdHasher), Box::new(SystemClock), - Box::new(ProdVerifier::default()), + Box::new(ProdVerifier::::default()), Box::new(scheduler::basic_bisecting_schedule), - Box::new(ProdPredicates), + Box::new(ProdPredicates::::default()), ) } @@ -102,15 +106,13 @@ impl LightClientBuilder { options: Options, light_store: Box, io: Box, - hasher: Box, clock: Box, verifier: Box, scheduler: Box, - predicates: Box, + predicates: Box>, ) -> Self { Self { peer_id, - hasher, io, verifier, light_store, @@ -118,6 +120,7 @@ impl LightClientBuilder { scheduler, options, predicates, + _phantom: PhantomData, state: NoTrustedState, } } @@ -126,7 +129,7 @@ impl LightClientBuilder { fn trust_light_block( mut self, trusted_state: LightBlock, - ) -> Result, Error> { + ) -> Result, Error> { self.validate(&trusted_state)?; // TODO(liamsi, romac): it is unclear if this should be Trusted or only Verified @@ -137,7 +140,9 @@ impl LightClientBuilder { /// Keep using the latest verified or trusted block in the light store. /// Such a block must exists otherwise this will fail. - pub fn trust_from_store(self) -> Result, Error> { + pub fn trust_from_store( + self, + ) -> Result, Error> { let trusted_state = self .light_store .highest_trusted_or_verified() @@ -151,7 +156,7 @@ impl LightClientBuilder { self, trusted_height: Height, trusted_hash: Hash, - ) -> Result, Error> { + ) -> Result, Error> { let trusted_state = self .io .fetch_light_block(AtHeight::At(trusted_height)) @@ -165,7 +170,10 @@ impl LightClientBuilder { )); } - let header_hash = self.hasher.hash_header(&trusted_state.signed_header.header); + let header_hash = { + let serialized = trusted_state.signed_header.header.serialize_to_preimage(); + Hash::Sha256(simple_hash_from_byte_vectors::(serialized)) + }; if header_hash != trusted_hash { return Err(Error::hash_mismatch(trusted_hash, header_hash)); @@ -190,7 +198,6 @@ impl LightClientBuilder { .validator_sets_match( &light_block.validators, light_block.signed_header.header.validators_hash, - &*self.hasher, ) .map_err(Error::invalid_light_block)?; @@ -198,7 +205,6 @@ impl LightClientBuilder { .next_validators_match( &light_block.next_validators, light_block.signed_header.header.next_validators_hash, - &*self.hasher, ) .map_err(Error::invalid_light_block)?; @@ -206,22 +212,24 @@ impl LightClientBuilder { } } -impl LightClientBuilder { +impl LightClientBuilder +where + HostFunctions: HostFunctionsProvider, +{ /// Build the light client [`Instance`]. #[must_use] - pub fn build(self) -> Instance { + pub fn build(self) -> Instance { let state = State { light_store: self.light_store, verification_trace: VerificationTrace::new(), }; - let light_client = LightClient::from_boxed( + let light_client = LightClient::::from_boxed( self.peer_id, self.options, self.clock, self.scheduler, self.verifier, - self.hasher, self.io, ); diff --git a/light-client/src/builder/supervisor.rs b/light-client/src/builder/supervisor.rs index f8baeb118..c9c07852a 100644 --- a/light-client/src/builder/supervisor.rs +++ b/light-client/src/builder/supervisor.rs @@ -1,5 +1,6 @@ use core::time::Duration; +use tendermint_light_client_verifier::host_functions::HostFunctionsProvider; #[cfg(feature = "rpc-client")] use { crate::evidence::ProdEvidenceReporter, crate::fork_detector::ProdForkDetector, @@ -19,17 +20,17 @@ pub struct Done; /// Builder for the [`Supervisor`] #[must_use] -pub struct SupervisorBuilder { - instances: PeerListBuilder, +pub struct SupervisorBuilder { + instances: PeerListBuilder>, addresses: PeerListBuilder, evidence_reporting_timeout: Option, #[allow(dead_code)] state: State, } -impl SupervisorBuilder { +impl SupervisorBuilder { /// Private method to move from one state to another - fn with_state(self, state: Next) -> SupervisorBuilder { + fn with_state(self, state: Next) -> SupervisorBuilder { SupervisorBuilder { instances: self.instances, addresses: self.addresses, @@ -45,13 +46,13 @@ impl SupervisorBuilder { } } -impl Default for SupervisorBuilder { +impl Default for SupervisorBuilder { fn default() -> Self { Self::new() } } -impl SupervisorBuilder { +impl SupervisorBuilder { /// Create an empty builder pub fn new() -> Self { Self { @@ -67,8 +68,8 @@ impl SupervisorBuilder { mut self, peer_id: PeerId, address: tendermint_rpc::Url, - instance: Instance, - ) -> SupervisorBuilder { + instance: Instance, + ) -> SupervisorBuilder { self.instances.primary(peer_id, instance); self.addresses.primary(peer_id, address); @@ -76,14 +77,14 @@ impl SupervisorBuilder { } } -impl SupervisorBuilder { +impl SupervisorBuilder { /// Add a witness [`Instance`]. pub fn witness( mut self, peer_id: PeerId, address: tendermint_rpc::Url, - instance: Instance, - ) -> SupervisorBuilder { + instance: Instance, + ) -> SupervisorBuilder { self.instances.witness(peer_id, instance); self.addresses.witness(peer_id, address); @@ -93,8 +94,8 @@ impl SupervisorBuilder { /// Add multiple witnesses at once. pub fn witnesses( mut self, - witnesses: impl IntoIterator, - ) -> Result, Error> { + witnesses: impl IntoIterator)>, + ) -> Result, Error> { let mut iter = witnesses.into_iter().peekable(); if iter.peek().is_none() { return Err(Error::empty_witness_list()); @@ -109,24 +110,32 @@ impl SupervisorBuilder { } } -impl SupervisorBuilder { +impl SupervisorBuilder +where + HostFunctions: HostFunctionsProvider, +{ /// Build a production (non-mock) [`Supervisor`]. #[must_use] #[cfg(feature = "rpc-client")] - pub fn build_prod(self) -> Supervisor { + pub fn build_prod(self) -> Supervisor { let timeout = self.evidence_reporting_timeout; let (instances, addresses) = self.inner(); Supervisor::new( instances, - ProdForkDetector::default(), + ProdForkDetector::::default(), ProdEvidenceReporter::new(addresses.into_values(), timeout), ) } /// Get the underlying list of instances and addresses. #[must_use] - pub fn inner(self) -> (PeerList, PeerList) { + pub fn inner( + self, + ) -> ( + PeerList>, + PeerList, + ) { (self.instances.build(), self.addresses.build()) } } diff --git a/light-client/src/errors.rs b/light-client/src/errors.rs index 3b70493d5..9ec5fb697 100644 --- a/light-client/src/errors.rs +++ b/light-client/src/errors.rs @@ -2,7 +2,7 @@ use std::{fmt::Debug, time::Duration}; -use flex_error::{define_error, DisplayError, TraceError}; +use flex_error::{define_error, DisplayError}; use flume; // Re-export for backward compatibility diff --git a/light-client/src/fork_detector.rs b/light-client/src/fork_detector.rs index fb3ab0501..0706f1bb0 100644 --- a/light-client/src/fork_detector.rs +++ b/light-client/src/fork_detector.rs @@ -1,6 +1,11 @@ //! Fork detection data structures and implementation. use async_trait::async_trait; +use sp_std::marker::PhantomData; +use tendermint::Hash; +use tendermint_light_client_verifier::{ + host_functions::HostFunctionsProvider, merkle::simple_hash_from_byte_vectors, +}; use crate::{ errors::{Error, ErrorDetail}, @@ -9,7 +14,6 @@ use crate::{ supervisor::Instance, verifier::{ errors::ErrorExt, - operations::{Hasher, ProdHasher}, types::{LightBlock, PeerId, Status}, }, }; @@ -41,14 +45,14 @@ pub enum Fork { /// Interface for a fork detector #[async_trait] -pub trait ForkDetector: Send + Sync { +pub trait ForkDetector: Send + Sync { /// Detect forks using the given verified block, trusted block, /// and list of witnesses to verify the given light block against. async fn detect_forks( &self, verified_block: &LightBlock, trusted_block: &LightBlock, - witnesses: Vec<&Instance>, + witnesses: Vec<&Instance>, ) -> Result; } @@ -62,37 +66,25 @@ pub trait ForkDetector: Send + Sync { /// - If the verification succeeds, we have a real fork /// - If verification fails because of lack of trust, we have a potential fork. /// - If verification fails for any other reason, the witness is deemed faulty. -pub struct ProdForkDetector { - hasher: Box, -} - -impl ProdForkDetector { - /// Construct a new fork detector that will use the given header hasher. - pub fn new(hasher: impl Hasher + 'static) -> Self { - Self { - hasher: Box::new(hasher), - } - } -} - -impl Default for ProdForkDetector { - fn default() -> Self { - Self::new(ProdHasher) - } -} +#[derive(Default)] +pub struct ProdForkDetector(PhantomData); #[async_trait] -impl ForkDetector for ProdForkDetector { +impl ForkDetector for ProdForkDetector +where + HostFunctions: HostFunctionsProvider, +{ /// Perform fork detection. See the documentation `ProdForkDetector` for details. async fn detect_forks( &self, verified_block: &LightBlock, trusted_block: &LightBlock, - witnesses: Vec<&Instance>, + witnesses: Vec<&Instance>, ) -> Result { - let primary_hash = self - .hasher - .hash_header(&verified_block.signed_header.header); + let primary_hash = { + let serialized = verified_block.signed_header.header.serialize_to_preimage(); + Hash::Sha256(simple_hash_from_byte_vectors::(serialized)) + }; let mut forks = Vec::with_capacity(witnesses.len()); @@ -104,7 +96,10 @@ impl ForkDetector for ProdForkDetector { .get_or_fetch_block(verified_block.height(), &mut state) .await?; - let witness_hash = self.hasher.hash_header(&witness_block.signed_header.header); + let witness_hash = { + let serialized = witness_block.signed_header.header.serialize_to_preimage(); + Hash::Sha256(simple_hash_from_byte_vectors::(serialized)) + }; if primary_hash == witness_hash { // Hashes match, continue with next witness, if any. diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index fe764b4b7..a5b6d497a 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -2,7 +2,10 @@ //! //! [1]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/spec/lightclient/verification/verification.md -use core::fmt; +use sp_std::fmt; + +use sp_std::marker::PhantomData; +use tendermint_light_client_verifier::host_functions::HostFunctionsProvider; // Re-export for backward compatibility pub use crate::verifier::options::Options; @@ -12,7 +15,6 @@ use crate::{ errors::Error, state::State, verifier::{ - operations::Hasher, types::{Height, LightBlock, PeerId, Status}, Verdict, Verifier, }, @@ -28,7 +30,7 @@ use crate::{ /// of the header, more than two-thirds of the next validators of a new block are /// correct for the duration of the trusted period. The fault-tolerant read operation /// is designed for this security model. -pub struct LightClient { +pub struct LightClient { /// The peer id of the peer this client is connected to pub peer: PeerId, /// Options for this light client @@ -38,13 +40,10 @@ pub struct LightClient { scheduler: Box, verifier: Box, io: Box, - - // Only used in verify_backwards when "unstable" feature is enabled - #[allow(dead_code)] - hasher: Box, + _phantom: PhantomData, } -impl fmt::Debug for LightClient { +impl fmt::Debug for LightClient { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LightClient") .field("peer", &self.peer) @@ -53,7 +52,10 @@ impl fmt::Debug for LightClient { } } -impl LightClient { +impl LightClient + where + HostFunctions: HostFunctionsProvider, +{ /// Constructs a new light client pub fn new( peer: PeerId, @@ -61,7 +63,6 @@ impl LightClient { clock: impl Clock + 'static, scheduler: impl Scheduler + 'static, verifier: impl Verifier + 'static, - hasher: impl Hasher + 'static, io: impl AsyncIo + 'static, ) -> Self { Self { @@ -70,8 +71,8 @@ impl LightClient { clock: Box::new(clock), scheduler: Box::new(scheduler), verifier: Box::new(verifier), - hasher: Box::new(hasher), io: Box::new(io), + _phantom: PhantomData, } } @@ -82,7 +83,6 @@ impl LightClient { clock: Box, scheduler: Box, verifier: Box, - hasher: Box, io: Box, ) -> Self { Self { @@ -92,7 +92,7 @@ impl LightClient { scheduler, verifier, io, - hasher, + _phantom: PhantomData, } } @@ -167,7 +167,7 @@ impl LightClient { assert!(trusted_store_contains_block_at_target_height( state.light_store.as_ref(), - target_height + target_height, )); Ok(block) @@ -232,14 +232,14 @@ impl LightClient { // the `Verified` status or higher if already trusted. let new_status = Status::most_trusted(Status::Verified, status); state.light_store.update(¤t_block, new_status); - }, + } Verdict::Invalid(e) => { // Verification failed, add the block to the light store with `Failed` status, // and abort. state.light_store.update(¤t_block, Status::Failed); return Err(Error::invalid_light_block(e)); - }, + } Verdict::NotEnoughTrust(_) => { // The current block cannot be trusted because of a missing overlap in the // validator sets. Add the block to the light store with @@ -247,7 +247,7 @@ impl LightClient { // attempt to raise the height of the highest trusted state // until there is enough overlap. state.light_store.update(¤t_block, Status::Unverified); - }, + } } // Compute the next height to fetch and verify @@ -300,7 +300,10 @@ impl LightClient { target_height: Height, state: &mut State, ) -> Result { - use std::convert::TryFrom; + use sp_std::convert::TryFrom; + use tendermint::Hash; + + use tendermint_light_client_verifier::merkle::simple_hash_from_byte_vectors; let root = state .light_store @@ -332,7 +335,10 @@ impl LightClient { .last_block_id .ok_or_else(|| Error::missing_last_block_id(latest.height()))?; - let current_hash = self.hasher.hash_header(¤t.signed_header.header); + let current_hash = { + let serialized = current.signed_header.header.serialize_to_preimage(); + Hash::Sha256(simple_hash_from_byte_vectors::(serialized)) + }; if current_hash != latest_last_block_id.hash { return Err(Error::invalid_adjacent_headers( diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index a38f84e37..10db953b1 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -3,7 +3,9 @@ use async_recursion::async_recursion; use async_trait::async_trait; use flume; +use sp_std::fmt::Debug; use tendermint::evidence::{ConflictingHeadersEvidence, Evidence}; +use tendermint_light_client_verifier::host_functions::HostFunctionsProvider; use crate::{ errors::Error, @@ -56,17 +58,17 @@ enum HandleInput { /// A light client `Instance` packages a `LightClient` together with its `State`. #[derive(Debug)] -pub struct Instance { +pub struct Instance { /// The light client for this instance - pub light_client: LightClient, + pub light_client: LightClient, /// The state of the light client for this instance pub state: State, } -impl Instance { +impl Instance { /// Constructs a new instance from the given light client and its state. - pub fn new(light_client: LightClient, state: State) -> Self { + pub fn new(light_client: LightClient, state: State) -> Self { Self { light_client, state, @@ -120,11 +122,11 @@ impl Instance { /// std::thread::sleep(Duration::from_millis(800)); /// } /// ``` -pub struct Supervisor { +pub struct Supervisor { /// List of peers and their instances (primary, witnesses, full and faulty nodes) - peers: PeerList, + peers: PeerList>, /// An instance of the fork detector - fork_detector: Box, + fork_detector: Box>, /// Reporter of fork evidence evidence_reporter: Box, /// Channel through which to reply to `Handle`s @@ -133,7 +135,10 @@ pub struct Supervisor { receiver: flume::Receiver, } -impl std::fmt::Debug for Supervisor { +impl Debug for Supervisor +where + HostFunctions: HostFunctionsProvider, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Supervisor") .field("peers", &self.peers) @@ -142,13 +147,16 @@ impl std::fmt::Debug for Supervisor { } // Ensure the `Supervisor` can be sent across thread boundaries. -static_assertions::assert_impl_all!(Supervisor: Send); +// static_assertions::assert_impl_all!(Supervisor: Send); -impl Supervisor { +impl Supervisor +where + HostFunctions: HostFunctionsProvider, +{ /// Constructs a new supervisor from the given list of peers and fork detector instance. pub fn new( - peers: PeerList, - fork_detector: impl ForkDetector + 'static, + peers: PeerList>, + fork_detector: impl ForkDetector + 'static, evidence_reporter: impl EvidenceReporter + 'static, ) -> Self { let (sender, receiver) = flume::unbounded::(); @@ -452,6 +460,7 @@ mod tests { use tendermint::{ block::Height, evidence::Duration as DurationStr, trust_threshold::TrustThresholdFraction, }; + use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; use tendermint_rpc::{ self as rpc, response_error::{Code, ResponseError}, @@ -471,7 +480,7 @@ mod tests { fork_detector::ProdForkDetector, store::{memory::MemoryStore, LightStore}, tests::{MockClock, MockEvidenceReporter, MockIo, TrustOptions}, - verifier::{operations::ProdHasher, options::Options, types::Time, ProdVerifier}, + verifier::{options::Options, types::Time, ProdVerifier}, }; trait IntoLightBlock { @@ -494,7 +503,7 @@ mod tests { trust_options: TrustOptions, io: MockIo, now: Time, - ) -> Instance { + ) -> Instance { let trusted_height = trust_options.height; let trusted_state = block_on(io.fetch_light_block(AtHeight::At(trusted_height))) .expect("could not 'request' light block"); @@ -513,19 +522,18 @@ mod tests { clock_drift: Duration::from_secs(0), }; - let verifier = ProdVerifier::default(); + let verifier = ProdVerifier::::default(); let clock = MockClock { now }; let scheduler = scheduler::basic_bisecting_schedule; - let hasher = ProdHasher::default(); let light_client = - LightClient::new(peer_id, options, clock, scheduler, verifier, hasher, io); + LightClient::new(peer_id, options, clock, scheduler, verifier, io); Instance::new(light_client, state) } async fn run_bisection_test( - peer_list: PeerList, + peer_list: PeerList>, height_to_verify: u64, ) -> (Result, LatestStatus) { let supervisor = Supervisor::new( @@ -549,7 +557,7 @@ mod tests { primary: Option>, witnesses: Option>>, now: Time, - ) -> PeerList { + ) -> PeerList> { let trust_options = TrustOptions { period: DurationStr(Duration::new(604800, 0)), height: Height::try_from(1_u64).expect("Error while making height"), diff --git a/light-client/src/tests.rs b/light-client/src/tests.rs index dcdb02691..f6adfd08b 100644 --- a/light-client/src/tests.rs +++ b/light-client/src/tests.rs @@ -8,6 +8,7 @@ use tendermint::{ block::Height as HeightStr, evidence::{Duration as DurationStr, Evidence}, }; +use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; use tendermint_rpc as rpc; use tendermint_rpc::abci::transaction::Hash; @@ -152,7 +153,7 @@ pub fn verify_single( clock_drift: Duration, now: Time, ) -> Result { - let verifier = ProdVerifier::default(); + let verifier = ProdVerifier::::default(); let options = Options { trust_threshold, @@ -175,7 +176,7 @@ pub fn verify_single( pub async fn verify_bisection( untrusted_height: Height, - light_client: &mut LightClient, + light_client: &mut LightClient, state: &mut State, ) -> Result, Error> { light_client diff --git a/light-client/tests/light_client.rs b/light-client/tests/light_client.rs index 92717025a..dfe16de64 100644 --- a/light-client/tests/light_client.rs +++ b/light-client/tests/light_client.rs @@ -12,12 +12,12 @@ use tendermint_light_client::{ store::{memory::MemoryStore, LightStore}, tests::*, verifier::{ - operations::ProdHasher, options::Options, types::{LightBlock, Status}, ProdVerifier, }, }; +use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; use tendermint_testgen::{light_block::default_peer_id, Tester}; // Link to JSON test files repo: @@ -63,8 +63,7 @@ fn run_test(tc: LightClientTest) -> BisectionTestResult { verification_trace: HashMap::new(), }; - let verifier = ProdVerifier::default(); - let hasher = ProdHasher::default(); + let verifier = ProdVerifier::::default(); let mut light_client = LightClient::new( primary, @@ -72,7 +71,6 @@ fn run_test(tc: LightClientTest) -> BisectionTestResult { clock, scheduler::basic_bisecting_schedule, verifier, - hasher, io.clone(), ); diff --git a/light-client/tests/supervisor.rs b/light-client/tests/supervisor.rs index 4a62cf804..96fa85cbe 100644 --- a/light-client/tests/supervisor.rs +++ b/light-client/tests/supervisor.rs @@ -14,17 +14,17 @@ use tendermint_light_client::{ supervisor::{Handle, Instance, Supervisor}, tests::{LightClientTest, MockClock, MockEvidenceReporter, MockIo, TrustOptions}, verifier::{ - operations::ProdHasher, options::Options, types::{LightBlock, PeerId, Status, Time}, ProdVerifier, }, }; +use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; use tendermint_testgen::Tester; const TEST_FILES_PATH: &str = "./tests/support/"; -fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: Time) -> Instance { +fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: Time) -> Instance { let trusted_height = trust_options.height; let trusted_state = block_on(io.fetch_light_block(AtHeight::At(trusted_height))) .expect("could not 'request' light block"); @@ -44,11 +44,10 @@ fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: }; let clock = MockClock { now }; - let verifier = ProdVerifier::default(); - let hasher = ProdHasher::default(); + let verifier = ProdVerifier::::default(); let scheduler = scheduler::basic_bisecting_schedule; - let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, hasher, io); + let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io); Instance::new(light_client, state) } diff --git a/tools/kvstore-test/Cargo.toml b/tools/kvstore-test/Cargo.toml index de47a5b41..57e3b079e 100644 --- a/tools/kvstore-test/Cargo.toml +++ b/tools/kvstore-test/Cargo.toml @@ -14,7 +14,7 @@ contracts = "0.4.0" futures = "0.3" tendermint = { version = "0.24.0-pre.2", path = "../../tendermint" } tendermint-light-client = { version = "0.24.0-pre.2", path = "../../light-client", features = ["unstable"] } -tendermint-light-client-verifier = { version = "0.24.0-pre.2", path = "../../light-client-verifier" } +tendermint-light-client-verifier = { version = "0.24.0-pre.2", path = "../../light-client-verifier", features = ["test-helpers"] } tendermint-rpc = { version = "0.24.0-pre.2", path = "../../rpc", features = [ "http-client", "websocket-client" ] } tokio = { version = "1.17", features = [ "rt-multi-thread", "macros" ] } tracing = "0.1" diff --git a/tools/kvstore-test/tests/light-client.rs b/tools/kvstore-test/tests/light-client.rs index 654d680d6..1c8e7b2e9 100644 --- a/tools/kvstore-test/tests/light-client.rs +++ b/tools/kvstore-test/tests/light-client.rs @@ -30,6 +30,7 @@ use tendermint_rpc as rpc; use std::convert::TryFrom; use std::time::Duration; +use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; struct TestEvidenceReporter; @@ -47,7 +48,7 @@ impl EvidenceReporter for TestEvidenceReporter { } } -async fn make_instance(peer_id: PeerId, options: LightClientOptions, address: rpc::Url) -> Instance { +async fn make_instance(peer_id: PeerId, options: LightClientOptions, address: rpc::Url) -> Instance { let rpc_client = rpc::HttpClient::new(address).unwrap(); let io = RpcIo::new(peer_id, rpc_client.clone(), Some(Duration::from_secs(2))); let latest_block = io.fetch_light_block(AtHeight::Highest).await.unwrap(); @@ -67,7 +68,7 @@ async fn make_instance(peer_id: PeerId, options: LightClientOptions, address: rp .build() } -async fn make_supervisor() -> Supervisor { +async fn make_supervisor() -> Supervisor { let primary: PeerId = "BADFADAD0BEFEEDC0C0ADEADBEEFC0FFEEFACADE".parse().unwrap(); let witness: PeerId = "CEFEEDBADFADAD0C0CEEFACADE0ADEADBEEFC0FF".parse().unwrap(); From a0b3f16d0594aa09f6c07fe2202cf101c9c8fb54 Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Tue, 21 Jun 2022 12:54:29 +0100 Subject: [PATCH 06/10] refactor api to use result interface --- light-client-verifier/src/host_functions.rs | 41 +++++++------------ .../src/operations/voting_power.rs | 4 +- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index aa5537608..53092823a 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -8,10 +8,10 @@ pub trait HostFunctionsProvider: Send + Sync + Default + Debug + 'static { fn sha2_256(preimage: &[u8]) -> [u8; 32]; /// Verify an ed25519 signature - fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool; + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()>; /// verify secp256k1 signatures - fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool; + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()>; } #[cfg(any(feature = "test-helpers", test))] @@ -26,38 +26,28 @@ pub mod helper { sp_core::hashing::sha2_256(preimage) } - fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> bool { + fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { use sp_core::{ed25519, ByteArray, Pair}; - let result = ed25519::Signature::from_slice(sig) - .ok_or(()) - .and_then(|sig| { - let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; - Ok((sig, public_key)) - }); + let signature = ed25519::Signature::from_slice(sig) + .ok_or(())?; - if let Ok((sig, public_key)) = result { - return ed25519::Pair::verify(&sig, msg, &public_key); + let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; + if ed25519::Pair::verify(&signature, msg, &public_key) { + return Ok(()) } - - false + Err(()) } - fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> bool { + fn secp256k1_verify(sig: &[u8], message: &[u8], public: &[u8]) -> Result<(), ()> { use sp_core::{ecdsa, ByteArray, Pair}; - let result = ecdsa::Signature::from_slice(sig.clone()) - .ok_or(()) - .and_then(|sig| { - let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; - Ok((public, sig)) - }); - - if let Ok((public, signature)) = result { - return ecdsa::Pair::verify_weak(&sig, message, &public); + let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; + if ecdsa::Pair::verify_weak(&sig, message, &public) { + return Ok(()) } - false + Err(()) } } } @@ -69,13 +59,12 @@ mod tests { use crate::host_functions::{helper::HostFunctionsManager, HostFunctionsProvider}; #[test] - #[should_panic] // not super sure what the problem is here but secpk256 is optional so 🤷🏾‍ fn test_secpk1256_verification() { let public = hex!("043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a"); let msg = hex!("313233343030"); let sig = hex!("304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0"); - assert!(HostFunctionsManager::secp256k1_verify(&sig, &msg, &public)) + assert!(HostFunctionsManager::secp256k1_verify(&sig, &msg, &public).is_ok()) } } diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 0cda1746f..848125c8a 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -188,10 +188,10 @@ fn verify_signature( signature: &[u8], ) -> bool { match pubkey { - PublicKey::Ed25519(pk) => H::ed25519_verify(signature, message, pk.as_ref()), + PublicKey::Ed25519(pk) => H::ed25519_verify(signature, message, pk.as_ref()).is_ok(), /// TODO: secp256k1 #[cfg(feature = "secp256k1")] - PublicKey::Secp256k1(pk) => H::secp256k1_verify(signature, message, &pk.to_bytes()[..]), + PublicKey::Secp256k1(pk) => H::secp256k1_verify(signature, message, &pk.to_bytes()[..]).is_ok(), _ => unreachable!(), } } From 337f046e86b9a24a1be0c0c34f3a4b53202282b5 Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Tue, 21 Jun 2022 12:56:51 +0100 Subject: [PATCH 07/10] rename to CryptoProvider, CryptoManager --- light-client-verifier/src/host_functions.rs | 12 ++++---- light-client-verifier/src/merkle.rs | 22 +++++++------- .../src/operations/commit_validator.rs | 8 ++--- .../src/operations/voting_power.rs | 24 +++++++-------- light-client-verifier/src/predicates.rs | 30 +++++++++---------- light-client-verifier/src/verifier.rs | 6 ++-- light-client/src/builder/light_client.rs | 6 ++-- light-client/src/builder/supervisor.rs | 4 +-- light-client/src/fork_detector.rs | 4 +-- light-client/src/light_client.rs | 4 +-- light-client/src/supervisor.rs | 16 +++++----- light-client/src/tests.rs | 6 ++-- light-client/tests/light_client.rs | 4 +-- light-client/tests/supervisor.rs | 6 ++-- tools/kvstore-test/tests/light-client.rs | 6 ++-- 15 files changed, 79 insertions(+), 79 deletions(-) diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 53092823a..62c126dcf 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -3,7 +3,7 @@ use sp_std::fmt::Debug; /// Host functions that the light client needs for crypto operations. -pub trait HostFunctionsProvider: Send + Sync + Default + Debug + 'static { +pub trait CryptoProvider: Send + Sync + Default + Debug + 'static { /// sha256 hash function fn sha2_256(preimage: &[u8]) -> [u8; 32]; @@ -16,12 +16,12 @@ pub trait HostFunctionsProvider: Send + Sync + Default + Debug + 'static { #[cfg(any(feature = "test-helpers", test))] pub mod helper { - use crate::host_functions::HostFunctionsProvider; + use crate::host_functions::CryptoProvider; #[derive(Default, Debug)] - pub struct HostFunctionsManager; + pub struct CryptoManager; - impl HostFunctionsProvider for HostFunctionsManager { + impl CryptoProvider for CryptoManager { fn sha2_256(preimage: &[u8]) -> [u8; 32] { sp_core::hashing::sha2_256(preimage) } @@ -56,7 +56,7 @@ pub mod helper { mod tests { use hex_literal::hex; - use crate::host_functions::{helper::HostFunctionsManager, HostFunctionsProvider}; + use crate::host_functions::{helper::CryptoManager, CryptoProvider}; #[test] // not super sure what the problem is here but secpk256 is optional so 🤷🏾‍ @@ -65,6 +65,6 @@ mod tests { let msg = hex!("313233343030"); let sig = hex!("304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0"); - assert!(HostFunctionsManager::secp256k1_verify(&sig, &msg, &public).is_ok()) + assert!(CryptoManager::secp256k1_verify(&sig, &msg, &public).is_ok()) } } diff --git a/light-client-verifier/src/merkle.rs b/light-client-verifier/src/merkle.rs index 89572fc6d..c0a8d21aa 100644 --- a/light-client-verifier/src/merkle.rs +++ b/light-client-verifier/src/merkle.rs @@ -1,6 +1,6 @@ //! Merkle tree used in Tendermint networks -use crate::{host_functions::HostFunctionsProvider, prelude::*}; +use crate::{host_functions::CryptoProvider, prelude::*}; /// Size of Merkle root hash pub const HASH_SIZE: usize = 32; @@ -11,12 +11,12 @@ pub type Hash = [u8; HASH_SIZE]; /// Compute a simple Merkle root from vectors of arbitrary byte vectors. /// The leaves of the tree are the bytes of the given byte vectors in /// the given order. -pub fn simple_hash_from_byte_vectors(byte_vecs: Vec>) -> Hash { +pub fn simple_hash_from_byte_vectors(byte_vecs: Vec>) -> Hash { simple_hash_from_byte_slices_inner::(byte_vecs.as_slice()) } // recurse into subtrees -fn simple_hash_from_byte_slices_inner(byte_slices: &[Vec]) -> Hash { +fn simple_hash_from_byte_slices_inner(byte_slices: &[Vec]) -> Hash { let length = byte_slices.len(); match length { 0 => empty_hash::(), @@ -41,7 +41,7 @@ fn get_split_point(length: usize) -> usize { } // tmhash({}) -fn empty_hash() -> Hash { +fn empty_hash() -> Hash { // the empty string / byte slice let empty = Vec::with_capacity(0); @@ -50,7 +50,7 @@ fn empty_hash() -> Hash { } // tmhash(0x00 || leaf) -fn leaf_hash(bytes: &[u8]) -> Hash { +fn leaf_hash(bytes: &[u8]) -> Hash { // make a new array starting with 0 and copy in the bytes let mut leaf_bytes = Vec::with_capacity(bytes.len() + 1); leaf_bytes.push(0x00); @@ -61,7 +61,7 @@ fn leaf_hash(bytes: &[u8]) -> Hash { } // tmhash(0x01 || left || right) -fn inner_hash(left: &[u8], right: &[u8]) -> Hash { +fn inner_hash(left: &[u8], right: &[u8]) -> Hash { // make a new array starting with 0x1 and copy in the bytes let mut inner_bytes = Vec::with_capacity(left.len() + right.len() + 1); inner_bytes.push(0x01); @@ -75,7 +75,7 @@ fn inner_hash(left: &[u8], right: &[u8]) -> Hash { #[cfg(test)] mod tests { use super::*; - use crate::host_functions::helper::HostFunctionsManager; // TODO: use non-subtle ? + use crate::host_functions::helper::CryptoManager; // TODO: use non-subtle ? #[test] fn test_get_split_point() { @@ -98,7 +98,7 @@ mod tests { let empty_tree_root = &hex::decode(empty_tree_root_hex).unwrap(); let empty_tree: Vec> = vec![vec![]; 0]; - let root = simple_hash_from_byte_vectors::(empty_tree); + let root = simple_hash_from_byte_vectors::(empty_tree); assert_eq!(empty_tree_root, &root); } @@ -109,7 +109,7 @@ mod tests { let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); let one_empty_leaf: Vec> = vec![vec![]; 1]; - let root = simple_hash_from_byte_vectors::(one_empty_leaf); + let root = simple_hash_from_byte_vectors::(one_empty_leaf); assert_eq!(empty_leaf_root, &root); } @@ -121,7 +121,7 @@ mod tests { let leaf_root = &hex::decode(leaf_root_hex).unwrap(); let leaf_tree: Vec> = vec![leaf_string.as_bytes().to_vec(); 1]; - let root = simple_hash_from_byte_vectors::(leaf_tree); + let root = simple_hash_from_byte_vectors::(leaf_tree); assert_eq!(leaf_root, &root); } @@ -133,7 +133,7 @@ mod tests { let node_hash = &hex::decode(node_hash_hex).unwrap(); let hash = - inner_hash::(left_string.as_bytes(), right_string.as_bytes()); + inner_hash::(left_string.as_bytes(), right_string.as_bytes()); assert_eq!(node_hash, &hash); } } diff --git a/light-client-verifier/src/operations/commit_validator.rs b/light-client-verifier/src/operations/commit_validator.rs index e485908ee..38cf189cf 100644 --- a/light-client-verifier/src/operations/commit_validator.rs +++ b/light-client-verifier/src/operations/commit_validator.rs @@ -6,7 +6,7 @@ use tendermint::block::CommitSig; use crate::{ errors::VerificationError, - host_functions::HostFunctionsProvider, + host_functions::CryptoProvider, merkle::simple_hash_from_byte_vectors, types::{SignedHeader, ValidatorSet}, }; @@ -30,15 +30,15 @@ pub trait CommitValidator: Send + Sync { /// Production-ready implementation of a commit validator #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProdCommitValidator(PhantomData); +pub struct ProdCommitValidator(PhantomData); -impl Default for ProdCommitValidator { +impl Default for ProdCommitValidator { fn default() -> Self { Self(PhantomData) } } -impl CommitValidator for ProdCommitValidator { +impl CommitValidator for ProdCommitValidator { fn validate( &self, signed_header: &SignedHeader, diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 848125c8a..79a5f9187 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -13,7 +13,7 @@ use tendermint::{ use crate::{ errors::VerificationError, - host_functions::HostFunctionsProvider, + host_functions::CryptoProvider, prelude::*, types::{Commit, SignedHeader, TrustThreshold, ValidatorSet}, }; @@ -42,7 +42,7 @@ impl fmt::Display for VotingPowerTally { /// Computes the voting power in a commit against a validator set. /// /// This trait provides default implementation of some helper functions. -pub trait VotingPowerCalculator: Send + Sync { +pub trait VotingPowerCalculator: Send + Sync { /// Compute the total voting power in a validator set fn total_power_of(&self, validator_set: &ValidatorSet) -> u64 { validator_set @@ -103,9 +103,9 @@ pub trait VotingPowerCalculator: Send + Sync { /// Default implementation of a `VotingPowerCalculator` #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct ProdVotingPowerCalculator(PhantomData); +pub struct ProdVotingPowerCalculator(PhantomData); -impl VotingPowerCalculator for ProdVotingPowerCalculator { +impl VotingPowerCalculator for ProdVotingPowerCalculator { fn voting_power_in( &self, signed_header: &SignedHeader, @@ -182,7 +182,7 @@ impl VotingPowerCalculator for ProdVotingPowerCalcu } } -fn verify_signature( +fn verify_signature( pubkey: PublicKey, message: &[u8], signature: &[u8], @@ -246,7 +246,7 @@ mod tests { use super::*; use crate::{ - errors::VerificationErrorDetail, host_functions::helper::HostFunctionsManager, + errors::VerificationErrorDetail, host_functions::helper::CryptoManager, types::LightBlock, }; @@ -258,7 +258,7 @@ mod tests { #[test] fn test_empty_signatures() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut light_block: LightBlock = TestgenLightBlock::new_default(10) @@ -279,7 +279,7 @@ mod tests { #[test] fn test_all_signatures_absent() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -301,7 +301,7 @@ mod tests { #[test] fn test_all_signatures_nil() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let validator_set = ValidatorSet::new(vec!["a", "b"]); @@ -323,7 +323,7 @@ mod tests { #[test] fn test_one_invalid_signature() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -351,7 +351,7 @@ mod tests { #[test] fn test_all_signatures_invalid() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut testgen_lb = TestgenLightBlock::new_default(10); @@ -373,7 +373,7 @@ mod tests { #[test] fn test_signatures_from_diff_valset() { - let vp_calculator = ProdVotingPowerCalculator::::default(); + let vp_calculator = ProdVotingPowerCalculator::::default(); let trust_threshold = TrustThreshold::default(); let mut light_block: LightBlock = TestgenLightBlock::new_default(10) diff --git a/light-client-verifier/src/predicates.rs b/light-client-verifier/src/predicates.rs index 3db4693ee..c88c7c8a6 100644 --- a/light-client-verifier/src/predicates.rs +++ b/light-client-verifier/src/predicates.rs @@ -6,7 +6,7 @@ use tendermint::{block::Height, hash::Hash}; use crate::{ errors::VerificationError, - host_functions::HostFunctionsProvider, + host_functions::CryptoProvider, merkle::simple_hash_from_byte_vectors, operations::{CommitValidator, VotingPowerCalculator}, prelude::*, @@ -18,7 +18,7 @@ use crate::{ #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct ProdPredicates(PhantomData); -impl VerificationPredicates for ProdPredicates {} +impl VerificationPredicates for ProdPredicates {} /// Defines the various predicates used to validate and verify light blocks. /// @@ -26,7 +26,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 { +pub trait VerificationPredicates: Send + Sync { /// Compare the provided validator_set_hash against the hash produced from hashing the validator /// set. fn validator_sets_match( @@ -218,7 +218,7 @@ mod tests { use crate::{ errors::{VerificationError, VerificationErrorDetail}, - host_functions::helper::HostFunctionsManager, + host_functions::helper::CryptoManager, operations::{ProdCommitValidator, ProdVotingPowerCalculator, VotingPowerTally}, predicates::{ProdPredicates, VerificationPredicates}, prelude::*, @@ -242,7 +242,7 @@ mod tests { let header_one = Header::new(&val).generate().unwrap(); let header_two = Header::new(&val).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let result_ok = vp.is_monotonic_bft_time(header_two.time, header_one.time); @@ -265,7 +265,7 @@ mod tests { let header_one = Header::new(&val).generate().unwrap(); let header_two = Header::new(&val).height(2).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let result_ok = vp.is_monotonic_height(header_two.height, header_one.height); @@ -288,7 +288,7 @@ mod tests { let val = Validator::new("val-1"); let header = Header::new(&[val]).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid header verifies let mut trusting_period = Duration::new(1000, 0); @@ -317,7 +317,7 @@ mod tests { let val = Validator::new("val-1"); let header = Header::new(&[val]).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); let one_second = Duration::new(1, 0); let now = OffsetDateTime::now_utc().try_into().unwrap(); @@ -348,7 +348,7 @@ mod tests { let bad_validator_set = ValidatorSet::new(vec!["bad-val"]).generate().unwrap(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // Test positive case // 1. For predicate: validator_sets_match @@ -413,7 +413,7 @@ mod tests { .unwrap() .signed_header; - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // 1. ensure valid signed header verifies let result_ok = @@ -448,8 +448,8 @@ mod tests { let mut signed_header = light_block.signed_header; let val_set = light_block.validators; - let vp = ProdPredicates::::default(); - let commit_validator = ProdCommitValidator::::default(); + let vp = ProdPredicates::::default(); + let commit_validator = ProdCommitValidator::::default(); // Test scenarios --> // 1. valid commit - must result "Ok" @@ -535,7 +535,7 @@ mod tests { let light_block2: LightBlock = test_lb1.next().generate().unwrap().into(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); // Test scenarios --> // 1. next_validator_set hash matches @@ -582,7 +582,7 @@ mod tests { let val_set = light_block.validators; let signed_header = light_block.signed_header; - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); let mut trust_threshold = TrustThreshold::new(1, 3).expect("Cannot make trust threshold"); let voting_power_calculator = ProdVotingPowerCalculator::default(); @@ -636,7 +636,7 @@ mod tests { let mut light_block: LightBlock = TestgenLightBlock::new_default(2).generate().unwrap().into(); - let vp = ProdPredicates::::default(); + let vp = ProdPredicates::::default(); let voting_power_calculator = ProdVotingPowerCalculator::default(); // Test scenarios --> diff --git a/light-client-verifier/src/verifier.rs b/light-client-verifier/src/verifier.rs index 580c5703d..f8f066f06 100644 --- a/light-client-verifier/src/verifier.rs +++ b/light-client-verifier/src/verifier.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::{ errors::{ErrorExt, VerificationError, VerificationErrorDetail}, - host_functions::HostFunctionsProvider, + host_functions::CryptoProvider, operations::{ voting_power::VotingPowerTally, CommitValidator, ProdCommitValidator, ProdVotingPowerCalculator, VotingPowerCalculator, @@ -103,7 +103,7 @@ where P: VerificationPredicates, C: VotingPowerCalculator, V: CommitValidator, - H: HostFunctionsProvider, + H: CryptoProvider, { /// Constructor. pub fn new(predicates: P, voting_power_calculator: C, commit_validator: V) -> Self { @@ -121,7 +121,7 @@ where P: VerificationPredicates, C: VotingPowerCalculator, V: CommitValidator, - H: HostFunctionsProvider, + H: CryptoProvider, { /// Validate the given light block state. /// diff --git a/light-client/src/builder/light_client.rs b/light-client/src/builder/light_client.rs index 63a35a584..2a0786547 100644 --- a/light-client/src/builder/light_client.rs +++ b/light-client/src/builder/light_client.rs @@ -25,7 +25,7 @@ use crate::{ store::LightStore, supervisor::Instance, verifier::{ - host_functions::HostFunctionsProvider, + host_functions::CryptoProvider, options::Options, predicates::VerificationPredicates, types::{LightBlock, PeerId, Status}, @@ -76,7 +76,7 @@ impl LightClientBuilder { impl LightClientBuilder where - HostFunctions: HostFunctionsProvider, + HostFunctions: CryptoProvider, { /// Initialize a builder for a production (non-mock) light client. #[cfg(feature = "rpc-client")] @@ -214,7 +214,7 @@ where impl LightClientBuilder where - HostFunctions: HostFunctionsProvider, + HostFunctions: CryptoProvider, { /// Build the light client [`Instance`]. #[must_use] diff --git a/light-client/src/builder/supervisor.rs b/light-client/src/builder/supervisor.rs index c9c07852a..5b437b369 100644 --- a/light-client/src/builder/supervisor.rs +++ b/light-client/src/builder/supervisor.rs @@ -1,6 +1,6 @@ use core::time::Duration; -use tendermint_light_client_verifier::host_functions::HostFunctionsProvider; +use tendermint_light_client_verifier::host_functions::CryptoProvider; #[cfg(feature = "rpc-client")] use { crate::evidence::ProdEvidenceReporter, crate::fork_detector::ProdForkDetector, @@ -112,7 +112,7 @@ impl SupervisorBuilder { impl SupervisorBuilder where - HostFunctions: HostFunctionsProvider, + HostFunctions: CryptoProvider, { /// Build a production (non-mock) [`Supervisor`]. #[must_use] diff --git a/light-client/src/fork_detector.rs b/light-client/src/fork_detector.rs index 0706f1bb0..d49a16db0 100644 --- a/light-client/src/fork_detector.rs +++ b/light-client/src/fork_detector.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use sp_std::marker::PhantomData; use tendermint::Hash; use tendermint_light_client_verifier::{ - host_functions::HostFunctionsProvider, merkle::simple_hash_from_byte_vectors, + host_functions::CryptoProvider, merkle::simple_hash_from_byte_vectors, }; use crate::{ @@ -72,7 +72,7 @@ pub struct ProdForkDetector(PhantomData); #[async_trait] impl ForkDetector for ProdForkDetector where - HostFunctions: HostFunctionsProvider, + HostFunctions: CryptoProvider, { /// Perform fork detection. See the documentation `ProdForkDetector` for details. async fn detect_forks( diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index a5b6d497a..3bf0168cf 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -5,7 +5,7 @@ use sp_std::fmt; use sp_std::marker::PhantomData; -use tendermint_light_client_verifier::host_functions::HostFunctionsProvider; +use tendermint_light_client_verifier::host_functions::CryptoProvider; // Re-export for backward compatibility pub use crate::verifier::options::Options; @@ -54,7 +54,7 @@ impl fmt::Debug for LightClient { impl LightClient where - HostFunctions: HostFunctionsProvider, + HostFunctions: CryptoProvider, { /// Constructs a new light client pub fn new( diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index 10db953b1..9af5d1708 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -5,7 +5,7 @@ use async_trait::async_trait; use flume; use sp_std::fmt::Debug; use tendermint::evidence::{ConflictingHeadersEvidence, Evidence}; -use tendermint_light_client_verifier::host_functions::HostFunctionsProvider; +use tendermint_light_client_verifier::host_functions::CryptoProvider; use crate::{ errors::Error, @@ -137,7 +137,7 @@ pub struct Supervisor { impl Debug for Supervisor where - HostFunctions: HostFunctionsProvider, + HostFunctions: CryptoProvider, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Supervisor") @@ -151,7 +151,7 @@ where impl Supervisor where - HostFunctions: HostFunctionsProvider, + HostFunctions: CryptoProvider, { /// Constructs a new supervisor from the given list of peers and fork detector instance. pub fn new( @@ -460,7 +460,7 @@ mod tests { use tendermint::{ block::Height, evidence::Duration as DurationStr, trust_threshold::TrustThresholdFraction, }; - use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; + use tendermint_light_client_verifier::host_functions::helper::CryptoManager; use tendermint_rpc::{ self as rpc, response_error::{Code, ResponseError}, @@ -503,7 +503,7 @@ mod tests { trust_options: TrustOptions, io: MockIo, now: Time, - ) -> Instance { + ) -> Instance { let trusted_height = trust_options.height; let trusted_state = block_on(io.fetch_light_block(AtHeight::At(trusted_height))) .expect("could not 'request' light block"); @@ -522,7 +522,7 @@ mod tests { clock_drift: Duration::from_secs(0), }; - let verifier = ProdVerifier::::default(); + let verifier = ProdVerifier::::default(); let clock = MockClock { now }; let scheduler = scheduler::basic_bisecting_schedule; @@ -533,7 +533,7 @@ mod tests { } async fn run_bisection_test( - peer_list: PeerList>, + peer_list: PeerList>, height_to_verify: u64, ) -> (Result, LatestStatus) { let supervisor = Supervisor::new( @@ -557,7 +557,7 @@ mod tests { primary: Option>, witnesses: Option>>, now: Time, - ) -> PeerList> { + ) -> PeerList> { let trust_options = TrustOptions { period: DurationStr(Duration::new(604800, 0)), height: Height::try_from(1_u64).expect("Error while making height"), diff --git a/light-client/src/tests.rs b/light-client/src/tests.rs index f6adfd08b..085ca1a8f 100644 --- a/light-client/src/tests.rs +++ b/light-client/src/tests.rs @@ -8,7 +8,7 @@ use tendermint::{ block::Height as HeightStr, evidence::{Duration as DurationStr, Evidence}, }; -use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; +use tendermint_light_client_verifier::host_functions::helper::CryptoManager; use tendermint_rpc as rpc; use tendermint_rpc::abci::transaction::Hash; @@ -153,7 +153,7 @@ pub fn verify_single( clock_drift: Duration, now: Time, ) -> Result { - let verifier = ProdVerifier::::default(); + let verifier = ProdVerifier::::default(); let options = Options { trust_threshold, @@ -176,7 +176,7 @@ pub fn verify_single( pub async fn verify_bisection( untrusted_height: Height, - light_client: &mut LightClient, + light_client: &mut LightClient, state: &mut State, ) -> Result, Error> { light_client diff --git a/light-client/tests/light_client.rs b/light-client/tests/light_client.rs index dfe16de64..2acc38690 100644 --- a/light-client/tests/light_client.rs +++ b/light-client/tests/light_client.rs @@ -17,7 +17,7 @@ use tendermint_light_client::{ ProdVerifier, }, }; -use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; +use tendermint_light_client_verifier::host_functions::helper::CryptoManager; use tendermint_testgen::{light_block::default_peer_id, Tester}; // Link to JSON test files repo: @@ -63,7 +63,7 @@ fn run_test(tc: LightClientTest) -> BisectionTestResult { verification_trace: HashMap::new(), }; - let verifier = ProdVerifier::::default(); + let verifier = ProdVerifier::::default(); let mut light_client = LightClient::new( primary, diff --git a/light-client/tests/supervisor.rs b/light-client/tests/supervisor.rs index 96fa85cbe..06c2a81d8 100644 --- a/light-client/tests/supervisor.rs +++ b/light-client/tests/supervisor.rs @@ -19,12 +19,12 @@ use tendermint_light_client::{ ProdVerifier, }, }; -use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; +use tendermint_light_client_verifier::host_functions::helper::CryptoManager; use tendermint_testgen::Tester; const TEST_FILES_PATH: &str = "./tests/support/"; -fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: Time) -> Instance { +fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: Time) -> Instance { let trusted_height = trust_options.height; let trusted_state = block_on(io.fetch_light_block(AtHeight::At(trusted_height))) .expect("could not 'request' light block"); @@ -44,7 +44,7 @@ fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: }; let clock = MockClock { now }; - let verifier = ProdVerifier::::default(); + let verifier = ProdVerifier::::default(); let scheduler = scheduler::basic_bisecting_schedule; let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io); diff --git a/tools/kvstore-test/tests/light-client.rs b/tools/kvstore-test/tests/light-client.rs index 1c8e7b2e9..1f0dfc09d 100644 --- a/tools/kvstore-test/tests/light-client.rs +++ b/tools/kvstore-test/tests/light-client.rs @@ -30,7 +30,7 @@ use tendermint_rpc as rpc; use std::convert::TryFrom; use std::time::Duration; -use tendermint_light_client_verifier::host_functions::helper::HostFunctionsManager; +use tendermint_light_client_verifier::host_functions::helper::CryptoManager; struct TestEvidenceReporter; @@ -48,7 +48,7 @@ impl EvidenceReporter for TestEvidenceReporter { } } -async fn make_instance(peer_id: PeerId, options: LightClientOptions, address: rpc::Url) -> Instance { +async fn make_instance(peer_id: PeerId, options: LightClientOptions, address: rpc::Url) -> Instance { let rpc_client = rpc::HttpClient::new(address).unwrap(); let io = RpcIo::new(peer_id, rpc_client.clone(), Some(Duration::from_secs(2))); let latest_block = io.fetch_light_block(AtHeight::Highest).await.unwrap(); @@ -68,7 +68,7 @@ async fn make_instance(peer_id: PeerId, options: LightClientOptions, address: rp .build() } -async fn make_supervisor() -> Supervisor { +async fn make_supervisor() -> Supervisor { let primary: PeerId = "BADFADAD0BEFEEDC0C0ADEADBEEFC0FFEEFACADE".parse().unwrap(); let witness: PeerId = "CEFEEDBADFADAD0C0CEEFACADE0ADEADBEEFC0FF".parse().unwrap(); From ed3480b2649d5ffa0ce444b8a7d6b0347be32139 Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Tue, 21 Jun 2022 12:59:46 +0100 Subject: [PATCH 08/10] fmt --- light-client-verifier/src/host_functions.rs | 7 +++---- light-client-verifier/src/merkle.rs | 3 +-- .../src/operations/voting_power.rs | 7 ++++--- light-client/src/light_client.rs | 15 ++++++--------- light-client/src/supervisor.rs | 3 +-- light-client/tests/supervisor.rs | 9 +++++++-- tendermint/src/evidence.rs | 6 +++--- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/light-client-verifier/src/host_functions.rs b/light-client-verifier/src/host_functions.rs index 62c126dcf..10506e3bf 100644 --- a/light-client-verifier/src/host_functions.rs +++ b/light-client-verifier/src/host_functions.rs @@ -29,12 +29,11 @@ pub mod helper { fn ed25519_verify(sig: &[u8], msg: &[u8], pub_key: &[u8]) -> Result<(), ()> { use sp_core::{ed25519, ByteArray, Pair}; - let signature = ed25519::Signature::from_slice(sig) - .ok_or(())?; + let signature = ed25519::Signature::from_slice(sig).ok_or(())?; let public_key = ed25519::Public::from_slice(pub_key).map_err(|_| ())?; if ed25519::Pair::verify(&signature, msg, &public_key) { - return Ok(()) + return Ok(()); } Err(()) } @@ -44,7 +43,7 @@ pub mod helper { let public = ecdsa::Public::from_slice(public).map_err(|_| ())?; if ecdsa::Pair::verify_weak(&sig, message, &public) { - return Ok(()) + return Ok(()); } Err(()) diff --git a/light-client-verifier/src/merkle.rs b/light-client-verifier/src/merkle.rs index c0a8d21aa..a664353ba 100644 --- a/light-client-verifier/src/merkle.rs +++ b/light-client-verifier/src/merkle.rs @@ -132,8 +132,7 @@ mod tests { let right_string = "N456"; let node_hash = &hex::decode(node_hash_hex).unwrap(); - let hash = - inner_hash::(left_string.as_bytes(), right_string.as_bytes()); + let hash = inner_hash::(left_string.as_bytes(), right_string.as_bytes()); assert_eq!(node_hash, &hash); } } diff --git a/light-client-verifier/src/operations/voting_power.rs b/light-client-verifier/src/operations/voting_power.rs index 79a5f9187..493e53a02 100644 --- a/light-client-verifier/src/operations/voting_power.rs +++ b/light-client-verifier/src/operations/voting_power.rs @@ -191,7 +191,9 @@ fn verify_signature( PublicKey::Ed25519(pk) => H::ed25519_verify(signature, message, pk.as_ref()).is_ok(), /// TODO: secp256k1 #[cfg(feature = "secp256k1")] - PublicKey::Secp256k1(pk) => H::secp256k1_verify(signature, message, &pk.to_bytes()[..]).is_ok(), + PublicKey::Secp256k1(pk) => { + H::secp256k1_verify(signature, message, &pk.to_bytes()[..]).is_ok() + }, _ => unreachable!(), } } @@ -246,8 +248,7 @@ mod tests { use super::*; use crate::{ - errors::VerificationErrorDetail, host_functions::helper::CryptoManager, - types::LightBlock, + errors::VerificationErrorDetail, host_functions::helper::CryptoManager, types::LightBlock, }; const EXPECTED_RESULT: VotingPowerTally = VotingPowerTally { diff --git a/light-client/src/light_client.rs b/light-client/src/light_client.rs index 3bf0168cf..6c44795c1 100644 --- a/light-client/src/light_client.rs +++ b/light-client/src/light_client.rs @@ -2,9 +2,7 @@ //! //! [1]: https://github.com/informalsystems/tendermint-rs/blob/master/docs/spec/lightclient/verification/verification.md -use sp_std::fmt; - -use sp_std::marker::PhantomData; +use sp_std::{fmt, marker::PhantomData}; use tendermint_light_client_verifier::host_functions::CryptoProvider; // Re-export for backward compatibility @@ -53,8 +51,8 @@ impl fmt::Debug for LightClient { } impl LightClient - where - HostFunctions: CryptoProvider, +where + HostFunctions: CryptoProvider, { /// Constructs a new light client pub fn new( @@ -232,14 +230,14 @@ impl LightClient // the `Verified` status or higher if already trusted. let new_status = Status::most_trusted(Status::Verified, status); state.light_store.update(¤t_block, new_status); - } + }, Verdict::Invalid(e) => { // Verification failed, add the block to the light store with `Failed` status, // and abort. state.light_store.update(¤t_block, Status::Failed); return Err(Error::invalid_light_block(e)); - } + }, Verdict::NotEnoughTrust(_) => { // The current block cannot be trusted because of a missing overlap in the // validator sets. Add the block to the light store with @@ -247,7 +245,7 @@ impl LightClient // attempt to raise the height of the highest trusted state // until there is enough overlap. state.light_store.update(¤t_block, Status::Unverified); - } + }, } // Compute the next height to fetch and verify @@ -302,7 +300,6 @@ impl LightClient ) -> Result { use sp_std::convert::TryFrom; use tendermint::Hash; - use tendermint_light_client_verifier::merkle::simple_hash_from_byte_vectors; let root = state diff --git a/light-client/src/supervisor.rs b/light-client/src/supervisor.rs index 9af5d1708..d54e874b8 100644 --- a/light-client/src/supervisor.rs +++ b/light-client/src/supervisor.rs @@ -526,8 +526,7 @@ mod tests { let clock = MockClock { now }; let scheduler = scheduler::basic_bisecting_schedule; - let light_client = - LightClient::new(peer_id, options, clock, scheduler, verifier, io); + let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io); Instance::new(light_client, state) } diff --git a/light-client/tests/supervisor.rs b/light-client/tests/supervisor.rs index 06c2a81d8..92ccf7e12 100644 --- a/light-client/tests/supervisor.rs +++ b/light-client/tests/supervisor.rs @@ -24,7 +24,12 @@ use tendermint_testgen::Tester; const TEST_FILES_PATH: &str = "./tests/support/"; -fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: Time) -> Instance { +fn make_instance( + peer_id: PeerId, + trust_options: TrustOptions, + io: MockIo, + now: Time, +) -> Instance { let trusted_height = trust_options.height; let trusted_state = block_on(io.fetch_light_block(AtHeight::At(trusted_height))) .expect("could not 'request' light block"); @@ -47,7 +52,7 @@ fn make_instance(peer_id: PeerId, trust_options: TrustOptions, io: MockIo, now: let verifier = ProdVerifier::::default(); let scheduler = scheduler::basic_bisecting_schedule; - let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io); + let light_client = LightClient::new(peer_id, options, clock, scheduler, verifier, io); Instance::new(light_client, state) } diff --git a/tendermint/src/evidence.rs b/tendermint/src/evidence.rs index 49da2f59d..22191474d 100644 --- a/tendermint/src/evidence.rs +++ b/tendermint/src/evidence.rs @@ -9,9 +9,9 @@ use serde::{Deserialize, Serialize}; use tendermint_proto::{ google::protobuf::Duration as RawDuration, types::{ - evidence::{Sum as RawSum, Sum}, - DuplicateVoteEvidence as RawDuplicateVoteEvidence, Evidence as RawEvidence, - EvidenceList as RawEvidenceList, EvidenceParams as RawEvidenceParams, + evidence::Sum as RawSum, DuplicateVoteEvidence as RawDuplicateVoteEvidence, + Evidence as RawEvidence, EvidenceList as RawEvidenceList, + EvidenceParams as RawEvidenceParams, }, Protobuf, }; From 0368affdbdb676407406083c5ccc6bf0c4c87dbc Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Tue, 21 Jun 2022 13:08:51 +0100 Subject: [PATCH 09/10] reset .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 348b09737..a066462b4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,6 @@ Cargo.lock # Proptest regressions dumps **/*.proptest-regressions -.idea - # Light Client WASM light-client-js/pkg/ light-client-js/examples/verifier-web/node_modules/ From f160afc1c4a3bb2fbce0092f7a6eda3a3f142b97 Mon Sep 17 00:00:00 2001 From: Seun Lanlege Date: Tue, 21 Jun 2022 13:24:21 +0100 Subject: [PATCH 10/10] fix light client verifier --- light-client-js/Cargo.toml | 4 +--- light-client-js/src/lib.rs | 3 ++- light-client-verifier/Cargo.toml | 3 ++- tendermint/src/evidence.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/light-client-js/Cargo.toml b/light-client-js/Cargo.toml index 8cec3c26c..a8bb683cb 100644 --- a/light-client-js/Cargo.toml +++ b/light-client-js/Cargo.toml @@ -22,10 +22,8 @@ default = ["console_error_panic_hook"] [dependencies] serde = { version = "1.0", default-features = false, features = [ "derive" ] } serde_json = { version = "1.0", default-features = false } -# TODO(thane): Remove once https://github.com/rustwasm/wasm-bindgen/issues/2508 is resolved -syn = { version = "1.0.95", default-features = false } tendermint = { version = "0.24.0-pre.2", default-features = false, path = "../tendermint" } -tendermint-light-client-verifier = { version = "0.24.0-pre.2", default-features = false, path = "../light-client-verifier" } +tendermint-light-client-verifier = { version = "0.24.0-pre.2", default-features = false, path = "../light-client-verifier", features = ["test-helpers"] } wasm-bindgen = { version = "0.2.63", default-features = false, features = [ "serde-serialize" ] } # The `console_error_panic_hook` crate provides better debugging of panics by diff --git a/light-client-js/src/lib.rs b/light-client-js/src/lib.rs index 16cb9896d..8f7ff5e63 100644 --- a/light-client-js/src/lib.rs +++ b/light-client-js/src/lib.rs @@ -21,6 +21,7 @@ use tendermint_light_client_verifier::{ ProdVerifier, Verifier, }; use wasm_bindgen::{prelude::*, JsValue}; +use tendermint_light_client_verifier::host_functions::helper::CryptoManager; // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global // allocator. @@ -33,7 +34,7 @@ static ALLOC: wee_alloc::WeeAlloc<'_> = wee_alloc::WeeAlloc::INIT; pub fn verify(untrusted: &JsValue, trusted: &JsValue, options: &JsValue, now: &JsValue) -> JsValue { let result = deserialize_params(untrusted, trusted, options, now).map( |(untrusted, trusted, options, now)| { - let verifier = ProdVerifier::default(); + let verifier = ProdVerifier::::default(); verifier.verify( untrusted.as_untrusted_state(), trusted.as_trusted_state(), diff --git a/light-client-verifier/Cargo.toml b/light-client-verifier/Cargo.toml index 70d969f5e..41053e2a1 100644 --- a/light-client-verifier/Cargo.toml +++ b/light-client-verifier/Cargo.toml @@ -28,8 +28,9 @@ std = [ "serde/std", "time/std", "flex-error/std", + "flex-error/eyre_tracer" ] -default = ["flex-error/std", "flex-error/eyre_tracer"] +default = ["std"] secp256k1 = ["tendermint/secp256k1"] test-helpers = [ "sp-core"] diff --git a/tendermint/src/evidence.rs b/tendermint/src/evidence.rs index 22191474d..1633ba6a7 100644 --- a/tendermint/src/evidence.rs +++ b/tendermint/src/evidence.rs @@ -47,8 +47,8 @@ impl TryFrom for Evidence { fn try_from(value: RawEvidence) -> Result { match value.sum.ok_or_else(Error::invalid_evidence)? { - Sum::DuplicateVoteEvidence(ev) => Ok(Evidence::DuplicateVote(Box::new(ev.try_into()?))), - Sum::LightClientAttackEvidence(_ev) => Ok(Evidence::LightClientAttackEvidence), + RawSum::DuplicateVoteEvidence(ev) => Ok(Evidence::DuplicateVote(Box::new(ev.try_into()?))), + RawSum::LightClientAttackEvidence(_ev) => Ok(Evidence::LightClientAttackEvidence), } } }