From 06fab7d539bad247fd95de4d0d32fe87164f67ad Mon Sep 17 00:00:00 2001 From: Soares Chen Date: Thu, 26 Oct 2023 15:02:17 -0400 Subject: [PATCH] Initial refactoring of TendermintClientState::verify_header into CGP component --- .../src/client/verify.rs | 213 +++++++++++------- crates/ibc/Cargo.toml | 1 + .../client_state/update_client.rs | 2 + 3 files changed, 140 insertions(+), 76 deletions(-) diff --git a/crates/ibc-cosmos-components/src/client/verify.rs b/crates/ibc-cosmos-components/src/client/verify.rs index f94228dc3..916c35c97 100644 --- a/crates/ibc-cosmos-components/src/client/verify.rs +++ b/crates/ibc-cosmos-components/src/client/verify.rs @@ -1,106 +1,167 @@ use alloc::format; +use core::fmt::Display; +use cgp_core::prelude::*; use ibc::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState; use ibc::clients::ics07_tendermint::error::{Error, IntoResult}; use ibc::clients::ics07_tendermint::header::Header as TmHeader; -use ibc::clients::ics07_tendermint::ValidationContext as TmValidationContext; use ibc::core::ics02_client::error::ClientError; use ibc::core::ics24_host::identifier::{ChainId, ClientId}; use ibc::core::ics24_host::path::ClientConsensusStatePath; +use ibc::core::timestamp::Timestamp; +use ibc::core::ContextError; use ibc::prelude::ToString; use tendermint_light_client_verifier::options::Options; use tendermint_light_client_verifier::types::{TrustedBlockState, UntrustedBlockState}; use tendermint_light_client_verifier::{ProdVerifier, Verifier}; -pub trait HasClientStateMethods { +pub trait HasChainId { fn chain_id(&self) -> &ChainId; +} +pub trait HasLightClientOptions { fn light_client_options(&self) -> Options; +} +pub trait HasVerifier { fn verifier(&self) -> &ProdVerifier; } -pub fn verify_header( - client_state: &ClientState, - ctx: &ClientValidationContext, - client_id: &ClientId, - header: TmHeader, -) -> Result<(), ClientError> +pub trait HasHostTimestamp { + fn host_timestamp(&self) -> Result; +} + +pub trait HasConsensusState: HasAnyConsensusStateType { + fn consensus_state( + &self, + client_cons_state_path: &ClientConsensusStatePath, + ) -> Result; +} + +pub trait HasAnyConsensusStateType { + type AnyConsensusState; +} + +pub trait HasClientStateType { + type ClientState; +} + +pub trait HasModule { + type ModuleId; + + type Module; +} + +pub trait CanGetRoute: HasModule { + fn get_route(&self, module_id: &Self::ModuleId) -> &Self::Module; +} + +pub struct TendermintClientType; + +#[derive_component(HeaderVerifierComponent, HeaderVerifier)] +pub trait CanVerifyHeader: HasClientStateType + HasErrorType { + fn verify_header( + &self, + client_state: &Self::ClientState, + client_id: &ClientId, + header: TmHeader, + ) -> Result<(), Self::Error>; +} + +pub struct VerifyTendermintHeader; + +impl HeaderVerifier for VerifyTendermintHeader where - ClientState: HasClientStateMethods, - ClientValidationContext: TmValidationContext, + Context: HasConsensusState + + HasHostTimestamp + + HasClientStateType + + HasErrorType, + Context::AnyConsensusState: TryInto, + >::Error: Display, + Context::ClientState: HasChainId + HasLightClientOptions + HasVerifier, { - // Checks that the header fields are valid. - header.validate_basic()?; + fn verify_header( + ctx: &Context, + client_state: &Context::ClientState, + client_id: &ClientId, + header: TmHeader, + ) -> Result<(), ClientError> + where + Context: HasConsensusState + HasHostTimestamp + HasClientStateType, + Context::AnyConsensusState: TryInto, + >::Error: Display, + Context::ClientState: HasChainId + HasLightClientOptions + HasVerifier, + { + // Checks that the header fields are valid. + header.validate_basic()?; - // The tendermint-light-client crate though works on heights that are assumed - // to have the same revision number. We ensure this here. - header.verify_chain_id_version_matches_height(client_state.chain_id())?; + // The tendermint-light-client crate though works on heights that are assumed + // to have the same revision number. We ensure this here. + header.verify_chain_id_version_matches_height(client_state.chain_id())?; - // Delegate to tendermint-light-client, which contains the required checks - // of the new header against the trusted consensus state. - { - let trusted_state = { - let trusted_client_cons_state_path = - ClientConsensusStatePath::new(client_id, &header.trusted_height); - let trusted_consensus_state: TmConsensusState = ctx - .consensus_state(&trusted_client_cons_state_path)? - .try_into() - .map_err(|err| ClientError::Other { - description: err.to_string(), - })?; - - check_header_trusted_next_validator_set(&header, &trusted_consensus_state)?; - - TrustedBlockState { - chain_id: &client_state - .chain_id() - .to_string() - .try_into() - .map_err(|e| ClientError::Other { - description: format!("failed to parse chain id: {}", e), - })?, - header_time: trusted_consensus_state.timestamp, - height: header - .trusted_height - .revision_height() - .try_into() - .map_err(|_| ClientError::ClientSpecific { - description: Error::InvalidHeaderHeight { - height: header.trusted_height.revision_height(), - } - .to_string(), - })?, - next_validators: &header.trusted_next_validator_set, - next_validators_hash: trusted_consensus_state.next_validators_hash, - } - }; - - let untrusted_state = UntrustedBlockState { - signed_header: &header.signed_header, - validators: &header.validator_set, - // NB: This will skip the - // VerificationPredicates::next_validators_match check for the - // untrusted state. - next_validators: None, - }; - - let options = client_state.light_client_options(); - let now = - ctx.host_timestamp()? - .into_tm_time() - .ok_or_else(|| ClientError::ClientSpecific { + // Delegate to tendermint-light-client, which contains the required checks + // of the new header against the trusted consensus state. + { + let trusted_state = + { + let trusted_client_cons_state_path = + ClientConsensusStatePath::new(client_id, &header.trusted_height); + let trusted_consensus_state: TmConsensusState = ctx + .consensus_state(&trusted_client_cons_state_path)? + .try_into() + .map_err(|err| ClientError::Other { + description: err.to_string(), + })?; + + check_header_trusted_next_validator_set(&header, &trusted_consensus_state)?; + + TrustedBlockState { + chain_id: &client_state + .chain_id() + .to_string() + .try_into() + .map_err(|e| ClientError::Other { + description: format!("failed to parse chain id: {}", e), + })?, + header_time: trusted_consensus_state.timestamp, + height: header.trusted_height.revision_height().try_into().map_err( + |_| ClientError::ClientSpecific { + description: Error::InvalidHeaderHeight { + height: header.trusted_height.revision_height(), + } + .to_string(), + }, + )?, + next_validators: &header.trusted_next_validator_set, + next_validators_hash: trusted_consensus_state.next_validators_hash, + } + }; + + let untrusted_state = UntrustedBlockState { + signed_header: &header.signed_header, + validators: &header.validator_set, + // NB: This will skip the + // VerificationPredicates::next_validators_match check for the + // untrusted state. + next_validators: None, + }; + + let options = client_state.light_client_options(); + let now = ctx.host_timestamp()?.into_tm_time().ok_or_else(|| { + ClientError::ClientSpecific { description: "host timestamp is not a valid TM timestamp".to_string(), - })?; + } + })?; - // main header verification, delegated to the tendermint-light-client crate. - client_state - .verifier() - .verify_update_header(untrusted_state, trusted_state, &options, now) - .into_result()?; - } + // main header verification, delegated to the tendermint-light-client crate. + client_state + .verifier() + .verify_update_header(untrusted_state, trusted_state, &options, now) + .into_result()?; + } - Ok(()) + Ok(()) + } } fn check_header_trusted_next_validator_set( diff --git a/crates/ibc/Cargo.toml b/crates/ibc/Cargo.toml index ae4d4d56b..ee4f0e703 100644 --- a/crates/ibc/Cargo.toml +++ b/crates/ibc/Cargo.toml @@ -66,6 +66,7 @@ num-traits = { version = "0.2.15", default-features = false } derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display", "try_into"] } uint = { version = "0.9", default-features = false } primitive-types = { version = "0.12.0", default-features = false, features = ["serde_no_std"] } +cgp-core = { version = "0.1.0" } ## for codec encode or decode parity-scale-codec = { version = "3.0.0", default-features = false, features = ["full"], optional = true } diff --git a/crates/ibc/src/clients/ics07_tendermint/client_state/update_client.rs b/crates/ibc/src/clients/ics07_tendermint/client_state/update_client.rs index a2fabec1a..c3f8e62e2 100644 --- a/crates/ibc/src/clients/ics07_tendermint/client_state/update_client.rs +++ b/crates/ibc/src/clients/ics07_tendermint/client_state/update_client.rs @@ -23,6 +23,8 @@ impl ClientState { where ClientValidationContext: TmValidationContext, { + // VerifyTendermintHeader::verify_header(ctx, self, client_id, header) + // Checks that the header fields are valid. header.validate_basic()?;