From 1c3ba5084b3f1a189c3df3ac3a414e8db9a8748e Mon Sep 17 00:00:00 2001 From: Rano | Ranadeep Date: Wed, 7 Feb 2024 05:41:18 +0100 Subject: [PATCH 01/40] imp(testkit): Mock IBC context with `basecoin-store` types (#1068) * non empty default CommitmentPrefix * update ibc mock client types * fix tests for updated mock types * generalize MockContextConfig::into * rm clone from MockContext * add with_{client,consensus}_state * add blocks_since in utils.rs * client takes host timestamp by default * refactor relayer context test * fix few tests * add ibc-query and basecoin-store deps * add basecoin-store in mock ibc context * refactor for updated mock ibc context * imp timeout test * public git commit as dep source * fix spelling * update MockClient types * fix failing tests * rm unused utils and deps * fix cargo doc lint * use ibc host paths in mock context * rm redundant curly brackets --------- Co-authored-by: Farhad Shabani --- Cargo.toml | 3 + .../ics23-commitment/types/src/commitment.rs | 8 +- ibc-testkit/Cargo.toml | 2 + ibc-testkit/src/fixtures/core/context.rs | 17 +- ibc-testkit/src/fixtures/mod.rs | 2 +- ibc-testkit/src/relayer/context.rs | 59 +- .../testapp/ibc/clients/mock/client_state.rs | 41 +- .../ibc/clients/mock/consensus_state.rs | 11 +- .../src/testapp/ibc/clients/mock/header.rs | 14 +- ibc-testkit/src/testapp/ibc/clients/mod.rs | 9 +- .../src/testapp/ibc/core/client_ctx.rs | 441 ++++---- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 975 ++++++++++++------ ibc-testkit/src/testapp/ibc/core/types.rs | 401 ++++--- ibc-testkit/src/testapp/ibc/mod.rs | 1 + ibc-testkit/src/testapp/ibc/utils.rs | 6 + .../tests/core/ics02_client/update_client.rs | 47 +- .../tests/core/ics02_client/upgrade_client.rs | 2 +- .../core/ics03_connection/conn_open_ack.rs | 2 +- .../core/ics03_connection/conn_open_init.rs | 2 +- .../core/ics03_connection/conn_open_try.rs | 2 +- .../tests/core/ics04_channel/send_packet.rs | 19 +- .../tests/core/ics04_channel/timeout.rs | 3 +- ibc-testkit/tests/core/router.rs | 20 +- 23 files changed, 1296 insertions(+), 791 deletions(-) create mode 100644 ibc-testkit/src/testapp/ibc/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 6007c4f27..ac45f6c20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,3 +105,6 @@ tendermint-testgen = { version = "0.34.0", default-features = fals # parity dependencies parity-scale-codec = { version = "3.6.5", default-features = false, features = ["full"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } + +[patch.crates-io] +ibc-proto = { git = "https://github.com/cosmos/ibc-proto-rs", rev = "7643fa3" } diff --git a/ibc-core/ics23-commitment/types/src/commitment.rs b/ibc-core/ics23-commitment/types/src/commitment.rs index b24b60a88..da5580f86 100644 --- a/ibc-core/ics23-commitment/types/src/commitment.rs +++ b/ibc-core/ics23-commitment/types/src/commitment.rs @@ -144,11 +144,17 @@ impl<'a> TryFrom<&'a CommitmentProofBytes> for MerkleProof { )] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, PartialEq, Eq, Hash, Default)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct CommitmentPrefix { bytes: Vec, } +impl Default for CommitmentPrefix { + fn default() -> Self { + Self { bytes: vec![0x00] } + } +} + impl CommitmentPrefix { pub fn as_bytes(&self) -> &[u8] { &self.bytes diff --git a/ibc-testkit/Cargo.toml b/ibc-testkit/Cargo.toml index 55546cf8c..94ccac0d3 100644 --- a/ibc-testkit/Cargo.toml +++ b/ibc-testkit/Cargo.toml @@ -31,6 +31,8 @@ typed-builder = { version = "0.18.0" } # ibc dependencies ibc = { workspace = true, features = ["std"] } ibc-proto = { workspace = true } +ibc-query = { version = "0.50.0", path = "../ibc-query" } +basecoin-store = { git = "https://github.com/informalsystems/basecoin-rs", rev = "dc3b43a" } # cosmos dependencies tendermint = { workspace = true } diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index a886777e7..f6522ce2b 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -1,22 +1,22 @@ -use alloc::sync::Arc; +use alloc::fmt::Debug; use core::cmp::min; use core::ops::{Add, Sub}; use core::time::Duration; +use basecoin_store::context::ProvableStore; use ibc::core::client::types::Height; use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; -use parking_lot::Mutex; use tendermint_testgen::Validator as TestgenValidator; use typed_builder::TypedBuilder; use crate::hosts::block::{HostBlock, HostType}; -use crate::testapp::ibc::core::types::{MockContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; +use crate::testapp::ibc::core::types::{MockGenericContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; /// Configuration of the `MockContext` type for generating dummy contexts. #[derive(Debug, TypedBuilder)] -#[builder(build_method(into = MockContext))] +#[builder(build_method(into))] pub struct MockContextConfig { #[builder(default = HostType::Mock)] host_type: HostType, @@ -41,7 +41,10 @@ pub struct MockContextConfig { latest_timestamp: Timestamp, } -impl From for MockContext { +impl From for MockGenericContext +where + S: ProvableStore + Debug + Default, +{ fn from(params: MockContextConfig) -> Self { assert_ne!( params.max_history_size, 0, @@ -115,13 +118,13 @@ impl From for MockContext { .collect() }; - MockContext { + MockGenericContext { host_chain_type: params.host_type, host_chain_id: params.host_id.clone(), max_history_size: params.max_history_size, history, block_time: params.block_time, - ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), + ibc_store: MockIbcStore::default(), } } } diff --git a/ibc-testkit/src/fixtures/mod.rs b/ibc-testkit/src/fixtures/mod.rs index af14cadb9..709cb4206 100644 --- a/ibc-testkit/src/fixtures/mod.rs +++ b/ibc-testkit/src/fixtures/mod.rs @@ -12,7 +12,7 @@ pub enum Expect { Failure(Option), } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Fixture { pub ctx: MockContext, pub msg: M, diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index d41331345..c35cd080f 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -1,3 +1,6 @@ +use alloc::fmt::Debug; + +use basecoin_store::context::ProvableStore; use ibc::core::client::types::Height; use ibc::core::handler::types::error::ContextError; use ibc::core::host::types::identifiers::ClientId; @@ -6,7 +9,7 @@ use ibc::core::primitives::prelude::*; use ibc::core::primitives::Signer; use crate::testapp::ibc::clients::AnyClientState; -use crate::testapp::ibc::core::types::MockContext; +use crate::testapp::ibc::core::types::MockGenericContext; /// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to /// relay packets between chains. This trait comprises the dependencies towards a single chain. /// Most of the functions in this represent wrappers over the ABCI interface. @@ -24,7 +27,10 @@ pub trait RelayerContext { fn signer(&self) -> Signer; } -impl RelayerContext for MockContext { +impl RelayerContext for MockGenericContext +where + S: ProvableStore + Debug, +{ fn query_latest_height(&self) -> Result { ValidationContext::host_height(self) } @@ -59,7 +65,7 @@ mod tests { use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use crate::testapp::ibc::core::router::MockRouter; - use crate::testapp::ibc::core::types::MockClientConfig; + use crate::testapp::ibc::core::types::{MockClientConfig, MockContext}; /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` /// context, assuming that the latest header on the source context is `src_header`. @@ -126,30 +132,37 @@ mod tests { let mut ctx_a = MockContextConfig::builder() .host_id(chain_id_a.clone()) .latest_height(chain_a_start_height) - .build() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_on_a_for_b.clone()) - .latest_height(client_on_a_for_b_height) - .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. - .build(), - ); - // dummy; not actually used in client updates - let mut router_a = MockRouter::new_with_transfer(); + .build::(); let mut ctx_b = MockContextConfig::builder() - .host_id(chain_id_b) + .host_id(chain_id_b.clone()) .host_type(HostType::SyntheticTendermint) .latest_height(chain_b_start_height) - .build() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_a) - .client_id(client_on_b_for_a.clone()) - .latest_height(client_on_b_for_a_height) - .build(), - ); + .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A + .build::(); + + ctx_a = ctx_a.with_client_config( + MockClientConfig::builder() + .client_chain_id(chain_id_b) + .client_id(client_on_a_for_b.clone()) + .latest_height(client_on_a_for_b_height) + .latest_timestamp(ctx_b.timestamp_at(client_on_a_for_b_height)) + .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. + .build(), + ); + + ctx_b = ctx_b.with_client_config( + MockClientConfig::builder() + .client_chain_id(chain_id_a) + .client_id(client_on_b_for_a.clone()) + .latest_height(client_on_b_for_a_height) + .latest_timestamp(ctx_a.timestamp_at(client_on_b_for_a_height)) + .build(), + ); + + // dummy; not actually used in client updates + let mut router_a = MockRouter::new_with_transfer(); + // dummy; not actually used in client updates let mut router_b = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index f798960d3..70ed7466b 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -21,9 +21,7 @@ use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use crate::testapp::ibc::clients::mock::header::{MockHeader, MOCK_HEADER_TYPE_URL}; use crate::testapp::ibc::clients::mock::misbehaviour::{Misbehaviour, MOCK_MISBEHAVIOUR_TYPE_URL}; -use crate::testapp::ibc::clients::mock::proto::{ - ClientState as RawMockClientState, Header as RawMockHeader, -}; +use crate::testapp::ibc::clients::mock::proto::ClientState as RawMockClientState; pub const MOCK_CLIENT_STATE_TYPE_URL: &str = "/ibc.mock.ClientState"; pub const MOCK_CLIENT_TYPE: &str = "9999-mock"; @@ -39,14 +37,16 @@ pub fn client_type() -> ClientType { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct MockClientState { pub header: MockHeader, - pub frozen_height: Option, + pub trusting_period: Duration, + pub frozen: bool, } impl MockClientState { pub fn new(header: MockHeader) -> Self { Self { header, - frozen_height: None, + trusting_period: Duration::from_nanos(0), + frozen: false, } } @@ -58,15 +58,22 @@ impl MockClientState { None } - pub fn with_frozen_height(self, frozen_height: Height) -> Self { + pub fn with_trusting_period(self, trusting_period: Duration) -> Self { Self { - frozen_height: Some(frozen_height), + trusting_period, + ..self + } + } + + pub fn with_frozen_height(self, _frozen_height: Height) -> Self { + Self { + frozen: true, ..self } } pub fn is_frozen(&self) -> bool { - self.frozen_height.is_some() + self.frozen } fn expired(&self, _elapsed: Duration) -> bool { @@ -80,17 +87,25 @@ impl TryFrom for MockClientState { type Error = ClientError; fn try_from(raw: RawMockClientState) -> Result { - Ok(Self::new(raw.header.expect("Never fails").try_into()?)) + Ok(Self { + header: raw + .header + .ok_or(ClientError::Other { + description: "header is not present".into(), + })? + .try_into()?, + trusting_period: Duration::from_nanos(raw.trusting_period), + frozen: raw.frozen, + }) } } impl From for RawMockClientState { fn from(value: MockClientState) -> Self { RawMockClientState { - header: Some(RawMockHeader { - height: Some(value.header.height().into()), - timestamp: value.header.timestamp.nanoseconds(), - }), + header: Some(value.header.into()), + trusting_period: value.trusting_period.as_nanos() as _, + frozen: value.frozen, } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs index 8a2e31e58..43db3edc4 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs @@ -6,9 +6,7 @@ use ibc::core::primitives::Timestamp; use ibc::primitives::proto::{Any, Protobuf}; use crate::testapp::ibc::clients::mock::header::MockHeader; -use crate::testapp::ibc::clients::mock::proto::{ - ConsensusState as RawMockConsensusState, Header as RawMockHeader, -}; +use crate::testapp::ibc::clients::mock::proto::ConsensusState as RawMockConsensusState; pub const MOCK_CONSENSUS_STATE_TYPE_URL: &str = "/ibc.mock.ConsensusState"; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -40,7 +38,7 @@ impl TryFrom for MockConsensusState { let raw_header = raw.header.ok_or(ClientError::MissingRawConsensusState)?; Ok(Self { - header: MockHeader::try_from(raw_header)?, + header: raw_header.try_into()?, root: CommitmentRoot::from(vec![0]), }) } @@ -49,10 +47,7 @@ impl TryFrom for MockConsensusState { impl From for RawMockConsensusState { fn from(value: MockConsensusState) -> Self { RawMockConsensusState { - header: Some(RawMockHeader { - height: Some(value.header.height().into()), - timestamp: value.header.timestamp.nanoseconds(), - }), + header: Some(value.header.into()), } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index 072b7bba7..ab4785054 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -45,11 +45,15 @@ impl TryFrom for MockHeader { Ok(MockHeader { height: raw .height - .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ClientError::MissingClientMessage)?, - - timestamp: Timestamp::from_nanoseconds(raw.timestamp) - .map_err(ClientError::InvalidPacketTimestamp)?, + .ok_or(ClientError::Other { + description: "missing height".into(), + })? + .try_into()?, + timestamp: Timestamp::from_nanoseconds(raw.timestamp).map_err(|err| { + ClientError::Other { + description: err.to_string(), + } + })?, }) } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mod.rs b/ibc-testkit/src/testapp/ibc/clients/mod.rs index e5f7ad7be..d897d5338 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mod.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mod.rs @@ -1,5 +1,8 @@ pub mod mock; +use alloc::fmt::Debug; + +use basecoin_store::context::ProvableStore; use derive_more::{From, TryInto}; use ibc::clients::tendermint::client_state::ClientState as TmClientState; use ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; @@ -17,11 +20,11 @@ use crate::testapp::ibc::clients::mock::client_state::{ use crate::testapp::ibc::clients::mock::consensus_state::{ MockConsensusState, MOCK_CONSENSUS_STATE_TYPE_URL, }; -use crate::testapp::ibc::core::types::MockContext; +use crate::testapp::ibc::core::types::MockGenericContext; #[derive(Debug, Clone, From, PartialEq, ClientState)] -#[validation(MockContext)] -#[execution(MockContext)] +#[validation(MockGenericContext)] +#[execution(MockGenericContext)] pub enum AnyClientState { Tendermint(TmClientState), Mock(MockClientState), diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index 227c647af..25728e54b 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -1,6 +1,9 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; +use core::fmt::Debug; +use basecoin_store::context::{ProvableStore, Store}; +use basecoin_store::types::Height as StoreHeight; use ibc::clients::tendermint::context::{ CommonContext as TmCommonContext, ValidationContext as TmValidationContext, }; @@ -9,13 +12,16 @@ use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; use ibc::core::handler::types::error::ContextError; use ibc::core::host::types::identifiers::{ChannelId, ClientId, PortId}; -use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath}; +use ibc::core::host::types::path::{ + ClientConsensusStatePath, ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, Path, +}; use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; +use ibc::primitives::prelude::{format, *}; use crate::testapp::ibc::clients::mock::client_state::MockClientContext; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; -use crate::testapp::ibc::core::types::MockContext; +use crate::testapp::ibc::core::types::MockGenericContext; pub type PortChannelIdMap = BTreeMap>; @@ -30,27 +36,10 @@ pub struct MockClientRecord { pub consensus_states: BTreeMap, } -impl MockClientContext for MockContext { - type ConversionError = &'static str; - type AnyConsensusState = AnyConsensusState; - - fn host_timestamp(&self) -> Result { - ValidationContext::host_timestamp(self) - } - - fn host_height(&self) -> Result { - ValidationContext::host_height(self) - } - - fn consensus_state( - &self, - client_cons_state_path: &ClientConsensusStatePath, - ) -> Result { - ValidationContext::consensus_state(self, client_cons_state_path) - } -} - -impl TmCommonContext for MockContext { +impl MockClientContext for MockGenericContext +where + S: ProvableStore + Debug, +{ type ConversionError = &'static str; type AnyConsensusState = AnyConsensusState; @@ -68,218 +57,290 @@ impl TmCommonContext for MockContext { ) -> Result { ValidationContext::consensus_state(self, client_cons_state_path) } - - fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { - let ibc_store = self.ibc_store.lock(); - let client_record = - ibc_store - .clients - .get(client_id) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?; - - let heights = client_record.consensus_states.keys().cloned().collect(); - - Ok(heights) - } } - -impl TmValidationContext for MockContext { - fn next_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError> { - let ibc_store = self.ibc_store.lock(); - let client_record = - ibc_store - .clients - .get(client_id) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?; - - // Get the consensus state heights and sort them in ascending order. - let mut heights: Vec = client_record.consensus_states.keys().cloned().collect(); - heights.sort(); - - // Search for next state. - for h in heights { - if h > *height { - // unwrap should never happen, as the consensus state for h must exist - return Ok(Some( - client_record - .consensus_states - .get(&h) - .expect("Never fails") - .clone(), - )); - } - } - Ok(None) - } - - fn prev_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError> { - let ibc_store = self.ibc_store.lock(); - let client_record = - ibc_store - .clients - .get(client_id) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?; - - // Get the consensus state heights and sort them in descending order. - let mut heights: Vec = client_record.consensus_states.keys().cloned().collect(); - heights.sort_by(|a, b| b.cmp(a)); - - // Search for previous state. - for h in heights { - if h < *height { - // unwrap should never happen, as the consensus state for h must exist - return Ok(Some( - client_record - .consensus_states - .get(&h) - .expect("Never fails") - .clone(), - )); - } - } - Ok(None) - } -} - -impl ClientValidationContext for MockContext { +impl ClientValidationContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Returns the time and height when the client state for the given + /// [`ClientId`] was updated with a header for the given [`Height`] fn update_meta( &self, client_id: &ClientId, height: &Height, ) -> Result<(Timestamp, Height), ContextError> { - let key = (client_id.clone(), *height); - (|| { - let ibc_store = self.ibc_store.lock(); - let time = ibc_store.client_processed_times.get(&key)?; - let height = ibc_store.client_processed_heights.get(&key)?; - Some((*time, *height)) - })() - .ok_or(ClientError::UpdateMetaDataNotFound { - client_id: key.0, - height: key.1, - }) - .map_err(ContextError::from) + let client_update_time_path = ClientUpdateTimePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + let processed_timestamp = self + .ibc_store + .client_processed_times + .get(StoreHeight::Pending, &client_update_time_path) + .ok_or(ClientError::UpdateMetaDataNotFound { + client_id: client_id.clone(), + height: *height, + })?; + let client_update_height_path = ClientUpdateHeightPath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + let processed_height = self + .ibc_store + .client_processed_heights + .get(StoreHeight::Pending, &client_update_height_path) + .ok_or(ClientError::UpdateMetaDataNotFound { + client_id: client_id.clone(), + height: *height, + })?; + + Ok((processed_timestamp, processed_height)) } } -impl ClientExecutionContext for MockContext { +impl ClientExecutionContext for MockGenericContext +where + S: ProvableStore + Debug, +{ type V = Self; + type AnyClientState = AnyClientState; + type AnyConsensusState = AnyConsensusState; + /// Called upon successful client creation and update fn store_client_state( &mut self, client_state_path: ClientStatePath, client_state: Self::AnyClientState, ) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - let client_id = client_state_path.0; - let client_record = ibc_store - .clients - .entry(client_id) - .or_insert(MockClientRecord { - consensus_states: Default::default(), - client_state: Default::default(), - }); - - client_record.client_state = Some(client_state); + self.ibc_store + .client_state_store + .set(client_state_path.clone(), client_state) + .map_err(|_| ClientError::Other { + description: "Client state store error".to_string(), + })?; Ok(()) } + /// Called upon successful client creation and update fn store_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, consensus_state: Self::AnyConsensusState, ) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - let client_record = ibc_store - .clients - .entry(consensus_state_path.client_id) - .or_insert(MockClientRecord { - consensus_states: Default::default(), - client_state: Default::default(), - }); - - let height = Height::new( - consensus_state_path.revision_number, - consensus_state_path.revision_height, - ) - .expect("Never fails"); - client_record - .consensus_states - .insert(height, consensus_state); - + self.ibc_store + .consensus_state_store + .set(consensus_state_path, consensus_state) + .map_err(|_| ClientError::Other { + description: "Consensus state store error".to_string(), + })?; Ok(()) } - fn delete_consensus_state( + /// Called upon successful client update. Implementations are expected to + /// use this to record the time and height at which this update (or header) + /// was processed. + fn store_update_meta( &mut self, - consensus_state_path: ClientConsensusStatePath, + client_id: ClientId, + height: Height, + host_timestamp: Timestamp, + host_height: Height, ) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - let client_record = ibc_store - .clients - .entry(consensus_state_path.client_id) - .or_insert(MockClientRecord { - consensus_states: Default::default(), - client_state: Default::default(), - }); - - let height = Height::new( - consensus_state_path.revision_number, - consensus_state_path.revision_height, - ) - .expect("Never fails"); - - client_record.consensus_states.remove(&height); - + let client_update_time_path = ClientUpdateTimePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + self.ibc_store + .client_processed_times + .set(client_update_time_path, host_timestamp) + .map_err(|_| ClientError::Other { + description: "store update error".into(), + })?; + let client_update_height_path = ClientUpdateHeightPath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + self.ibc_store + .client_processed_heights + .set(client_update_height_path, host_height) + .map_err(|_| ClientError::Other { + description: "store update error".into(), + })?; Ok(()) } + /// Delete the update metadata associated with the client at the specified + /// height. fn delete_update_meta( &mut self, client_id: ClientId, height: Height, ) -> Result<(), ContextError> { - let key = (client_id.clone(), height); - let mut ibc_store = self.ibc_store.lock(); - ibc_store.client_processed_times.remove(&key); - ibc_store.client_processed_heights.remove(&key); + let client_update_time_path = ClientUpdateTimePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + self.ibc_store + .client_processed_times + .delete(client_update_time_path); + let client_update_height_path = ClientUpdateHeightPath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + self.ibc_store + .client_processed_heights + .delete(client_update_height_path); Ok(()) } - fn store_update_meta( + fn delete_consensus_state( &mut self, - client_id: ClientId, - height: Height, - host_timestamp: Timestamp, - host_height: Height, + consensus_state_path: ClientConsensusStatePath, ) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - ibc_store - .client_processed_times - .insert((client_id.clone(), height), host_timestamp); - ibc_store - .client_processed_heights - .insert((client_id, height), host_height); + self.ibc_store + .consensus_state_store + .delete(consensus_state_path); Ok(()) } } + +impl TmCommonContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + type ConversionError = &'static str; + type AnyConsensusState = AnyConsensusState; + + fn host_timestamp(&self) -> Result { + ValidationContext::host_timestamp(self) + } + + fn host_height(&self) -> Result { + ValidationContext::host_height(self) + } + + fn consensus_state( + &self, + client_cons_state_path: &ClientConsensusStatePath, + ) -> Result { + ValidationContext::consensus_state(self, client_cons_state_path) + } + + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { + let path = format!("clients/{}/consensusStates", client_id) + .try_into() + .map_err(|_| ClientError::Other { + description: "Invalid consensus state path".into(), + })?; + + self.ibc_store + .consensus_state_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { + Some(consensus_path) + } else { + None + } + }) + .map(|consensus_path| { + let height = Height::new( + consensus_path.revision_number, + consensus_path.revision_height, + )?; + Ok(height) + }) + .collect() + } +} + +impl TmValidationContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + fn next_consensus_state( + &self, + client_id: &ClientId, + height: &Height, + ) -> Result, ContextError> { + let path = format!("clients/{client_id}/consensusStates") + .try_into() + .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers + + let keys = self.ibc_store.store.get_keys(&path); + let found_path = keys.into_iter().find_map(|path| { + if let Ok(Path::ClientConsensusState(path)) = path.try_into() { + if height + > &Height::new(path.revision_number, path.revision_height).expect("no error") + { + return Some(path); + } + } + None + }); + + if let Some(path) = found_path { + let consensus_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, &path) + .ok_or(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height: *height, + })?; + + Ok(Some(consensus_state)) + } else { + Ok(None) + } + } + + fn prev_consensus_state( + &self, + client_id: &ClientId, + height: &Height, + ) -> Result, ContextError> { + let path = format!("clients/{client_id}/consensusStates") + .try_into() + .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers + + let keys = self.ibc_store.store.get_keys(&path); + let pos = keys.iter().position(|path| { + if let Ok(Path::ClientConsensusState(path)) = path.clone().try_into() { + height + >= &Height::new(path.revision_number, path.revision_height).expect("no error") + } else { + false + } + }); + + if let Some(pos) = pos { + if pos > 0 { + let prev_path = match keys[pos - 1].clone().try_into() { + Ok(Path::ClientConsensusState(p)) => p, + _ => unreachable!(), // safety - path retrieved from store + }; + let consensus_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, &prev_path) + .ok_or(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height: *height, + })?; + return Ok(Some(consensus_state)); + } + } + Ok(None) + } +} diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 5c561d14e..aaf2df979 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -1,343 +1,282 @@ //! Implementation of a global context mock. Used in testing handlers of all IBC modules. -use core::ops::Add; +use alloc::fmt::Debug; use core::time::Duration; -use ibc::clients::tendermint::client_state::ClientState; -use ibc::core::channel::types::channel::ChannelEnd; +use basecoin_store::context::ProvableStore; +use basecoin_store::types::height::Height as StoreHeight; +use ibc::core::channel::types::channel::{ChannelEnd, IdentifiedChannelEnd}; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; use ibc::core::channel::types::error::{ChannelError, PacketError}; -use ibc::core::channel::types::packet::Receipt; +use ibc::core::channel::types::packet::{PacketState, Receipt}; +use ibc::core::client::context::consensus_state::ConsensusState; use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::connection::types::error::ConnectionError; -use ibc::core::connection::types::ConnectionEnd; +use ibc::core::connection::types::version::Version as ConnectionVersion; +use ibc::core::connection::types::{ConnectionEnd, IdentifiedConnectionEnd}; use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::events::IbcEvent; use ibc::core::host::types::identifiers::{ClientId, ConnectionId, Sequence}; use ibc::core::host::types::path::{ - AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, CommitmentPath, - ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, + AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, + CommitmentPath, ConnectionPath, NextChannelSequencePath, NextClientSequencePath, + NextConnectionSequencePath, Path, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; use ibc::core::host::{ExecutionContext, ValidationContext}; use ibc::core::primitives::prelude::*; use ibc::core::primitives::{Signer, Timestamp}; use ibc::primitives::proto::Any; +use ibc::primitives::ToVec; +use ibc_query::core::context::{ProvableContext, QueryContext}; -use super::types::MockContext; -use crate::testapp::ibc::clients::mock::client_state::MockClientState; +use super::types::MockGenericContext; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::testapp::ibc::utils::blocks_since; -impl ValidationContext for MockContext { +impl ValidationContext for MockGenericContext +where + S: ProvableStore + Debug, +{ type V = Self; type E = Self; type AnyConsensusState = AnyConsensusState; type AnyClientState = AnyClientState; fn client_state(&self, client_id: &ClientId) -> Result { - match self.ibc_store.lock().clients.get(client_id) { - Some(client_record) => { - client_record - .client_state - .clone() - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - }) - } - None => Err(ClientError::ClientStateNotFound { + Ok(self + .ibc_store + .client_state_store + .get(StoreHeight::Pending, &ClientStatePath(client_id.clone())) + .ok_or(ClientError::ClientStateNotFound { client_id: client_id.clone(), - }), - } - .map_err(ContextError::ClientError) + })?) } fn decode_client_state(&self, client_state: Any) -> Result { - if let Ok(client_state) = ClientState::try_from(client_state.clone()) { - client_state.inner().validate().map_err(ClientError::from)?; - Ok(client_state.into()) - } else if let Ok(client_state) = MockClientState::try_from(client_state.clone()) { - Ok(client_state.into()) - } else { - Err(ClientError::UnknownClientStateType { - client_state_type: client_state.type_url, - }) - } - .map_err(ContextError::ClientError) + Ok(AnyClientState::try_from(client_state)?) } fn consensus_state( &self, client_cons_state_path: &ClientConsensusStatePath, - ) -> Result { - let client_id = &client_cons_state_path.client_id; + ) -> Result { let height = Height::new( client_cons_state_path.revision_number, client_cons_state_path.revision_height, - )?; - match self.ibc_store.lock().clients.get(client_id) { - Some(client_record) => match client_record.consensus_states.get(&height) { - Some(consensus_state) => Ok(consensus_state.clone()), - None => Err(ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), - height, - }), - }, - None => Err(ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), + ) + .map_err(|_| ClientError::InvalidHeight)?; + let consensus_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, client_cons_state_path) + .ok_or(ClientError::ConsensusStateNotFound { + client_id: client_cons_state_path.client_id.clone(), height, - }), - } - .map_err(ContextError::ClientError) + })?; + + Ok(consensus_state) } fn host_height(&self) -> Result { - Ok(self.latest_height()) + // TODO(rano): height sync with block and merkle tree + Ok(self.history.last().expect("atleast one block").height()) } fn host_timestamp(&self) -> Result { - Ok(self - .history - .last() - .expect("history cannot be empty") - .timestamp() - .add(self.block_time) - .expect("Never fails")) + let host_height = self.host_height()?; + let host_cons_state = self.host_consensus_state(&host_height)?; + Ok(host_cons_state.timestamp()) } - fn host_consensus_state(&self, height: &Height) -> Result { - match self.host_block(height) { - Some(block_ref) => Ok(block_ref.clone().into()), - None => Err(ClientError::MissingLocalConsensusState { height: *height }), - } - .map_err(ConnectionError::Client) - .map_err(ContextError::ConnectionError) + fn host_consensus_state( + &self, + height: &Height, + ) -> Result { + // TODO(rano): height sync with block and merkle tree + let height_delta = blocks_since(self.host_height().expect("no error"), *height) + .expect("no error") as usize; + + let index = self + .history + .len() + .checked_sub(1 + height_delta) + .ok_or(ClientError::MissingLocalConsensusState { height: *height })?; + + let consensus_state = self.history[index].clone().into(); + Ok(consensus_state) } fn client_counter(&self) -> Result { - Ok(self.ibc_store.lock().client_ids_counter) + Ok(self + .ibc_store + .client_counter + .get(StoreHeight::Pending, &NextClientSequencePath) + .ok_or(ClientError::Other { + description: "client counter not found".into(), + })?) } - fn connection_end(&self, cid: &ConnectionId) -> Result { - match self.ibc_store.lock().connections.get(cid) { - Some(connection_end) => Ok(connection_end.clone()), - None => Err(ConnectionError::ConnectionNotFound { - connection_id: cid.clone(), - }), - } - .map_err(ContextError::ConnectionError) + fn connection_end(&self, conn_id: &ConnectionId) -> Result { + Ok(self + .ibc_store + .connection_end_store + .get(StoreHeight::Pending, &ConnectionPath::new(conn_id)) + .ok_or(ConnectionError::ConnectionNotFound { + connection_id: conn_id.clone(), + })?) } - fn validate_self_client( - &self, - client_state_of_host_on_counterparty: Any, - ) -> Result<(), ContextError> { - let mock_client_state = MockClientState::try_from(client_state_of_host_on_counterparty) - .map_err(|_| ConnectionError::InvalidClientState { - reason: "client must be a mock client".to_string(), - }) - .map_err(ContextError::ConnectionError)?; - - if mock_client_state.is_frozen() { - return Err(ClientError::ClientFrozen { - description: String::new(), - } - .into()); - } - - let self_chain_id = &self.host_chain_id; - let self_revision_number = self_chain_id.revision_number(); - if self_revision_number != mock_client_state.latest_height().revision_number() { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client is not in the same revision as the chain. expected: {}, got: {}", - self_revision_number, - mock_client_state.latest_height().revision_number() - ), - }, - )); - } - - let host_current_height = self.latest_height().increment(); - if mock_client_state.latest_height() >= host_current_height { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client has latest height {} greater than or equal to chain height {}", - mock_client_state.latest_height(), - host_current_height - ), - }, - )); - } - + fn validate_self_client(&self, _counterparty_client_state: Any) -> Result<(), ContextError> { Ok(()) } fn commitment_prefix(&self) -> CommitmentPrefix { - CommitmentPrefix::try_from(b"mock".to_vec()).expect("Never fails") + // this is prefix of ibc store + // using default, as in our mock context, we don't store any other data + CommitmentPrefix::default() } fn connection_counter(&self) -> Result { - Ok(self.ibc_store.lock().connection_ids_counter) + Ok(self + .ibc_store + .conn_counter + .get(StoreHeight::Pending, &NextConnectionSequencePath) + .ok_or(ConnectionError::Other { + description: "connection counter not found".into(), + })?) } - fn channel_end(&self, chan_end_path: &ChannelEndPath) -> Result { - let port_id = &chan_end_path.0; - let channel_id = &chan_end_path.1; + fn get_compatible_versions(&self) -> Vec { + vec![ConnectionVersion::default()] + } - match self + fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { + let channel_end = self .ibc_store - .lock() - .channels - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(channel_end) => Ok(channel_end.clone()), - None => Err(ChannelError::ChannelNotFound { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::ChannelError) + .channel_end_store + .get( + StoreHeight::Pending, + &ChannelEndPath::new(&channel_end_path.0, &channel_end_path.1), + ) + .ok_or(ChannelError::MissingChannel)?; + Ok(channel_end) } fn get_next_sequence_send( &self, seq_send_path: &SeqSendPath, ) -> Result { - let port_id = &seq_send_path.0; - let channel_id = &seq_send_path.1; - - match self + let seq_send = self .ibc_store - .lock() - .next_sequence_send - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextSendSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) + .send_sequence_store + .get( + StoreHeight::Pending, + &SeqSendPath::new(&seq_send_path.0, &seq_send_path.1), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(seq_send) } fn get_next_sequence_recv( &self, seq_recv_path: &SeqRecvPath, ) -> Result { - let port_id = &seq_recv_path.0; - let channel_id = &seq_recv_path.1; - - match self + let seq_recv = self .ibc_store - .lock() - .next_sequence_recv - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextRecvSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) + .recv_sequence_store + .get( + StoreHeight::Pending, + &SeqRecvPath::new(&seq_recv_path.0, &seq_recv_path.1), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(seq_recv) } fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result { - let port_id = &seq_ack_path.0; - let channel_id = &seq_ack_path.1; - - match self + let seq_ack = self .ibc_store - .lock() - .next_sequence_ack - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextAckSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) + .ack_sequence_store + .get( + StoreHeight::Pending, + &SeqAckPath::new(&seq_ack_path.0, &seq_ack_path.1), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(seq_ack) } fn get_packet_commitment( &self, commitment_path: &CommitmentPath, ) -> Result { - let port_id = &commitment_path.port_id; - let channel_id = &commitment_path.channel_id; - let seq = &commitment_path.sequence; - - match self + let commitment = self .ibc_store - .lock() - .packet_commitment - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(commitment) => Ok(commitment.clone()), - None => Err(PacketError::PacketCommitmentNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) + .packet_commitment_store + .get( + StoreHeight::Pending, + &CommitmentPath::new( + &commitment_path.port_id, + &commitment_path.channel_id, + commitment_path.sequence, + ), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(commitment) } fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result { - let port_id = &receipt_path.port_id; - let channel_id = &receipt_path.channel_id; - let seq = &receipt_path.sequence; - - match self + let receipt = self .ibc_store - .lock() - .packet_receipt - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(receipt) => Ok(receipt.clone()), - None => Err(PacketError::PacketReceiptNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) + .packet_receipt_store + .is_path_set( + StoreHeight::Pending, + &ReceiptPath::new( + &receipt_path.port_id, + &receipt_path.channel_id, + receipt_path.sequence, + ), + ) + .then_some(Receipt::Ok) + .ok_or(PacketError::PacketReceiptNotFound { + sequence: receipt_path.sequence, + })?; + Ok(receipt) } fn get_packet_acknowledgement( &self, ack_path: &AckPath, ) -> Result { - let port_id = &ack_path.port_id; - let channel_id = &ack_path.channel_id; - let seq = &ack_path.sequence; - - match self + let ack = self .ibc_store - .lock() - .packet_acknowledgement - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(ack) => Ok(ack.clone()), - None => Err(PacketError::PacketAcknowledgementNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) - } - + .packet_ack_store + .get( + StoreHeight::Pending, + &AckPath::new(&ack_path.port_id, &ack_path.channel_id, ack_path.sequence), + ) + .ok_or(PacketError::PacketAcknowledgementNotFound { + sequence: ack_path.sequence, + })?; + Ok(ack) + } + + /// Returns a counter on the number of channel ids have been created thus far. + /// The value of this counter should increase only via method + /// `ChannelKeeper::increase_channel_counter`. fn channel_counter(&self) -> Result { - Ok(self.ibc_store.lock().channel_ids_counter) + Ok(self + .ibc_store + .channel_counter + .get(StoreHeight::Pending, &NextChannelSequencePath) + .ok_or(ChannelError::Other { + description: "channel counter not found".into(), + })?) } + /// Returns the maximum expected time per block fn max_expected_time_per_block(&self) -> Duration { - self.block_time + Duration::from_secs(8) } fn validate_message_signer(&self, _signer: &Signer) -> Result<(), ContextError> { @@ -349,44 +288,456 @@ impl ValidationContext for MockContext { } } -impl ExecutionContext for MockContext { - fn get_client_execution_context(&mut self) -> &mut Self::E { - self +/// Trait to provide proofs in gRPC service blanket implementations. +impl ProvableContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Returns the proof for the given [`Height`] and [`Path`] + fn get_proof(&self, height: Height, path: &Path) -> Option> { + self.ibc_store + .store + .get_proof(height.revision_height().into(), &path.to_string().into()) + .map(|p| p.to_vec()) + } +} + +/// Trait to complete the gRPC service blanket implementations. +impl QueryContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Returns the list of all client states. + fn client_states(&self) -> Result, ContextError> { + let path = "clients".to_owned().into(); + + self.ibc_store + .client_state_store + .get_keys(&path) + .into_iter() + .filter_map(|path| { + if let Ok(Path::ClientState(client_path)) = path.try_into() { + Some(client_path) + } else { + None + } + }) + .map(|client_state_path| { + let client_state = self + .ibc_store + .client_state_store + .get(StoreHeight::Pending, &client_state_path) + .ok_or_else(|| ClientError::ClientStateNotFound { + client_id: client_state_path.0.clone(), + })?; + Ok((client_state_path.0, client_state)) + }) + .collect() + } + + /// Returns the list of all consensus states of the given client. + fn consensus_states( + &self, + client_id: &ClientId, + ) -> Result, ContextError> { + let path = format!("clients/{}/consensusStates", client_id) + .try_into() + .map_err(|_| ClientError::Other { + description: "Invalid consensus state path".into(), + })?; + + self.ibc_store + .consensus_state_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { + Some(consensus_path) + } else { + None + } + }) + .map(|consensus_path| { + let height = Height::new( + consensus_path.revision_number, + consensus_path.revision_height, + )?; + let client_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, &consensus_path) + .ok_or({ + ClientError::ConsensusStateNotFound { + client_id: consensus_path.client_id, + height, + } + })?; + Ok((height, client_state)) + }) + .collect() + } + + /// Returns the list of heights at which the consensus state of the given client was updated. + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { + let path = format!("clients/{}/consensusStates", client_id) + .try_into() + .map_err(|_| ClientError::Other { + description: "Invalid consensus state path".into(), + })?; + + self.ibc_store + .consensus_state_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { + Some(consensus_path) + } else { + None + } + }) + .map(|consensus_path| { + Ok(Height::new( + consensus_path.revision_number, + consensus_path.revision_height, + )?) + }) + .collect::, _>>() + } + + /// Connections queries all the IBC connections of a chain. + fn connection_ends(&self) -> Result, ContextError> { + let path = "connections".to_owned().into(); + + self.ibc_store + .connection_end_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Connection(connection_path)) = path.try_into() { + Some(connection_path) + } else { + None + } + }) + .map(|connection_path| { + let connection_end = self + .ibc_store + .connection_end_store + .get(StoreHeight::Pending, &connection_path) + .ok_or_else(|| ConnectionError::ConnectionNotFound { + connection_id: connection_path.0.clone(), + })?; + Ok(IdentifiedConnectionEnd { + connection_id: connection_path.0, + connection_end, + }) + }) + .collect() + } + + /// ClientConnections queries all the connection paths associated with a client. + fn client_connection_ends( + &self, + client_id: &ClientId, + ) -> Result, ContextError> { + let client_connection_path = ClientConnectionPath::new(client_id); + + Ok(self + .ibc_store + .connection_ids_store + .get(StoreHeight::Pending, &client_connection_path) + .unwrap_or_default()) + } + + /// Channels queries all the IBC channels of a chain. + fn channel_ends(&self) -> Result, ContextError> { + let path = "channelEnds".to_owned().into(); + + self.ibc_store + .channel_end_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ChannelEnd(channel_path)) = path.try_into() { + Some(channel_path) + } else { + None + } + }) + .map(|channel_path| { + let channel_end = self + .ibc_store + .channel_end_store + .get(StoreHeight::Pending, &channel_path) + .ok_or_else(|| ChannelError::ChannelNotFound { + port_id: channel_path.0.clone(), + channel_id: channel_path.1.clone(), + })?; + Ok(IdentifiedChannelEnd { + port_id: channel_path.0, + channel_id: channel_path.1, + channel_end, + }) + }) + .collect() + } + + /// PacketCommitments returns all the packet commitments associated with a channel. + fn packet_commitments( + &self, + channel_end_path: &ChannelEndPath, + ) -> Result, ContextError> { + let path = format!( + "commitments/ports/{}/channels/{}/sequences", + channel_end_path.0, channel_end_path.1 + ) + .try_into() + .map_err(|_| PacketError::Other { + description: "Invalid commitment path".into(), + })?; + + self.ibc_store + .packet_commitment_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Commitment(commitment_path)) = path.try_into() { + Some(commitment_path) + } else { + None + } + }) + .filter(|commitment_path| { + self.ibc_store + .packet_commitment_store + .get(StoreHeight::Pending, commitment_path) + .is_some() + }) + .map(|commitment_path| { + self.get_packet_commitment(&commitment_path) + .map(|packet| PacketState { + seq: commitment_path.sequence, + port_id: commitment_path.port_id, + chan_id: commitment_path.channel_id, + data: packet.as_ref().into(), + }) + }) + .collect::, _>>() + } + + /// PacketAcknowledgements returns all the packet acknowledgements associated with a channel. + /// Returns all the packet acknowledgements if sequences is empty. + fn packet_acknowledgements( + &self, + channel_end_path: &ChannelEndPath, + sequences: impl ExactSizeIterator, + ) -> Result, ContextError> { + let collected_paths: Vec<_> = if sequences.len() == 0 { + // if sequences is empty, return all the acks + let ack_path_prefix = format!( + "acks/ports/{}/channels/{}/sequences", + channel_end_path.0, channel_end_path.1 + ) + .try_into() + .map_err(|_| PacketError::Other { + description: "Invalid ack path".into(), + })?; + + self.ibc_store + .packet_ack_store + .get_keys(&ack_path_prefix) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Ack(ack_path)) = path.try_into() { + Some(ack_path) + } else { + None + } + }) + .collect() + } else { + sequences + .into_iter() + .map(|seq| AckPath::new(&channel_end_path.0, &channel_end_path.1, seq)) + .collect() + }; + + collected_paths + .into_iter() + .filter(|ack_path| { + self.ibc_store + .packet_ack_store + .get(StoreHeight::Pending, ack_path) + .is_some() + }) + .map(|ack_path| { + self.get_packet_acknowledgement(&ack_path) + .map(|packet| PacketState { + seq: ack_path.sequence, + port_id: ack_path.port_id, + chan_id: ack_path.channel_id, + data: packet.as_ref().into(), + }) + }) + .collect::, _>>() + } + + /// UnreceivedPackets returns all the unreceived IBC packets associated with + /// a channel and sequences. + fn unreceived_packets( + &self, + channel_end_path: &ChannelEndPath, + sequences: impl ExactSizeIterator, + ) -> Result, ContextError> { + // QUESTION. Currently only works for unordered channels; ordered channels + // don't use receipts. However, ibc-go does it this way. Investigate if + // this query only ever makes sense on unordered channels. + + Ok(sequences + .into_iter() + .map(|seq| ReceiptPath::new(&channel_end_path.0, &channel_end_path.1, seq)) + .filter(|receipt_path| { + self.ibc_store + .packet_receipt_store + .get(StoreHeight::Pending, receipt_path) + .is_none() + }) + .map(|receipts_path| receipts_path.sequence) + .collect()) + } + + /// UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a channel and sequences. + /// Returns all the unreceived acks if sequences is empty. + fn unreceived_acks( + &self, + channel_end_path: &ChannelEndPath, + sequences: impl ExactSizeIterator, + ) -> Result, ContextError> { + let collected_paths: Vec<_> = if sequences.len() == 0 { + // if sequences is empty, return all the acks + let commitment_path_prefix = format!( + "commitments/ports/{}/channels/{}/sequences", + channel_end_path.0, channel_end_path.1 + ) + .try_into() + .map_err(|_| PacketError::Other { + description: "Invalid commitment path".into(), + })?; + + self.ibc_store + .packet_commitment_store + .get_keys(&commitment_path_prefix) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Commitment(commitment_path)) = path.try_into() { + Some(commitment_path) + } else { + None + } + }) + .collect() + } else { + sequences + .into_iter() + .map(|seq| CommitmentPath::new(&channel_end_path.0, &channel_end_path.1, seq)) + .collect() + }; + + Ok(collected_paths + .into_iter() + .filter(|commitment_path: &CommitmentPath| -> bool { + self.ibc_store + .packet_commitment_store + .get(StoreHeight::Pending, commitment_path) + .is_some() + }) + .map(|commitment_path| commitment_path.sequence) + .collect()) } +} +impl ExecutionContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Called upon client creation. + /// Increases the counter which keeps track of how many clients have been created. + /// Should never fail. fn increase_client_counter(&mut self) -> Result<(), ContextError> { - self.ibc_store.lock().client_ids_counter += 1; + let current_sequence = self + .ibc_store + .client_counter + .get(StoreHeight::Pending, &NextClientSequencePath) + .ok_or(ClientError::Other { + description: "client counter not found".into(), + })?; + + self.ibc_store + .client_counter + .set(NextClientSequencePath, current_sequence + 1) + .map_err(|e| ClientError::Other { + description: format!("client counter update failed: {e:?}"), + })?; + Ok(()) } + /// Stores the given connection_end at path fn store_connection( &mut self, connection_path: &ConnectionPath, connection_end: ConnectionEnd, ) -> Result<(), ContextError> { - let connection_id = connection_path.0.clone(); self.ibc_store - .lock() - .connections - .insert(connection_id, connection_end); + .connection_end_store + .set(connection_path.clone(), connection_end) + .map_err(|_| ConnectionError::Other { + description: "Connection end store error".to_string(), + })?; Ok(()) } + /// Stores the given connection_id at a path associated with the client_id. fn store_connection_to_client( &mut self, client_connection_path: &ClientConnectionPath, conn_id: ConnectionId, ) -> Result<(), ContextError> { - let client_id = client_connection_path.0.clone(); + let mut conn_ids: Vec = self + .ibc_store + .connection_ids_store + .get(StoreHeight::Pending, client_connection_path) + .unwrap_or_default(); + conn_ids.push(conn_id); self.ibc_store - .lock() - .client_connections - .insert(client_id, conn_id); + .connection_ids_store + .set(client_connection_path.clone(), conn_ids) + .map_err(|_| ConnectionError::Other { + description: "Connection ids store error".to_string(), + })?; Ok(()) } + /// Called upon connection identifier creation (Init or Try process). + /// Increases the counter which keeps track of how many connections have been created. + /// Should never fail. fn increase_connection_counter(&mut self) -> Result<(), ContextError> { - self.ibc_store.lock().connection_ids_counter += 1; + let current_sequence = self + .ibc_store + .conn_counter + .get(StoreHeight::Pending, &NextConnectionSequencePath) + .ok_or(ConnectionError::Other { + description: "connection counter not found".into(), + })?; + + self.ibc_store + .conn_counter + .set(NextConnectionSequencePath, current_sequence + 1) + .map_err(|e| ConnectionError::Other { + description: format!("connection counter update failed: {e:?}"), + })?; + Ok(()) } @@ -396,42 +747,26 @@ impl ExecutionContext for MockContext { commitment: PacketCommitment, ) -> Result<(), ContextError> { self.ibc_store - .lock() - .packet_commitment - .entry(commitment_path.port_id.clone()) - .or_default() - .entry(commitment_path.channel_id.clone()) - .or_default() - .insert(commitment_path.sequence, commitment); + .packet_commitment_store + .set(commitment_path.clone(), commitment) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } - fn delete_packet_commitment( - &mut self, - commitment_path: &CommitmentPath, - ) -> Result<(), ContextError> { - self.ibc_store - .lock() - .packet_commitment - .get_mut(&commitment_path.port_id) - .and_then(|map| map.get_mut(&commitment_path.channel_id)) - .and_then(|map| map.remove(&commitment_path.sequence)); + fn delete_packet_commitment(&mut self, key: &CommitmentPath) -> Result<(), ContextError> { + self.ibc_store.packet_commitment_store.delete(key.clone()); Ok(()) } fn store_packet_receipt( &mut self, - path: &ReceiptPath, - receipt: Receipt, + receipt_path: &ReceiptPath, + _receipt: Receipt, ) -> Result<(), ContextError> { self.ibc_store - .lock() - .packet_receipt - .entry(path.port_id.clone()) - .or_default() - .entry(path.channel_id.clone()) - .or_default() - .insert(path.sequence, receipt); + .packet_receipt_store + .set_path(receipt_path.clone()) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } @@ -440,49 +775,30 @@ impl ExecutionContext for MockContext { ack_path: &AckPath, ack_commitment: AcknowledgementCommitment, ) -> Result<(), ContextError> { - let port_id = ack_path.port_id.clone(); - let channel_id = ack_path.channel_id.clone(); - let seq = ack_path.sequence; - self.ibc_store - .lock() - .packet_acknowledgement - .entry(port_id) - .or_default() - .entry(channel_id) - .or_default() - .insert(seq, ack_commitment); + .packet_ack_store + .set(ack_path.clone(), ack_commitment) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), ContextError> { - let port_id = ack_path.port_id.clone(); - let channel_id = ack_path.channel_id.clone(); - let sequence = ack_path.sequence; - - self.ibc_store - .lock() - .packet_acknowledgement - .get_mut(&port_id) - .and_then(|map| map.get_mut(&channel_id)) - .and_then(|map| map.remove(&sequence)); + self.ibc_store.packet_ack_store.delete(ack_path.clone()); Ok(()) } + /// Stores the given channel_end at a path associated with the port_id and channel_id. fn store_channel( &mut self, channel_end_path: &ChannelEndPath, channel_end: ChannelEnd, ) -> Result<(), ContextError> { - let port_id = channel_end_path.0.clone(); - let channel_id = channel_end_path.1.clone(); - self.ibc_store - .lock() - .channels - .entry(port_id) - .or_default() - .insert(channel_id, channel_end); + .channel_end_store + .set(channel_end_path.clone(), channel_end) + .map_err(|_| ChannelError::Other { + description: "Channel end store error".to_string(), + })?; Ok(()) } @@ -491,15 +807,10 @@ impl ExecutionContext for MockContext { seq_send_path: &SeqSendPath, seq: Sequence, ) -> Result<(), ContextError> { - let port_id = seq_send_path.0.clone(); - let channel_id = seq_send_path.1.clone(); - self.ibc_store - .lock() - .next_sequence_send - .entry(port_id) - .or_default() - .insert(channel_id, seq); + .send_sequence_store + .set(seq_send_path.clone(), seq) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } @@ -508,15 +819,10 @@ impl ExecutionContext for MockContext { seq_recv_path: &SeqRecvPath, seq: Sequence, ) -> Result<(), ContextError> { - let port_id = seq_recv_path.0.clone(); - let channel_id = seq_recv_path.1.clone(); - self.ibc_store - .lock() - .next_sequence_recv - .entry(port_id) - .or_default() - .insert(channel_id, seq); + .recv_sequence_store + .set(seq_recv_path.clone(), seq) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } @@ -525,30 +831,43 @@ impl ExecutionContext for MockContext { seq_ack_path: &SeqAckPath, seq: Sequence, ) -> Result<(), ContextError> { - let port_id = seq_ack_path.0.clone(); - let channel_id = seq_ack_path.1.clone(); - self.ibc_store - .lock() - .next_sequence_ack - .entry(port_id) - .or_default() - .insert(channel_id, seq); + .ack_sequence_store + .set(seq_ack_path.clone(), seq) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } fn increase_channel_counter(&mut self) -> Result<(), ContextError> { - self.ibc_store.lock().channel_ids_counter += 1; + let current_sequence = self + .ibc_store + .channel_counter + .get(StoreHeight::Pending, &NextChannelSequencePath) + .ok_or(ChannelError::Other { + description: "channel counter not found".into(), + })?; + + self.ibc_store + .channel_counter + .set(NextChannelSequencePath, current_sequence + 1) + .map_err(|e| ChannelError::Other { + description: format!("channel counter update failed: {e:?}"), + })?; + Ok(()) } fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError> { - self.ibc_store.lock().events.push(event); + self.ibc_store.events.lock().push(event); Ok(()) } fn log_message(&mut self, message: String) -> Result<(), ContextError> { - self.ibc_store.lock().logs.push(message); + self.ibc_store.logs.lock().push(message); Ok(()) } + + fn get_client_execution_context(&mut self) -> &mut Self::E { + self + } } diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 603d6d00d..d635e8951 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -1,17 +1,20 @@ //! Implementation of a global context mock. Used in testing handlers of all IBC modules. -use alloc::collections::btree_map::BTreeMap; +use alloc::collections::BTreeMap; use alloc::sync::Arc; use core::cmp::min; use core::fmt::Debug; use core::ops::{Add, Sub}; use core::time::Duration; +use basecoin_store::context::ProvableStore; +use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore, SharedStore}; +use basecoin_store::types::{BinStore, JsonStore, ProtobufStore, TypedSet, TypedStore}; use ibc::clients::tendermint::client_state::ClientState as TmClientState; use ibc::clients::tendermint::types::TENDERMINT_CLIENT_TYPE; use ibc::core::channel::types::channel::ChannelEnd; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; -use ibc::core::channel::types::packet::Receipt; +use ibc::core::client::context::ClientExecutionContext; use ibc::core::client::types::Height; use ibc::core::connection::types::ConnectionEnd; use ibc::core::entrypoint::dispatch; @@ -20,15 +23,24 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ ChainId, ChannelId, ClientId, ClientType, ConnectionId, PortId, Sequence, }; -use ibc::core::host::ValidationContext; +use ibc::core::host::types::path::{ + AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, + ClientUpdateHeightPath, ClientUpdateTimePath, CommitmentPath, ConnectionPath, + NextChannelSequencePath, NextClientSequencePath, NextConnectionSequencePath, ReceiptPath, + SeqAckPath, SeqRecvPath, SeqSendPath, +}; +use ibc::core::host::{ExecutionContext, ValidationContext}; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::core::router::router::Router; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::channel::v1::Channel as RawChannelEnd; +use ibc_proto::ibc::core::client::v1::Height as RawHeight; +use ibc_proto::ibc::core::connection::v1::ConnectionEnd as RawConnectionEnd; use parking_lot::Mutex; use tendermint_testgen::Validator as TestgenValidator; use typed_builder::TypedBuilder; -use super::client_ctx::{MockClientRecord, PortChannelIdMap}; use crate::fixtures::clients::tendermint::ClientStateConfig as TmClientStateConfig; use crate::fixtures::core::context::MockContextConfig; use crate::hosts::block::{HostBlock, HostType}; @@ -39,69 +51,124 @@ use crate::testapp::ibc::clients::mock::client_state::{ use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use crate::testapp::ibc::clients::mock::header::MockHeader; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::testapp::ibc::utils::blocks_since; pub const DEFAULT_BLOCK_TIME_SECS: u64 = 3; /// An object that stores all IBC related data. -#[derive(Clone, Debug, Default)] -pub struct MockIbcStore { - /// The set of all clients, indexed by their id. - pub clients: BTreeMap, - - /// Tracks the processed time for clients header updates - pub client_processed_times: BTreeMap<(ClientId, Height), Timestamp>, - - /// Tracks the processed height for the clients - pub client_processed_heights: BTreeMap<(ClientId, Height), Height>, - - /// Counter for the client identifiers, necessary for `increase_client_counter` and the - /// `client_counter` methods. - pub client_ids_counter: u64, - - /// Association between client ids and connection ids. - pub client_connections: BTreeMap, - - /// All the connections in the store. - pub connections: BTreeMap, - - /// Counter for connection identifiers (see `increase_connection_counter`). - pub connection_ids_counter: u64, - - /// Association between connection ids and channel ids. - pub connection_channels: BTreeMap>, - - /// Counter for channel identifiers (see `increase_channel_counter`). - pub channel_ids_counter: u64, - - /// All the channels in the store. TODO Make new key PortId X ChannelId - pub channels: PortChannelIdMap, - - /// Tracks the sequence number for the next packet to be sent. - pub next_sequence_send: PortChannelIdMap, +#[derive(Debug)] +pub struct MockIbcStore +where + S: ProvableStore + Debug, +{ + /// Handle to store instance. + /// The module is guaranteed exclusive access to all paths in the store key-space. + pub store: SharedStore, + /// A typed-store for next client counter sequence + pub client_counter: JsonStore, NextClientSequencePath, u64>, + /// A typed-store for next connection counter sequence + pub conn_counter: JsonStore, NextConnectionSequencePath, u64>, + /// A typed-store for next channel counter sequence + pub channel_counter: JsonStore, NextChannelSequencePath, u64>, + /// Tracks the processed time for client updates + pub client_processed_times: JsonStore, ClientUpdateTimePath, Timestamp>, + /// A typed-store to track the processed height for client updates + pub client_processed_heights: + ProtobufStore, ClientUpdateHeightPath, Height, RawHeight>, + /// A typed-store for AnyClientState + pub client_state_store: ProtobufStore, ClientStatePath, AnyClientState, Any>, + /// A typed-store for AnyConsensusState + pub consensus_state_store: + ProtobufStore, ClientConsensusStatePath, AnyConsensusState, Any>, + /// A typed-store for ConnectionEnd + pub connection_end_store: + ProtobufStore, ConnectionPath, ConnectionEnd, RawConnectionEnd>, + /// A typed-store for ConnectionIds + pub connection_ids_store: JsonStore, ClientConnectionPath, Vec>, + /// A typed-store for ChannelEnd + pub channel_end_store: ProtobufStore, ChannelEndPath, ChannelEnd, RawChannelEnd>, + /// A typed-store for send sequences + pub send_sequence_store: JsonStore, SeqSendPath, Sequence>, + /// A typed-store for receive sequences + pub recv_sequence_store: JsonStore, SeqRecvPath, Sequence>, + /// A typed-store for ack sequences + pub ack_sequence_store: JsonStore, SeqAckPath, Sequence>, + /// A typed-store for packet commitments + pub packet_commitment_store: BinStore, CommitmentPath, PacketCommitment>, + /// A typed-store for packet receipts + pub packet_receipt_store: TypedSet, ReceiptPath>, + /// A typed-store for packet ack + pub packet_ack_store: BinStore, AckPath, AcknowledgementCommitment>, + /// Map of host consensus states + pub consensus_states: Arc>>, + /// IBC Events + pub events: Arc>>, + /// message logs + pub logs: Arc>>, +} - /// Tracks the sequence number for the next packet to be received. - pub next_sequence_recv: PortChannelIdMap, +impl MockIbcStore +where + S: ProvableStore + Debug, +{ + pub fn new(store: S) -> Self { + let shared_store = SharedStore::new(store); - /// Tracks the sequence number for the next packet to be acknowledged. - pub next_sequence_ack: PortChannelIdMap, + let mut client_counter = TypedStore::new(shared_store.clone()); + let mut conn_counter = TypedStore::new(shared_store.clone()); + let mut channel_counter = TypedStore::new(shared_store.clone()); - pub packet_acknowledgement: PortChannelIdMap>, + client_counter + .set(NextClientSequencePath, 0) + .expect("no error"); - /// Constant-size commitments to packets data fields - pub packet_commitment: PortChannelIdMap>, + conn_counter + .set(NextConnectionSequencePath, 0) + .expect("no error"); - /// Used by unordered channel - pub packet_receipt: PortChannelIdMap>, + channel_counter + .set(NextChannelSequencePath, 0) + .expect("no error"); - /// Emitted IBC events in order - pub events: Vec, + Self { + client_counter, + conn_counter, + channel_counter, + client_processed_times: TypedStore::new(shared_store.clone()), + client_processed_heights: TypedStore::new(shared_store.clone()), + consensus_states: Arc::new(Mutex::new(Default::default())), + client_state_store: TypedStore::new(shared_store.clone()), + consensus_state_store: TypedStore::new(shared_store.clone()), + connection_end_store: TypedStore::new(shared_store.clone()), + connection_ids_store: TypedStore::new(shared_store.clone()), + channel_end_store: TypedStore::new(shared_store.clone()), + send_sequence_store: TypedStore::new(shared_store.clone()), + recv_sequence_store: TypedStore::new(shared_store.clone()), + ack_sequence_store: TypedStore::new(shared_store.clone()), + packet_commitment_store: TypedStore::new(shared_store.clone()), + packet_receipt_store: TypedStore::new(shared_store.clone()), + packet_ack_store: TypedStore::new(shared_store.clone()), + events: Arc::new(Mutex::new(Vec::new())), + logs: Arc::new(Mutex::new(Vec::new())), + store: shared_store, + } + } +} - /// Logs of the IBC module - pub logs: Vec, +impl Default for MockIbcStore +where + S: ProvableStore + Debug + Default, +{ + fn default() -> Self { + Self::new(S::default()) + } } /// A context implementing the dependencies necessary for testing any IBC module. #[derive(Debug)] -pub struct MockContext { +pub struct MockGenericContext +where + S: ProvableStore + Debug, +{ /// The type of host chain underlying this mock context. pub host_chain_type: HostType, @@ -119,9 +186,11 @@ pub struct MockContext { pub block_time: Duration, /// An object that stores all IBC related data. - pub ibc_store: Arc>, + pub ibc_store: MockIbcStore, } +pub type MockContext = MockGenericContext>>; + #[derive(Debug, TypedBuilder)] pub struct MockClientConfig { #[builder(default = ChainId::new("mockZ-1").expect("no error"))] @@ -133,8 +202,8 @@ pub struct MockClientConfig { latest_height: Height, #[builder(default)] consensus_state_heights: Vec, - #[builder(default = Timestamp::now())] - latest_timestamp: Timestamp, + #[builder(default, setter(strip_option))] + latest_timestamp: Option, #[builder(default = Duration::from_secs(64000))] trusting_period: Duration, @@ -147,35 +216,21 @@ pub struct MockClientConfig { /// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the /// creation of new domain objects. -impl Default for MockContext { +impl Default for MockGenericContext +where + S: ProvableStore + Debug + Default, +{ fn default() -> Self { MockContextConfig::builder().build() } } -/// A manual clone impl is provided because the tests are oblivious to the fact that the `ibc_store` -/// is a shared ptr. -impl Clone for MockContext { - fn clone(&self) -> Self { - let ibc_store = { - let ibc_store = self.ibc_store.lock().clone(); - Arc::new(Mutex::new(ibc_store)) - }; - - Self { - host_chain_type: self.host_chain_type, - host_chain_id: self.host_chain_id.clone(), - max_history_size: self.max_history_size, - history: self.history.clone(), - block_time: self.block_time, - ibc_store, - } - } -} - /// Implementation of internal interface for use in testing. The methods in this interface should /// _not_ be accessible to any Ics handler. -impl MockContext { +impl MockGenericContext +where + S: ProvableStore + Debug, +{ /// Creates a mock context. Parameter `max_history_size` determines how many blocks will /// the chain maintain in its history, which also determines the pruning window. Parameter /// `latest_height` determines the current height of the chain. This context @@ -189,7 +244,10 @@ impl MockContext { host_type: HostType, max_history_size: u64, latest_height: Height, - ) -> Self { + ) -> Self + where + S: Default, + { assert_ne!( max_history_size, 0, "The chain must have a non-zero max_history_size" @@ -212,7 +270,7 @@ impl MockContext { let block_time = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS); let next_block_timestamp = Timestamp::now().add(block_time).expect("Never fails"); - MockContext { + Self { host_chain_type: host_type, host_chain_id: host_id.clone(), max_history_size, @@ -232,7 +290,7 @@ impl MockContext { }) .collect(), block_time, - ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), + ibc_store: MockIbcStore::default(), } } @@ -249,7 +307,10 @@ impl MockContext { host_type: HostType, validator_history: &[Vec], latest_height: Height, - ) -> Self { + ) -> Self + where + S: Default, + { let max_history_size = validator_history.len() as u64 - 1; assert_ne!( @@ -295,16 +356,20 @@ impl MockContext { }) .collect(); - MockContext { + Self { host_chain_type: host_type, host_chain_id: host_id.clone(), max_history_size, history, block_time, - ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), + ibc_store: MockIbcStore::default(), } } + pub fn chain_revision_number(&self) -> u64 { + self.host_chain_id.revision_number() + } + /// Associates a client record to this context. /// Given a client id and a height, registers a new client in the context and also associates /// to this client a mock client state and a mock consensus state for height `height`. The type @@ -443,20 +508,45 @@ impl MockContext { ) } - pub fn with_client_config(self, client: MockClientConfig) -> Self { + pub fn with_client_state(mut self, client_id: &ClientId, client_state: AnyClientState) -> Self { + let client_state_path = ClientStatePath::new(client_id); + self.store_client_state(client_state_path, client_state) + .expect("error writing to store"); + self + } + + pub fn with_consensus_state( + mut self, + client_id: &ClientId, + height: Height, + consensus_state: AnyConsensusState, + ) -> Self { + let consensus_state_path = ClientConsensusStatePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + self.store_consensus_state(consensus_state_path, consensus_state) + .expect("error writing to store"); + + self + } + + pub fn with_client_config(mut self, client: MockClientConfig) -> Self { let cs_heights = if client.consensus_state_heights.is_empty() { vec![client.latest_height] } else { client.consensus_state_heights }; - fn blocks_since(a: Height, b: Height) -> Option { - (a.revision_number() == b.revision_number() - && a.revision_height() >= b.revision_height()) - .then(|| a.revision_height() - b.revision_height()) - } + let client_latest_timestamp = client + .latest_timestamp + .unwrap_or_else(|| self.latest_timestamp()); - let (client_state, consensus_states) = match client.client_type.as_str() { + let (client_state, consensus_states): ( + AnyClientState, + BTreeMap, + ) = match client.client_type.as_str() { MOCK_CLIENT_TYPE => { let blocks: Vec<_> = cs_heights .into_iter() @@ -466,8 +556,7 @@ impl MockContext { ( cs_height, MockHeader::new(cs_height).with_timestamp( - client - .latest_timestamp + client_latest_timestamp .sub(self.block_time * (n_blocks as u32)) .expect("never fails"), ), @@ -476,7 +565,7 @@ impl MockContext { .collect(); let client_state = MockClientState::new( - MockHeader::new(client.latest_height).with_timestamp(client.latest_timestamp), + MockHeader::new(client.latest_height).with_timestamp(client_latest_timestamp), ); let cs_states = blocks @@ -497,8 +586,7 @@ impl MockContext { HostBlock::generate_tm_block( client.client_chain_id.clone(), cs_height.revision_height(), - client - .latest_timestamp + client_latest_timestamp .sub(self.block_time * (n_blocks as u32)) .expect("never fails"), ), @@ -528,89 +616,73 @@ impl MockContext { _ => panic!("unknown client type"), }; - let client_record = MockClientRecord { - client_state: Some(client_state), - consensus_states, - }; + self = self.with_client_state(&client.client_id, client_state); + + for (height, consensus_state) in consensus_states { + self = self.with_consensus_state(&client.client_id, height, consensus_state); + } - self.ibc_store - .lock() - .clients - .insert(client.client_id.clone(), client_record); self } /// Associates a connection to this context. pub fn with_connection( - self, + mut self, connection_id: ConnectionId, connection_end: ConnectionEnd, ) -> Self { - self.ibc_store - .lock() - .connections - .insert(connection_id, connection_end); + let connection_path = ConnectionPath::new(&connection_id); + self.store_connection(&connection_path, connection_end) + .expect("error writing to store"); self } /// Associates a channel (in an arbitrary state) to this context. pub fn with_channel( - self, + mut self, port_id: PortId, chan_id: ChannelId, channel_end: ChannelEnd, ) -> Self { - let mut channels = self.ibc_store.lock().channels.clone(); - channels - .entry(port_id) - .or_default() - .insert(chan_id, channel_end); - self.ibc_store.lock().channels = channels; + let channel_end_path = ChannelEndPath::new(&port_id, &chan_id); + self.store_channel(&channel_end_path, channel_end) + .expect("error writing to store"); self } pub fn with_send_sequence( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq_number: Sequence, ) -> Self { - let mut next_sequence_send = self.ibc_store.lock().next_sequence_send.clone(); - next_sequence_send - .entry(port_id) - .or_default() - .insert(chan_id, seq_number); - self.ibc_store.lock().next_sequence_send = next_sequence_send; + let seq_send_path = SeqSendPath::new(&port_id, &chan_id); + self.store_next_sequence_send(&seq_send_path, seq_number) + .expect("error writing to store"); self } pub fn with_recv_sequence( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq_number: Sequence, ) -> Self { - let mut next_sequence_recv = self.ibc_store.lock().next_sequence_recv.clone(); - next_sequence_recv - .entry(port_id) - .or_default() - .insert(chan_id, seq_number); - self.ibc_store.lock().next_sequence_recv = next_sequence_recv; + let seq_recv_path = SeqRecvPath::new(&port_id, &chan_id); + self.store_next_sequence_recv(&seq_recv_path, seq_number) + .expect("error writing to store"); self } pub fn with_ack_sequence( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq_number: Sequence, ) -> Self { - let mut next_sequence_ack = self.ibc_store.lock().next_sequence_send.clone(); - next_sequence_ack - .entry(port_id) - .or_default() - .insert(chan_id, seq_number); - self.ibc_store.lock().next_sequence_ack = next_sequence_ack; + let seq_ack_path = SeqAckPath::new(&port_id, &chan_id); + self.store_next_sequence_ack(&seq_ack_path, seq_number) + .expect("error writing to store"); self } @@ -636,20 +708,15 @@ impl MockContext { } pub fn with_packet_commitment( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq: Sequence, data: PacketCommitment, ) -> Self { - let mut packet_commitment = self.ibc_store.lock().packet_commitment.clone(); - packet_commitment - .entry(port_id) - .or_default() - .entry(chan_id) - .or_default() - .insert(seq, data); - self.ibc_store.lock().packet_commitment = packet_commitment; + let commitment_path = CommitmentPath::new(&port_id, &chan_id, seq); + self.store_packet_commitment(&commitment_path, data) + .expect("error writing to store"); self } @@ -734,11 +801,8 @@ impl MockContext { } pub fn latest_client_states(&self, client_id: &ClientId) -> AnyClientState { - self.ibc_store.lock().clients[client_id] - .client_state - .as_ref() - .expect("Never fails") - .clone() + self.client_state(client_id) + .expect("error reading from store") } pub fn latest_consensus_states( @@ -746,35 +810,42 @@ impl MockContext { client_id: &ClientId, height: &Height, ) -> AnyConsensusState { - self.ibc_store.lock().clients[client_id] - .consensus_states - .get(height) - .expect("Never fails") - .clone() + self.consensus_state(&ClientConsensusStatePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + )) + .expect("error reading from store") } pub fn latest_height(&self) -> Height { - self.history - .last() - .expect("history cannot be empty") - .height() + self.host_height().expect("Never fails") } - pub fn ibc_store_share(&self) -> Arc> { - self.ibc_store.clone() + pub fn latest_timestamp(&self) -> Timestamp { + self.host_block(&self.latest_height()) + .expect("Never fails") + .timestamp() + } + + pub fn timestamp_at(&self, height: Height) -> Timestamp { + let n_blocks = blocks_since(self.latest_height(), height).expect("less or equal height"); + self.latest_timestamp() + .sub(self.block_time * (n_blocks as u32)) + .expect("Never fails") } pub fn query_latest_header(&self) -> Option { - let block_ref = self.host_block(&self.host_height().expect("Never fails")); + let block_ref = self.host_block(&self.latest_height()); block_ref.cloned() } pub fn get_events(&self) -> Vec { - self.ibc_store.lock().events.clone() + self.ibc_store.events.lock().clone() } pub fn get_logs(&self) -> Vec { - self.ibc_store.lock().logs.clone() + self.ibc_store.logs.lock().clone() } } diff --git a/ibc-testkit/src/testapp/ibc/mod.rs b/ibc-testkit/src/testapp/ibc/mod.rs index f13a3c645..fb9937e58 100644 --- a/ibc-testkit/src/testapp/ibc/mod.rs +++ b/ibc-testkit/src/testapp/ibc/mod.rs @@ -1,3 +1,4 @@ pub mod applications; pub mod clients; pub mod core; +pub mod utils; diff --git a/ibc-testkit/src/testapp/ibc/utils.rs b/ibc-testkit/src/testapp/ibc/utils.rs new file mode 100644 index 000000000..d5032cae1 --- /dev/null +++ b/ibc-testkit/src/testapp/ibc/utils.rs @@ -0,0 +1,6 @@ +use ibc::core::client::types::Height; + +pub fn blocks_since(a: Height, b: Height) -> Option { + (a.revision_number() == b.revision_number() && a.revision_height() >= b.revision_height()) + .then(|| a.revision_height() - b.revision_height()) +} diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index fc60fcbab..e098194e5 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -135,7 +135,7 @@ fn test_consensus_state_pruning() { .latest_height(client_height) .latest_timestamp(Timestamp::now()) .max_history_size(u64::MAX) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id.clone()) @@ -232,7 +232,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let mut ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -248,7 +248,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { .host_id(chain_id_b) .host_type(HostType::SyntheticTendermint) .latest_height(update_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -288,7 +288,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( // client state initialized with client_height, and // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. @@ -335,7 +335,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { .latest_height(update_height) .max_history_size(ctx_b_val_history.len() as u64 - 1) .validator_set_history(ctx_b_val_history) - .build(); + .build::(); let signer = dummy_account_id(); @@ -380,7 +380,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { let ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( // client state initialized with client_height, and // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. @@ -428,7 +428,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { .latest_height(update_height) .max_history_size(ctx_b_val_history.len() as u64 - 1) .validator_set_history(ctx_b_val_history) - .build(); + .build::(); let signer = dummy_account_id(); @@ -465,7 +465,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let mut ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -485,7 +485,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { .host_id(chain_id_b) .host_type(HostType::SyntheticTendermint) .latest_height(update_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -530,7 +530,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let mut ctx_a = MockContextConfig::builder() .host_id(ctx_a_chain_id) .latest_height(start_height) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(ctx_b_chain_id.clone()) @@ -547,7 +547,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { .host_id(ctx_b_chain_id) .host_type(HostType::SyntheticTendermint) .latest_height(client_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -617,14 +617,9 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { ClientState::from(client_state).into() }; - let mut ibc_store = ctx_a.ibc_store.lock(); - let client_record = ibc_store.clients.get_mut(&client_id).unwrap(); + ctx_a = ctx_a.with_client_state(&client_id, client_state); - client_record - .consensus_states - .insert(client_height, consensus_state); - - client_record.client_state = Some(client_state); + ctx_a = ctx_a.with_consensus_state(&client_id, client_height, consensus_state); } let latest_header_height = block.height(); @@ -663,7 +658,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { let ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(chain_start_height) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_id(client_id.clone()) @@ -678,7 +673,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { .host_id(ChainId::new("mockgaiaB-1").unwrap()) .host_type(HostType::SyntheticTendermint) .latest_height(client_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -825,7 +820,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -842,7 +837,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { .host_id(chain_id_b.clone()) .host_type(HostType::SyntheticTendermint) .latest_height(misbehaviour_height) - .build(); + .build::(); // Get chain-B's header at `misbehaviour_height` let header1: TmHeader = { @@ -887,7 +882,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -957,7 +952,7 @@ fn test_expired_client() { .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -995,7 +990,7 @@ fn test_client_update_max_clock_drift() { .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -1015,7 +1010,7 @@ fn test_client_update_max_clock_drift() { .latest_height(client_height) .latest_timestamp(timestamp) .max_history_size(u64::MAX) - .build(); + .build::(); while ctx_b.host_timestamp().expect("no error") < (ctx_a.host_timestamp().expect("no error") + max_clock_drift).expect("no error") diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index 84d5b1fbd..7b58f98e6 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -34,7 +34,7 @@ fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture Fixture { let ctx_new = MockContextConfig::builder() .host_id(ChainId::new(&format!("mockgaia-{}", latest_height.revision_number())).unwrap()) .latest_height(latest_height) - .build(); + .build::(); let ctx = match ctx { Ctx::New => ctx_new, Ctx::NewWithConnection => ctx_new diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs index 007be597e..c3f978419 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs @@ -128,7 +128,7 @@ fn conn_open_init_no_context() { fn conn_open_init_no_version() { let mut fxt = conn_open_init_fixture(Ctx::WithClient, Msg::NoVersion); conn_open_init_validate(&fxt, Expect::Success); - let expected_version = ValidationContext::get_compatible_versions(&fxt.ctx.clone()); + let expected_version = ValidationContext::get_compatible_versions(&fxt.ctx); conn_open_init_execute(&mut fxt, Expect::Success, expected_version); } #[test] diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs index 5afca6581..9800aa789 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -53,7 +53,7 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture(); let ctx = match ctx_variant { Ctx::Default => MockContext::default(), Ctx::WithClient => ctx_new.with_client_config( diff --git a/ibc-testkit/tests/core/ics04_channel/send_packet.rs b/ibc-testkit/tests/core/ics04_channel/send_packet.rs index 333563ee0..7963eda89 100644 --- a/ibc-testkit/tests/core/ics04_channel/send_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/send_packet.rs @@ -26,8 +26,6 @@ fn send_packet_processing() { want_pass: bool, } - let context = MockContext::default(); - let chan_end_on_a = ChannelEnd::new( State::Open, Order::default(), @@ -84,14 +82,13 @@ fn send_packet_processing() { let tests: Vec = vec![ Test { name: "Processing fails because no channel exists in the context".to_string(), - ctx: context.clone(), + ctx: MockContext::default(), packet: packet.clone(), want_pass: false, }, Test { name: "Good parameters".to_string(), - ctx: context - .clone() + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -109,8 +106,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height same as destination chain height".to_string(), - ctx: context - .clone() + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -128,8 +124,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height one more than destination chain height".to_string(), - ctx: context - .clone() + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -147,7 +142,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout due to timestamp".to_string(), - ctx: context + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -173,7 +168,7 @@ fn send_packet_processing() { "send_packet: test passed but was supposed to fail for test: {}, \nparams {:?} {:?}", test.name, test.packet.clone(), - test.ctx.clone() + test.ctx ); let ibc_events = test.ctx.get_events(); @@ -194,7 +189,7 @@ fn send_packet_processing() { "send_packet: did not pass test: {}, \nparams {:?} {:?} error: {:?}", test.name, test.packet.clone(), - test.ctx.clone(), + test.ctx, e, ); } diff --git a/ibc-testkit/tests/core/ics04_channel/timeout.rs b/ibc-testkit/tests/core/ics04_channel/timeout.rs index 55d964b68..aeb3d2c0a 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -43,9 +43,10 @@ fn fixture() -> Fixture { let router = MockRouter::new_with_transfer(); + // in case of timeout, timeout timestamp should be less than host's timestamp + let timeout_timestamp = ctx.latest_timestamp().nanoseconds() - 1; let msg_proof_height = 2; let msg_timeout_height = 5; - let timeout_timestamp = Timestamp::now().nanoseconds(); let msg = MsgTimeout::try_from(dummy_raw_msg_timeout( msg_proof_height, diff --git a/ibc-testkit/tests/core/router.rs b/ibc-testkit/tests/core/router.rs index 70756530f..fd6cd00e1 100644 --- a/ibc-testkit/tests/core/router.rs +++ b/ibc-testkit/tests/core/router.rs @@ -1,3 +1,5 @@ +use std::ops::Add; + use ibc::apps::transfer::handler::send_transfer; use ibc::apps::transfer::types::error::TokenTransferError; use ibc::apps::transfer::types::msgs::transfer::MsgTransfer; @@ -33,6 +35,7 @@ use ibc_testkit::fixtures::core::connection::{ dummy_msg_conn_open_ack, dummy_msg_conn_open_init, dummy_msg_conn_open_init_with_client_id, dummy_msg_conn_open_try, msg_conn_open_try_with_client_id, }; +use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; @@ -87,14 +90,23 @@ fn routing_module_and_keepers() { let upgrade_client_height_second = Height::new(1, 1).unwrap(); // We reuse this same context across all tests. Nothing in particular needs parametrizing. - let mut ctx = MockContext::default(); + let mut ctx = MockContextConfig::builder() + // a future timestamp, so that submitted packets are considered from past + // not more than 5 secs, as later dummy_raw_msg_timeout_on_close(*, 5) is used + .latest_timestamp( + Timestamp::now() + .add(core::time::Duration::from_secs(4)) + .unwrap(), + ) + .build::(); let mut router = MockRouter::new_with_transfer(); + let header = MockHeader::new(start_client_height).with_current_timestamp(); + let create_client_msg = MsgCreateClient::new( - MockClientState::new(MockHeader::new(start_client_height).with_current_timestamp()).into(), - MockConsensusState::new(MockHeader::new(start_client_height).with_current_timestamp()) - .into(), + MockClientState::new(header).into(), + MockConsensusState::new(header).into(), default_signer.clone(), ); From 74203fb30d9c241e5e2a12896ec998f0357ee4f2 Mon Sep 17 00:00:00 2001 From: Rano | Ranadeep Date: Fri, 8 Mar 2024 05:22:37 +0100 Subject: [PATCH 02/40] imp(ibc-testkit): generalize `Host` for `MockContext` (#1107) * fix semantic conflict * happy clippy * fix next_consensus_state impl * update prev_consensus_state impl * use self.block_time over const * use constant timestamp over dynamic now * add new host impls * changes to ibc-testkit * rm deprecated methods * refactor tests * public fields for tm block params * forged client header update test * add todo comment * minor refactor * refactor tests * builder type for light client state * minor refactor * refactor tests * code opt * rename host structs * renamings * refactor tendermint host * mv year_2023 to utils * refactor the new tests * test malicious adjacent header update * fix tests * add changelog * rm unused chain_revision_number * add doc string for year_2023 * rename to query_latest_block --- .../improvements/1044-introduce-host-trait.md | 2 + ibc-testkit/src/fixtures/core/context.rs | 38 +- ibc-testkit/src/fixtures/mod.rs | 3 +- ibc-testkit/src/hosts/block.rs | 260 ------ ibc-testkit/src/hosts/mock.rs | 76 ++ ibc-testkit/src/hosts/mod.rs | 86 +- ibc-testkit/src/hosts/tendermint.rs | 218 +++++ ibc-testkit/src/lib.rs | 1 + ibc-testkit/src/relayer/context.rs | 55 +- ibc-testkit/src/testapp/ibc/clients/mod.rs | 5 +- .../src/testapp/ibc/core/client_ctx.rs | 88 +- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 23 +- ibc-testkit/src/testapp/ibc/core/types.rs | 745 +++++----------- ibc-testkit/src/utils/mod.rs | 19 + .../tests/core/ics02_client/create_client.rs | 7 +- .../tests/core/ics02_client/update_client.rs | 844 ++++++++++++------ .../tests/core/ics02_client/upgrade_client.rs | 20 +- .../core/ics03_connection/conn_open_ack.rs | 33 +- .../ics03_connection/conn_open_confirm.rs | 21 +- .../core/ics03_connection/conn_open_init.rs | 13 +- .../core/ics03_connection/conn_open_try.rs | 17 +- .../core/ics04_channel/acknowledgement.rs | 28 +- .../core/ics04_channel/chan_close_confirm.rs | 23 +- .../core/ics04_channel/chan_close_init.rs | 23 +- .../tests/core/ics04_channel/chan_open_ack.rs | 47 +- .../core/ics04_channel/chan_open_confirm.rs | 39 +- .../core/ics04_channel/chan_open_init.rs | 17 +- .../tests/core/ics04_channel/chan_open_try.rs | 23 +- .../tests/core/ics04_channel/recv_packet.rs | 28 +- .../tests/core/ics04_channel/send_packet.rs | 43 +- .../tests/core/ics04_channel/timeout.rs | 40 +- .../core/ics04_channel/timeout_on_close.rs | 12 +- ibc-testkit/tests/core/router.rs | 5 +- 33 files changed, 1513 insertions(+), 1389 deletions(-) create mode 100644 .changelog/unreleased/improvements/1044-introduce-host-trait.md delete mode 100644 ibc-testkit/src/hosts/block.rs create mode 100644 ibc-testkit/src/hosts/mock.rs create mode 100644 ibc-testkit/src/hosts/tendermint.rs create mode 100644 ibc-testkit/src/utils/mod.rs diff --git a/.changelog/unreleased/improvements/1044-introduce-host-trait.md b/.changelog/unreleased/improvements/1044-introduce-host-trait.md new file mode 100644 index 000000000..d2d1b2bfa --- /dev/null +++ b/.changelog/unreleased/improvements/1044-introduce-host-trait.md @@ -0,0 +1,2 @@ +- [ibc-testkit] Refactor `HostBlock` enum to `Host` trait + ([\#1044](https://github.com/cosmos/ibc-rs/issues/1044)) diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index f6522ce2b..2a047eba4 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -8,19 +8,19 @@ use ibc::core::client::types::Height; use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; -use tendermint_testgen::Validator as TestgenValidator; use typed_builder::TypedBuilder; -use crate::hosts::block::{HostBlock, HostType}; +use crate::hosts::TestHost; use crate::testapp::ibc::core::types::{MockGenericContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; +use crate::utils::year_2023; /// Configuration of the `MockContext` type for generating dummy contexts. #[derive(Debug, TypedBuilder)] #[builder(build_method(into))] -pub struct MockContextConfig { - #[builder(default = HostType::Mock)] - host_type: HostType, - +pub struct MockContextConfig +where + H: TestHost, +{ #[builder(default = ChainId::new("mockgaia-0").expect("Never fails"))] host_id: ChainId, @@ -32,20 +32,21 @@ pub struct MockContextConfig { max_history_size: u64, #[builder(default, setter(strip_option))] - validator_set_history: Option>>, + block_params_history: Option>, #[builder(default = Height::new(0, 5).expect("Never fails"))] latest_height: Height, - #[builder(default = Timestamp::now())] + #[builder(default = year_2023())] latest_timestamp: Timestamp, } -impl From for MockGenericContext +impl From> for MockGenericContext where S: ProvableStore + Debug + Default, + H: TestHost, { - fn from(params: MockContextConfig) -> Self { + fn from(params: MockContextConfig) -> Self { assert_ne!( params.max_history_size, 0, "The chain must have a non-zero max_history_size" @@ -74,15 +75,15 @@ where .add(params.block_time) .expect("Never fails"); - let history = if let Some(validator_set_history) = params.validator_set_history { + let host = H::with_chain_id(params.host_id); + + let history = if let Some(validator_set_history) = params.block_params_history { (0..n) .rev() .map(|i| { // generate blocks with timestamps -> N, N - BT, N - 2BT, ... // where N = now(), BT = block_time - HostBlock::generate_block_with_validators( - params.host_id.clone(), - params.host_type, + host.generate_block( params .latest_height .sub(i) @@ -92,7 +93,6 @@ where .sub(params.block_time * ((i + 1) as u32)) .expect("Never fails"), &validator_set_history[(n - i) as usize - 1], - &validator_set_history[(n - i) as usize], ) }) .collect() @@ -102,9 +102,7 @@ where .map(|i| { // generate blocks with timestamps -> N, N - BT, N - 2BT, ... // where N = now(), BT = block_time - HostBlock::generate_block( - params.host_id.clone(), - params.host_type, + host.generate_block( params .latest_height .sub(i) @@ -113,14 +111,14 @@ where next_block_timestamp .sub(params.block_time * ((i + 1) as u32)) .expect("Never fails"), + &H::BlockParams::default(), ) }) .collect() }; MockGenericContext { - host_chain_type: params.host_type, - host_chain_id: params.host_id.clone(), + host, max_history_size: params.max_history_size, history, block_time: params.block_time, diff --git a/ibc-testkit/src/fixtures/mod.rs b/ibc-testkit/src/fixtures/mod.rs index 709cb4206..686274bd6 100644 --- a/ibc-testkit/src/fixtures/mod.rs +++ b/ibc-testkit/src/fixtures/mod.rs @@ -6,6 +6,7 @@ use alloc::fmt::Debug; use ibc::core::handler::types::error::ContextError; use ibc::core::primitives::prelude::*; +use crate::hosts::MockHost; use crate::testapp::ibc::core::types::MockContext; pub enum Expect { Success, @@ -14,7 +15,7 @@ pub enum Expect { #[derive(Debug)] pub struct Fixture { - pub ctx: MockContext, + pub ctx: MockContext, pub msg: M, } diff --git a/ibc-testkit/src/hosts/block.rs b/ibc-testkit/src/hosts/block.rs deleted file mode 100644 index 526ca1784..000000000 --- a/ibc-testkit/src/hosts/block.rs +++ /dev/null @@ -1,260 +0,0 @@ -//! Host chain types and methods, used by context mock. - -use core::str::FromStr; - -use ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; -use ibc::clients::tendermint::types::proto::v1::Header as RawHeader; -use ibc::clients::tendermint::types::{Header, TENDERMINT_HEADER_TYPE_URL}; -use ibc::core::client::types::error::ClientError; -use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::ChainId; -use ibc::core::primitives::prelude::*; -use ibc::core::primitives::Timestamp; -use ibc::primitives::proto::{Any, Protobuf}; -use ibc::primitives::ToVec; -use tendermint::block::Header as TmHeader; -use tendermint::validator::Set as ValidatorSet; -use tendermint_testgen::light_block::TmLightBlock; -use tendermint_testgen::{ - Generator, Header as TestgenHeader, LightBlock as TestgenLightBlock, - Validator as TestgenValidator, -}; - -use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; -use crate::testapp::ibc::clients::mock::header::MockHeader; -use crate::testapp::ibc::clients::AnyConsensusState; - -/// Defines the different types of host chains that a mock context can emulate. -/// The variants are as follows: -/// - `Mock` defines that the context history consists of `MockHeader` blocks. -/// - `SyntheticTendermint`: the context has synthetically-generated Tendermint (light) blocks. -/// See also the `HostBlock` enum to get more insights into the underlying block type. -#[derive(Clone, Debug, Copy)] -pub enum HostType { - Mock, - SyntheticTendermint, -} - -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SyntheticTmBlock { - pub trusted_height: Height, - pub trusted_next_validators: ValidatorSet, - pub light_block: TmLightBlock, -} - -impl SyntheticTmBlock { - pub fn header(&self) -> &TmHeader { - &self.light_block.signed_header.header - } -} - -impl From for Header { - fn from(light_block: SyntheticTmBlock) -> Self { - let SyntheticTmBlock { - trusted_height, - trusted_next_validators, - light_block, - } = light_block; - Self { - signed_header: light_block.signed_header, - validator_set: light_block.validators, - trusted_height, - trusted_next_validator_set: trusted_next_validators, - } - } -} - -/// Depending on `HostType` (the type of host chain underlying a context mock), this enum defines -/// the type of blocks composing the history of the host chain. -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum HostBlock { - Mock(Box), - SyntheticTendermint(Box), -} - -impl HostBlock { - /// Returns the height of a block. - pub fn height(&self) -> Height { - match self { - HostBlock::Mock(header) => header.height(), - HostBlock::SyntheticTendermint(light_block) => Height::new( - ChainId::from_str(light_block.header().chain_id.as_str()) - .expect("Never fails") - .revision_number(), - light_block.header().height.value(), - ) - .expect("Never fails"), - } - } - - pub fn set_trusted_height(&mut self, height: Height) { - match self { - HostBlock::Mock(_) => {} - HostBlock::SyntheticTendermint(light_block) => light_block.trusted_height = height, - } - } - - pub fn set_trusted_next_validators_set(&mut self, trusted_next_validators: ValidatorSet) { - match self { - HostBlock::Mock(_) => {} - HostBlock::SyntheticTendermint(light_block) => { - light_block.trusted_next_validators = trusted_next_validators - } - } - } - - /// Returns the timestamp of a block. - pub fn timestamp(&self) -> Timestamp { - match self { - HostBlock::Mock(header) => header.timestamp, - HostBlock::SyntheticTendermint(light_block) => light_block.header().time.into(), - } - } - - /// Generates a new block at `height` for the given chain identifier and chain type. - pub fn generate_block( - chain_id: ChainId, - chain_type: HostType, - height: u64, - timestamp: Timestamp, - ) -> HostBlock { - match chain_type { - HostType::Mock => HostBlock::Mock(Box::new(MockHeader { - height: Height::new(chain_id.revision_number(), height).expect("Never fails"), - timestamp, - })), - HostType::SyntheticTendermint => HostBlock::SyntheticTendermint(Box::new( - Self::generate_tm_block(chain_id, height, timestamp), - )), - } - } - - /// Generates a new block at `height` for the given chain identifier, chain type and validator sets. - pub fn generate_block_with_validators( - chain_id: ChainId, - chain_type: HostType, - height: u64, - timestamp: Timestamp, - validators: &[TestgenValidator], - next_validators: &[TestgenValidator], - ) -> HostBlock { - match chain_type { - HostType::Mock => HostBlock::Mock(Box::new(MockHeader { - height: Height::new(chain_id.revision_number(), height).expect("Never fails"), - timestamp, - })), - HostType::SyntheticTendermint => { - let light_block = TestgenLightBlock::new_default_with_header( - TestgenHeader::new(validators) - .height(height) - .chain_id(chain_id.as_str()) - .next_validators(next_validators) - .time(timestamp.into_tm_time().expect("Never fails")), - ) - .validators(validators) - .next_validators(next_validators) - .generate() - .expect("Never fails"); - - HostBlock::SyntheticTendermint(Box::new(SyntheticTmBlock { - trusted_height: Height::new(chain_id.revision_number(), 1) - .expect("Never fails"), - trusted_next_validators: light_block.next_validators.clone(), - light_block, - })) - } - } - } - - pub fn generate_tm_block( - chain_id: ChainId, - height: u64, - timestamp: Timestamp, - ) -> SyntheticTmBlock { - let validators = [ - TestgenValidator::new("1").voting_power(50), - TestgenValidator::new("2").voting_power(50), - ]; - - let header = TestgenHeader::new(&validators) - .height(height) - .chain_id(chain_id.as_str()) - .next_validators(&validators) - .time(timestamp.into_tm_time().expect("Never fails")); - - let light_block = TestgenLightBlock::new_default_with_header(header) - .generate() - .expect("Never fails"); - - SyntheticTmBlock { - trusted_height: Height::new(chain_id.revision_number(), 1).expect("Never fails"), - trusted_next_validators: light_block.next_validators.clone(), - light_block, - } - } - - pub fn try_into_tm_block(self) -> Option { - match self { - HostBlock::Mock(_) => None, - HostBlock::SyntheticTendermint(tm_block) => Some(*tm_block), - } - } -} - -impl From for AnyConsensusState { - fn from(light_block: SyntheticTmBlock) -> Self { - let cs = TmConsensusState::from(light_block.header().clone()); - cs.into() - } -} - -impl From for AnyConsensusState { - fn from(any_block: HostBlock) -> Self { - match any_block { - HostBlock::Mock(mock_header) => MockConsensusState::new(*mock_header).into(), - HostBlock::SyntheticTendermint(light_block) => { - TmConsensusState::from(light_block.header().clone()).into() - } - } - } -} - -impl Protobuf for HostBlock {} - -impl TryFrom for HostBlock { - type Error = ClientError; - - fn try_from(_raw: Any) -> Result { - todo!() - } -} - -impl From for Any { - fn from(value: HostBlock) -> Self { - fn encode_light_block(light_block: SyntheticTmBlock) -> Vec { - let SyntheticTmBlock { - trusted_height, - trusted_next_validators, - light_block, - } = light_block; - - RawHeader { - signed_header: Some(light_block.signed_header.into()), - validator_set: Some(light_block.validators.into()), - trusted_height: Some(trusted_height.into()), - trusted_validators: Some(trusted_next_validators.into()), - } - .to_vec() - } - - match value { - HostBlock::Mock(mock_header) => (*mock_header).into(), - HostBlock::SyntheticTendermint(light_block) => Self { - type_url: TENDERMINT_HEADER_TYPE_URL.to_string(), - value: encode_light_block(*light_block), - }, - } - } -} diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs new file mode 100644 index 000000000..6fdec6309 --- /dev/null +++ b/ibc-testkit/src/hosts/mock.rs @@ -0,0 +1,76 @@ +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::ChainId; +use ibc::core::primitives::Timestamp; + +use super::{TestBlock, TestHeader, TestHost}; +use crate::testapp::ibc::clients::mock::client_state::MockClientState; +use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; +use crate::testapp::ibc::clients::mock::header::MockHeader; + +#[derive(Debug)] +pub struct Host(ChainId); + +impl TestHost for Host { + type Block = MockHeader; + type BlockParams = (); + type LightClientParams = (); + type ClientState = MockClientState; + + fn with_chain_id(chain_id: ChainId) -> Self { + Self(chain_id) + } + + fn chain_id(&self) -> &ChainId { + &self.0 + } + + fn generate_block( + &self, + height: u64, + timestamp: Timestamp, + _: &Self::BlockParams, + ) -> Self::Block { + MockHeader { + height: Height::new(self.chain_id().revision_number(), height).expect("Never fails"), + timestamp, + } + } + + fn generate_client_state( + &self, + latest_block: &Self::Block, + _: &Self::LightClientParams, + ) -> Self::ClientState { + MockClientState::new(*latest_block) + } +} + +impl TestBlock for MockHeader { + type Header = MockHeader; + + fn height(&self) -> Height { + self.height + } + + fn timestamp(&self) -> Timestamp { + self.timestamp + } +} + +impl From for MockConsensusState { + fn from(block: MockHeader) -> Self { + MockConsensusState::new(block) + } +} + +impl TestHeader for MockHeader { + type ConsensusState = MockConsensusState; + + fn height(&self) -> Height { + self.height + } + + fn timestamp(&self) -> Timestamp { + self.timestamp + } +} diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index a863eaad2..a683ad0c6 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -1 +1,85 @@ -pub mod block; +use core::fmt::Debug; + +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::ChainId; +use ibc::core::primitives::prelude::*; +use ibc::core::primitives::Timestamp; +use ibc::primitives::proto::Any; + +use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; + +pub mod mock; +pub mod tendermint; + +pub use mock::Host as MockHost; +pub use tendermint::Host as TendermintHost; + +/// TestHost is a trait that defines the interface for a host blockchain. +pub trait TestHost: Debug { + /// The type of block produced by the host. + type Block: TestBlock; + + /// The type of client state produced by the host. + type ClientState: Into; + + /// The type of block parameters to produce a block + type BlockParams: Debug + Default; + + /// The type of light client parameters to produce a light client state + type LightClientParams: Debug + Default; + + /// Create a new host with the given chain identifier. + fn with_chain_id(chain_id: ChainId) -> Self; + + /// The chain identifier of the host. + fn chain_id(&self) -> &ChainId; + + /// Generate a block at the given height and timestamp, using the provided parameters. + fn generate_block( + &self, + height: u64, + timestamp: Timestamp, + params: &Self::BlockParams, + ) -> Self::Block; + + /// Generate a client state using the block at the given height and the provided parameters. + fn generate_client_state( + &self, + latest_block: &Self::Block, + params: &Self::LightClientParams, + ) -> Self::ClientState; +} + +/// TestBlock is a trait that defines the interface for a block produced by a host blockchain. +pub trait TestBlock: Clone + Debug { + /// The type of header can be extracted from the block. + type Header: TestHeader + From; + + /// The height of the block. + fn height(&self) -> Height; + + /// The timestamp of the block. + fn timestamp(&self) -> Timestamp; + + /// Extract the header from the block. + fn into_header(self) -> Self::Header { + self.into() + } +} + +/// TestHeader is a trait that defines the interface for a header produced by a host blockchain. +pub trait TestHeader: Clone + Debug + Into { + /// The type of consensus state can be extracted from the header. + type ConsensusState: Into + From; + + /// The height of the block, as recorded in the header. + fn height(&self) -> Height; + + /// The timestamp of the block, as recorded in the header. + fn timestamp(&self) -> Timestamp; + + /// Extract the consensus state from the header. + fn into_consensus_state(self) -> Self::ConsensusState { + Self::ConsensusState::from(self) + } +} diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs new file mode 100644 index 000000000..67cccc11c --- /dev/null +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -0,0 +1,218 @@ +use core::str::FromStr; + +use ibc::clients::tendermint::client_state::ClientState; +use ibc::clients::tendermint::consensus_state::ConsensusState; +use ibc::clients::tendermint::types::proto::v1::Header as RawHeader; +use ibc::clients::tendermint::types::{Header, TENDERMINT_HEADER_TYPE_URL}; +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::ChainId; +use ibc::core::primitives::prelude::*; +use ibc::core::primitives::Timestamp; +use ibc::primitives::proto::Any; +use ibc::primitives::ToVec; +use tendermint::block::Header as TmHeader; +use tendermint::validator::Set as ValidatorSet; +use tendermint_testgen::light_block::TmLightBlock; +use tendermint_testgen::{ + Generator, Header as TestgenHeader, LightBlock as TestgenLightBlock, + Validator as TestgenValidator, +}; + +use super::{TestBlock, TestHeader, TestHost}; +use crate::fixtures::clients::tendermint::ClientStateConfig; +use crate::testapp::ibc::core::types::MockClientConfig; + +#[derive(Debug)] +pub struct Host(ChainId); + +impl TestHost for Host { + type Block = TendermintBlock; + type BlockParams = BlockParams; + type LightClientParams = MockClientConfig; + type ClientState = ClientState; + + fn with_chain_id(chain_id: ChainId) -> Self { + Self(chain_id) + } + + fn chain_id(&self) -> &ChainId { + &self.0 + } + + fn generate_block( + &self, + height: u64, + timestamp: Timestamp, + params: &Self::BlockParams, + ) -> Self::Block { + TendermintBlock( + TestgenLightBlock::new_default_with_header( + TestgenHeader::new(¶ms.validators) + .height(height) + .chain_id(self.chain_id().as_str()) + .next_validators(¶ms.next_validators) + .time(timestamp.into_tm_time().expect("Never fails")), + ) + .validators(¶ms.validators) + .next_validators(¶ms.next_validators) + .generate() + .expect("Never fails"), + ) + } + + fn generate_client_state( + &self, + latest_block: &Self::Block, + params: &Self::LightClientParams, + ) -> Self::ClientState { + let client_state: ClientState = ClientStateConfig::builder() + .chain_id(self.chain_id().clone()) + .latest_height(latest_block.height()) + .trusting_period(params.trusting_period) + .max_clock_drift(params.max_clock_drift) + .unbonding_period(params.unbonding_period) + .build() + .try_into() + .expect("never fails"); + + client_state.inner().validate().expect("never fails"); + + client_state + } +} + +#[derive(Debug, Clone)] +pub struct TendermintBlock(TmLightBlock); + +impl TendermintBlock { + pub fn inner(&self) -> &TmLightBlock { + &self.0 + } +} + +impl TestBlock for TendermintBlock { + type Header = TendermintHeader; + fn height(&self) -> Height { + Height::new( + ChainId::from_str(self.0.signed_header.header.chain_id.as_str()) + .expect("Never fails") + .revision_number(), + self.0.signed_header.header.height.value(), + ) + .expect("Never fails") + } + + fn timestamp(&self) -> Timestamp { + self.0.signed_header.header.time.into() + } +} + +#[derive(Debug)] +pub struct BlockParams { + pub validators: Vec, + pub next_validators: Vec, +} + +impl BlockParams { + pub fn from_validator_history(validator_history: Vec>) -> Vec { + validator_history + .windows(2) + .map(|vals| Self { + validators: vals[0].clone(), + next_validators: vals[1].clone(), + }) + .collect() + } +} + +impl Default for BlockParams { + fn default() -> Self { + let validators = vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ]; + + Self { + validators: validators.clone(), + next_validators: validators, + } + } +} + +#[derive(Debug, Clone)] +pub struct TendermintHeader(Header); + +impl TendermintHeader { + pub fn set_trusted_height(&mut self, trusted_height: Height) { + self.0.trusted_height = trusted_height + } + + pub fn set_trusted_next_validators_set(&mut self, trusted_next_validator_set: ValidatorSet) { + self.0.trusted_next_validator_set = trusted_next_validator_set + } + + pub fn header(&self) -> &TmHeader { + &self.0.signed_header.header + } +} + +impl TestHeader for TendermintHeader { + type ConsensusState = ConsensusState; + + fn height(&self) -> Height { + Height::new( + ChainId::from_str(self.0.signed_header.header.chain_id.as_str()) + .expect("Never fails") + .revision_number(), + self.0.signed_header.header.height.value(), + ) + .expect("Never fails") + } + + fn timestamp(&self) -> Timestamp { + self.0.signed_header.header.time.into() + } +} + +impl From for Header { + fn from(header: TendermintHeader) -> Self { + header.0 + } +} + +impl From for ConsensusState { + fn from(header: TendermintHeader) -> Self { + ConsensusState::from(header.0.signed_header.header) + } +} + +impl From for TendermintHeader { + fn from(block: TendermintBlock) -> Self { + let trusted_height = block.height(); + + let TmLightBlock { + signed_header, + validators: validator_set, + .. + } = block.0; + + let trusted_next_validator_set = validator_set.clone(); + + // by default trust the current height and validators + Self(Header { + signed_header, + validator_set, + trusted_height, + trusted_next_validator_set, + }) + } +} + +impl From for Any { + fn from(value: TendermintHeader) -> Self { + Self { + type_url: TENDERMINT_HEADER_TYPE_URL.to_string(), + value: RawHeader::from(value.0).to_vec(), + } + } +} diff --git a/ibc-testkit/src/lib.rs b/ibc-testkit/src/lib.rs index b2f737471..bfc4ac35d 100644 --- a/ibc-testkit/src/lib.rs +++ b/ibc-testkit/src/lib.rs @@ -19,3 +19,4 @@ pub mod fixtures; pub mod hosts; pub mod relayer; pub mod testapp; +pub mod utils; diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index c35cd080f..6083f12d6 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -8,6 +8,7 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Signer; +use crate::hosts::TestHost; use crate::testapp::ibc::clients::AnyClientState; use crate::testapp::ibc::core::types::MockGenericContext; /// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to @@ -27,9 +28,10 @@ pub trait RelayerContext { fn signer(&self) -> Signer; } -impl RelayerContext for MockGenericContext +impl RelayerContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { fn query_latest_height(&self) -> Result { ValidationContext::host_height(self) @@ -60,19 +62,19 @@ mod tests { use super::RelayerContext; use crate::fixtures::core::context::MockContextConfig; - use crate::hosts::block::{HostBlock, HostType}; + use crate::hosts::{MockHost, TendermintHost, TestBlock, TestHeader}; use crate::relayer::context::ClientId; use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use crate::testapp::ibc::core::router::MockRouter; - use crate::testapp::ibc::core::types::{MockClientConfig, MockContext}; + use crate::testapp::ibc::core::types::{LightClientBuilder, MockContext}; /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` /// context, assuming that the latest header on the source context is `src_header`. - pub(crate) fn build_client_update_datagram( + pub(crate) fn build_client_update_datagram( dest: &Ctx, client_id: &ClientId, - src_header: &HostBlock, + src_header: &H, ) -> Result where Ctx: RelayerContext, @@ -106,7 +108,7 @@ mod tests { // Client on destination chain can be updated. Ok(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), - client_message: (*src_header).clone().into(), + client_message: src_header.clone().into(), signer: dest.signer(), })) } @@ -132,31 +134,27 @@ mod tests { let mut ctx_a = MockContextConfig::builder() .host_id(chain_id_a.clone()) .latest_height(chain_a_start_height) - .build::(); + .build::>(); let mut ctx_b = MockContextConfig::builder() .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) .latest_height(chain_b_start_height) .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A - .build::(); - - ctx_a = ctx_a.with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b) - .client_id(client_on_a_for_b.clone()) - .latest_height(client_on_a_for_b_height) - .latest_timestamp(ctx_b.timestamp_at(client_on_a_for_b_height)) - .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. + .build::>(); + + ctx_a = ctx_a.with_light_client( + &client_on_a_for_b, + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_on_a_for_b_height]) .build(), ); - ctx_b = ctx_b.with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_a) - .client_id(client_on_b_for_a.clone()) - .latest_height(client_on_b_for_a_height) - .latest_timestamp(ctx_a.timestamp_at(client_on_b_for_a_height)) + ctx_b = ctx_b.with_light_client( + &client_on_b_for_a, + LightClientBuilder::init() + .context(&ctx_a) + .consensus_heights([client_on_b_for_a_height]) .build(), ); @@ -169,9 +167,12 @@ mod tests { for _i in 0..num_iterations { // Update client on chain B to latest height of A. // - create the client update message with the latest header from A - let a_latest_header = ctx_a.query_latest_header().unwrap(); - let client_msg_b_res = - build_client_update_datagram(&ctx_b, &client_on_b_for_a, &a_latest_header); + let a_latest_header = ctx_a.query_latest_block().unwrap(); + let client_msg_b_res = build_client_update_datagram( + &ctx_b, + &client_on_b_for_a, + &a_latest_header.into_header(), + ); assert!( client_msg_b_res.is_ok(), @@ -203,7 +204,7 @@ mod tests { // Update client on chain A to latest height of B. // - create the client update message with the latest header from B // The test uses LightClientBlock that does not store the trusted height - let mut b_latest_header = ctx_b.query_latest_header().unwrap(); + let mut b_latest_header = ctx_b.query_latest_block().unwrap().clone().into_header(); let th = b_latest_header.height(); b_latest_header.set_trusted_height(th.decrement().unwrap()); diff --git a/ibc-testkit/src/testapp/ibc/clients/mod.rs b/ibc-testkit/src/testapp/ibc/clients/mod.rs index 8aaf1088a..caa49fdc0 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mod.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mod.rs @@ -15,6 +15,7 @@ use ibc::core::primitives::prelude::*; use ibc::derive::{ClientState, ConsensusState}; use ibc::primitives::proto::{Any, Protobuf}; +use crate::hosts::TestHost; use crate::testapp::ibc::clients::mock::client_state::{ MockClientState, MOCK_CLIENT_STATE_TYPE_URL, }; @@ -24,8 +25,8 @@ use crate::testapp::ibc::clients::mock::consensus_state::{ use crate::testapp::ibc::core::types::MockGenericContext; #[derive(Debug, Clone, From, PartialEq, ClientState)] -#[validation(MockGenericContext)] -#[execution(MockGenericContext)] +#[validation(MockGenericContext)] +#[execution(MockGenericContext)] pub enum AnyClientState { Tendermint(TmClientState), Mock(MockClientState), diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index 25728e54b..f813cd19d 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -1,5 +1,3 @@ -use alloc::collections::BTreeMap; -use alloc::vec::Vec; use core::fmt::Debug; use basecoin_store::context::{ProvableStore, Store}; @@ -17,8 +15,9 @@ use ibc::core::host::types::path::{ }; use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; -use ibc::primitives::prelude::{format, *}; +use ibc::primitives::prelude::*; +use crate::hosts::TestHost; use crate::testapp::ibc::clients::mock::client_state::MockClientContext; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use crate::testapp::ibc::core::types::MockGenericContext; @@ -36,9 +35,10 @@ pub struct MockClientRecord { pub consensus_states: BTreeMap, } -impl MockClientContext for MockGenericContext +impl MockClientContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { type ConversionError = &'static str; type AnyConsensusState = AnyConsensusState; @@ -58,9 +58,10 @@ where ValidationContext::consensus_state(self, client_cons_state_path) } } -impl ClientValidationContext for MockGenericContext +impl ClientValidationContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { /// Returns the time and height when the client state for the given /// [`ClientId`] was updated with a header for the given [`Height`] @@ -100,9 +101,10 @@ where } } -impl ClientExecutionContext for MockGenericContext +impl ClientExecutionContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { type V = Self; @@ -213,9 +215,10 @@ where } } -impl TmCommonContext for MockGenericContext +impl TmCommonContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { type ConversionError = &'static str; type AnyConsensusState = AnyConsensusState; @@ -264,9 +267,10 @@ where } } -impl TmValidationContext for MockGenericContext +impl TmValidationContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { fn next_consensus_state( &self, @@ -281,7 +285,7 @@ where let found_path = keys.into_iter().find_map(|path| { if let Ok(Path::ClientConsensusState(path)) = path.try_into() { if height - > &Height::new(path.revision_number, path.revision_height).expect("no error") + < &Height::new(path.revision_number, path.revision_height).expect("no error") { return Some(path); } @@ -289,20 +293,19 @@ where None }); - if let Some(path) = found_path { - let consensus_state = self - .ibc_store - .consensus_state_store - .get(StoreHeight::Pending, &path) - .ok_or(ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), - height: *height, - })?; - - Ok(Some(consensus_state)) - } else { - Ok(None) - } + let consensus_state = found_path + .map(|path| { + self.ibc_store + .consensus_state_store + .get(StoreHeight::Pending, &path) + .ok_or_else(|| ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height: *height, + }) + }) + .transpose()?; + + Ok(consensus_state) } fn prev_consensus_state( @@ -315,32 +318,29 @@ where .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers let keys = self.ibc_store.store.get_keys(&path); - let pos = keys.iter().position(|path| { - if let Ok(Path::ClientConsensusState(path)) = path.clone().try_into() { - height - >= &Height::new(path.revision_number, path.revision_height).expect("no error") - } else { - false + let found_path = keys.into_iter().rev().find_map(|path| { + if let Ok(Path::ClientConsensusState(path)) = path.try_into() { + if height + > &Height::new(path.revision_number, path.revision_height).expect("no error") + { + return Some(path); + } } + None }); - if let Some(pos) = pos { - if pos > 0 { - let prev_path = match keys[pos - 1].clone().try_into() { - Ok(Path::ClientConsensusState(p)) => p, - _ => unreachable!(), // safety - path retrieved from store - }; - let consensus_state = self - .ibc_store + let consensus_state = found_path + .map(|path| { + self.ibc_store .consensus_state_store - .get(StoreHeight::Pending, &prev_path) - .ok_or(ClientError::ConsensusStateNotFound { + .get(StoreHeight::Pending, &path) + .ok_or_else(|| ClientError::ConsensusStateNotFound { client_id: client_id.clone(), height: *height, - })?; - return Ok(Some(consensus_state)); - } - } - Ok(None) + }) + }) + .transpose()?; + + Ok(consensus_state) } } diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index aaf2df979..c61781495 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -32,12 +32,14 @@ use ibc::primitives::ToVec; use ibc_query::core::context::{ProvableContext, QueryContext}; use super::types::MockGenericContext; +use crate::hosts::{TestBlock, TestHeader, TestHost}; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use crate::testapp::ibc::utils::blocks_since; -impl ValidationContext for MockGenericContext +impl ValidationContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { type V = Self; type E = Self; @@ -104,7 +106,11 @@ where .checked_sub(1 + height_delta) .ok_or(ClientError::MissingLocalConsensusState { height: *height })?; - let consensus_state = self.history[index].clone().into(); + let consensus_state = self.history[index] + .clone() + .into_header() + .into_consensus_state() + .into(); Ok(consensus_state) } @@ -276,7 +282,7 @@ where /// Returns the maximum expected time per block fn max_expected_time_per_block(&self) -> Duration { - Duration::from_secs(8) + self.block_time } fn validate_message_signer(&self, _signer: &Signer) -> Result<(), ContextError> { @@ -289,9 +295,10 @@ where } /// Trait to provide proofs in gRPC service blanket implementations. -impl ProvableContext for MockGenericContext +impl ProvableContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { /// Returns the proof for the given [`Height`] and [`Path`] fn get_proof(&self, height: Height, path: &Path) -> Option> { @@ -303,9 +310,10 @@ where } /// Trait to complete the gRPC service blanket implementations. -impl QueryContext for MockGenericContext +impl QueryContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { /// Returns the list of all client states. fn client_states(&self) -> Result, ContextError> { @@ -441,7 +449,7 @@ where &self, client_id: &ClientId, ) -> Result, ContextError> { - let client_connection_path = ClientConnectionPath::new(client_id); + let client_connection_path = ClientConnectionPath::new(client_id.clone()); Ok(self .ibc_store @@ -657,9 +665,10 @@ where } } -impl ExecutionContext for MockGenericContext +impl ExecutionContext for MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { /// Called upon client creation. /// Increases the counter which keeps track of how many clients have been created. diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 63b376e10..04f4ed29a 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -1,7 +1,6 @@ //! Implementation of a global context mock. Used in testing handlers of all IBC modules. use alloc::sync::Arc; -use core::cmp::min; use core::fmt::Debug; use core::ops::{Add, Sub}; use core::time::Duration; @@ -9,8 +8,6 @@ use core::time::Duration; use basecoin_store::context::ProvableStore; use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore, SharedStore}; use basecoin_store::types::{BinStore, JsonStore, ProtobufStore, TypedSet, TypedStore}; -use ibc::clients::tendermint::client_state::ClientState as TmClientState; -use ibc::clients::tendermint::types::TENDERMINT_CLIENT_TYPE; use ibc::core::channel::types::channel::ChannelEnd; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; use ibc::core::client::context::ClientExecutionContext; @@ -19,9 +16,7 @@ use ibc::core::connection::types::ConnectionEnd; use ibc::core::entrypoint::dispatch; use ibc::core::handler::types::events::IbcEvent; use ibc::core::handler::types::msgs::MsgEnvelope; -use ibc::core::host::types::identifiers::{ - ChainId, ChannelId, ClientId, ClientType, ConnectionId, PortId, Sequence, -}; +use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, CommitmentPath, ConnectionPath, @@ -37,18 +32,11 @@ use ibc_proto::ibc::core::channel::v1::Channel as RawChannelEnd; use ibc_proto::ibc::core::client::v1::Height as RawHeight; use ibc_proto::ibc::core::connection::v1::ConnectionEnd as RawConnectionEnd; use parking_lot::Mutex; -use tendermint_testgen::Validator as TestgenValidator; use typed_builder::TypedBuilder; -use crate::fixtures::clients::tendermint::ClientStateConfig as TmClientStateConfig; use crate::fixtures::core::context::MockContextConfig; -use crate::hosts::block::{HostBlock, HostType}; +use crate::hosts::{TestBlock, TestHeader, TestHost}; use crate::relayer::error::RelayerError; -use crate::testapp::ibc::clients::mock::client_state::{ - client_type as mock_client_type, MockClientState, MOCK_CLIENT_TYPE, -}; -use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; -use crate::testapp::ibc::clients::mock::header::MockHeader; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use crate::testapp::ibc::utils::blocks_since; pub const DEFAULT_BLOCK_TIME_SECS: u64 = 3; @@ -164,22 +152,20 @@ where /// A context implementing the dependencies necessary for testing any IBC module. #[derive(Debug)] -pub struct MockGenericContext +pub struct MockGenericContext where S: ProvableStore + Debug, + H: TestHost, { /// The type of host chain underlying this mock context. - pub host_chain_type: HostType, - - /// Host chain identifier. - pub host_chain_id: ChainId, + pub host: H, /// Maximum size for the history of the host chain. Any block older than this is pruned. pub max_history_size: u64, /// The chain of blocks underlying this context. A vector of size up to `max_history_size` /// blocks, ascending order by their height (latest block is on the last position). - pub history: Vec, + pub history: Vec, /// Average time duration between blocks pub block_time: Duration, @@ -188,327 +174,99 @@ where pub ibc_store: MockIbcStore, } -pub type MockContext = MockGenericContext>>; +pub type MockContext = MockGenericContext>, H>; #[derive(Debug, TypedBuilder)] pub struct MockClientConfig { - #[builder(default = ChainId::new("mockZ-1").expect("no error"))] - client_chain_id: ChainId, - #[builder(default)] - client_id: ClientId, - #[builder(default = mock_client_type())] - client_type: ClientType, - latest_height: Height, - #[builder(default)] - consensus_state_heights: Vec, - #[builder(default, setter(strip_option))] - latest_timestamp: Option, - #[builder(default = Duration::from_secs(64000))] - trusting_period: Duration, + pub trusting_period: Duration, #[builder(default = Duration::from_millis(3000))] - max_clock_drift: Duration, + pub max_clock_drift: Duration, #[builder(default = Duration::from_secs(128000))] - unbonding_period: Duration, + pub unbonding_period: Duration, +} + +impl Default for MockClientConfig { + fn default() -> Self { + Self::builder().build() + } } /// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the /// creation of new domain objects. -impl Default for MockGenericContext +impl Default for MockGenericContext where S: ProvableStore + Debug + Default, + H: TestHost, { fn default() -> Self { MockContextConfig::builder().build() } } -/// Implementation of internal interface for use in testing. The methods in this interface should -/// _not_ be accessible to any Ics handler. -impl MockGenericContext +pub struct LightClientState { + pub client_state: H::ClientState, + pub consensus_states: + BTreeMap::Header as TestHeader>::ConsensusState>, +} + +impl Default for LightClientState where - S: ProvableStore + Debug, + H: TestHost, { - /// Creates a mock context. Parameter `max_history_size` determines how many blocks will - /// the chain maintain in its history, which also determines the pruning window. Parameter - /// `latest_height` determines the current height of the chain. This context - /// has support to emulate two type of underlying chains: Mock or SyntheticTendermint. - #[deprecated( - since = "0.50.0", - note = "Please use `MockContextConfig::builder().build()` instead" - )] - pub fn new( - host_id: ChainId, - host_type: HostType, - max_history_size: u64, - latest_height: Height, - ) -> Self - where - S: Default, - { - assert_ne!( - max_history_size, 0, - "The chain must have a non-zero max_history_size" - ); - - assert_ne!( - latest_height.revision_height(), - 0, - "The chain must have a non-zero revision_height" - ); - - // Compute the number of blocks to store. - let n = min(max_history_size, latest_height.revision_height()); - - assert_eq!( - host_id.revision_number(), - latest_height.revision_number(), - "The version in the chain identifier must match the version in the latest height" - ); - - let block_time = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS); - let next_block_timestamp = Timestamp::now().add(block_time).expect("Never fails"); - Self { - host_chain_type: host_type, - host_chain_id: host_id.clone(), - max_history_size, - history: (0..n) - .rev() - .map(|i| { - // generate blocks with timestamps -> N, N - BT, N - 2BT, ... - // where N = now(), BT = block_time - HostBlock::generate_block( - host_id.clone(), - host_type, - latest_height.sub(i).expect("Never fails").revision_height(), - next_block_timestamp - .sub(Duration::from_secs(DEFAULT_BLOCK_TIME_SECS * (i + 1))) - .expect("Never fails"), - ) - }) - .collect(), - block_time, - ibc_store: MockIbcStore::default(), - } - } - - /// Same as [Self::new] but with custom validator sets for each block. - /// Note: the validator history is used accordingly for current validator set and next validator set. - /// `validator_history[i]` and `validator_history[i+1]` is i'th block's current and next validator set. - /// The number of blocks will be `validator_history.len() - 1` due to the above. - #[deprecated( - since = "0.50.0", - note = "Please use `MockContextConfig::builder().build()` instead" - )] - pub fn new_with_validator_history( - host_id: ChainId, - host_type: HostType, - validator_history: &[Vec], - latest_height: Height, - ) -> Self - where - S: Default, - { - let max_history_size = validator_history.len() as u64 - 1; - - assert_ne!( - max_history_size, 0, - "The chain must have a non-zero max_history_size" - ); - - assert_ne!( - latest_height.revision_height(), - 0, - "The chain must have a non-zero revision_height" - ); - - assert!( - max_history_size <= latest_height.revision_height(), - "The number of blocks must be greater than the number of validator set histories" - ); - - assert_eq!( - host_id.revision_number(), - latest_height.revision_number(), - "The version in the chain identifier must match the version in the latest height" - ); - - let block_time = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS); - let next_block_timestamp = Timestamp::now().add(block_time).expect("Never fails"); - - let history = (0..max_history_size) - .rev() - .map(|i| { - // generate blocks with timestamps -> N, N - BT, N - 2BT, ... - // where N = now(), BT = block_time - HostBlock::generate_block_with_validators( - host_id.clone(), - host_type, - latest_height.sub(i).expect("Never fails").revision_height(), - next_block_timestamp - .sub(Duration::from_secs(DEFAULT_BLOCK_TIME_SECS * (i + 1))) - .expect("Never fails"), - &validator_history[(max_history_size - i) as usize - 1], - &validator_history[(max_history_size - i) as usize], - ) - }) - .collect(); - - Self { - host_chain_type: host_type, - host_chain_id: host_id.clone(), - max_history_size, - history, - block_time, - ibc_store: MockIbcStore::default(), - } - } - - pub fn chain_revision_number(&self) -> u64 { - self.host_chain_id.revision_number() - } - - /// Associates a client record to this context. - /// Given a client id and a height, registers a new client in the context and also associates - /// to this client a mock client state and a mock consensus state for height `height`. The type - /// of this client is implicitly assumed to be Mock. - #[deprecated( - since = "0.50.0", - note = "Please use `MockClientConfig::builder().build()` instead" - )] - pub fn with_client(self, client_id: &ClientId, height: Height) -> Self { - // NOTE: this is wrong; the client chain ID is supposed to represent - // the chain ID of the counterparty chain. But at this point this is - // too ingrained in our tests; `with_client()` is called everywhere, - // which delegates to this. - let client_chain_id = self.host_chain_id.clone(); - - self.with_client_config( - MockClientConfig::builder() - .client_chain_id(client_chain_id) - .client_id(client_id.clone()) - .latest_height(height) - .build(), - ) + fn default() -> Self { + let context = MockContext::::default(); + LightClientBuilder::init().context(&context).build() } +} - /// Similar to `with_client`, this function associates a client record to this context, but - /// additionally permits to parametrize two details of the client. If `client_type` is None, - /// then the client will have type Mock, otherwise the specified type. If - /// `consensus_state_height` is None, then the client will be initialized with a consensus - /// state matching the same height as the client state (`client_state_height`). - #[deprecated( - since = "0.50.0", - note = "Please use `MockClientConfig::builder().build()` instead" - )] - pub fn with_client_parametrized( - self, - client_id: &ClientId, - client_state_height: Height, - client_type: Option, - consensus_state_height: Option, - ) -> Self { - // NOTE: this is wrong; the client chain ID is supposed to represent - // the chain ID of the counterparty chain. But at this point this is - // too ingrained in our tests; `with_client()` is called everywhere, - // which delegates to this. - let client_chain_id = self.host_chain_id.clone(); - - self.with_client_config( - MockClientConfig::builder() - .client_chain_id(client_chain_id) - .client_id(client_id.clone()) - .latest_height(client_state_height) - .client_type(client_type.unwrap_or_else(mock_client_type)) - .consensus_state_heights( - vec![consensus_state_height.unwrap_or(client_state_height)], - ) - .build(), - ) +impl LightClientState +where + H: TestHost, +{ + pub fn with_latest_height(height: Height) -> Self { + let context = MockContextConfig::builder() + .latest_height(height) + .build::>(); + LightClientBuilder::init().context(&context).build() } +} - #[deprecated( - since = "0.50.0", - note = "Please use `MockClientConfig::builder().build()` instead" - )] - pub fn with_client_parametrized_with_chain_id( - self, - client_chain_id: ChainId, - client_id: &ClientId, - client_state_height: Height, - client_type: Option, - consensus_state_height: Option, - ) -> Self { - self.with_client_config( - MockClientConfig::builder() - .client_chain_id(client_chain_id) - .client_id(client_id.clone()) - .latest_height(client_state_height) - .client_type(client_type.unwrap_or_else(mock_client_type)) - .consensus_state_heights( - vec![consensus_state_height.unwrap_or(client_state_height)], - ) - .build(), - ) - } +#[derive(TypedBuilder)] +#[builder(builder_method(name = init), build_method(into))] +pub struct LightClientBuilder<'a, H: TestHost> { + context: &'a MockContext, + #[builder(default, setter(into))] + consensus_heights: Vec, + #[builder(default)] + params: H::LightClientParams, +} - #[deprecated( - since = "0.50.0", - note = "Please use `MockClientConfig::builder().build()` instead" - )] - pub fn with_client_parametrized_history( - self, - client_id: &ClientId, - client_state_height: Height, - client_type: Option, - consensus_state_height: Option, - ) -> Self { - let client_chain_id = self.host_chain_id.clone(); - let current_consensus_height = consensus_state_height.unwrap_or(client_state_height); - let prev_consensus_height = current_consensus_height - .sub(1) - .unwrap_or(client_state_height); - self.with_client_config( - MockClientConfig::builder() - .client_chain_id(client_chain_id) - .client_id(client_id.clone()) - .latest_height(client_state_height) - .client_type(client_type.unwrap_or_else(mock_client_type)) - .consensus_state_heights(vec![prev_consensus_height, current_consensus_height]) - .build(), - ) - } +impl<'a, H> From> for LightClientState +where + H: TestHost, +{ + fn from(builder: LightClientBuilder<'a, H>) -> Self { + let LightClientBuilder { + context, + consensus_heights, + params, + } = builder; - #[deprecated( - since = "0.50.0", - note = "Please use `MockClientConfig::builder().build()` instead" - )] - pub fn with_client_parametrized_history_with_chain_id( - self, - client_chain_id: ChainId, - client_id: &ClientId, - client_state_height: Height, - client_type: Option, - consensus_state_height: Option, - ) -> Self { - let current_consensus_height = consensus_state_height.unwrap_or(client_state_height); - let prev_consensus_height = current_consensus_height - .sub(1) - .unwrap_or(client_state_height); - self.with_client_config( - MockClientConfig::builder() - .client_chain_id(client_chain_id) - .client_id(client_id.clone()) - .latest_height(client_state_height) - .client_type(client_type.unwrap_or_else(mock_client_type)) - .consensus_state_heights(vec![prev_consensus_height, current_consensus_height]) - .build(), - ) + context.generate_light_client(consensus_heights, ¶ms) } +} +/// Implementation of internal interface for use in testing. The methods in this interface should +/// _not_ be accessible to any Ics handler. +impl MockGenericContext +where + S: ProvableStore + Debug, + H: TestHost, +{ pub fn with_client_state(mut self, client_id: &ClientId, client_state: AnyClientState) -> Self { - let client_state_path = ClientStatePath::new(client_id); + let client_state_path = ClientStatePath::new(client_id.clone()); self.store_client_state(client_state_path, client_state) .expect("error writing to store"); self @@ -531,94 +289,56 @@ where self } - pub fn with_client_config(mut self, client: MockClientConfig) -> Self { - let cs_heights = if client.consensus_state_heights.is_empty() { - vec![client.latest_height] + pub fn generate_light_client( + &self, + mut consensus_heights: Vec, + client_params: &H::LightClientParams, + ) -> LightClientState { + let client_height = if let Some(&height) = consensus_heights.last() { + height } else { - client.consensus_state_heights + consensus_heights.push(self.latest_height()); + self.latest_height() }; - let client_latest_timestamp = client - .latest_timestamp - .unwrap_or_else(|| self.latest_timestamp()); - - let (client_state, consensus_states): ( - AnyClientState, - BTreeMap, - ) = match client.client_type.as_str() { - MOCK_CLIENT_TYPE => { - let blocks: Vec<_> = cs_heights - .into_iter() - .map(|cs_height| { - let n_blocks = blocks_since(client.latest_height, cs_height) - .expect("less or equal height"); - ( - cs_height, - MockHeader::new(cs_height).with_timestamp( - client_latest_timestamp - .sub(self.block_time * (n_blocks as u32)) - .expect("never fails"), - ), - ) - }) - .collect(); - - let client_state = MockClientState::new( - MockHeader::new(client.latest_height).with_timestamp(client_latest_timestamp), - ); + let client_state = self.host.generate_client_state( + self.host_block(&client_height) + .expect("latest block exists"), + client_params, + ); - let cs_states = blocks - .into_iter() - .map(|(height, block)| (height, MockConsensusState::new(block).into())) - .collect(); + let consensus_states = consensus_heights + .into_iter() + .map(|height| { + ( + height, + self.host_block(&height) + .expect("block exists") + .clone() + .into_header() + .into_consensus_state(), + ) + }) + .collect(); - (client_state.into(), cs_states) - } - TENDERMINT_CLIENT_TYPE => { - let blocks: Vec<_> = cs_heights - .into_iter() - .map(|cs_height| { - let n_blocks = blocks_since(client.latest_height, cs_height) - .expect("less or equal height"); - ( - cs_height, - HostBlock::generate_tm_block( - client.client_chain_id.clone(), - cs_height.revision_height(), - client_latest_timestamp - .sub(self.block_time * (n_blocks as u32)) - .expect("never fails"), - ), - ) - }) - .collect(); - - let client_state: TmClientState = TmClientStateConfig::builder() - .chain_id(client.client_chain_id) - .latest_height(client.latest_height) - .trusting_period(client.trusting_period) - .max_clock_drift(client.max_clock_drift) - .unbonding_period(client.unbonding_period) - .build() - .try_into() - .expect("never fails"); - - client_state.inner().validate().expect("never fails"); - - let cs_states = blocks - .into_iter() - .map(|(height, block)| (height, block.into())) - .collect(); - - (client_state.into(), cs_states) - } - _ => panic!("unknown client type"), - }; + LightClientState { + client_state, + consensus_states, + } + } - self = self.with_client_state(&client.client_id, client_state); + pub fn with_light_client( + mut self, + client_id: &ClientId, + light_client: LightClientState, + ) -> Self + where + RH: TestHost, + { + self = self.with_client_state(client_id, light_client.client_state.into()); - for (height, consensus_state) in consensus_states { - self = self.with_consensus_state(&client.client_id, height, consensus_state); + for (height, consensus_state) in light_client.consensus_states { + self = self.with_consensus_state(client_id, height, consensus_state.into()); } self @@ -721,7 +441,7 @@ where /// Accessor for a block of the local (host) chain from this context. /// Returns `None` if the block at the requested height does not exist. - pub fn host_block(&self, target_height: &Height) -> Option<&HostBlock> { + pub fn host_block(&self, target_height: &Height) -> Option<&H::Block> { let target = target_height.revision_height(); let latest = self.latest_height().revision_height(); @@ -736,14 +456,14 @@ where /// Triggers the advancing of the host chain, by extending the history of blocks (or headers). pub fn advance_host_chain_height(&mut self) { let latest_block = self.history.last().expect("history cannot be empty"); - let new_block = HostBlock::generate_block( - self.host_chain_id.clone(), - self.host_chain_type, + + let new_block = self.host.generate_block( latest_block.height().increment().revision_height(), latest_block .timestamp() .add(self.block_time) .expect("Never fails"), + &H::BlockParams::default(), ); // Append the new header at the tip of the history. @@ -834,9 +554,8 @@ where .expect("Never fails") } - pub fn query_latest_header(&self) -> Option { - let block_ref = self.host_block(&self.latest_height()); - block_ref.cloned() + pub fn query_latest_block(&self) -> Option<&H::Block> { + self.host_block(&self.latest_height()) } pub fn get_events(&self) -> Vec { @@ -855,6 +574,7 @@ mod tests { use ibc::core::channel::types::error::{ChannelError, PacketError}; use ibc::core::channel::types::packet::Packet; use ibc::core::channel::types::Version; + use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::Signer; use ibc::core::router::module::Module; use ibc::core::router::types::module::{ModuleExtras, ModuleId}; @@ -862,145 +582,108 @@ mod tests { use super::*; use crate::fixtures::core::channel::PacketConfig; use crate::fixtures::core::signer::dummy_bech32_account; + use crate::hosts::{MockHost, TendermintHost}; use crate::testapp::ibc::core::router::MockRouter; #[test] - fn test_history_manipulation() { - pub struct Test { + fn test_history_manipulation_mock() { + pub struct Test { name: String, - ctx: MockContext, + ctx: MockContext, } - let cv = 1; // The version to use for all chains. - - let mock_chain_id = ChainId::new(&format!("mockgaia-{cv}")).unwrap(); - - let tests: Vec = vec![ - Test { - name: "Empty history, small pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(2) - .latest_height(Height::new(cv, 1).expect("Never fails")) - .build(), - }, - Test { - name: "[Synthetic TM host] Empty history, small pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .host_type(HostType::SyntheticTendermint) - .max_history_size(2) - .latest_height(Height::new(cv, 1).expect("Never fails")) - .build(), - }, - Test { - name: "Large pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(30) - .latest_height(Height::new(cv, 2).expect("Never fails")) - .build(), - }, - Test { - name: "[Synthetic TM host] Large pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .host_type(HostType::SyntheticTendermint) - .max_history_size(30) - .latest_height(Height::new(cv, 2).expect("Never fails")) - .build(), - }, - Test { - name: "Small pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(3) - .latest_height(Height::new(cv, 30).expect("Never fails")) - .build(), - }, - Test { - name: "[Synthetic TM host] Small pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .host_type(HostType::SyntheticTendermint) - .max_history_size(3) - .latest_height(Height::new(cv, 30).expect("Never fails")) - .build(), - }, - Test { - name: "Small pruning window, small starting height".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(3) - .latest_height(Height::new(cv, 2).expect("Never fails")) - .build(), - }, - Test { - name: "[Synthetic TM host] Small pruning window, small starting height".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .host_type(HostType::SyntheticTendermint) - .max_history_size(3) - .latest_height(Height::new(cv, 2).expect("Never fails")) - .build(), - }, - Test { - name: "Large pruning window, large starting height".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(50) - .latest_height(Height::new(cv, 2000).expect("Never fails")) - .build(), - }, - Test { - name: "[Synthetic TM host] Large pruning window, large starting height".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id) - .host_type(HostType::SyntheticTendermint) - .max_history_size(50) - .latest_height(Height::new(cv, 2000).expect("Never fails")) - .build(), - }, - ]; - for mut test in tests { - // All tests should yield a valid context after initialization. - assert!( - test.ctx.validate().is_ok(), - "failed in test {} while validating context {:?}", - test.name, - test.ctx - ); - - let current_height = test.ctx.latest_height(); - - // After advancing the chain's height, the context should still be valid. - test.ctx.advance_host_chain_height(); - assert!( - test.ctx.validate().is_ok(), - "failed in test {} while validating context {:?}", - test.name, - test.ctx - ); - - let next_height = current_height.increment(); - assert_eq!( - test.ctx.latest_height(), - next_height, - "failed while increasing height for context {:?}", - test.ctx - ); - - assert_eq!( - test.ctx - .host_block(¤t_height) - .expect("Never fails") - .height(), - current_height, - "failed while fetching height {:?} of context {:?}", - current_height, - test.ctx - ); + fn run_tests(sub_title: &str) { + let cv = 1; // The version to use for all chains. + let mock_chain_id = ChainId::new(&format!("mockgaia-{cv}")).unwrap(); + + let tests: Vec> = vec![ + Test { + name: "Empty history, small pruning window".to_string(), + ctx: MockContextConfig::builder() + .host_id(mock_chain_id.clone()) + .max_history_size(2) + .latest_height(Height::new(cv, 1).expect("Never fails")) + .build(), + }, + Test { + name: "Large pruning window".to_string(), + ctx: MockContextConfig::builder() + .host_id(mock_chain_id.clone()) + .max_history_size(30) + .latest_height(Height::new(cv, 2).expect("Never fails")) + .build(), + }, + Test { + name: "Small pruning window".to_string(), + ctx: MockContextConfig::builder() + .host_id(mock_chain_id.clone()) + .max_history_size(3) + .latest_height(Height::new(cv, 30).expect("Never fails")) + .build(), + }, + Test { + name: "Small pruning window, small starting height".to_string(), + ctx: MockContextConfig::builder() + .host_id(mock_chain_id.clone()) + .max_history_size(3) + .latest_height(Height::new(cv, 2).expect("Never fails")) + .build(), + }, + Test { + name: "Large pruning window, large starting height".to_string(), + ctx: MockContextConfig::builder() + .host_id(mock_chain_id.clone()) + .max_history_size(50) + .latest_height(Height::new(cv, 2000).expect("Never fails")) + .build(), + }, + ]; + + for mut test in tests { + // All tests should yield a valid context after initialization. + assert!( + test.ctx.validate().is_ok(), + "failed in test [{}] {} while validating context {:?}", + sub_title, + test.name, + test.ctx + ); + + let current_height = test.ctx.latest_height(); + + // After advancing the chain's height, the context should still be valid. + test.ctx.advance_host_chain_height(); + assert!( + test.ctx.validate().is_ok(), + "failed in test [{}] {} while validating context {:?}", + sub_title, + test.name, + test.ctx + ); + + let next_height = current_height.increment(); + assert_eq!( + test.ctx.latest_height(), + next_height, + "failed while increasing height for context {:?}", + test.ctx + ); + + assert_eq!( + test.ctx + .host_block(¤t_height) + .expect("Never fails") + .height(), + current_height, + "failed while fetching height {:?} of context {:?}", + current_height, + test.ctx + ); + } } + + run_tests::("Mock Host"); + run_tests::("Synthetic TM Host"); } #[test] diff --git a/ibc-testkit/src/utils/mod.rs b/ibc-testkit/src/utils/mod.rs new file mode 100644 index 000000000..50880f7e1 --- /dev/null +++ b/ibc-testkit/src/utils/mod.rs @@ -0,0 +1,19 @@ +use ibc::primitives::Timestamp; +use tendermint::Time; + +/// Returns a `Timestamp` representation of beginning of year 2023. +/// +/// This is introduced to initialize [`MockGenericContext`](crate::testapp::ibc::core::types::MockGenericContext)s +/// with the same latest timestamp by default. +/// If two [`MockGenericContext`](crate::testapp::ibc::core::types::MockGenericContext) +/// are initialized using [`Time::now()`], second one will have a greater timestamp than the first one. +/// So, the latest header of the second context can not be submitted to first one. +/// We can still set a custom timestamp via [`MockContextConfig`](crate::fixtures::core::context::MockContextConfig). +pub fn year_2023() -> Timestamp { + // TODO(rano): can we turn this into a fixture? + + // Sun Jan 01 2023 00:00:00 GMT+0000 + Time::from_unix_timestamp(1_672_531_200, 0) + .expect("should be a valid time") + .into() +} diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 3540aefb6..b8e75ae98 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -13,6 +13,7 @@ use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; use ibc_testkit::fixtures::core::signer::dummy_account_id; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; @@ -24,7 +25,7 @@ use test_log::test; #[test] fn test_create_client_ok() { - let mut ctx = MockContext::default(); + let mut ctx = MockContext::::default(); let mut router = MockRouter::new_with_transfer(); let signer = dummy_account_id(); let height = Height::new(0, 42).unwrap(); @@ -57,7 +58,7 @@ fn test_create_client_ok() { fn test_tm_create_client_ok() { let signer = dummy_account_id(); - let mut ctx = MockContext::default(); + let mut ctx = MockContext::::default(); let mut router = MockRouter::new_with_transfer(); @@ -93,7 +94,7 @@ fn test_tm_create_client_ok() { fn test_invalid_frozen_tm_client_creation() { let signer = dummy_account_id(); - let ctx = MockContext::default(); + let ctx = MockContext::::default(); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index ca9d99961..a590e183c 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -19,12 +19,13 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChainId, ClientId, ClientType}; use ibc::core::host::types::path::ClientConsensusStatePath; use ibc::core::host::ValidationContext; -use ibc::core::primitives::{downcast, Timestamp}; +use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; use ibc::primitives::ToVec; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; -use ibc_testkit::hosts::block::{HostBlock, HostType}; +use ibc_testkit::hosts::tendermint::BlockParams; +use ibc_testkit::hosts::{MockHost, TendermintHost, TestBlock, TestHeader, TestHost}; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; @@ -32,12 +33,15 @@ use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; use ibc_testkit::testapp::ibc::clients::mock::misbehaviour::Misbehaviour as MockMisbehaviour; use ibc_testkit::testapp::ibc::clients::AnyConsensusState; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{ + LightClientBuilder, LightClientState, MockClientConfig, MockContext, +}; +use ibc_testkit::testapp::ibc::utils::blocks_since; use rstest::*; use tendermint_testgen::Validator as TestgenValidator; struct Fixture { - ctx: MockContext, + ctx: MockContext, router: MockRouter, } @@ -45,11 +49,9 @@ struct Fixture { fn fixture() -> Fixture { let client_id = ClientId::default(); - let ctx = MockContext::default().with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(Height::new(0, 42).unwrap()) - .build(), + let ctx = MockContext::::default().with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); let router = MockRouter::new_with_transfer(); @@ -120,13 +122,14 @@ fn test_update_client_with_prev_header() { let height_1 = Height::new(0, 43).unwrap(); let height_2 = Height::new(0, 44).unwrap(); - let mut ctx = MockContext::default().with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_type(tm_client_type()) - .client_id(client_id.clone()) - .latest_height(latest_height) - .build(), + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .latest_height(latest_height) + .build::>(); + + let mut ctx = MockContext::::default().with_light_client( + &client_id, + LightClientBuilder::init().context(&ctx_b).build(), ); let mut router = MockRouter::new_with_transfer(); @@ -136,13 +139,15 @@ fn test_update_client_with_prev_header() { target_height: Height, trusted_height: Height, ) -> MsgEnvelope { - let mut tm_block = HostBlock::generate_tm_block( - chain_id, - target_height.revision_height(), - Timestamp::now(), - ); + let mut tm_block = TendermintHost::with_chain_id(chain_id) + .generate_block( + target_height.revision_height(), + Timestamp::now(), + &Default::default(), + ) + .into_header(); - tm_block.trusted_height = trusted_height; + tm_block.set_trusted_height(trusted_height); let msg = MsgUpdateClient { client_id, @@ -204,20 +209,26 @@ fn test_consensus_state_pruning() { let client_id = tm_client_type().build_client_id(0); + let ctx_b = MockContextConfig::builder() + .host_id(chain_id.clone()) + .latest_height(client_height) + .build::>(); + let mut ctx = MockContextConfig::builder() .host_id(chain_id.clone()) - .host_type(HostType::SyntheticTendermint) .latest_height(client_height) .latest_timestamp(Timestamp::now()) .max_history_size(u64::MAX) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id.clone()) - .client_id(client_id.clone()) - .latest_height(client_height) - .client_type(tm_client_type()) - .trusting_period(Duration::from_secs(3)) + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init() + .context(&ctx_b) + .params( + MockClientConfig::builder() + .trusting_period(Duration::from_secs(3)) + .build(), + ) .build(), ); @@ -233,7 +244,8 @@ fn test_consensus_state_pruning() { ctx.advance_host_chain_height(); - let mut block = ctx.host_block(&update_height).unwrap().clone(); + let block = ctx.host_block(&update_height).unwrap().clone(); + let mut block = block.into_header(); block.set_trusted_height(client_height); @@ -304,30 +316,29 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let update_height = Height::new(1, 21).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b) + .latest_height(update_height) + .build::>(); + let mut ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. - .latest_height(client_height) + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height]) .build(), ); let mut router = MockRouter::new_with_transfer(); - let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b) - .host_type(HostType::SyntheticTendermint) - .latest_height(update_height) - .build::(); - let signer = dummy_account_id(); - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + let block = ctx_b.host_block(&update_height).unwrap().clone(); + let mut block = block.into_header(); block.set_trusted_height(client_height); let latest_header_height = block.height(); @@ -360,68 +371,76 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { let client_height = Height::new(1, 20).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - let mut ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) - .latest_height(Height::new(1, 1).unwrap()) - .build::() - .with_client_config( - // client state initialized with client_height, and - // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .latest_height(client_height) - .client_type(tm_client_type()) - .build(), - ); - - let mut router_a = MockRouter::new_with_transfer(); - let ctx_b_val_history = vec![ - // First two validator sets are default at client creation - // // validator set of height-20 vec![ TestgenValidator::new("1").voting_power(50), TestgenValidator::new("2").voting_power(50), ], + // next validator set of height-20 // validator set of height-21 vec![ - TestgenValidator::new("1").voting_power(50), - TestgenValidator::new("2").voting_power(50), + TestgenValidator::new("1").voting_power(34), + TestgenValidator::new("2").voting_power(66), ], + // next validator set of height-21 // validator set of height-22 + // overlap maintains 1/3 power in older set vec![ - TestgenValidator::new("1").voting_power(30), - TestgenValidator::new("2").voting_power(70), + TestgenValidator::new("1").voting_power(1), + TestgenValidator::new("4").voting_power(99), ], - // validator set of height-23 + // next validator set of height-22 vec![ TestgenValidator::new("1").voting_power(20), TestgenValidator::new("2").voting_power(80), ], ]; - let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2); + let block_params = BlockParams::from_validator_history(ctx_b_val_history); + + let update_height = client_height.add(block_params.len() as u64 - 1); + + assert_eq!(update_height.revision_height(), 22); let ctx_b = MockContextConfig::builder() .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) .latest_height(update_height) - .max_history_size(ctx_b_val_history.len() as u64 - 1) - .validator_set_history(ctx_b_val_history) - .build::(); + .max_history_size(block_params.len() as u64) + .block_params_history(block_params) + .build::>(); + + let mut ctx_a = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .build::>() + .with_light_client( + &client_id, + // remote light client initialized with client_height + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height]) + .build(), + ); + + let mut router_a = MockRouter::new_with_transfer(); let signer = dummy_account_id(); - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); - block.set_trusted_height(client_height); + let mut block = ctx_b + .host_block(&update_height) + .unwrap() + .clone() + .into_header(); - let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { - HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), - _ => panic!("unexpected host block type"), - }; + let trusted_next_validator_set = ctx_b + .host_block(&client_height) + .expect("no error") + .inner() + .next_validators + .clone(); + block.set_trusted_height(client_height); block.set_trusted_next_validators_set(trusted_next_validator_set); let latest_header_height = block.height(); @@ -446,47 +465,140 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { assert_eq!(client_state.latest_height(), latest_header_height); } +// TODO(rano): refactor the validator change tests to use a single test function + #[rstest] -fn test_update_synthetic_tendermint_client_validator_change_fail() { +fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() { let client_id = tm_client_type().build_client_id(0); let client_height = Height::new(1, 20).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + let ctx_b_val_history = vec![ + // validator set of height-20 + vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ], + // next validator set of height-20 + // validator set of height-21 + vec![ + TestgenValidator::new("1").voting_power(45), + TestgenValidator::new("2").voting_power(55), + ], + // next validator set of height-21 + // validator set of height-22 + vec![ + TestgenValidator::new("1").voting_power(30), + TestgenValidator::new("2").voting_power(70), + ], + // next validator set of height-22 + vec![ + TestgenValidator::new("1").voting_power(20), + TestgenValidator::new("2").voting_power(80), + ], + ]; + + let block_params = BlockParams::from_validator_history(ctx_b_val_history); + + let update_height = client_height.add(block_params.len() as u64 - 1); + + assert_eq!(update_height.revision_height(), 22); + + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .latest_height(update_height) + .max_history_size(block_params.len() as u64) + .block_params_history(block_params) + .build::>(); + let ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build::() - .with_client_config( - // client state initialized with client_height, and - // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .latest_height(client_height) - .client_type(tm_client_type()) + .build::>() + .with_light_client( + &client_id, + // remote light client initialized with client_height + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height]) .build(), ); let router = MockRouter::new_with_transfer(); + let signer = dummy_account_id(); + + // next validator set from height-20 + let trusted_next_validator_set = ctx_b + .host_block(&client_height) + .expect("no error") + .inner() + .next_validators + .clone(); + + // next validator set from height-21 + let mistrusted_next_validator_set = ctx_b + .host_block(&client_height.increment()) + .expect("no error") + .inner() + .next_validators + .clone(); + + // ensure the next validator sets are different + assert_ne!( + mistrusted_next_validator_set.hash(), + trusted_next_validator_set.hash() + ); + + let mut block = ctx_b + .host_block(&update_height) + .unwrap() + .clone() + .into_header(); + + // set the trusted height to height-20 + block.set_trusted_height(client_height); + // set the trusted next validator set from height-21, which is different than height-20 + block.set_trusted_next_validators_set(mistrusted_next_validator_set); + + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, + }; + + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let res = validate(&ctx_a, &router, msg_envelope); + + assert!(res.is_err()); +} + +#[rstest] +fn test_update_synthetic_tendermint_client_validator_change_fail() { + let client_id = tm_client_type().build_client_id(0); + let client_height = Height::new(1, 20).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + let ctx_b_val_history = vec![ - // First two validator sets are default at client creation - // // validator set of height-20 vec![ TestgenValidator::new("1").voting_power(50), TestgenValidator::new("2").voting_power(50), ], - // incorrect next validator set for height-20 + // next validator set of height-20 // validator set of height-21 vec![ - TestgenValidator::new("1").voting_power(45), - TestgenValidator::new("2").voting_power(55), + TestgenValidator::new("1").voting_power(90), + TestgenValidator::new("2").voting_power(10), ], + // next validator set of height-21 // validator set of height-22 + // overlap doesn't maintain 1/3 power in older set vec![ - TestgenValidator::new("1").voting_power(30), - TestgenValidator::new("2").voting_power(70), + // TestgenValidator::new("1").voting_power(0), + TestgenValidator::new("4").voting_power(90), + TestgenValidator::new("2").voting_power(10), ], // validator set of height-23 vec![ @@ -495,37 +607,254 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { ], ]; - let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2); + let block_params = BlockParams::from_validator_history(ctx_b_val_history); + + let update_height = client_height.add(block_params.len() as u64 - 1); + + assert_eq!(update_height.revision_height(), 22); let ctx_b = MockContextConfig::builder() .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) .latest_height(update_height) - .max_history_size(ctx_b_val_history.len() as u64 - 1) - .validator_set_history(ctx_b_val_history) - .build::(); + .max_history_size(block_params.len() as u64) + .block_params_history(block_params) + .build::>(); + + let ctx_a = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .build::>() + .with_light_client( + &client_id, + // remote light client initialized with client_height + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height]) + .build(), + ); + + let router_a = MockRouter::new_with_transfer(); let signer = dummy_account_id(); - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + let trusted_next_validator_set = ctx_b + .host_block(&client_height) + .expect("no error") + .inner() + .next_validators + .clone(); + + let mut block = ctx_b + .host_block(&update_height) + .unwrap() + .clone() + .into_header(); + block.set_trusted_height(client_height); + block.set_trusted_next_validators_set(trusted_next_validator_set); - let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { - HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), - _ => panic!("unexpected host block type"), + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + + assert!(res.is_err()); +} + +#[rstest] +fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { + let client_id = tm_client_type().build_client_id(0); + let client_height = Height::new(1, 20).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let ctx_b_val_history = vec![ + // First two validator sets are default at client creation + // + // validator set of height-20 + vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ], + // validator set of height-21 + // next validator set of height-20 + vec![ + TestgenValidator::new("1").voting_power(34), + TestgenValidator::new("2").voting_power(66), + ], + // validator set of height-22 + // next validator set of height-21 + vec![ + TestgenValidator::new("4").voting_power(90), + TestgenValidator::new("2").voting_power(10), + ], + // next validator set of height-22 + vec![ + TestgenValidator::new("1").voting_power(20), + TestgenValidator::new("2").voting_power(80), + ], + ]; + let mut block_params = BlockParams::from_validator_history(ctx_b_val_history); + + if let Some(block_param) = block_params.last_mut() { + // forged validator set of height-22 + block_param.validators = vec![TestgenValidator::new("1").voting_power(100)]; + } + + let update_height = client_height.add(block_params.len() as u64 - 1); + + assert_eq!(update_height.revision_height(), 22); + + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .latest_height(update_height) + .max_history_size(block_params.len() as u64) + .block_params_history(block_params) + .build::>(); + + let mut ctx_a = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .build::>() + .with_light_client( + &client_id, + // remote light client initialized with client_height + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height]) + .build(), + ); + + let mut router_a = MockRouter::new_with_transfer(); + + let signer = dummy_account_id(); + + let mut block = ctx_b + .host_block(&update_height) + .unwrap() + .clone() + .into_header(); + + let trusted_next_validator_set = ctx_b + .host_block(&client_height) + .expect("no error") + .inner() + .next_validators + .clone(); + + block.set_trusted_height(client_height); block.set_trusted_next_validators_set(trusted_next_validator_set); + let latest_header_height = block.height(); let msg = MsgUpdateClient { client_id, client_message: block.into(), signer, }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); - let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + assert!(res.is_ok()); - let res = validate(&ctx_a, &router, msg_envelope); + let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + assert!(res.is_ok(), "result: {res:?}"); + + let client_state = ctx_a.client_state(&msg.client_id).unwrap(); + assert!(client_state + .status(&ctx_a, &msg.client_id) + .unwrap() + .is_active()); + assert_eq!(client_state.latest_height(), latest_header_height); +} + +#[rstest] +fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_fail() { + let client_id = tm_client_type().build_client_id(0); + let client_height = Height::new(1, 21).unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + let ctx_b_val_history = vec![ + // validator set of height-21 + vec![ + TestgenValidator::new("1").voting_power(34), + TestgenValidator::new("2").voting_power(66), + ], + // next validator set of height-21 + // validator set of height-22 + vec![ + TestgenValidator::new("4").voting_power(90), + TestgenValidator::new("2").voting_power(10), + ], + // next validator set of height-22 + vec![ + TestgenValidator::new("1").voting_power(20), + TestgenValidator::new("2").voting_power(80), + ], + ]; + + let mut block_params = BlockParams::from_validator_history(ctx_b_val_history); + + if let Some(block_param) = block_params.last_mut() { + // forged validator set of height-22 + block_param.validators = vec![TestgenValidator::new("1").voting_power(100)]; + } + + let update_height = client_height.add(block_params.len() as u64 - 1); + + assert_eq!(update_height.revision_height(), 22); + + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .latest_height(update_height) + .max_history_size(block_params.len() as u64) + .block_params_history(block_params) + .build::>(); + + let ctx_a = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .latest_height(Height::new(1, 1).unwrap()) + .build::>() + .with_light_client( + &client_id, + // remote light client initialized with client_height + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height]) + .build(), + ); + + let router_a = MockRouter::new_with_transfer(); + + let signer = dummy_account_id(); + + let mut block = ctx_b + .host_block(&update_height) + .unwrap() + .clone() + .into_header(); + + let trusted_next_validator_set = ctx_b + .host_block(&client_height) + .expect("no error") + .inner() + .next_validators + .clone(); + + block.set_trusted_height(client_height); + block.set_trusted_next_validators_set(trusted_next_validator_set); + + let msg = MsgUpdateClient { + client_id, + client_message: block.into(), + signer, + }; + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + + let res = validate(&ctx_a, &router_a, msg_envelope.clone()); assert!(res.is_err()); } @@ -537,34 +866,29 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let update_height = Height::new(1, 21).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b) + .latest_height(update_height) + .build::>(); + let mut ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. - .latest_height(client_height) - .consensus_state_heights(vec![ - client_height.sub(1).expect("no error"), - client_height, - ]) + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height.sub(1).expect("no error"), client_height]) .build(), ); let mut router = MockRouter::new_with_transfer(); - let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b) - .host_type(HostType::SyntheticTendermint) - .latest_height(update_height) - .build::(); - let signer = dummy_account_id(); - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + let block = ctx_b.host_block(&update_height).unwrap().clone(); + let mut block = block.into_header(); let trusted_height = client_height.clone().sub(1).unwrap(); block.set_trusted_height(trusted_height); @@ -602,48 +926,42 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let ctx_b_chain_id = ChainId::new("mockgaiaB-1").unwrap(); let start_height = Height::new(1, 11).unwrap(); + let ctx_b = MockContextConfig::builder() + .host_id(ctx_b_chain_id) + .latest_height(client_height) + .max_history_size(blocks_since(client_height, start_height).expect("no error") + 1) + .build::>(); + let mut ctx_a = MockContextConfig::builder() .host_id(ctx_a_chain_id) .latest_height(start_height) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(ctx_b_chain_id.clone()) - .client_id(client_id.clone()) - .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. - .latest_height(client_height) - .consensus_state_heights(vec![start_height]) + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([start_height]) .build(), ); let mut router_a = MockRouter::new_with_transfer(); - let ctx_b = MockContextConfig::builder() - .host_id(ctx_b_chain_id) - .host_type(HostType::SyntheticTendermint) - .latest_height(client_height) - .build::(); - let signer = dummy_account_id(); let block = ctx_b.host_block(&client_height).unwrap().clone(); + let mut block = block.into_header(); // Update the trusted height of the header to point to the previous height // (`start_height` in this case). // // Note: The current MockContext interface doesn't allow us to // do this without a major redesign. - let block = match block { - HostBlock::SyntheticTendermint(mut theader) => { - // current problem: the timestamp of the new header doesn't match the timestamp of - // the stored consensus state. If we hack them to match, then commit check fails. - // FIXME: figure out why they don't match. - theader.trusted_height = start_height; - - HostBlock::SyntheticTendermint(theader) - } - _ => block, - }; + + // current problem: the timestamp of the new header doesn't match the timestamp of + // the stored consensus state. If we hack them to match, then commit check fails. + // FIXME: figure out why they don't match. + + block.set_trusted_height(start_height); // Update the client height to `client_height` // @@ -652,9 +970,9 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { { // FIXME: idea: we need to update the light client with the latest block from // chain B - let consensus_state: AnyConsensusState = block.clone().into(); + let consensus_state: AnyConsensusState = block.clone().into_consensus_state().into(); - let tm_block = downcast!(block.clone() => HostBlock::SyntheticTendermint).unwrap(); + let tm_block = █ let chain_id = ChainId::from_str(tm_block.header().chain_id.as_str()).unwrap(); @@ -730,33 +1048,29 @@ fn test_update_synthetic_tendermint_client_lower_height() { let chain_start_height = Height::new(1, 11).unwrap(); + let ctx_b = MockContextConfig::builder() + .host_id(ChainId::new("mockgaiaB-1").unwrap()) + .latest_height(client_height) + .build::>(); + let ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(chain_start_height) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. - .latest_height(client_height) - .build(), + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init().context(&ctx_b).build(), ); let router = MockRouter::new_with_transfer(); - let ctx_b = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaB-1").unwrap()) - .host_type(HostType::SyntheticTendermint) - .latest_height(client_height) - .build::(); - let signer = dummy_account_id(); let block_ref = ctx_b.host_block(&client_update_height).unwrap(); let msg = MsgUpdateClient { client_id, - client_message: block_ref.clone().into(), + client_message: block_ref.clone().into_header().into(), signer, }; @@ -795,7 +1109,10 @@ fn test_update_client_events(fixture: Fixture) { ibc_events[0], IbcEvent::Message(MessageEvent::Client) )); - let update_client_event = downcast!(&ibc_events[1] => IbcEvent::UpdateClient).unwrap(); + + let IbcEvent::UpdateClient(update_client_event) = &ibc_events[1] else { + panic!("UpdateClient event is expected") + }; assert_eq!(update_client_event.client_id(), &client_id); assert_eq!(update_client_event.client_type(), &mock_client_type()); @@ -804,7 +1121,11 @@ fn test_update_client_events(fixture: Fixture) { assert_eq!(update_client_event.header(), &header.to_vec()); } -fn ensure_misbehaviour(ctx: &MockContext, client_id: &ClientId, client_type: &ClientType) { +fn ensure_misbehaviour( + ctx: &MockContext, + client_id: &ClientId, + client_type: &ClientType, +) { let client_state = ctx.client_state(client_id).unwrap(); let status = client_state.status(ctx, client_id).unwrap(); @@ -817,8 +1138,9 @@ fn ensure_misbehaviour(ctx: &MockContext, client_id: &ClientId, client_type: &Cl ibc_events[0], IbcEvent::Message(MessageEvent::Client), )); - let misbehaviour_client_event = - downcast!(&ibc_events[1] => IbcEvent::ClientMisbehaviour).unwrap(); + let IbcEvent::ClientMisbehaviour(misbehaviour_client_event) = &ibc_events[1] else { + panic!("ClientMisbehaviour event is expected") + }; assert_eq!(misbehaviour_client_event.client_id(), client_id); assert_eq!(misbehaviour_client_event.client_type(), client_type); } @@ -854,11 +1176,9 @@ fn test_submit_misbehaviour_nonexisting_client(fixture: Fixture) { let msg_envelope = msg_update_client(&ClientId::from_str("nonexistingclient").unwrap()); - let ctx = MockContext::default().with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(Height::new(0, 42).unwrap()) - .build(), + let ctx = MockContext::::default().with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); let res = validate(&ctx, &router, msg_envelope); assert!(res.is_err()); @@ -872,11 +1192,9 @@ fn test_client_update_misbehaviour_nonexisting_client(fixture: Fixture) { let msg_envelope = msg_update_client(&ClientId::from_str("nonexistingclient").unwrap()); - let ctx = MockContext::default().with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(Height::new(0, 42).unwrap()) - .build(), + let ctx = MockContext::::default().with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); let res = validate(&ctx, &router, msg_envelope); assert!(res.is_err()); @@ -891,44 +1209,45 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { let misbehaviour_height = Height::new(1, 21).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + // Create a mock context for chain-B + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .latest_height(misbehaviour_height) + .build::>(); + // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_type(tm_client_type()) - .latest_height(client_height) + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_height]) .build(), ); let mut router_a = MockRouter::new_with_transfer(); - // Create a mock context for chain-B - let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) - .latest_height(misbehaviour_height) - .build::(); - // Get chain-B's header at `misbehaviour_height` let header1: TmHeader = { - let mut block = ctx_b.host_block(&misbehaviour_height).unwrap().clone(); + let block = ctx_b.host_block(&misbehaviour_height).unwrap().clone(); + let mut block = block.into_header(); block.set_trusted_height(client_height); - block.try_into_tm_block().unwrap().into() + block.into() }; // Generate an equivocal header for chain-B at `misbehaviour_height` let header2 = { - let mut tm_block = HostBlock::generate_tm_block( - chain_id_b, - misbehaviour_height.revision_height(), - Timestamp::now(), - ); - tm_block.trusted_height = client_height; + let mut tm_block = TendermintHost::with_chain_id(chain_id_b) + .generate_block( + misbehaviour_height.revision_height(), + Timestamp::now(), + &Default::default(), + ) + .into_header(); + tm_block.set_trusted_height(client_height); tm_block.into() }; @@ -953,30 +1272,33 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let misbehaviour_height = Height::new(1, 21).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .latest_height(client_height) + .build::>(); + // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .client_type(tm_client_type()) - .latest_height(client_height) - .build(), + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init().context(&ctx_b).build(), ); let mut router_a = MockRouter::new_with_transfer(); // Generate `header1` for chain-B let header1 = { - let mut tm_block = HostBlock::generate_tm_block( - chain_id_b.clone(), - misbehaviour_height.revision_height(), - Timestamp::now(), - ); - tm_block.trusted_height = client_height; + let mut tm_block = TendermintHost::with_chain_id(chain_id_b.clone()) + .generate_block( + misbehaviour_height.revision_height(), + Timestamp::now(), + &Default::default(), + ) + .into_header(); + tm_block.set_trusted_height(client_height); tm_block }; @@ -985,12 +1307,14 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let header2 = { let timestamp = Timestamp::from_nanoseconds(Timestamp::now().nanoseconds() + 1_000_000_000).unwrap(); - let mut tm_block = HostBlock::generate_tm_block( - chain_id_b, - misbehaviour_height.revision_height(), - timestamp, - ); - tm_block.trusted_height = client_height; + let mut tm_block = TendermintHost::with_chain_id(chain_id_b) + .generate_block( + misbehaviour_height.revision_height(), + timestamp, + &Default::default(), + ) + .into_header(); + tm_block.set_trusted_height(client_height); tm_block }; @@ -1023,19 +1347,26 @@ fn test_expired_client() { let trusting_period = Duration::from_secs(64); + let ctx_b = MockContextConfig::builder() + .host_id(chain_id_b) + .latest_height(client_height) + .latest_timestamp(timestamp) + .build::>(); + let mut ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .latest_height(client_height) - .client_type(tm_client_type()) - .latest_timestamp(timestamp) - .trusting_period(trusting_period) + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init() + .context(&ctx_b) + .params( + MockClientConfig::builder() + .trusting_period(trusting_period) + .build(), + ) .build(), ); @@ -1061,32 +1392,32 @@ fn test_client_update_max_clock_drift() { let max_clock_drift = Duration::from_secs(64); + let mut ctx_b = MockContextConfig::builder() + .host_id(chain_id_b.clone()) + .latest_height(client_height) + .latest_timestamp(timestamp) + .max_history_size(u64::MAX) + .build::>(); + let ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_id.clone()) - .latest_height(client_height) - .client_type(tm_client_type()) - .latest_timestamp(timestamp) - .max_clock_drift(max_clock_drift) + .build::>() + .with_light_client( + &client_id, + LightClientBuilder::init() + .context(&ctx_b) + .params( + MockClientConfig::builder() + .max_clock_drift(max_clock_drift) + .build(), + ) .build(), ); let router_a = MockRouter::new_with_transfer(); - let mut ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) - .host_type(HostType::SyntheticTendermint) - .latest_height(client_height) - .latest_timestamp(timestamp) - .max_history_size(u64::MAX) - .build::(); - while ctx_b.host_timestamp().expect("no error") < (ctx_a.host_timestamp().expect("no error") + max_clock_drift).expect("no error") { @@ -1100,13 +1431,16 @@ fn test_client_update_max_clock_drift() { let signer = dummy_account_id(); - let mut block = ctx_b.host_block(&update_height).unwrap().clone(); + let block = ctx_b.host_block(&update_height).unwrap().clone(); + let mut block = block.into_header(); block.set_trusted_height(client_height); - let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") { - HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(), - _ => panic!("unexpected host block type"), - }; + let trusted_next_validator_set = ctx_b + .host_block(&client_height) + .expect("no error") + .inner() + .next_validators + .clone(); block.set_trusted_next_validators_set(trusted_next_validator_set); diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index 7b58f98e6..b94c2b009 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -8,16 +8,16 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::path::ClientConsensusStatePath; use ibc::core::host::ValidationContext; -use ibc::core::primitives::downcast; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; use ibc_testkit::fixtures::core::client::dummy_msg_upgrade_client; use ibc_testkit::fixtures::{Expect, Fixture}; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; enum Ctx { Default, @@ -33,12 +33,10 @@ enum Msg { fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { let client_id = mock_client_type().build_client_id(0); - let ctx_default = MockContext::default(); - let ctx_with_client = MockContext::default().with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(Height::new(0, 42).unwrap()) - .build(), + let ctx_default = MockContext::::default(); + let ctx_with_client = MockContext::::default().with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); let ctx = match ctx_variant { Ctx::Default => ctx_default, @@ -102,8 +100,10 @@ fn upgrade_client_execute(fxt: &mut Fixture, expect: Expect) { ibc_events[0], IbcEvent::Message(MessageEvent::Client) )); - let upgrade_client_event = - downcast!(&ibc_events[1] => IbcEvent::UpgradeClient).unwrap(); + + let IbcEvent::UpgradeClient(upgrade_client_event) = &ibc_events[1] else { + panic!("UpgradeClient event is expected") + }; let plan_height = Height::new(1, 26).unwrap(); assert_eq!(upgrade_client_event.client_id(), &fxt.msg.client_id); diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs index de2386e8d..ff31c346a 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs @@ -16,8 +16,9 @@ use ibc::core::primitives::ZERO_DURATION; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_ack; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use test_log::test; enum Ctx { @@ -57,35 +58,29 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { let mut conn_end_open = default_conn_end.clone(); conn_end_open.set_state(State::Open); // incorrect field - let ctx_default = MockContext::default(); + let ctx_default = MockContext::::default(); let ctx_new = MockContextConfig::builder() .host_id(ChainId::new(&format!("mockgaia-{}", latest_height.revision_number())).unwrap()) .latest_height(latest_height) - .build::(); + .build::>(); let ctx = match ctx { Ctx::New => ctx_new, Ctx::NewWithConnection => ctx_new - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(proof_height) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(proof_height), ) .with_connection(conn_id, default_conn_end), Ctx::DefaultWithConnection => ctx_default - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(proof_height) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(proof_height), ) .with_connection(conn_id, default_conn_end), Ctx::NewWithConnectionEndOpen => ctx_new - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(proof_height) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(proof_height), ) .with_connection(conn_id, conn_end_open), }; @@ -157,7 +152,7 @@ fn conn_open_ack_execute(fxt: &mut Fixture, expect: Expect IbcEvent::OpenAckConnection(e) => e, _ => unreachable!(), }; - let conn_end = ::connection_end( + let conn_end = as ValidationContext>::connection_end( &fxt.ctx, conn_open_try_event.conn_id_on_a(), ) diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs index 67eafe48f..6497a0d26 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs @@ -13,8 +13,9 @@ use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; use ibc_testkit::fixtures::core::connection::dummy_conn_open_confirm; use ibc_testkit::fixtures::{Expect, Fixture}; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use test_log::test; enum Ctx { @@ -32,7 +33,7 @@ fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), ); - let ctx_default = MockContext::default(); + let ctx_default = MockContext::::default(); let incorrect_conn_end_state = ConnectionEnd::new( State::Init, @@ -49,19 +50,15 @@ fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { let ctx = match ctx { Ctx::Default => ctx_default, Ctx::IncorrectConnection => ctx_default - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(Height::new(0, 10).unwrap()) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), ) .with_connection(msg.conn_id_on_b.clone(), incorrect_conn_end_state), Ctx::CorrectConnection => ctx_default - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(Height::new(0, 10).unwrap()) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), ) .with_connection(msg.conn_id_on_b.clone(), correct_conn_end), }; diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs index c3f978419..b23f86bd1 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs @@ -12,8 +12,9 @@ use ibc_testkit::fixtures::core::connection::{ msg_conn_open_with_version, }; use ibc_testkit::fixtures::{Expect, Fixture}; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use test_log::test; enum Ctx { @@ -39,13 +40,11 @@ fn conn_open_init_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture msg_conn_open_init_with_counterparty_conn_id(msg_default, 2), }; - let ctx_default = MockContext::default(); + let ctx_default = MockContext::::default(); let ctx = match ctx_variant { - Ctx::WithClient => ctx_default.with_client_config( - MockClientConfig::builder() - .client_id(msg.client_id_on_a.clone()) - .latest_height(Height::new(0, 10).unwrap()) - .build(), + Ctx::WithClient => ctx_default.with_light_client( + &msg.client_id_on_a, + LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), ), _ => ctx_default, }; diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs index 9800aa789..2f07bec36 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -9,8 +9,9 @@ use ibc::core::primitives::prelude::*; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_try; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use test_log::test; enum Ctx { @@ -53,14 +54,14 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture(); + .build::>(); let ctx = match ctx_variant { - Ctx::Default => MockContext::default(), - Ctx::WithClient => ctx_new.with_client_config( - MockClientConfig::builder() - .client_id(msg.client_id_on_b.clone()) - .latest_height(Height::new(0, client_cons_state_height).unwrap()) - .build(), + Ctx::Default => MockContext::::default(), + Ctx::WithClient => ctx_new.with_light_client( + &msg.client_id_on_b, + LightClientState::::with_latest_height( + Height::new(0, client_cons_state_height).unwrap(), + ), ), }; Fixture { ctx, msg } diff --git a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs index 056936fd1..c51ec2e83 100644 --- a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs +++ b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs @@ -15,13 +15,14 @@ use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, Por use ibc::core::host::ExecutionContext; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_acknowledgement; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; use test_log::test; struct Fixture { - ctx: MockContext, + ctx: MockContext, router: MockRouter, client_height: Height, msg: MsgAcknowledgement, @@ -34,10 +35,9 @@ struct Fixture { #[fixture] fn fixture() -> Fixture { let client_height = Height::new(0, 2).unwrap(); - let ctx = MockContext::default().with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + let ctx = MockContext::::default().with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ); let router = MockRouter::new_with_transfer(); @@ -121,10 +121,9 @@ fn ack_success_no_packet_commitment(fixture: Fixture) { .. } = fixture; let ctx = ctx - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_channel( PortId::transfer(), @@ -155,11 +154,10 @@ fn ack_success_happy_path(fixture: Fixture) { client_height, .. } = fixture; - let mut ctx: MockContext = ctx - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + let mut ctx = ctx + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_channel( PortId::transfer(), diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs index d337c7d4a..0dd2a4658 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs @@ -13,15 +13,16 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; #[test] fn test_chan_close_confirm_validate() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); - let default_context = MockContext::default(); + let default_context = MockContext::::default(); let client_consensus_state_height = default_context.host_height().unwrap(); let conn_end = ConnectionEnd::new( @@ -53,11 +54,9 @@ fn test_chan_close_confirm_validate() { .unwrap(); let context = default_context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(client_consensus_state_height) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(client_consensus_state_height), ) .with_connection(conn_id, conn_end) .with_channel( @@ -80,7 +79,7 @@ fn test_chan_close_confirm_validate() { fn test_chan_close_confirm_execute() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); - let default_context = MockContext::default(); + let default_context = MockContext::::default(); let client_consensus_state_height = default_context.host_height().unwrap(); let conn_end = ConnectionEnd::new( @@ -112,11 +111,9 @@ fn test_chan_close_confirm_execute() { .unwrap(); let mut context = default_context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(client_consensus_state_height) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(client_consensus_state_height), ) .with_connection(conn_id, conn_end) .with_channel( diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs index ccf040f85..a1480fa74 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs @@ -13,9 +13,10 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_init; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; #[test] fn test_chan_close_init_validate() { @@ -49,15 +50,13 @@ fn test_chan_close_init_validate() { .unwrap(); let context = { - let default_context = MockContext::default(); + let default_context = MockContext::::default(); let client_consensus_state_height = default_context.host_height().unwrap(); default_context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(client_consensus_state_height) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(client_consensus_state_height), ) .with_connection(conn_id, conn_end) .with_channel( @@ -109,15 +108,13 @@ fn test_chan_close_init_execute() { .unwrap(); let mut context = { - let default_context = MockContext::default(); + let default_context = MockContext::::default(); let client_consensus_state_height = default_context.host_height().unwrap(); default_context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id.clone()) - .latest_height(client_consensus_state_height) - .build(), + .with_light_client( + &client_id, + LightClientState::::with_latest_height(client_consensus_state_height), ) .with_connection(conn_id, conn_end) .with_channel( diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs index 6a584e2ca..b36dbd65f 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs @@ -14,14 +14,15 @@ use ibc::core::primitives::*; use ibc::core::router::types::module::ModuleId; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_ack; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; use test_log::test; pub struct Fixture { - pub context: MockContext, + pub context: MockContext, pub router: MockRouter, pub module_id: ModuleId, pub msg: MsgChannelOpenAck, @@ -35,7 +36,7 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { let proof_height = 10; - let context = MockContext::default(); + let context = MockContext::::default(); let module_id = ModuleId::new(MODULE_ID_STR.to_string()); let router = MockRouter::new_with_transfer(); @@ -90,11 +91,9 @@ fn chan_open_ack_happy_path(fixture: Fixture) { } = fixture; let context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_a.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_a, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_a, conn_end_on_a) .with_channel( @@ -125,11 +124,9 @@ fn chan_open_ack_execute_happy_path(fixture: Fixture) { } = fixture; let mut context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_a.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_a, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_a, conn_end_on_a) .with_channel( @@ -167,11 +164,9 @@ fn chan_open_ack_fail_no_connection(fixture: Fixture) { } = fixture; let context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_a.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_a, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_channel( msg.port_id_on_a.clone(), @@ -202,11 +197,9 @@ fn chan_open_ack_fail_no_channel(fixture: Fixture) { .. } = fixture; let context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_a.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_a, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_a, conn_end_on_a); @@ -242,11 +235,9 @@ fn chan_open_ack_fail_channel_wrong_state(fixture: Fixture) { ) .unwrap(); let context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_a.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_a, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_a, conn_end_on_a) .with_channel( diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs index f01b4adfd..33d589b05 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs @@ -13,14 +13,15 @@ use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId}; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; use test_log::test; pub struct Fixture { - pub context: MockContext, + pub context: MockContext, pub router: MockRouter, pub msg: MsgChannelOpenConfirm, pub client_id_on_b: ClientId, @@ -33,7 +34,7 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { let proof_height = 10; - let context = MockContext::default(); + let context = MockContext::::default(); let router = MockRouter::new_with_transfer(); @@ -87,11 +88,9 @@ fn chan_open_confirm_validate_happy_path(fixture: Fixture) { } = fixture; let context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_b.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_b, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_b, conn_end_on_b) .with_channel( @@ -122,11 +121,9 @@ fn chan_open_confirm_execute_happy_path(fixture: Fixture) { } = fixture; let mut context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_b.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_b, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_b, conn_end_on_b) .with_channel( @@ -166,11 +163,9 @@ fn chan_open_confirm_fail_no_channel(fixture: Fixture) { .. } = fixture; let context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_b.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_b, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_b, conn_end_on_b); @@ -206,11 +201,9 @@ fn chan_open_confirm_fail_channel_wrong_state(fixture: Fixture) { ) .unwrap(); let context = context - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_b.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_b.clone(), + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_b, conn_end_on_b) .with_channel( diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs index d79f8989c..9949b0079 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs @@ -10,13 +10,14 @@ use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_init; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_init; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; use test_log::test; pub struct Fixture { - pub ctx: MockContext, + pub ctx: MockContext, pub router: MockRouter, pub msg: MsgEnvelope, } @@ -28,7 +29,7 @@ fn fixture() -> Fixture { let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_init)); - let default_ctx = MockContext::default(); + let default_ctx = MockContext::::default(); let router = MockRouter::new_with_transfer(); let msg_conn_init = dummy_msg_conn_open_init(); @@ -46,11 +47,9 @@ fn fixture() -> Fixture { .unwrap(); let ctx = default_ctx - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_a.clone()) - .latest_height(client_height) - .build(), + .with_light_client( + &client_id_on_a, + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a); @@ -114,7 +113,7 @@ fn chan_open_init_execute_happy_path(fixture: Fixture) { fn chan_open_init_fail_no_connection(fixture: Fixture) { let Fixture { router, msg, .. } = fixture; - let res = validate(&MockContext::default(), &router, msg); + let res = validate(&MockContext::::default(), &router, msg); assert!( res.is_err(), diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs index 35d8a55a7..56dd4421a 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs @@ -12,14 +12,15 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_try; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; use test_log::test; pub struct Fixture { - pub ctx: MockContext, + pub ctx: MockContext, pub router: MockRouter, pub msg: MsgEnvelope, pub client_id_on_b: ClientId, @@ -54,7 +55,7 @@ fn fixture() -> Fixture { let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_try)); - let ctx = MockContext::default(); + let ctx = MockContext::::default(); let router = MockRouter::new_with_transfer(); @@ -83,11 +84,9 @@ fn chan_open_try_validate_happy_path(fixture: Fixture) { } = fixture; let ctx = ctx - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_b.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_b, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_b, conn_end_on_b); @@ -110,11 +109,9 @@ fn chan_open_try_execute_happy_path(fixture: Fixture) { } = fixture; let mut ctx = ctx - .with_client_config( - MockClientConfig::builder() - .client_id(client_id_on_b.clone()) - .latest_height(Height::new(0, proof_height).unwrap()) - .build(), + .with_light_client( + &client_id_on_b, + LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_b, conn_end_on_b); diff --git a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs index 773390f17..07fbccff4 100644 --- a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs @@ -16,14 +16,15 @@ use ibc::core::host::ExecutionContext; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::{dummy_msg_recv_packet, dummy_raw_msg_recv_packet}; use ibc_testkit::fixtures::core::signer::dummy_account_id; +use ibc_testkit::hosts::MockHost; use ibc_testkit::relayer::context::RelayerContext; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; use test_log::test; pub struct Fixture { - pub context: MockContext, + pub context: MockContext, pub router: MockRouter, pub client_height: Height, pub host_height: Height, @@ -34,7 +35,7 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { - let context = MockContext::default(); + let context = MockContext::::default(); let router = MockRouter::new_with_transfer(); @@ -114,10 +115,9 @@ fn recv_packet_validate_happy_path(fixture: Fixture) { let packet = &msg.packet; let mut context = context - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_b) .with_channel( @@ -192,10 +192,9 @@ fn recv_packet_timeout_expired(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg_packet_old)); let context = context - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_b) .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_b) @@ -222,10 +221,9 @@ fn recv_packet_execute_happy_path(fixture: Fixture) { .. } = fixture; let mut ctx = context - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_b) .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_b); diff --git a/ibc-testkit/tests/core/ics04_channel/send_packet.rs b/ibc-testkit/tests/core/ics04_channel/send_packet.rs index 7963eda89..205ab9dba 100644 --- a/ibc-testkit/tests/core/ics04_channel/send_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/send_packet.rs @@ -14,14 +14,15 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_packet; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::hosts::MockHost; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use test_log::test; #[test] fn send_packet_processing() { struct Test { name: String, - ctx: MockContext, + ctx: MockContext, packet: Packet, want_pass: bool, } @@ -82,17 +83,16 @@ fn send_packet_processing() { let tests: Vec = vec![ Test { name: "Processing fails because no channel exists in the context".to_string(), - ctx: MockContext::default(), + ctx: MockContext::::default(), packet: packet.clone(), want_pass: false, }, Test { name: "Good parameters".to_string(), - ctx: MockContext::default() - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + ctx: MockContext::::default() + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a.clone()) .with_channel( @@ -106,11 +106,10 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height same as destination chain height".to_string(), - ctx: MockContext::default() - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + ctx: MockContext::::default() + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a.clone()) .with_channel( @@ -124,11 +123,10 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height one more than destination chain height".to_string(), - ctx: MockContext::default() - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + ctx: MockContext::::default() + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a.clone()) .with_channel( @@ -142,11 +140,10 @@ fn send_packet_processing() { }, Test { name: "Packet timeout due to timestamp".to_string(), - ctx: MockContext::default() - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + ctx: MockContext::::default() + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a) .with_channel(PortId::transfer(), ChannelId::default(), chan_end_on_a) diff --git a/ibc-testkit/tests/core/ics04_channel/timeout.rs b/ibc-testkit/tests/core/ics04_channel/timeout.rs index aeb3d2c0a..baa0d4683 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -15,12 +15,13 @@ use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, Por use ibc::core::host::ExecutionContext; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; struct Fixture { - ctx: MockContext, + ctx: MockContext, pub router: MockRouter, client_height: Height, msg: MsgTimeout, @@ -33,10 +34,9 @@ struct Fixture { #[fixture] fn fixture() -> Fixture { let client_height = Height::new(0, 2).unwrap(); - let ctx = MockContext::default().with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + let ctx = MockContext::::default().with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ); let client_height = Height::new(0, 2).unwrap(); @@ -109,10 +109,9 @@ fn timeout_fail_no_channel(fixture: Fixture) { client_height, .. } = fixture; - let ctx = ctx.with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + let ctx = ctx.with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ); let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); let res = validate(&ctx, &router, msg_envelope); @@ -186,10 +185,9 @@ fn timeout_fail_proof_timeout_not_reached(fixture: Fixture) { let packet = msg.packet.clone(); let mut ctx = ctx - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a) .with_channel( @@ -267,10 +265,9 @@ fn timeout_unordered_channel_validate(fixture: Fixture) { let packet = msg.packet.clone(); let mut ctx = ctx - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a) .with_channel( @@ -317,10 +314,9 @@ fn timeout_ordered_channel_validate(fixture: Fixture) { let packet = msg.packet.clone(); let mut ctx = ctx - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + .with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::default(), conn_end_on_a) .with_channel( diff --git a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs index c0321e8b1..b80bd9149 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs @@ -14,12 +14,13 @@ use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, Por use ibc::core::host::ExecutionContext; use ibc::core::primitives::*; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout_on_close; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{MockClientConfig, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; use rstest::*; pub struct Fixture { - pub context: MockContext, + pub context: MockContext, pub router: MockRouter, pub msg: MsgTimeoutOnClose, pub packet_commitment: PacketCommitment, @@ -30,10 +31,9 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { let client_height = Height::new(0, 2).unwrap(); - let context = MockContext::default().with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + let context = MockContext::::default().with_light_client( + &ClientId::default(), + LightClientState::::with_latest_height(client_height), ); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/router.rs b/ibc-testkit/tests/core/router.rs index cc977d56e..95991a434 100644 --- a/ibc-testkit/tests/core/router.rs +++ b/ibc-testkit/tests/core/router.rs @@ -37,6 +37,7 @@ use ibc_testkit::fixtures::core::connection::{ }; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; +use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; @@ -68,7 +69,7 @@ fn routing_module_and_keepers() { } } - type StateCheckFn = dyn FnOnce(&MockContext) -> bool; + type StateCheckFn = dyn FnOnce(&MockContext) -> bool; // Test parameters struct Test { @@ -98,7 +99,7 @@ fn routing_module_and_keepers() { .add(core::time::Duration::from_secs(4)) .unwrap(), ) - .build::(); + .build::>(); let mut router = MockRouter::new_with_transfer(); From 5bdab7c3cdac81ecd9fb7771106214be4afa6aec Mon Sep 17 00:00:00 2001 From: Rano | Ranadeep Date: Fri, 29 Mar 2024 12:46:17 +0100 Subject: [PATCH 03/40] feat(ibc-testkit): revamp `MockContext` with chain capabilities (#1135) * imp: impl ctxs on MockIbcStore * fix: set update_meta whenever build light client * imp: generate_client_state should take latest_height * imp: remove unnecessary latest_client_states method * refactor: migrate host relevant fields/methods * fix: get back validate_self_client * imp: bring context types up under src * rm default for CommitmentPrefix * rm default height and timestamp impl for TestBlock * default MockStore * AnyClient and AnyConsensus state in MockIbcStore * at least one consensus state when bootstrapping light client * revision number in MockIbcStore * advance height in MockIbcStore * sync host and ibc store advance in conext * add host params to build host * update host trait * convenient type generics for host associated types * update MockHost * return existing block header with correct timestamp * update TendermintHost * update MockGenericContext impl * update mock context building * add implied trait bounds * refactor method * rm redundant imports and impl * call advance_block on context * update MockContext tests * rm ClientStateCommon * use HostParams to build a host * rm using max_history_size * update few tests * ignore failing tests * clippy::use_self * clippy::flat_map_option * clippy::cloned_instead_of_copied * clippy::redundant_clone * clippy::redundant_type_annotations * clippy::as_underscore * disable slow testcase * prune old host consensus state * prune host consensus history in fixture * enable ignored test * return client_id in fixture * fix and enable failing test * avoid Arc and Mutex * fix doc build * refactor host trait and impls * into over Self::from * fix msrv * explicit relative or global path for pub use * clippy::map_identity * clippy::inconsistent_struct_constructor * clippy::std_instead_of_core * use commitment_root for block generation * rename consensus_states to host_consensus_states * use basecoin proof specs * update tests * imp: add history() under TestHost * chore: add docstring for some of methods under TestHost * update TestHost trait * update MockHost and TendermintHost * add MockGenericContext::generate_genesis_block * update MockContextConfig * update tests * update remaining tests * rm comments and rename test * rm HostParams --------- Co-authored-by: Farhad Shabani --- .../ics23-commitment/types/src/commitment.rs | 6 - ibc-testkit/src/context.rs | 488 ++++++++++++++ .../src/fixtures/applications/transfer.rs | 4 +- .../src/fixtures/clients/tendermint.rs | 4 +- ibc-testkit/src/fixtures/core/channel/mod.rs | 23 +- .../src/fixtures/core/channel/packet.rs | 2 +- ibc-testkit/src/fixtures/core/client/mod.rs | 6 +- .../src/fixtures/core/connection/mod.rs | 9 +- ibc-testkit/src/fixtures/core/context.rs | 129 ++-- ibc-testkit/src/fixtures/mod.rs | 5 +- ibc-testkit/src/hosts/mock.rs | 54 +- ibc-testkit/src/hosts/mod.rs | 98 ++- ibc-testkit/src/hosts/tendermint.rs | 92 ++- ibc-testkit/src/lib.rs | 1 + ibc-testkit/src/relayer/context.rs | 27 +- .../testapp/ibc/clients/mock/client_state.rs | 18 +- .../ibc/clients/mock/consensus_state.rs | 6 +- .../src/testapp/ibc/clients/mock/header.rs | 6 +- .../testapp/ibc/clients/mock/misbehaviour.rs | 4 +- .../src/testapp/ibc/clients/mock/mod.rs | 2 +- ibc-testkit/src/testapp/ibc/clients/mod.rs | 30 +- .../src/testapp/ibc/core/client_ctx.rs | 63 +- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 178 ++--- .../src/testapp/ibc/core/router/mod.rs | 2 +- ibc-testkit/src/testapp/ibc/core/types.rs | 628 +++--------------- ibc-testkit/src/testapp/ibc/mod.rs | 1 - ibc-testkit/src/testapp/ibc/utils.rs | 6 - ibc-testkit/src/utils/mod.rs | 4 +- .../tests/core/ics02_client/create_client.rs | 18 +- .../tests/core/ics02_client/update_client.rs | 352 ++++++---- .../tests/core/ics02_client/upgrade_client.rs | 12 +- .../core/ics03_connection/conn_open_ack.rs | 63 +- .../ics03_connection/conn_open_confirm.rs | 39 +- .../core/ics03_connection/conn_open_init.rs | 21 +- .../core/ics03_connection/conn_open_try.rs | 37 +- .../core/ics04_channel/acknowledgement.rs | 30 +- .../core/ics04_channel/chan_close_confirm.rs | 15 +- .../core/ics04_channel/chan_close_init.rs | 15 +- .../tests/core/ics04_channel/chan_open_ack.rs | 15 +- .../core/ics04_channel/chan_open_confirm.rs | 13 +- .../core/ics04_channel/chan_open_init.rs | 13 +- .../tests/core/ics04_channel/chan_open_try.rs | 13 +- .../tests/core/ics04_channel/recv_packet.rs | 30 +- .../tests/core/ics04_channel/send_packet.rs | 7 +- .../tests/core/ics04_channel/timeout.rs | 72 +- .../core/ics04_channel/timeout_on_close.rs | 27 +- ibc-testkit/tests/core/router.rs | 25 +- 47 files changed, 1426 insertions(+), 1287 deletions(-) create mode 100644 ibc-testkit/src/context.rs delete mode 100644 ibc-testkit/src/testapp/ibc/utils.rs diff --git a/ibc-core/ics23-commitment/types/src/commitment.rs b/ibc-core/ics23-commitment/types/src/commitment.rs index f5c23edc2..25f4ca579 100644 --- a/ibc-core/ics23-commitment/types/src/commitment.rs +++ b/ibc-core/ics23-commitment/types/src/commitment.rs @@ -148,12 +148,6 @@ pub struct CommitmentPrefix { bytes: Vec, } -impl Default for CommitmentPrefix { - fn default() -> Self { - Self { bytes: vec![0x00] } - } -} - impl CommitmentPrefix { pub fn as_bytes(&self) -> &[u8] { &self.bytes diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs new file mode 100644 index 000000000..846c08a91 --- /dev/null +++ b/ibc-testkit/src/context.rs @@ -0,0 +1,488 @@ +use core::fmt::Debug; +use core::time::Duration; + +use basecoin_store::avl::get_proof_spec as basecoin_proof_spec; +use basecoin_store::context::ProvableStore; +use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore}; +use ibc::core::channel::types::channel::ChannelEnd; +use ibc::core::channel::types::commitment::PacketCommitment; +use ibc::core::client::context::client_state::ClientStateValidation; +use ibc::core::client::context::ClientExecutionContext; +use ibc::core::client::types::Height; +use ibc::core::commitment_types::specs::ProofSpecs; +use ibc::core::connection::types::ConnectionEnd; +use ibc::core::entrypoint::dispatch; +use ibc::core::handler::types::events::IbcEvent; +use ibc::core::handler::types::msgs::MsgEnvelope; +use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; +use ibc::core::host::types::path::{ + ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, ConnectionPath, + SeqAckPath, SeqRecvPath, SeqSendPath, +}; +use ibc::core::host::{ExecutionContext, ValidationContext}; +use ibc::core::router::router::Router; +use ibc::primitives::prelude::*; +use ibc::primitives::Timestamp; +use typed_builder::TypedBuilder; + +use super::testapp::ibc::core::types::{LightClientState, MockIbcStore}; +use crate::fixtures::core::context::MockContextConfig; +use crate::hosts::{HostClientState, TestBlock, TestHeader, TestHost}; +use crate::relayer::error::RelayerError; +use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::testapp::ibc::core::types::DEFAULT_BLOCK_TIME_SECS; + +/// A context implementing the dependencies necessary for testing any IBC module. +#[derive(Debug)] +pub struct MockGenericContext +where + S: ProvableStore + Debug, + H: TestHost, + HostClientState: ClientStateValidation>, +{ + /// The type of host chain underlying this mock context. + pub host: H, + + /// An object that stores all IBC related data. + pub ibc_store: MockIbcStore, +} + +pub type MockStore = RevertibleStore>; +pub type MockContext = MockGenericContext; + +#[derive(Debug, TypedBuilder)] +pub struct MockClientConfig { + #[builder(default = Duration::from_secs(64000))] + pub trusting_period: Duration, + #[builder(default = Duration::from_millis(3000))] + pub max_clock_drift: Duration, + #[builder(default = Duration::from_secs(128_000))] + pub unbonding_period: Duration, + #[builder(default = vec![basecoin_proof_spec()].into())] + pub proof_specs: ProofSpecs, +} + +impl Default for MockClientConfig { + fn default() -> Self { + Self::builder().build() + } +} + +/// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are +/// present, and the chain has Height(5). This should be used sparingly, mostly for testing the +/// creation of new domain objects. +impl Default for MockGenericContext +where + S: ProvableStore + Debug + Default, + H: TestHost, + HostClientState: ClientStateValidation>, +{ + fn default() -> Self { + MockContextConfig::builder().build() + } +} + +/// Implementation of internal interface for use in testing. The methods in this interface should +/// _not_ be accessible to any Ics handler. +impl MockGenericContext +where + S: ProvableStore + Debug, + H: TestHost, + HostClientState: ClientStateValidation>, +{ + pub fn ibc_store(&self) -> &MockIbcStore { + &self.ibc_store + } + + pub fn host_block(&self, target_height: &Height) -> Option { + self.host.get_block(target_height) + } + + pub fn query_latest_block(&self) -> Option { + self.host.get_block(&self.latest_height()) + } + + pub fn advance_block_up_to(mut self, target_height: Height) -> Self { + let latest_height = self.host.latest_height(); + if target_height.revision_number() != latest_height.revision_number() { + panic!("Cannot advance history of the chain to a different revision number!") + } else if target_height.revision_height() < latest_height.revision_height() { + panic!("Cannot rewind history of the chain to a smaller revision height!") + } else { + // Repeatedly advance the host chain height till we hit the desired height + while self.host.latest_height().revision_height() < target_height.revision_height() { + self.advance_block() + } + } + self + } + + pub fn generate_genesis_block(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { + // commit store + let app_hash = self.ibc_store.commit().expect("no error"); + + // generate and push genesis block + let genesis_block = self.host.generate_block(app_hash, 1, genesis_time, params); + self.host.push_block(genesis_block); + + // store it in ibc context as host consensus state + self.ibc_store.store_host_consensus_state( + self.host + .latest_block() + .into_header() + .into_consensus_state() + .into(), + ); + } + + pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) { + // commit store + let app_hash = self.ibc_store.commit().expect("no error"); + + // generate a new block + self.host.advance_block(app_hash, block_time, params); + + // store it in ibc context as host consensus state + self.ibc_store.store_host_consensus_state( + self.host + .latest_block() + .into_header() + .into_consensus_state() + .into(), + ); + } + + pub fn advance_block(&mut self) { + self.advance_with_block_params( + Duration::from_secs(DEFAULT_BLOCK_TIME_SECS), + &Default::default(), + ) + } + + pub fn prune_block_till(&mut self, height: &Height) { + self.host.prune_block_till(height); + self.ibc_store.prune_host_consensus_states_till(height); + } + + pub fn latest_height(&self) -> Height { + let latest_ibc_height = self.ibc_store.host_height().expect("Never fails"); + let latest_host_height = self.host.latest_height(); + assert_eq!( + latest_ibc_height, latest_host_height, + "The IBC store and the host chain must have the same height" + ); + latest_ibc_height + } + + pub fn latest_timestamp(&self) -> Timestamp { + self.host.latest_block().timestamp() + } + + pub fn timestamp_at(&self, height: Height) -> Timestamp { + self.host + .get_block(&height) + .expect("block exists") + .timestamp() + } + + pub fn with_client_state(mut self, client_id: &ClientId, client_state: AnyClientState) -> Self { + let client_state_path = ClientStatePath::new(client_id.clone()); + self.ibc_store + .store_client_state(client_state_path, client_state) + .expect("error writing to store"); + self + } + + pub fn with_consensus_state( + mut self, + client_id: &ClientId, + height: Height, + consensus_state: AnyConsensusState, + ) -> Self { + let consensus_state_path = ClientConsensusStatePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + self.ibc_store + .store_consensus_state(consensus_state_path, consensus_state) + .expect("error writing to store"); + + self + } + + pub fn generate_light_client( + &self, + mut consensus_heights: Vec, + client_params: &H::LightClientParams, + ) -> LightClientState { + let client_height = if let Some(&height) = consensus_heights.last() { + height + } else { + consensus_heights.push(self.latest_height()); + self.latest_height() + }; + + let client_state = self + .host + .generate_client_state(&client_height, client_params); + + let consensus_states = consensus_heights + .into_iter() + .map(|height| { + ( + height, + self.host_block(&height) + .expect("block exists") + .into_header() + .into_consensus_state(), + ) + }) + .collect(); + + LightClientState { + client_state, + consensus_states, + } + } + + pub fn with_light_client( + mut self, + client_id: &ClientId, + light_client: LightClientState, + ) -> Self + where + RH: TestHost, + { + self = self.with_client_state(client_id, light_client.client_state.into()); + + for (height, consensus_state) in light_client.consensus_states { + self = self.with_consensus_state(client_id, height, consensus_state.into()); + + self.ibc_store + .store_update_meta( + client_id.clone(), + height, + self.latest_timestamp(), + self.latest_height(), + ) + .expect("error writing to store"); + } + + self + } + + /// Associates a connection to this context. + pub fn with_connection( + mut self, + connection_id: ConnectionId, + connection_end: ConnectionEnd, + ) -> Self { + let connection_path = ConnectionPath::new(&connection_id); + self.ibc_store + .store_connection(&connection_path, connection_end) + .expect("error writing to store"); + self + } + + /// Associates a channel (in an arbitrary state) to this context. + pub fn with_channel( + mut self, + port_id: PortId, + chan_id: ChannelId, + channel_end: ChannelEnd, + ) -> Self { + let channel_end_path = ChannelEndPath::new(&port_id, &chan_id); + self.ibc_store + .store_channel(&channel_end_path, channel_end) + .expect("error writing to store"); + self + } + + pub fn with_send_sequence( + mut self, + port_id: PortId, + chan_id: ChannelId, + seq_number: Sequence, + ) -> Self { + let seq_send_path = SeqSendPath::new(&port_id, &chan_id); + self.ibc_store + .store_next_sequence_send(&seq_send_path, seq_number) + .expect("error writing to store"); + self + } + + pub fn with_recv_sequence( + mut self, + port_id: PortId, + chan_id: ChannelId, + seq_number: Sequence, + ) -> Self { + let seq_recv_path = SeqRecvPath::new(&port_id, &chan_id); + self.ibc_store + .store_next_sequence_recv(&seq_recv_path, seq_number) + .expect("error writing to store"); + self + } + + pub fn with_ack_sequence( + mut self, + port_id: PortId, + chan_id: ChannelId, + seq_number: Sequence, + ) -> Self { + let seq_ack_path = SeqAckPath::new(&port_id, &chan_id); + self.ibc_store + .store_next_sequence_ack(&seq_ack_path, seq_number) + .expect("error writing to store"); + self + } + + pub fn with_packet_commitment( + mut self, + port_id: PortId, + chan_id: ChannelId, + seq: Sequence, + data: PacketCommitment, + ) -> Self { + let commitment_path = CommitmentPath::new(&port_id, &chan_id, seq); + self.ibc_store + .store_packet_commitment(&commitment_path, data) + .expect("error writing to store"); + self + } + + /// A datagram passes from the relayer to the IBC module (on host chain). + /// Alternative method to `Ics18Context::send` that does not exercise any serialization. + /// Used in testing the Ics18 algorithms, hence this may return a Ics18Error. + pub fn deliver( + &mut self, + router: &mut impl Router, + msg: MsgEnvelope, + ) -> Result<(), RelayerError> { + dispatch(&mut self.ibc_store, router, msg).map_err(RelayerError::TransactionFailed)?; + // Create a new block. + self.advance_block(); + Ok(()) + } + + pub fn get_events(&self) -> Vec { + self.ibc_store.events.lock().clone() + } + + pub fn get_logs(&self) -> Vec { + self.ibc_store.logs.lock().clone() + } +} + +#[cfg(test)] +mod tests { + use ibc::core::client::context::consensus_state::ConsensusState; + + use super::*; + use crate::hosts::{HostConsensusState, MockHost, TendermintHost}; + use crate::testapp::ibc::core::types::DefaultIbcStore; + + #[test] + fn test_mock_history_validation() { + pub struct Test + where + H: TestHost, + HostConsensusState: ConsensusState, + HostClientState: ClientStateValidation, + { + name: String, + ctx: MockContext, + } + + fn run_tests(sub_title: &str) + where + H: TestHost, + HostConsensusState: ConsensusState, + HostClientState: ClientStateValidation, + { + let cv = 0; // The version to use for all chains. + + let tests: Vec> = vec![ + Test { + name: "Empty history, small pruning window".to_string(), + ctx: MockContextConfig::builder() + .latest_height(Height::new(cv, 1).expect("Never fails")) + .build(), + }, + Test { + name: "Large pruning window".to_string(), + ctx: MockContextConfig::builder() + .latest_height(Height::new(cv, 2).expect("Never fails")) + .build(), + }, + Test { + name: "Small pruning window".to_string(), + ctx: MockContextConfig::builder() + .latest_height(Height::new(cv, 30).expect("Never fails")) + .build(), + }, + Test { + name: "Small pruning window, small starting height".to_string(), + ctx: MockContextConfig::builder() + .latest_height(Height::new(cv, 2).expect("Never fails")) + .build(), + }, + // This is disabled, as now we generate all the blocks till latest_height + // Generating 2000 Tendermint blocks is slow. + // Test { + // name: "Large pruning window, large starting height".to_string(), + // ctx: MockContextConfig::builder() + // .latest_height(Height::new(cv, 2000).expect("Never fails")) + // .build(), + // }, + ]; + + for mut test in tests { + // All tests should yield a valid context after initialization. + assert!( + test.ctx.host.validate().is_ok(), + "failed in test [{}] {} while validating context {:?}", + sub_title, + test.name, + test.ctx + ); + + let current_height = test.ctx.latest_height(); + + // After advancing the chain's height, the context should still be valid. + test.ctx.advance_block(); + assert!( + test.ctx.host.validate().is_ok(), + "failed in test [{}] {} while validating context {:?}", + sub_title, + test.name, + test.ctx + ); + + let next_height = current_height.increment(); + assert_eq!( + test.ctx.latest_height(), + next_height, + "failed while increasing height for context {:?}", + test.ctx + ); + + assert_eq!( + test.ctx + .host + .get_block(¤t_height) + .expect("Never fails") + .height(), + current_height, + "failed while fetching height {:?} of context {:?}", + current_height, + test.ctx + ); + } + } + + run_tests::("Mock Host"); + run_tests::("Synthetic TM Host"); + } +} diff --git a/ibc-testkit/src/fixtures/applications/transfer.rs b/ibc-testkit/src/fixtures/applications/transfer.rs index 35e71fac0..dfd16a5e0 100644 --- a/ibc-testkit/src/fixtures/applications/transfer.rs +++ b/ibc-testkit/src/fixtures/applications/transfer.rs @@ -26,7 +26,7 @@ pub struct MsgTransferConfig { impl From for MsgTransfer { fn from(config: MsgTransferConfig) -> Self { - MsgTransfer { + Self { port_id_on_a: config.port_id_on_a, chan_id_on_a: config.chan_id_on_a, packet_data: config.packet_data, @@ -67,7 +67,7 @@ pub struct PacketDataConfig { impl From for PacketData { fn from(config: PacketDataConfig) -> Self { - PacketData { + Self { token: config.token, sender: config.sender, receiver: config.receiver, diff --git a/ibc-testkit/src/fixtures/clients/tendermint.rs b/ibc-testkit/src/fixtures/clients/tendermint.rs index f96ad0e6b..e10ed576c 100644 --- a/ibc-testkit/src/fixtures/clients/tendermint.rs +++ b/ibc-testkit/src/fixtures/clients/tendermint.rs @@ -100,7 +100,7 @@ impl TryFrom for TmClientState { config.allow_update, )?; - Ok(TmClientState::from(client_state)) + Ok(client_state.into()) } } @@ -146,7 +146,7 @@ pub fn dummy_ics07_header() -> Header { // Build a set of validators. // Below are test values inspired form `test_validator_set()` in tendermint-rs. - let v1: ValidatorInfo = ValidatorInfo::new( + let v1 = ValidatorInfo::new( PublicKey::from_raw_ed25519( &hex::decode_upper("F349539C7E5EF7C49549B09C4BFC2335318AB0FE51FBFAA2433B4F13E816F4A7") .expect("Never fails"), diff --git a/ibc-testkit/src/fixtures/core/channel/mod.rs b/ibc-testkit/src/fixtures/core/channel/mod.rs index a1375cd72..65a507075 100644 --- a/ibc-testkit/src/fixtures/core/channel/mod.rs +++ b/ibc-testkit/src/fixtures/core/channel/mod.rs @@ -10,22 +10,23 @@ mod recv_packet; mod timeout; mod timeout_on_close; -pub use acknowledgement::*; -pub use chan_close_confirm::*; -pub use chan_close_init::*; -pub use chan_open_ack::*; -pub use chan_open_confirm::*; -pub use chan_open_init::*; -pub use chan_open_try::*; use ibc::core::channel::types::proto::v1::{ Channel as RawChannel, Counterparty as RawCounterparty, }; use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc::primitives::prelude::*; -pub use packet::*; -pub use recv_packet::*; -pub use timeout::*; -pub use timeout_on_close::*; + +pub use self::acknowledgement::*; +pub use self::chan_close_confirm::*; +pub use self::chan_close_init::*; +pub use self::chan_open_ack::*; +pub use self::chan_open_confirm::*; +pub use self::chan_open_init::*; +pub use self::chan_open_try::*; +pub use self::packet::*; +pub use self::recv_packet::*; +pub use self::timeout::*; +pub use self::timeout_on_close::*; /// Returns a dummy `RawCounterparty`, for testing purposes only! /// Can be optionally parametrized with a specific channel identifier. diff --git a/ibc-testkit/src/fixtures/core/channel/packet.rs b/ibc-testkit/src/fixtures/core/channel/packet.rs index b33e1b2a4..97815a728 100644 --- a/ibc-testkit/src/fixtures/core/channel/packet.rs +++ b/ibc-testkit/src/fixtures/core/channel/packet.rs @@ -31,7 +31,7 @@ pub struct PacketConfig { impl From for Packet { fn from(config: PacketConfig) -> Self { - Packet { + Self { seq_on_a: config.seq_on_a, port_id_on_a: config.port_id_on_a, chan_id_on_a: config.chan_id_on_a, diff --git a/ibc-testkit/src/fixtures/core/client/mod.rs b/ibc-testkit/src/fixtures/core/client/mod.rs index a536d778c..442bed21d 100644 --- a/ibc-testkit/src/fixtures/core/client/mod.rs +++ b/ibc-testkit/src/fixtures/core/client/mod.rs @@ -5,10 +5,10 @@ mod msg_update_client; mod msg_upgrade_client; #[cfg(feature = "serde")] -pub use msg_create_client::*; +pub use self::msg_create_client::*; #[cfg(feature = "serde")] -pub use msg_update_client::*; -pub use msg_upgrade_client::*; +pub use self::msg_update_client::*; +pub use self::msg_upgrade_client::*; #[cfg(test)] mod tests { diff --git a/ibc-testkit/src/fixtures/core/connection/mod.rs b/ibc-testkit/src/fixtures/core/connection/mod.rs index 5b8388147..3460a3f10 100644 --- a/ibc-testkit/src/fixtures/core/connection/mod.rs +++ b/ibc-testkit/src/fixtures/core/connection/mod.rs @@ -3,16 +3,17 @@ mod conn_open_confirm; mod conn_open_init; mod conn_open_try; -pub use conn_open_ack::*; -pub use conn_open_confirm::*; -pub use conn_open_init::*; -pub use conn_open_try::*; use ibc::core::commitment_types::proto::v1::MerklePrefix; use ibc::core::connection::types::proto::v1::Counterparty as RawCounterparty; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::primitives::prelude::*; use typed_builder::TypedBuilder; +pub use self::conn_open_ack::*; +pub use self::conn_open_confirm::*; +pub use self::conn_open_init::*; +pub use self::conn_open_try::*; + #[derive(TypedBuilder, Debug)] #[builder(build_method(into = RawCounterparty))] pub struct CounterpartyConfig { diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 2a047eba4..724a706a6 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -1,17 +1,16 @@ use alloc::fmt::Debug; -use core::cmp::min; -use core::ops::{Add, Sub}; use core::time::Duration; use basecoin_store::context::ProvableStore; +use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use typed_builder::TypedBuilder; -use crate::hosts::TestHost; -use crate::testapp::ibc::core::types::{MockGenericContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; +use crate::context::MockGenericContext; +use crate::hosts::{HostClientState, TestBlock, TestHost}; +use crate::testapp::ibc::core::types::{MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; use crate::utils::year_2023; /// Configuration of the `MockContext` type for generating dummy contexts. @@ -21,108 +20,78 @@ pub struct MockContextConfig where H: TestHost, { - #[builder(default = ChainId::new("mockgaia-0").expect("Never fails"))] - host_id: ChainId, + #[builder(default)] + pub host: H, #[builder(default = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS))] block_time: Duration, - // may panic if validator_set_history size is less than max_history_size + 1 - #[builder(default = 5)] - max_history_size: u64, + #[builder(default = year_2023())] + latest_timestamp: Timestamp, - #[builder(default, setter(strip_option))] - block_params_history: Option>, + #[builder(default)] + block_params_history: Vec, #[builder(default = Height::new(0, 5).expect("Never fails"))] latest_height: Height, - - #[builder(default = year_2023())] - latest_timestamp: Timestamp, } impl From> for MockGenericContext where S: ProvableStore + Debug + Default, H: TestHost, + HostClientState: ClientStateValidation>, { fn from(params: MockContextConfig) -> Self { - assert_ne!( - params.max_history_size, 0, - "The chain must have a non-zero max_history_size" - ); - assert_ne!( params.latest_height.revision_height(), 0, "The chain must have a non-zero revision_height" ); - // Compute the number of blocks to store. - let n = min( - params.max_history_size, - params.latest_height.revision_height(), + // timestamp at height 1 + let genesis_timestamp = (params.latest_timestamp + - (params.block_time + * u32::try_from(params.latest_height.revision_height() - 1).expect("no overflow"))) + .expect("no underflow"); + + let mut context = Self { + ibc_store: MockIbcStore::new( + params.latest_height.revision_number(), + Default::default(), + ), + host: params.host, + }; + + // store is a height 0; no block + + context.generate_genesis_block(genesis_timestamp, &Default::default()); + + // store is a height 1; one block + + context = context.advance_block_up_to( + params + .latest_height + .sub(params.block_params_history.len() as u64) + .expect("no error"), ); + for block_params in params.block_params_history { + context.advance_with_block_params(params.block_time, &block_params); + } + assert_eq!( - params.host_id.revision_number(), - params.latest_height.revision_number(), - "The version in the chain identifier must match the version in the latest height" + context.host.latest_block().height(), + params.latest_height, + "The latest height in the host must match the latest height in the context" ); - let next_block_timestamp = params - .latest_timestamp - .add(params.block_time) - .expect("Never fails"); - - let host = H::with_chain_id(params.host_id); - - let history = if let Some(validator_set_history) = params.block_params_history { - (0..n) - .rev() - .map(|i| { - // generate blocks with timestamps -> N, N - BT, N - 2BT, ... - // where N = now(), BT = block_time - host.generate_block( - params - .latest_height - .sub(i) - .expect("Never fails") - .revision_height(), - next_block_timestamp - .sub(params.block_time * ((i + 1) as u32)) - .expect("Never fails"), - &validator_set_history[(n - i) as usize - 1], - ) - }) - .collect() - } else { - (0..n) - .rev() - .map(|i| { - // generate blocks with timestamps -> N, N - BT, N - 2BT, ... - // where N = now(), BT = block_time - host.generate_block( - params - .latest_height - .sub(i) - .expect("Never fails") - .revision_height(), - next_block_timestamp - .sub(params.block_time * ((i + 1) as u32)) - .expect("Never fails"), - &H::BlockParams::default(), - ) - }) - .collect() - }; + assert_eq!( + context.host.latest_block().timestamp(), + params.latest_timestamp, + "The latest timestamp in the host must match the latest timestamp in the context" + ); - MockGenericContext { - host, - max_history_size: params.max_history_size, - history, - block_time: params.block_time, - ibc_store: MockIbcStore::default(), - } + context } } diff --git a/ibc-testkit/src/fixtures/mod.rs b/ibc-testkit/src/fixtures/mod.rs index 686274bd6..f6b51ef46 100644 --- a/ibc-testkit/src/fixtures/mod.rs +++ b/ibc-testkit/src/fixtures/mod.rs @@ -6,8 +6,7 @@ use alloc::fmt::Debug; use ibc::core::handler::types::error::ContextError; use ibc::core::primitives::prelude::*; -use crate::hosts::MockHost; -use crate::testapp::ibc::core::types::MockContext; +use crate::testapp::ibc::core::types::DefaultIbcStore; pub enum Expect { Success, Failure(Option), @@ -15,7 +14,7 @@ pub enum Expect { #[derive(Debug)] pub struct Fixture { - pub ctx: MockContext, + pub ctx: DefaultIbcStore, pub msg: M, } diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index 6fdec6309..067d41a14 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -1,52 +1,80 @@ +use alloc::collections::VecDeque; +use alloc::vec::Vec; + use ibc::core::client::types::Height; use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::Timestamp; +use typed_builder::TypedBuilder; use super::{TestBlock, TestHeader, TestHost}; use crate::testapp::ibc::clients::mock::client_state::MockClientState; use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use crate::testapp::ibc::clients::mock::header::MockHeader; -#[derive(Debug)] -pub struct Host(ChainId); +#[derive(TypedBuilder, Debug)] +pub struct MockHost { + /// Unique identifier for the chain. + #[builder(default = ChainId::new("mock-0").expect("Never fails"))] + pub chain_id: ChainId, + /// The chain of blocks underlying this context. + #[builder(default)] + pub history: VecDeque, +} + +impl Default for MockHost { + fn default() -> Self { + Self::builder().build() + } +} -impl TestHost for Host { +impl TestHost for MockHost { type Block = MockHeader; + type ClientState = MockClientState; type BlockParams = (); type LightClientParams = (); - type ClientState = MockClientState; - fn with_chain_id(chain_id: ChainId) -> Self { - Self(chain_id) + fn history(&self) -> &VecDeque { + &self.history } - fn chain_id(&self) -> &ChainId { - &self.0 + fn push_block(&mut self, block: Self::Block) { + self.history.push_back(block); + } + + fn prune_block_till(&mut self, height: &Height) { + while let Some(block) = self.history.front() { + if &block.height() <= height { + self.history.pop_front(); + } else { + break; + } + } } fn generate_block( &self, + _: Vec, height: u64, timestamp: Timestamp, _: &Self::BlockParams, ) -> Self::Block { MockHeader { - height: Height::new(self.chain_id().revision_number(), height).expect("Never fails"), + height: Height::new(self.chain_id.revision_number(), height).expect("Never fails"), timestamp, } } fn generate_client_state( &self, - latest_block: &Self::Block, + latest_height: &Height, _: &Self::LightClientParams, ) -> Self::ClientState { - MockClientState::new(*latest_block) + MockClientState::new(self.get_block(latest_height).expect("height exists")) } } impl TestBlock for MockHeader { - type Header = MockHeader; + type Header = Self; fn height(&self) -> Height { self.height @@ -59,7 +87,7 @@ impl TestBlock for MockHeader { impl From for MockConsensusState { fn from(block: MockHeader) -> Self { - MockConsensusState::new(block) + Self::new(block) } } diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index 056efd7cb..387723a64 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -1,27 +1,35 @@ +pub mod mock; +pub mod tendermint; + +use alloc::collections::VecDeque; use core::fmt::Debug; +use core::ops::Add; +use core::time::Duration; use ibc::core::client::context::consensus_state::ConsensusState; use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; +pub use self::mock::MockHost; +pub use self::tendermint::TendermintHost; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; -pub mod mock; -pub mod tendermint; - -pub use mock::Host as MockHost; -pub use tendermint::Host as TendermintHost; +pub type HostClientState = ::ClientState; +pub type HostBlock = ::Block; +pub type HostBlockParams = ::BlockParams; +pub type HostLightClientParams = ::LightClientParams; +pub type HostHeader = as TestBlock>::Header; +pub type HostConsensusState = as TestHeader>::ConsensusState; /// TestHost is a trait that defines the interface for a host blockchain. -pub trait TestHost: Debug { +pub trait TestHost: Default + Debug + Sized { /// The type of block produced by the host. type Block: TestBlock; /// The type of client state produced by the host. - type ClientState: Into; + type ClientState: Into + Debug; /// The type of block parameters to produce a block type BlockParams: Debug + Default; @@ -29,15 +37,63 @@ pub trait TestHost: Debug { /// The type of light client parameters to produce a light client state type LightClientParams: Debug + Default; - /// Create a new host with the given chain identifier. - fn with_chain_id(chain_id: ChainId) -> Self; + /// The history of blocks produced by the host chain. + fn history(&self) -> &VecDeque; - /// The chain identifier of the host. - fn chain_id(&self) -> &ChainId; + /// Returns true if the host chain has no blocks. + fn is_empty(&self) -> bool { + self.history().is_empty() + } + + /// The latest height of the host chain. + fn latest_height(&self) -> Height { + self.latest_block().height() + } + + /// The latest block of the host chain. + fn latest_block(&self) -> Self::Block { + self.history().back().cloned().expect("no error") + } + + /// Get the block at the given height. + fn get_block(&self, target_height: &Height) -> Option { + self.history() + .get(target_height.revision_height() as usize - 1) + .cloned() // indexed from 1 + } + + /// Add a block to the host chain. + fn push_block(&mut self, block: Self::Block); + + /// Prune blocks until the given height. + fn prune_block_till(&mut self, height: &Height); + + /// Triggers the advancing of the host chain, by extending the history of blocks (or headers). + fn advance_block( + &mut self, + commitment_root: Vec, + block_time: Duration, + params: &Self::BlockParams, + ) { + let latest_block = self.latest_block(); + + let height = TestBlock::height(&latest_block) + .increment() + .revision_height(); + let timestamp = TestBlock::timestamp(&latest_block) + .add(block_time) + .expect("Never fails"); + + let new_block = self.generate_block(commitment_root, height, timestamp, params); + + // History is not full yet. + self.push_block(new_block); + } /// Generate a block at the given height and timestamp, using the provided parameters. fn generate_block( &self, + commitment_root: Vec, height: u64, timestamp: Timestamp, params: &Self::BlockParams, @@ -46,9 +102,23 @@ pub trait TestHost: Debug { /// Generate a client state using the block at the given height and the provided parameters. fn generate_client_state( &self, - latest_block: &Self::Block, + latest_height: &Height, params: &Self::LightClientParams, ) -> Self::ClientState; + + fn validate(&self) -> Result<(), String> { + // Check that headers in the history are in sequential order. + let latest_height = self.latest_height(); + let mut current_height = Height::min(latest_height.revision_number()); + + while current_height <= latest_height { + if current_height != self.get_block(¤t_height).expect("no error").height() { + return Err("block height does not match".to_owned()); + } + current_height = current_height.increment(); + } + Ok(()) + } } /// TestBlock is a trait that defines the interface for a block produced by a host blockchain. @@ -71,7 +141,7 @@ pub trait TestBlock: Clone + Debug { /// TestHeader is a trait that defines the interface for a header produced by a host blockchain. pub trait TestHeader: Clone + Debug + Into { /// The type of consensus state can be extracted from the header. - type ConsensusState: ConsensusState + Into + From; + type ConsensusState: ConsensusState + Into + From + Clone + Debug; /// The height of the block, as recorded in the header. fn height(&self) -> Height; diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 67cccc11c..91c19b2f8 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -1,3 +1,4 @@ +use alloc::collections::VecDeque; use core::str::FromStr; use ibc::clients::tendermint::client_state::ClientState; @@ -17,30 +18,55 @@ use tendermint_testgen::{ Generator, Header as TestgenHeader, LightBlock as TestgenLightBlock, Validator as TestgenValidator, }; +use typed_builder::TypedBuilder; -use super::{TestBlock, TestHeader, TestHost}; +use crate::context::MockClientConfig; use crate::fixtures::clients::tendermint::ClientStateConfig; -use crate::testapp::ibc::core::types::MockClientConfig; +use crate::hosts::{TestBlock, TestHeader, TestHost}; + +#[derive(TypedBuilder, Debug)] +pub struct TendermintHost { + /// Unique identifier for the chain. + #[builder(default = ChainId::new("mock-0").expect("Never fails"))] + pub chain_id: ChainId, + /// The chain of blocks underlying this context. + #[builder(default)] + pub history: VecDeque, +} -#[derive(Debug)] -pub struct Host(ChainId); +impl Default for TendermintHost { + fn default() -> Self { + Self::builder().build() + } +} -impl TestHost for Host { +impl TestHost for TendermintHost { type Block = TendermintBlock; + type ClientState = ClientState; type BlockParams = BlockParams; type LightClientParams = MockClientConfig; - type ClientState = ClientState; - fn with_chain_id(chain_id: ChainId) -> Self { - Self(chain_id) + fn history(&self) -> &VecDeque { + &self.history } - fn chain_id(&self) -> &ChainId { - &self.0 + fn push_block(&mut self, block: Self::Block) { + self.history.push_back(block); + } + + fn prune_block_till(&mut self, height: &Height) { + while let Some(block) = self.history.front() { + if &block.height() <= height { + self.history.pop_front(); + } else { + break; + } + } } fn generate_block( &self, + commitment_root: Vec, height: u64, timestamp: Timestamp, params: &Self::BlockParams, @@ -48,8 +74,9 @@ impl TestHost for Host { TendermintBlock( TestgenLightBlock::new_default_with_header( TestgenHeader::new(¶ms.validators) + .app_hash(commitment_root.try_into().expect("infallible")) .height(height) - .chain_id(self.chain_id().as_str()) + .chain_id(self.chain_id.as_str()) .next_validators(¶ms.next_validators) .time(timestamp.into_tm_time().expect("Never fails")), ) @@ -62,15 +89,20 @@ impl TestHost for Host { fn generate_client_state( &self, - latest_block: &Self::Block, + latest_height: &Height, params: &Self::LightClientParams, ) -> Self::ClientState { let client_state: ClientState = ClientStateConfig::builder() - .chain_id(self.chain_id().clone()) - .latest_height(latest_block.height()) + .chain_id(self.chain_id.clone()) + .latest_height( + self.get_block(latest_height) + .expect("block exists") + .height(), + ) .trusting_period(params.trusting_period) .max_clock_drift(params.max_clock_drift) .unbonding_period(params.unbonding_period) + .proof_specs(params.proof_specs.clone()) .build() .try_into() .expect("never fails"); @@ -92,6 +124,7 @@ impl TendermintBlock { impl TestBlock for TendermintBlock { type Header = TendermintHeader; + fn height(&self) -> Height { Height::new( ChainId::from_str(self.0.signed_header.header.chain_id.as_str()) @@ -107,7 +140,7 @@ impl TestBlock for TendermintBlock { } } -#[derive(Debug)] +#[derive(Debug, TypedBuilder)] pub struct BlockParams { pub validators: Vec, pub next_validators: Vec, @@ -117,9 +150,11 @@ impl BlockParams { pub fn from_validator_history(validator_history: Vec>) -> Vec { validator_history .windows(2) - .map(|vals| Self { - validators: vals[0].clone(), - next_validators: vals[1].clone(), + .map(|vals| { + Self::builder() + .validators(vals[0].clone()) + .next_validators(vals[1].clone()) + .build() }) .collect() } @@ -127,15 +162,16 @@ impl BlockParams { impl Default for BlockParams { fn default() -> Self { - let validators = vec![ - TestgenValidator::new("1").voting_power(50), - TestgenValidator::new("2").voting_power(50), - ]; - - Self { - validators: validators.clone(), - next_validators: validators, - } + Self::builder() + .validators(vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ]) + .next_validators(vec![ + TestgenValidator::new("1").voting_power(50), + TestgenValidator::new("2").voting_power(50), + ]) + .build() } } @@ -182,7 +218,7 @@ impl From for Header { impl From for ConsensusState { fn from(header: TendermintHeader) -> Self { - ConsensusState::from(header.0.signed_header.header) + header.0.signed_header.header.into() } } diff --git a/ibc-testkit/src/lib.rs b/ibc-testkit/src/lib.rs index bfc4ac35d..f4326ba1d 100644 --- a/ibc-testkit/src/lib.rs +++ b/ibc-testkit/src/lib.rs @@ -15,6 +15,7 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +pub mod context; pub mod fixtures; pub mod hosts; pub mod relayer; diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 1ea3441be..7694749ed 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -1,17 +1,19 @@ use alloc::fmt::Debug; use basecoin_store::context::ProvableStore; +use ibc::clients::tendermint::context::ValidationContext; +use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::Height; use ibc::core::handler::types::error::ContextError; use ibc::core::host::types::identifiers::ClientId; -use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Signer; -use crate::hosts::TestHost; +use crate::context::MockGenericContext; +use crate::hosts::{HostClientState, TestHost}; use crate::testapp::ibc::clients::AnyClientState; -use crate::testapp::ibc::core::types::MockGenericContext; +use crate::testapp::ibc::core::types::MockIbcStore; /// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to /// relay packets between chains. This trait comprises the dependencies towards a single chain. /// Most of the functions in this represent wrappers over the ABCI interface. @@ -33,14 +35,15 @@ impl RelayerContext for MockGenericContext where S: ProvableStore + Debug, H: TestHost, + HostClientState: ClientStateValidation>, { fn query_latest_height(&self) -> Result { - ValidationContext::host_height(self) + self.ibc_store.host_height() } fn query_client_full_state(&self, client_id: &ClientId) -> Option { // Forward call to Ics2. - self.client_state(client_id).ok() + self.ibc_store.client_state(client_id).ok() } fn signer(&self) -> Signer { @@ -53,7 +56,6 @@ where #[cfg(test)] mod tests { use ibc::clients::tendermint::types::client_type as tm_client_type; - use ibc::core::client::context::client_state::ClientStateCommon; use ibc::core::client::types::msgs::{ClientMsg, MsgUpdateClient}; use ibc::core::client::types::Height; use ibc::core::handler::types::msgs::MsgEnvelope; @@ -62,13 +64,14 @@ mod tests { use tracing::debug; use super::RelayerContext; + use crate::context::MockContext; use crate::fixtures::core::context::MockContextConfig; - use crate::hosts::{MockHost, TendermintHost, TestBlock, TestHeader}; + use crate::hosts::{MockHost, TendermintHost, TestBlock, TestHeader, TestHost}; use crate::relayer::context::ClientId; use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use crate::testapp::ibc::core::router::MockRouter; - use crate::testapp::ibc::core::types::{LightClientBuilder, MockContext}; + use crate::testapp::ibc::core::types::LightClientBuilder; /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` /// context, assuming that the latest header on the source context is `src_header`. @@ -133,12 +136,12 @@ mod tests { // Create two mock contexts, one for each chain. let mut ctx_a = MockContextConfig::builder() - .host_id(chain_id_a.clone()) + .host(MockHost::builder().chain_id(chain_id_a).build()) .latest_height(chain_a_start_height) .build::>(); let mut ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(chain_b_start_height) .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A .build::>(); @@ -185,7 +188,7 @@ mod tests { // - send the message to B. We bypass ICS18 interface and call directly into // MockContext `recv` method (to avoid additional serialization steps). let dispatch_res_b = ctx_b.deliver(&mut router_b, MsgEnvelope::Client(client_msg_b)); - let validation_res = ctx_b.validate(); + let validation_res = ctx_b.host.validate(); assert!( validation_res.is_ok(), "context validation failed with error {validation_res:?} for context {ctx_b:?}", @@ -224,7 +227,7 @@ mod tests { // - send the message to A let dispatch_res_a = ctx_a.deliver(&mut router_a, MsgEnvelope::Client(client_msg_a)); - let validation_res = ctx_a.validate(); + let validation_res = ctx_a.host.validate(); assert!( validation_res.is_ok(), "context validation failed with error {validation_res:?} for context {ctx_a:?}", diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 17f3a3bde..658d97fe4 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -98,9 +98,13 @@ impl TryFrom for MockClientState { impl From for RawMockClientState { fn from(value: MockClientState) -> Self { - RawMockClientState { + Self { header: Some(value.header.into()), - trusting_period: value.trusting_period.as_nanos() as _, + trusting_period: value + .trusting_period + .as_nanos() + .try_into() + .expect("no overflow"), frozen: value.frozen, } } @@ -130,7 +134,7 @@ impl TryFrom for MockClientState { impl From for Any { fn from(client_state: MockClientState) -> Self { - Any { + Self { type_url: MOCK_CLIENT_STATE_TYPE_URL.to_string(), value: Protobuf::::encode_vec(client_state), } @@ -188,7 +192,7 @@ impl ClientStateCommon for MockClientState { _proof_upgrade_consensus_state: CommitmentProofBytes, _root: &CommitmentRoot, ) -> Result<(), ClientError> { - let upgraded_mock_client_state = MockClientState::try_from(upgraded_client_state)?; + let upgraded_mock_client_state = Self::try_from(upgraded_client_state)?; MockConsensusState::try_from(upgraded_consensus_state)?; if self.latest_height() >= upgraded_mock_client_state.latest_height() { return Err(UpgradeClientError::LowUpgradeHeight { @@ -305,7 +309,7 @@ where impl ClientStateExecution for MockClientState where E: ClientExecutionContext + MockClientContext, - E::ClientStateRef: From, + E::ClientStateRef: From, E::ConsensusStateRef: ConsensusStateConverter, { fn initialise( @@ -338,7 +342,7 @@ where let header = MockHeader::try_from(header)?; let header_height = header.height; - let new_client_state = MockClientState::new(header); + let new_client_state = Self::new(header); let new_consensus_state = MockConsensusState::new(header); ctx.store_consensus_state( @@ -386,7 +390,7 @@ where upgraded_client_state: Any, upgraded_consensus_state: Any, ) -> Result { - let new_client_state = MockClientState::try_from(upgraded_client_state)?; + let new_client_state = Self::try_from(upgraded_client_state)?; let new_consensus_state = MockConsensusState::try_from(upgraded_consensus_state)?; let latest_height = new_client_state.latest_height(); diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs index 43db3edc4..0640970e1 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs @@ -18,7 +18,7 @@ pub struct MockConsensusState { impl MockConsensusState { pub fn new(header: MockHeader) -> Self { - MockConsensusState { + Self { header, root: CommitmentRoot::from(vec![0]), } @@ -46,7 +46,7 @@ impl TryFrom for MockConsensusState { impl From for RawMockConsensusState { fn from(value: MockConsensusState) -> Self { - RawMockConsensusState { + Self { header: Some(value.header.into()), } } @@ -78,7 +78,7 @@ impl TryFrom for MockConsensusState { impl From for Any { fn from(consensus_state: MockConsensusState) -> Self { - Any { + Self { type_url: MOCK_CONSENSUS_STATE_TYPE_URL.to_string(), value: Protobuf::::encode_vec(consensus_state), } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index 9e874e7d4..804db7ab0 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -42,7 +42,7 @@ impl TryFrom for MockHeader { type Error = ClientError; fn try_from(raw: RawMockHeader) -> Result { - Ok(MockHeader { + Ok(Self { height: raw .height .ok_or(ClientError::Other { @@ -60,7 +60,7 @@ impl TryFrom for MockHeader { impl From for RawMockHeader { fn from(value: MockHeader) -> Self { - RawMockHeader { + Self { height: Some(value.height.into()), timestamp: value.timestamp.nanoseconds(), } @@ -112,7 +112,7 @@ impl TryFrom for MockHeader { impl From for Any { fn from(header: MockHeader) -> Self { - Any { + Self { type_url: MOCK_HEADER_TYPE_URL.to_string(), value: Protobuf::::encode_vec(header), } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs b/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs index 15ea17a40..ff4664da4 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/misbehaviour.rs @@ -38,7 +38,7 @@ impl TryFrom for Misbehaviour { impl From for RawMisbehaviour { fn from(value: Misbehaviour) -> Self { - RawMisbehaviour { + Self { client_id: value.client_id.to_string(), header1: Some(value.header1.into()), header2: Some(value.header2.into()), @@ -70,7 +70,7 @@ impl TryFrom for Misbehaviour { impl From for Any { fn from(misbehaviour: Misbehaviour) -> Self { - Any { + Self { type_url: MOCK_MISBEHAVIOUR_TYPE_URL.to_string(), value: Protobuf::::encode_vec(misbehaviour), } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs b/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs index ec880383c..d26c2865c 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs @@ -6,5 +6,5 @@ pub mod misbehaviour; /// Re-exports mock proto types from the `ibc-proto` crate pub mod proto { - pub use ibc_proto::ibc::mock::*; + pub use ::ibc_proto::ibc::mock::*; } diff --git a/ibc-testkit/src/testapp/ibc/clients/mod.rs b/ibc-testkit/src/testapp/ibc/clients/mod.rs index cb57570f7..05fe539da 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mod.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mod.rs @@ -11,27 +11,43 @@ use ibc::clients::tendermint::types::{ TENDERMINT_CLIENT_STATE_TYPE_URL, TENDERMINT_CONSENSUS_STATE_TYPE_URL, }; use ibc::core::client::types::error::ClientError; +use ibc::core::client::types::Height; use ibc::core::primitives::prelude::*; use ibc::derive::{ClientState, ConsensusState}; use ibc::primitives::proto::{Any, Protobuf}; -use crate::hosts::TestHost; +use super::core::types::MockIbcStore; use crate::testapp::ibc::clients::mock::client_state::{ MockClientState, MOCK_CLIENT_STATE_TYPE_URL, }; use crate::testapp::ibc::clients::mock::consensus_state::{ MockConsensusState, MOCK_CONSENSUS_STATE_TYPE_URL, }; -use crate::testapp::ibc::core::types::MockGenericContext; #[derive(Debug, Clone, From, PartialEq, ClientState)] -#[validation(MockGenericContext)] -#[execution(MockGenericContext)] +#[validation(MockIbcStore)] +#[execution(MockIbcStore)] pub enum AnyClientState { Tendermint(TmClientState), Mock(MockClientState), } +impl AnyClientState { + pub fn latest_height(&self) -> Height { + match self { + Self::Tendermint(cs) => cs.inner().latest_height, + Self::Mock(cs) => cs.latest_height(), + } + } + + pub fn is_frozen(&self) -> bool { + match self { + Self::Tendermint(cs) => cs.inner().is_frozen(), + Self::Mock(cs) => cs.is_frozen(), + } + } +} + impl Protobuf for AnyClientState {} impl TryFrom for AnyClientState { @@ -61,13 +77,13 @@ impl From for Any { impl From for AnyClientState { fn from(client_state: ClientStateType) -> Self { - AnyClientState::Tendermint(client_state.into()) + Self::Tendermint(client_state.into()) } } impl From for AnyConsensusState { fn from(consensus_state: ConsensusStateType) -> Self { - AnyConsensusState::Tendermint(consensus_state.into()) + Self::Tendermint(consensus_state.into()) } } @@ -77,8 +93,6 @@ pub enum AnyConsensusState { Mock(MockConsensusState), } -impl Protobuf for AnyConsensusState {} - impl TryFrom for AnyConsensusState { type Error = ClientError; diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index d491cb6ac..b06c40746 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -15,10 +15,9 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; use ibc::primitives::prelude::*; -use crate::hosts::TestHost; +use super::types::MockIbcStore; use crate::testapp::ibc::clients::mock::client_state::MockClientContext; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; -use crate::testapp::ibc::core::types::MockGenericContext; pub type PortChannelIdMap = BTreeMap>; @@ -33,10 +32,9 @@ pub struct MockClientRecord { pub consensus_states: BTreeMap, } -impl MockClientContext for MockGenericContext +impl MockClientContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { fn host_timestamp(&self) -> Result { ValidationContext::host_timestamp(self) @@ -47,10 +45,9 @@ where } } -impl TmValidationContext for MockGenericContext +impl TmValidationContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { fn host_timestamp(&self) -> Result { ValidationContext::host_timestamp(self) @@ -68,11 +65,10 @@ where description: "Invalid consensus state path".into(), })?; - self.ibc_store - .consensus_state_store + self.consensus_state_store .get_keys(&path) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { Some(consensus_path) } else { @@ -97,7 +93,7 @@ where .try_into() .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers - let keys = self.ibc_store.store.get_keys(&path); + let keys = self.store.get_keys(&path); let found_path = keys.into_iter().find_map(|path| { if let Ok(Path::ClientConsensusState(path)) = path.try_into() { if height @@ -111,8 +107,7 @@ where let consensus_state = found_path .map(|path| { - self.ibc_store - .consensus_state_store + self.consensus_state_store .get(StoreHeight::Pending, &path) .ok_or_else(|| ClientError::ConsensusStateNotFound { client_id: client_id.clone(), @@ -133,7 +128,7 @@ where .try_into() .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers - let keys = self.ibc_store.store.get_keys(&path); + let keys = self.store.get_keys(&path); let found_path = keys.into_iter().rev().find_map(|path| { if let Ok(Path::ClientConsensusState(path)) = path.try_into() { if height @@ -147,8 +142,7 @@ where let consensus_state = found_path .map(|path| { - self.ibc_store - .consensus_state_store + self.consensus_state_store .get(StoreHeight::Pending, &path) .ok_or_else(|| ClientError::ConsensusStateNotFound { client_id: client_id.clone(), @@ -161,17 +155,15 @@ where } } -impl ClientValidationContext for MockGenericContext +impl ClientValidationContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { type ClientStateRef = AnyClientState; type ConsensusStateRef = AnyConsensusState; fn client_state(&self, client_id: &ClientId) -> Result { Ok(self - .ibc_store .client_state_store .get(StoreHeight::Pending, &ClientStatePath(client_id.clone())) .ok_or(ClientError::ClientStateNotFound { @@ -189,7 +181,6 @@ where ) .map_err(|_| ClientError::InvalidHeight)?; let consensus_state = self - .ibc_store .consensus_state_store .get(StoreHeight::Pending, client_cons_state_path) .ok_or(ClientError::ConsensusStateNotFound { @@ -213,7 +204,6 @@ where height.revision_height(), ); let processed_timestamp = self - .ibc_store .client_processed_times .get(StoreHeight::Pending, &client_update_time_path) .ok_or(ClientError::UpdateMetaDataNotFound { @@ -226,7 +216,6 @@ where height.revision_height(), ); let processed_height = self - .ibc_store .client_processed_heights .get(StoreHeight::Pending, &client_update_height_path) .ok_or(ClientError::UpdateMetaDataNotFound { @@ -238,10 +227,9 @@ where } } -impl ClientExecutionContext for MockGenericContext +impl ClientExecutionContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { type ClientStateMut = AnyClientState; @@ -251,9 +239,8 @@ where client_state_path: ClientStatePath, client_state: Self::ClientStateRef, ) -> Result<(), ContextError> { - self.ibc_store - .client_state_store - .set(client_state_path.clone(), client_state) + self.client_state_store + .set(client_state_path, client_state) .map_err(|_| ClientError::Other { description: "Client state store error".to_string(), })?; @@ -267,8 +254,7 @@ where consensus_state_path: ClientConsensusStatePath, consensus_state: Self::ConsensusStateRef, ) -> Result<(), ContextError> { - self.ibc_store - .consensus_state_store + self.consensus_state_store .set(consensus_state_path, consensus_state) .map_err(|_| ClientError::Other { description: "Consensus state store error".to_string(), @@ -280,9 +266,7 @@ where &mut self, consensus_state_path: ClientConsensusStatePath, ) -> Result<(), ContextError> { - self.ibc_store - .consensus_state_store - .delete(consensus_state_path); + self.consensus_state_store.delete(consensus_state_path); Ok(()) } @@ -298,16 +282,13 @@ where height.revision_number(), height.revision_height(), ); - self.ibc_store - .client_processed_times - .delete(client_update_time_path); + self.client_processed_times.delete(client_update_time_path); let client_update_height_path = ClientUpdateHeightPath::new( - client_id.clone(), + client_id, height.revision_number(), height.revision_height(), ); - self.ibc_store - .client_processed_heights + self.client_processed_heights .delete(client_update_height_path); Ok(()) } @@ -327,19 +308,17 @@ where height.revision_number(), height.revision_height(), ); - self.ibc_store - .client_processed_times + self.client_processed_times .set(client_update_time_path, host_timestamp) .map_err(|_| ClientError::Other { description: "store update error".into(), })?; let client_update_height_path = ClientUpdateHeightPath::new( - client_id.clone(), + client_id, height.revision_number(), height.revision_height(), ); - self.ibc_store - .client_processed_heights + self.client_processed_heights .set(client_update_height_path, host_height) .map_err(|_| ClientError::Other { description: "store update error".into(), diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 9ec048898..37a74b20f 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -3,7 +3,7 @@ use core::fmt::Debug; use core::time::Duration; -use basecoin_store::context::ProvableStore; +use basecoin_store::context::{ProvableStore, Store}; use basecoin_store::types::Height as StoreHeight; use ibc::core::channel::types::channel::{ChannelEnd, IdentifiedChannelEnd}; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; @@ -29,24 +29,22 @@ use ibc::core::primitives::{Signer, Timestamp}; use ibc::primitives::ToVec; use ibc_query::core::context::{ProvableContext, QueryContext}; -use crate::hosts::{TestBlock, TestHeader, TestHost}; -use crate::testapp::ibc::clients::mock::client_state::MockClientState; -use crate::testapp::ibc::core::types::MockGenericContext; -use crate::testapp::ibc::utils::blocks_since; +use super::types::{MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; +use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; -impl ValidationContext for MockGenericContext +impl ValidationContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { type V = Self; - type HostClientState = MockClientState; - type HostConsensusState = - <<::Block as TestBlock>::Header as TestHeader>::ConsensusState; + type HostClientState = AnyClientState; + type HostConsensusState = AnyConsensusState; fn host_height(&self) -> Result { - // TODO(rano): height sync with block and merkle tree - Ok(self.history.last().expect("atleast one block").height()) + Ok(Height::new( + *self.revision_number.lock(), + self.store.current_height(), + )?) } fn host_timestamp(&self) -> Result { @@ -57,7 +55,6 @@ where fn client_counter(&self) -> Result { Ok(self - .ibc_store .client_counter .get(StoreHeight::Pending, &NextClientSequencePath) .ok_or(ClientError::Other { @@ -69,20 +66,11 @@ where &self, height: &Height, ) -> Result { - // TODO(rano): height sync with block and merkle tree - let height_delta = blocks_since(self.host_height().expect("no error"), *height) - .expect("no error") as usize; - - let index = self - .history - .len() - .checked_sub(1 + height_delta) - .ok_or(ClientError::MissingLocalConsensusState { height: *height })?; - - Ok(self.history[index] - .clone() - .into_header() - .into_consensus_state()) + let consensus_states_binding = self.host_consensus_states.lock(); + Ok(consensus_states_binding + .get(&height.revision_height()) + .cloned() + .ok_or(ClientError::MissingLocalConsensusState { height: *height })?) } fn validate_self_client( @@ -96,8 +84,9 @@ where .into()); } - let self_chain_id = &self.host.chain_id(); - let self_revision_number = self_chain_id.revision_number(); + let latest_height = self.host_height()?; + + let self_revision_number = latest_height.revision_number(); if self_revision_number != client_state_of_host_on_counterparty .latest_height() @@ -116,7 +105,7 @@ where )); } - let host_current_height = self.latest_height().increment(); + let host_current_height = latest_height.increment(); if client_state_of_host_on_counterparty.latest_height() >= host_current_height { return Err(ContextError::ConnectionError( ConnectionError::InvalidClientState { @@ -134,7 +123,6 @@ where fn connection_end(&self, conn_id: &ConnectionId) -> Result { Ok(self - .ibc_store .connection_end_store .get(StoreHeight::Pending, &ConnectionPath::new(conn_id)) .ok_or(ConnectionError::ConnectionNotFound { @@ -150,7 +138,6 @@ where fn connection_counter(&self) -> Result { Ok(self - .ibc_store .conn_counter .get(StoreHeight::Pending, &NextConnectionSequencePath) .ok_or(ConnectionError::Other { @@ -160,7 +147,6 @@ where fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { Ok(self - .ibc_store .channel_end_store .get( StoreHeight::Pending, @@ -174,7 +160,6 @@ where seq_send_path: &SeqSendPath, ) -> Result { Ok(self - .ibc_store .send_sequence_store .get( StoreHeight::Pending, @@ -188,7 +173,6 @@ where seq_recv_path: &SeqRecvPath, ) -> Result { Ok(self - .ibc_store .recv_sequence_store .get( StoreHeight::Pending, @@ -199,7 +183,6 @@ where fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result { Ok(self - .ibc_store .ack_sequence_store .get( StoreHeight::Pending, @@ -213,7 +196,6 @@ where commitment_path: &CommitmentPath, ) -> Result { Ok(self - .ibc_store .packet_commitment_store .get( StoreHeight::Pending, @@ -228,7 +210,6 @@ where fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result { Ok(self - .ibc_store .packet_receipt_store .is_path_set( StoreHeight::Pending, @@ -249,7 +230,6 @@ where ack_path: &AckPath, ) -> Result { Ok(self - .ibc_store .packet_ack_store .get( StoreHeight::Pending, @@ -265,7 +245,6 @@ where /// `ChannelKeeper::increase_channel_counter`. fn channel_counter(&self) -> Result { Ok(self - .ibc_store .channel_counter .get(StoreHeight::Pending, &NextChannelSequencePath) .ok_or(ChannelError::Other { @@ -275,7 +254,7 @@ where /// Returns the maximum expected time per block fn max_expected_time_per_block(&self) -> Duration { - self.block_time + Duration::from_secs(DEFAULT_BLOCK_TIME_SECS) } fn validate_message_signer(&self, _signer: &Signer) -> Result<(), ContextError> { @@ -288,32 +267,28 @@ where } /// Trait to provide proofs in gRPC service blanket implementations. -impl ProvableContext for MockGenericContext +impl ProvableContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { /// Returns the proof for the given [`Height`] and [`Path`] fn get_proof(&self, height: Height, path: &Path) -> Option> { - self.ibc_store - .store + self.store .get_proof(height.revision_height().into(), &path.to_string().into()) .map(|p| p.to_vec()) } } /// Trait to complete the gRPC service blanket implementations. -impl QueryContext for MockGenericContext +impl QueryContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { /// Returns the list of all client states. fn client_states(&self) -> Result)>, ContextError> { let path = "clients".to_owned().into(); - self.ibc_store - .client_state_store + self.client_state_store .get_keys(&path) .into_iter() .filter_map(|path| { @@ -325,7 +300,6 @@ where }) .map(|client_state_path| { let client_state = self - .ibc_store .client_state_store .get(StoreHeight::Pending, &client_state_path) .ok_or_else(|| ClientError::ClientStateNotFound { @@ -347,11 +321,10 @@ where description: "Invalid consensus state path".into(), })?; - self.ibc_store - .consensus_state_store + self.consensus_state_store .get_keys(&path) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { Some(consensus_path) } else { @@ -364,7 +337,6 @@ where consensus_path.revision_height, )?; let client_state = self - .ibc_store .consensus_state_store .get(StoreHeight::Pending, &consensus_path) .ok_or({ @@ -386,11 +358,10 @@ where description: "Invalid consensus state path".into(), })?; - self.ibc_store - .consensus_state_store + self.consensus_state_store .get_keys(&path) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { Some(consensus_path) } else { @@ -410,11 +381,10 @@ where fn connection_ends(&self) -> Result, ContextError> { let path = "connections".to_owned().into(); - self.ibc_store - .connection_end_store + self.connection_end_store .get_keys(&path) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::Connection(connection_path)) = path.try_into() { Some(connection_path) } else { @@ -423,7 +393,6 @@ where }) .map(|connection_path| { let connection_end = self - .ibc_store .connection_end_store .get(StoreHeight::Pending, &connection_path) .ok_or_else(|| ConnectionError::ConnectionNotFound { @@ -445,7 +414,6 @@ where let client_connection_path = ClientConnectionPath::new(client_id.clone()); Ok(self - .ibc_store .connection_ids_store .get(StoreHeight::Pending, &client_connection_path) .unwrap_or_default()) @@ -455,11 +423,10 @@ where fn channel_ends(&self) -> Result, ContextError> { let path = "channelEnds".to_owned().into(); - self.ibc_store - .channel_end_store + self.channel_end_store .get_keys(&path) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::ChannelEnd(channel_path)) = path.try_into() { Some(channel_path) } else { @@ -468,7 +435,6 @@ where }) .map(|channel_path| { let channel_end = self - .ibc_store .channel_end_store .get(StoreHeight::Pending, &channel_path) .ok_or_else(|| ChannelError::ChannelNotFound { @@ -498,11 +464,10 @@ where description: "Invalid commitment path".into(), })?; - self.ibc_store - .packet_commitment_store + self.packet_commitment_store .get_keys(&path) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::Commitment(commitment_path)) = path.try_into() { Some(commitment_path) } else { @@ -510,8 +475,7 @@ where } }) .filter(|commitment_path| { - self.ibc_store - .packet_commitment_store + self.packet_commitment_store .get(StoreHeight::Pending, commitment_path) .is_some() }) @@ -545,11 +509,10 @@ where description: "Invalid ack path".into(), })?; - self.ibc_store - .packet_ack_store + self.packet_ack_store .get_keys(&ack_path_prefix) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::Ack(ack_path)) = path.try_into() { Some(ack_path) } else { @@ -567,8 +530,7 @@ where collected_paths .into_iter() .filter(|ack_path| { - self.ibc_store - .packet_ack_store + self.packet_ack_store .get(StoreHeight::Pending, ack_path) .is_some() }) @@ -599,8 +561,7 @@ where .into_iter() .map(|seq| ReceiptPath::new(&channel_end_path.0, &channel_end_path.1, seq)) .filter(|receipt_path| { - self.ibc_store - .packet_receipt_store + self.packet_receipt_store .get(StoreHeight::Pending, receipt_path) .is_none() }) @@ -626,11 +587,10 @@ where description: "Invalid commitment path".into(), })?; - self.ibc_store - .packet_commitment_store + self.packet_commitment_store .get_keys(&commitment_path_prefix) .into_iter() - .flat_map(|path| { + .filter_map(|path| { if let Ok(Path::Commitment(commitment_path)) = path.try_into() { Some(commitment_path) } else { @@ -648,8 +608,7 @@ where Ok(collected_paths .into_iter() .filter(|commitment_path: &CommitmentPath| -> bool { - self.ibc_store - .packet_commitment_store + self.packet_commitment_store .get(StoreHeight::Pending, commitment_path) .is_some() }) @@ -658,10 +617,9 @@ where } } -impl ExecutionContext for MockGenericContext +impl ExecutionContext for MockIbcStore where S: ProvableStore + Debug, - H: TestHost, { type E = Self; @@ -674,15 +632,13 @@ where /// Should never fail. fn increase_client_counter(&mut self) -> Result<(), ContextError> { let current_sequence = self - .ibc_store .client_counter .get(StoreHeight::Pending, &NextClientSequencePath) .ok_or(ClientError::Other { description: "client counter not found".into(), })?; - self.ibc_store - .client_counter + self.client_counter .set(NextClientSequencePath, current_sequence + 1) .map_err(|e| ClientError::Other { description: format!("client counter update failed: {e:?}"), @@ -697,8 +653,7 @@ where connection_path: &ConnectionPath, connection_end: ConnectionEnd, ) -> Result<(), ContextError> { - self.ibc_store - .connection_end_store + self.connection_end_store .set(connection_path.clone(), connection_end) .map_err(|_| ConnectionError::Other { description: "Connection end store error".to_string(), @@ -713,13 +668,11 @@ where conn_id: ConnectionId, ) -> Result<(), ContextError> { let mut conn_ids: Vec = self - .ibc_store .connection_ids_store .get(StoreHeight::Pending, client_connection_path) .unwrap_or_default(); conn_ids.push(conn_id); - self.ibc_store - .connection_ids_store + self.connection_ids_store .set(client_connection_path.clone(), conn_ids) .map_err(|_| ConnectionError::Other { description: "Connection ids store error".to_string(), @@ -732,15 +685,13 @@ where /// Should never fail. fn increase_connection_counter(&mut self) -> Result<(), ContextError> { let current_sequence = self - .ibc_store .conn_counter .get(StoreHeight::Pending, &NextConnectionSequencePath) .ok_or(ConnectionError::Other { description: "connection counter not found".into(), })?; - self.ibc_store - .conn_counter + self.conn_counter .set(NextConnectionSequencePath, current_sequence + 1) .map_err(|e| ConnectionError::Other { description: format!("connection counter update failed: {e:?}"), @@ -754,8 +705,7 @@ where commitment_path: &CommitmentPath, commitment: PacketCommitment, ) -> Result<(), ContextError> { - self.ibc_store - .packet_commitment_store + self.packet_commitment_store .set(commitment_path.clone(), commitment) .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) @@ -765,9 +715,7 @@ where &mut self, commitment_path: &CommitmentPath, ) -> Result<(), ContextError> { - self.ibc_store - .packet_commitment_store - .delete(commitment_path.clone()); + self.packet_commitment_store.delete(commitment_path.clone()); Ok(()) } @@ -776,8 +724,7 @@ where receipt_path: &ReceiptPath, _receipt: Receipt, ) -> Result<(), ContextError> { - self.ibc_store - .packet_receipt_store + self.packet_receipt_store .set_path(receipt_path.clone()) .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) @@ -788,15 +735,14 @@ where ack_path: &AckPath, ack_commitment: AcknowledgementCommitment, ) -> Result<(), ContextError> { - self.ibc_store - .packet_ack_store + self.packet_ack_store .set(ack_path.clone(), ack_commitment) .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), ContextError> { - self.ibc_store.packet_ack_store.delete(ack_path.clone()); + self.packet_ack_store.delete(ack_path.clone()); Ok(()) } @@ -805,8 +751,7 @@ where channel_end_path: &ChannelEndPath, channel_end: ChannelEnd, ) -> Result<(), ContextError> { - self.ibc_store - .channel_end_store + self.channel_end_store .set(channel_end_path.clone(), channel_end) .map_err(|_| ChannelError::Other { description: "Channel end store error".to_string(), @@ -819,8 +764,7 @@ where seq_send_path: &SeqSendPath, seq: Sequence, ) -> Result<(), ContextError> { - self.ibc_store - .send_sequence_store + self.send_sequence_store .set(seq_send_path.clone(), seq) .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) @@ -831,8 +775,7 @@ where seq_recv_path: &SeqRecvPath, seq: Sequence, ) -> Result<(), ContextError> { - self.ibc_store - .recv_sequence_store + self.recv_sequence_store .set(seq_recv_path.clone(), seq) .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) @@ -843,8 +786,7 @@ where seq_ack_path: &SeqAckPath, seq: Sequence, ) -> Result<(), ContextError> { - self.ibc_store - .ack_sequence_store + self.ack_sequence_store .set(seq_ack_path.clone(), seq) .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) @@ -852,15 +794,13 @@ where fn increase_channel_counter(&mut self) -> Result<(), ContextError> { let current_sequence = self - .ibc_store .channel_counter .get(StoreHeight::Pending, &NextChannelSequencePath) .ok_or(ChannelError::Other { description: "channel counter not found".into(), })?; - self.ibc_store - .channel_counter + self.channel_counter .set(NextChannelSequencePath, current_sequence + 1) .map_err(|e| ChannelError::Other { description: format!("channel counter update failed: {e:?}"), @@ -870,12 +810,12 @@ where } fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError> { - self.ibc_store.events.lock().push(event); + self.events.lock().push(event); Ok(()) } fn log_message(&mut self, message: String) -> Result<(), ContextError> { - self.ibc_store.logs.lock().push(message); + self.logs.lock().push(message); Ok(()) } } diff --git a/ibc-testkit/src/testapp/ibc/core/router/mod.rs b/ibc-testkit/src/testapp/ibc/core/router/mod.rs index 6629c0e08..faaf47994 100644 --- a/ibc-testkit/src/testapp/ibc/core/router/mod.rs +++ b/ibc-testkit/src/testapp/ibc/core/router/mod.rs @@ -1,4 +1,4 @@ mod context; mod types; -pub use types::*; +pub use self::types::*; diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 28037cf5d..8fe4cddcf 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -2,31 +2,25 @@ use alloc::sync::Arc; use core::fmt::Debug; -use core::ops::{Add, Sub}; -use core::time::Duration; -use basecoin_store::context::ProvableStore; -use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore, SharedStore}; +use basecoin_store::context::{ProvableStore, Store}; +use basecoin_store::impls::SharedStore; use basecoin_store::types::{BinStore, JsonStore, ProtobufStore, TypedSet, TypedStore}; use ibc::core::channel::types::channel::ChannelEnd; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; -use ibc::core::client::context::{ClientExecutionContext, ClientValidationContext}; +use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::types::Height; use ibc::core::connection::types::ConnectionEnd; -use ibc::core::entrypoint::dispatch; use ibc::core::handler::types::events::IbcEvent; -use ibc::core::handler::types::msgs::MsgEnvelope; -use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; +use ibc::core::host::types::identifiers::{ConnectionId, Sequence}; use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, CommitmentPath, ConnectionPath, NextChannelSequencePath, NextClientSequencePath, NextConnectionSequencePath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; -use ibc::core::host::{ExecutionContext, ValidationContext}; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; -use ibc::core::router::router::Router; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::channel::v1::Channel as RawChannelEnd; use ibc_proto::ibc::core::client::v1::Height as RawHeight; @@ -34,19 +28,24 @@ use ibc_proto::ibc::core::connection::v1::ConnectionEnd as RawConnectionEnd; use parking_lot::Mutex; use typed_builder::TypedBuilder; +use crate::context::{MockContext, MockStore}; use crate::fixtures::core::context::MockContextConfig; -use crate::hosts::{TestBlock, TestHeader, TestHost}; -use crate::relayer::error::RelayerError; +use crate::hosts::{HostClientState, TestBlock, TestHeader, TestHost}; +use crate::testapp::ibc::clients::mock::header::MockHeader; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; -use crate::testapp::ibc::utils::blocks_since; pub const DEFAULT_BLOCK_TIME_SECS: u64 = 3; +pub type DefaultIbcStore = MockIbcStore; + /// An object that stores all IBC related data. #[derive(Debug)] pub struct MockIbcStore where S: ProvableStore + Debug, { + /// chain revision number, + pub revision_number: Arc>, + /// Handle to store instance. /// The module is guaranteed exclusive access to all paths in the store key-space. pub store: SharedStore, @@ -86,7 +85,7 @@ where /// A typed-store for packet ack pub packet_ack_store: BinStore, AckPath, AcknowledgementCommitment>, /// Map of host consensus states - pub consensus_states: Arc>>, + pub host_consensus_states: Arc>>, /// IBC Events pub events: Arc>>, /// message logs @@ -97,7 +96,7 @@ impl MockIbcStore where S: ProvableStore + Debug, { - pub fn new(store: S) -> Self { + pub fn new(revision_number: u64, store: S) -> Self { let shared_store = SharedStore::new(store); let mut client_counter = TypedStore::new(shared_store.clone()); @@ -117,12 +116,13 @@ where .expect("no error"); Self { + revision_number: Arc::new(Mutex::new(revision_number)), client_counter, conn_counter, channel_counter, client_processed_times: TypedStore::new(shared_store.clone()), client_processed_heights: TypedStore::new(shared_store.clone()), - consensus_states: Arc::new(Mutex::new(Default::default())), + host_consensus_states: Arc::new(Mutex::new(Default::default())), client_state_store: TypedStore::new(shared_store.clone()), consensus_state_store: TypedStore::new(shared_store.clone()), connection_end_store: TypedStore::new(shared_store.clone()), @@ -139,431 +139,34 @@ where store: shared_store, } } -} -impl Default for MockIbcStore -where - S: ProvableStore + Debug + Default, -{ - fn default() -> Self { - Self::new(S::default()) + pub fn commit(&mut self) -> Result, as Store>::Error> { + self.store.commit() } -} -/// A context implementing the dependencies necessary for testing any IBC module. -#[derive(Debug)] -pub struct MockGenericContext -where - S: ProvableStore + Debug, - H: TestHost, -{ - /// The type of host chain underlying this mock context. - pub host: H, - - /// Maximum size for the history of the host chain. Any block older than this is pruned. - pub max_history_size: u64, - - /// The chain of blocks underlying this context. A vector of size up to `max_history_size` - /// blocks, ascending order by their height (latest block is on the last position). - pub history: Vec, - - /// Average time duration between blocks - pub block_time: Duration, - - /// An object that stores all IBC related data. - pub ibc_store: MockIbcStore, -} - -pub type MockContext = MockGenericContext>, H>; - -#[derive(Debug, TypedBuilder)] -pub struct MockClientConfig { - #[builder(default = Duration::from_secs(64000))] - pub trusting_period: Duration, - #[builder(default = Duration::from_millis(3000))] - pub max_clock_drift: Duration, - #[builder(default = Duration::from_secs(128_000))] - pub unbonding_period: Duration, -} - -impl Default for MockClientConfig { - fn default() -> Self { - Self::builder().build() + pub fn store_host_consensus_state(&mut self, consensus_state: AnyConsensusState) { + self.host_consensus_states + .lock() + .insert(self.store.current_height(), consensus_state); } -} -/// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are -/// present, and the chain has Height(5). This should be used sparingly, mostly for testing the -/// creation of new domain objects. -impl Default for MockGenericContext -where - S: ProvableStore + Debug + Default, - H: TestHost, -{ - fn default() -> Self { - MockContextConfig::builder().build() + pub fn prune_host_consensus_states_till(&self, height: &Height) { + assert!(height.revision_number() == *self.revision_number.lock()); + let mut history = self.host_consensus_states.lock(); + history.retain(|h, _| h > &height.revision_height()); } } -pub struct LightClientState { - pub client_state: H::ClientState, - pub consensus_states: - BTreeMap::Header as TestHeader>::ConsensusState>, -} - -impl Default for LightClientState +impl Default for MockIbcStore where - H: TestHost, + S: ProvableStore + Debug + Default, { fn default() -> Self { - let context = MockContext::::default(); - LightClientBuilder::init().context(&context).build() - } -} - -impl LightClientState -where - H: TestHost, -{ - pub fn with_latest_height(height: Height) -> Self { - let context = MockContextConfig::builder() - .latest_height(height) - .build::>(); - LightClientBuilder::init().context(&context).build() - } -} - -#[derive(TypedBuilder)] -#[builder(builder_method(name = init), build_method(into))] -pub struct LightClientBuilder<'a, H: TestHost> { - context: &'a MockContext, - #[builder(default, setter(into))] - consensus_heights: Vec, - #[builder(default)] - params: H::LightClientParams, -} - -impl<'a, H> From> for LightClientState -where - H: TestHost, -{ - fn from(builder: LightClientBuilder<'a, H>) -> Self { - let LightClientBuilder { - context, - consensus_heights, - params, - } = builder; - - context.generate_light_client(consensus_heights, ¶ms) - } -} - -/// Implementation of internal interface for use in testing. The methods in this interface should -/// _not_ be accessible to any Ics handler. -impl MockGenericContext -where - S: ProvableStore + Debug, - H: TestHost, -{ - pub fn with_client_state(mut self, client_id: &ClientId, client_state: AnyClientState) -> Self { - let client_state_path = ClientStatePath::new(client_id.clone()); - self.store_client_state(client_state_path, client_state) - .expect("error writing to store"); - self - } - - pub fn with_consensus_state( - mut self, - client_id: &ClientId, - height: Height, - consensus_state: AnyConsensusState, - ) -> Self { - let consensus_state_path = ClientConsensusStatePath::new( - client_id.clone(), - height.revision_number(), - height.revision_height(), - ); - self.store_consensus_state(consensus_state_path, consensus_state) - .expect("error writing to store"); - - self - } - - pub fn generate_light_client( - &self, - mut consensus_heights: Vec, - client_params: &H::LightClientParams, - ) -> LightClientState { - let client_height = if let Some(&height) = consensus_heights.last() { - height - } else { - consensus_heights.push(self.latest_height()); - self.latest_height() - }; - - let client_state = self.host.generate_client_state( - self.host_block(&client_height) - .expect("latest block exists"), - client_params, - ); - - let consensus_states = consensus_heights - .into_iter() - .map(|height| { - ( - height, - self.host_block(&height) - .expect("block exists") - .clone() - .into_header() - .into_consensus_state(), - ) - }) - .collect(); - - LightClientState { - client_state, - consensus_states, - } - } - - pub fn with_light_client( - mut self, - client_id: &ClientId, - light_client: LightClientState, - ) -> Self - where - RH: TestHost, - { - self = self.with_client_state(client_id, light_client.client_state.into()); - - for (height, consensus_state) in light_client.consensus_states { - self = self.with_consensus_state(client_id, height, consensus_state.into()); - } - - self - } - - /// Associates a connection to this context. - pub fn with_connection( - mut self, - connection_id: ConnectionId, - connection_end: ConnectionEnd, - ) -> Self { - let connection_path = ConnectionPath::new(&connection_id); - self.store_connection(&connection_path, connection_end) - .expect("error writing to store"); - self - } - - /// Associates a channel (in an arbitrary state) to this context. - pub fn with_channel( - mut self, - port_id: PortId, - chan_id: ChannelId, - channel_end: ChannelEnd, - ) -> Self { - let channel_end_path = ChannelEndPath::new(&port_id, &chan_id); - self.store_channel(&channel_end_path, channel_end) - .expect("error writing to store"); - self - } - - pub fn with_send_sequence( - mut self, - port_id: PortId, - chan_id: ChannelId, - seq_number: Sequence, - ) -> Self { - let seq_send_path = SeqSendPath::new(&port_id, &chan_id); - self.store_next_sequence_send(&seq_send_path, seq_number) - .expect("error writing to store"); - self - } - - pub fn with_recv_sequence( - mut self, - port_id: PortId, - chan_id: ChannelId, - seq_number: Sequence, - ) -> Self { - let seq_recv_path = SeqRecvPath::new(&port_id, &chan_id); - self.store_next_sequence_recv(&seq_recv_path, seq_number) - .expect("error writing to store"); - self - } - - pub fn with_ack_sequence( - mut self, - port_id: PortId, - chan_id: ChannelId, - seq_number: Sequence, - ) -> Self { - let seq_ack_path = SeqAckPath::new(&port_id, &chan_id); - self.store_next_sequence_ack(&seq_ack_path, seq_number) - .expect("error writing to store"); - self - } - - pub fn with_height(self, target_height: Height) -> Self { - let latest_height = self.latest_height(); - if target_height.revision_number() > latest_height.revision_number() { - unimplemented!() - } else if target_height.revision_number() < latest_height.revision_number() { - panic!("Cannot rewind history of the chain to a smaller revision number!") - } else if target_height.revision_height() < latest_height.revision_height() { - panic!("Cannot rewind history of the chain to a smaller revision height!") - } else if target_height.revision_height() > latest_height.revision_height() { - // Repeatedly advance the host chain height till we hit the desired height - let mut ctx = self; - while ctx.latest_height().revision_height() < target_height.revision_height() { - ctx.advance_host_chain_height() - } - ctx - } else { - // Both the revision number and height match - self - } - } - - pub fn with_packet_commitment( - mut self, - port_id: PortId, - chan_id: ChannelId, - seq: Sequence, - data: PacketCommitment, - ) -> Self { - let commitment_path = CommitmentPath::new(&port_id, &chan_id, seq); - self.store_packet_commitment(&commitment_path, data) - .expect("error writing to store"); - self - } - - /// Accessor for a block of the local (host) chain from this context. - /// Returns `None` if the block at the requested height does not exist. - pub fn host_block(&self, target_height: &Height) -> Option<&H::Block> { - let target = target_height.revision_height(); - let latest = self.latest_height().revision_height(); - - // Check that the block is not too advanced, nor has it been pruned. - if (target > latest) || (target <= latest - self.history.len() as u64) { - None // Block for requested height does not exist in history. - } else { - Some(&self.history[self.history.len() + target as usize - latest as usize - 1]) - } - } - - /// Triggers the advancing of the host chain, by extending the history of blocks (or headers). - pub fn advance_host_chain_height(&mut self) { - let latest_block = self.history.last().expect("history cannot be empty"); - - let new_block = self.host.generate_block( - latest_block.height().increment().revision_height(), - latest_block - .timestamp() - .add(self.block_time) - .expect("Never fails"), - &H::BlockParams::default(), - ); - - // Append the new header at the tip of the history. - if self.history.len() as u64 >= self.max_history_size { - // History is full, we rotate and replace the tip with the new header. - self.history.rotate_left(1); - self.history[self.max_history_size as usize - 1] = new_block; - } else { - // History is not full yet. - self.history.push(new_block); - } - } - - /// A datagram passes from the relayer to the IBC module (on host chain). - /// Alternative method to `Ics18Context::send` that does not exercise any serialization. - /// Used in testing the Ics18 algorithms, hence this may return a Ics18Error. - pub fn deliver( - &mut self, - router: &mut impl Router, - msg: MsgEnvelope, - ) -> Result<(), RelayerError> { - dispatch(self, router, msg).map_err(RelayerError::TransactionFailed)?; - // Create a new block. - self.advance_host_chain_height(); - Ok(()) - } - - /// Validates this context. Should be called after the context is mutated by a test. - pub fn validate(&self) -> Result<(), String> { - // Check that the number of entries is not higher than window size. - if self.history.len() as u64 > self.max_history_size { - return Err("too many entries".to_string()); - } - - // Check the content of the history. - if !self.history.is_empty() { - // Get the highest block. - let lh = &self.history[self.history.len() - 1]; - // Check latest is properly updated with highest header height. - if lh.height() != self.latest_height() { - return Err("latest height is not updated".to_string()); - } - } - - // Check that headers in the history are in sequential order. - for i in 1..self.history.len() { - let ph = &self.history[i - 1]; - let h = &self.history[i]; - if ph.height().increment() != h.height() { - return Err("headers in history not sequential".to_string()); - } - } - Ok(()) - } - - pub fn latest_client_states(&self, client_id: &ClientId) -> AnyClientState { - self.client_state(client_id) - .expect("error reading from store") - } - - pub fn latest_consensus_states( - &self, - client_id: &ClientId, - height: &Height, - ) -> AnyConsensusState { - self.consensus_state(&ClientConsensusStatePath::new( - client_id.clone(), - height.revision_number(), - height.revision_height(), - )) - .expect("error reading from store") - } - - pub fn latest_height(&self) -> Height { - self.host_height().expect("Never fails") - } - - pub fn latest_timestamp(&self) -> Timestamp { - self.host_block(&self.latest_height()) - .expect("Never fails") - .timestamp() - } - - pub fn timestamp_at(&self, height: Height) -> Timestamp { - let n_blocks = blocks_since(self.latest_height(), height).expect("less or equal height"); - self.latest_timestamp() - .sub(self.block_time * (n_blocks as u32)) - .expect("Never fails") - } - - pub fn query_latest_block(&self) -> Option<&H::Block> { - self.host_block(&self.latest_height()) - } - - pub fn get_events(&self) -> Vec { - self.ibc_store.events.lock().clone() - } - - pub fn get_logs(&self) -> Vec { - self.ibc_store.logs.lock().clone() + // Note: this creates a MockIbcStore which has MockConsensusState as Host ConsensusState + let mut ibc_store = Self::new(0, S::default()); + ibc_store.commit().expect("no error"); + ibc_store.store_host_consensus_state(MockHeader::default().into_consensus_state().into()); + ibc_store } } @@ -574,118 +177,17 @@ mod tests { use ibc::core::channel::types::error::{ChannelError, PacketError}; use ibc::core::channel::types::packet::Packet; use ibc::core::channel::types::Version; - use ibc::core::host::types::identifiers::ChainId; + use ibc::core::host::types::identifiers::{ChannelId, PortId}; use ibc::core::primitives::Signer; use ibc::core::router::module::Module; + use ibc::core::router::router::Router; use ibc::core::router::types::module::{ModuleExtras, ModuleId}; use super::*; use crate::fixtures::core::channel::PacketConfig; use crate::fixtures::core::signer::dummy_bech32_account; - use crate::hosts::{MockHost, TendermintHost}; use crate::testapp::ibc::core::router::MockRouter; - #[test] - fn test_history_manipulation_mock() { - pub struct Test { - name: String, - ctx: MockContext, - } - - fn run_tests(sub_title: &str) { - let cv = 1; // The version to use for all chains. - let mock_chain_id = ChainId::new(&format!("mockgaia-{cv}")).unwrap(); - - let tests: Vec> = vec![ - Test { - name: "Empty history, small pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(2) - .latest_height(Height::new(cv, 1).expect("Never fails")) - .build(), - }, - Test { - name: "Large pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(30) - .latest_height(Height::new(cv, 2).expect("Never fails")) - .build(), - }, - Test { - name: "Small pruning window".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(3) - .latest_height(Height::new(cv, 30).expect("Never fails")) - .build(), - }, - Test { - name: "Small pruning window, small starting height".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(3) - .latest_height(Height::new(cv, 2).expect("Never fails")) - .build(), - }, - Test { - name: "Large pruning window, large starting height".to_string(), - ctx: MockContextConfig::builder() - .host_id(mock_chain_id.clone()) - .max_history_size(50) - .latest_height(Height::new(cv, 2000).expect("Never fails")) - .build(), - }, - ]; - - for mut test in tests { - // All tests should yield a valid context after initialization. - assert!( - test.ctx.validate().is_ok(), - "failed in test [{}] {} while validating context {:?}", - sub_title, - test.name, - test.ctx - ); - - let current_height = test.ctx.latest_height(); - - // After advancing the chain's height, the context should still be valid. - test.ctx.advance_host_chain_height(); - assert!( - test.ctx.validate().is_ok(), - "failed in test [{}] {} while validating context {:?}", - sub_title, - test.name, - test.ctx - ); - - let next_height = current_height.increment(); - assert_eq!( - test.ctx.latest_height(), - next_height, - "failed while increasing height for context {:?}", - test.ctx - ); - - assert_eq!( - test.ctx - .host_block(¤t_height) - .expect("Never fails") - .height(), - current_height, - "failed while fetching height {:?} of context {:?}", - current_height, - test.ctx - ); - } - } - - run_tests::("Mock Host"); - run_tests::("Synthetic TM Host"); - } - #[test] fn test_router() { #[derive(Debug, Default)] @@ -912,3 +414,63 @@ mod tests { ]; } } + +pub struct LightClientState { + pub client_state: H::ClientState, + pub consensus_states: + BTreeMap::Header as TestHeader>::ConsensusState>, +} + +impl Default for LightClientState +where + H: TestHost, + HostClientState: ClientStateValidation, +{ + fn default() -> Self { + let context = MockContext::::default(); + LightClientBuilder::init().context(&context).build() + } +} + +impl LightClientState +where + H: TestHost, + HostClientState: ClientStateValidation, +{ + pub fn with_latest_height(height: Height) -> Self { + let context = MockContextConfig::builder() + .latest_height(height) + .build::>(); + LightClientBuilder::init().context(&context).build() + } +} + +#[derive(TypedBuilder)] +#[builder(builder_method(name = init), build_method(into))] +pub struct LightClientBuilder<'a, H> +where + H: TestHost, + HostClientState: ClientStateValidation, +{ + context: &'a MockContext, + #[builder(default, setter(into))] + consensus_heights: Vec, + #[builder(default)] + params: H::LightClientParams, +} + +impl<'a, H> From> for LightClientState +where + H: TestHost, + HostClientState: ClientStateValidation, +{ + fn from(builder: LightClientBuilder<'a, H>) -> Self { + let LightClientBuilder { + context, + consensus_heights, + params, + } = builder; + + context.generate_light_client(consensus_heights, ¶ms) + } +} diff --git a/ibc-testkit/src/testapp/ibc/mod.rs b/ibc-testkit/src/testapp/ibc/mod.rs index fb9937e58..f13a3c645 100644 --- a/ibc-testkit/src/testapp/ibc/mod.rs +++ b/ibc-testkit/src/testapp/ibc/mod.rs @@ -1,4 +1,3 @@ pub mod applications; pub mod clients; pub mod core; -pub mod utils; diff --git a/ibc-testkit/src/testapp/ibc/utils.rs b/ibc-testkit/src/testapp/ibc/utils.rs deleted file mode 100644 index d5032cae1..000000000 --- a/ibc-testkit/src/testapp/ibc/utils.rs +++ /dev/null @@ -1,6 +0,0 @@ -use ibc::core::client::types::Height; - -pub fn blocks_since(a: Height, b: Height) -> Option { - (a.revision_number() == b.revision_number() && a.revision_height() >= b.revision_height()) - .then(|| a.revision_height() - b.revision_height()) -} diff --git a/ibc-testkit/src/utils/mod.rs b/ibc-testkit/src/utils/mod.rs index 50880f7e1..502eff987 100644 --- a/ibc-testkit/src/utils/mod.rs +++ b/ibc-testkit/src/utils/mod.rs @@ -3,9 +3,9 @@ use tendermint::Time; /// Returns a `Timestamp` representation of beginning of year 2023. /// -/// This is introduced to initialize [`MockGenericContext`](crate::testapp::ibc::core::types::MockGenericContext)s +/// This is introduced to initialize [`MockGenericContext`](crate::context::MockGenericContext)s /// with the same latest timestamp by default. -/// If two [`MockGenericContext`](crate::testapp::ibc::core::types::MockGenericContext) +/// If two [`MockGenericContext`](crate::context::MockGenericContext) /// are initialized using [`Time::now()`], second one will have a greater timestamp than the first one. /// So, the latest header of the second context can not be submitted to first one. /// We can still set a custom timestamp via [`MockContextConfig`](crate::fixtures::core::context::MockContextConfig). diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 827c00ca7..d2b0dffdd 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -1,3 +1,4 @@ +use basecoin_store::impls::InMemoryStore; use ibc::clients::tendermint::types::{ client_type as tm_client_type, ConsensusState as TmConsensusState, }; @@ -14,19 +15,18 @@ use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; use ibc_testkit::fixtures::core::signer::dummy_account_id; -use ibc_testkit::hosts::{MockHost, TendermintHost}; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::testapp::ibc::core::types::{DefaultIbcStore, MockIbcStore}; use test_log::test; #[test] fn test_create_client_ok() { - let mut ctx = MockContext::::default(); + let mut ctx = DefaultIbcStore::default(); let mut router = MockRouter::new_with_transfer(); let signer = dummy_account_id(); let height = Height::new(0, 42).unwrap(); @@ -51,7 +51,7 @@ fn test_create_client_ok() { assert!(res.is_ok(), "execution happy path"); let expected_client_state = - ClientStateRef::>::try_from(msg.client_state).unwrap(); + ClientStateRef::::try_from(msg.client_state).unwrap(); assert_eq!(expected_client_state.client_type(), client_type); assert_eq!(ctx.client_state(&client_id).unwrap(), expected_client_state); } @@ -60,7 +60,7 @@ fn test_create_client_ok() { fn test_tm_create_client_ok() { let signer = dummy_account_id(); - let mut ctx = MockContext::::default(); + let mut ctx = DefaultIbcStore::default(); let mut router = MockRouter::new_with_transfer(); @@ -88,7 +88,7 @@ fn test_tm_create_client_ok() { assert!(res.is_ok(), "tendermint client execution happy path"); let expected_client_state = - ClientStateRef::>::try_from(msg.client_state).unwrap(); + ClientStateRef::>::try_from(msg.client_state).unwrap(); assert_eq!(expected_client_state.client_type(), client_type); assert_eq!(ctx.client_state(&client_id).unwrap(), expected_client_state); } @@ -97,7 +97,7 @@ fn test_tm_create_client_ok() { fn test_invalid_frozen_tm_client_creation() { let signer = dummy_account_id(); - let ctx = MockContext::::default(); + let ctx = DefaultIbcStore::default(); let router = MockRouter::new_with_transfer(); @@ -114,9 +114,9 @@ fn test_invalid_frozen_tm_client_creation() { signer, ); - let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope.clone()); + let res = validate(&ctx, &router, msg_envelope); assert!(matches!( res, diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index be3542f6e..818dedfdb 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -1,13 +1,15 @@ +use core::fmt::Debug; use core::str::FromStr; use core::time::Duration; +use basecoin_store::context::ProvableStore; use ibc::clients::tendermint::client_state::ClientState; use ibc::clients::tendermint::types::proto::v1::{ClientState as RawTmClientState, Fraction}; use ibc::clients::tendermint::types::{ client_type as tm_client_type, ClientState as TmClientState, Header as TmHeader, Misbehaviour as TmMisbehaviour, }; -use ibc::core::client::context::client_state::{ClientStateCommon, ClientStateValidation}; +use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::msgs::{ClientMsg, MsgUpdateClient}; use ibc::core::client::types::proto::v1::Height as RawHeight; @@ -22,6 +24,7 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; use ibc::primitives::ToVec; +use ibc_testkit::context::{MockClientConfig, MockContext}; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::hosts::tendermint::BlockParams; @@ -33,10 +36,7 @@ use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; use ibc_testkit::testapp::ibc::clients::mock::misbehaviour::Misbehaviour as MockMisbehaviour; use ibc_testkit::testapp::ibc::clients::AnyConsensusState; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{ - LightClientBuilder, LightClientState, MockClientConfig, MockContext, -}; -use ibc_testkit::testapp::ibc::utils::blocks_since; +use ibc_testkit::testapp::ibc::core::types::{LightClientBuilder, LightClientState, MockIbcStore}; use rstest::*; use tendermint_testgen::Validator as TestgenValidator; @@ -97,16 +97,16 @@ fn test_update_client_ok(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); - let res = validate(&ctx, &router, msg_envelope.clone()); + let res = validate(&ctx.ibc_store, &router, msg_envelope.clone()); assert!(res.is_ok(), "validation happy path"); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok(), "execution happy path"); assert_eq!( - ctx.client_state(&msg.client_id).unwrap(), + ctx.ibc_store.client_state(&msg.client_id).unwrap(), MockClientState::new(MockHeader::new(height).with_timestamp(timestamp)).into() ); } @@ -123,14 +123,21 @@ fn test_update_client_with_prev_header() { let height_2 = Height::new(0, 44).unwrap(); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host( + TendermintHost::builder() + .chain_id(chain_id_b.clone()) + .build(), + ) .latest_height(latest_height) .build::>(); - let mut ctx = MockContext::::default().with_light_client( - &client_id, - LightClientBuilder::init().context(&ctx_b).build(), - ); + let mut ctx = MockContext::::default() + .with_light_client( + &client_id, + LightClientBuilder::init().context(&ctx_b).build(), + ) + .ibc_store; + let mut router = MockRouter::new_with_transfer(); fn build_msg_from_header( @@ -139,8 +146,11 @@ fn test_update_client_with_prev_header() { target_height: Height, trusted_height: Height, ) -> MsgEnvelope { - let mut tm_block = TendermintHost::with_chain_id(chain_id) + let mut tm_block = TendermintHost::builder() + .chain_id(chain_id) + .build() .generate_block( + Vec::new(), target_height.revision_height(), Timestamp::now(), &Default::default(), @@ -210,15 +220,14 @@ fn test_consensus_state_pruning() { let client_id = tm_client_type().build_client_id(0); let ctx_b = MockContextConfig::builder() - .host_id(chain_id.clone()) + .host(TendermintHost::builder().chain_id(chain_id.clone()).build()) .latest_height(client_height) .build::>(); let mut ctx = MockContextConfig::builder() - .host_id(chain_id.clone()) + .host(TendermintHost::builder().chain_id(chain_id).build()) .latest_height(client_height) .latest_timestamp(Timestamp::now()) - .max_history_size(u64::MAX) .build::>() .with_light_client( &client_id, @@ -234,7 +243,7 @@ fn test_consensus_state_pruning() { let mut router = MockRouter::new_with_transfer(); - let start_host_timestamp = ctx.host_timestamp().unwrap(); + let start_host_timestamp = ctx.ibc_store.host_timestamp().unwrap(); // Move the chain forward by 2 blocks to pass the trusting period. for _ in 1..=2 { @@ -242,7 +251,7 @@ fn test_consensus_state_pruning() { let update_height = ctx.latest_height(); - ctx.advance_host_chain_height(); + ctx.advance_block(); let block = ctx.host_block(&update_height).unwrap().clone(); let mut block = block.into_header(); @@ -257,8 +266,8 @@ fn test_consensus_state_pruning() { let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let _ = validate(&ctx, &router, msg_envelope.clone()); - let _ = execute(&mut ctx, &mut router, msg_envelope); + let _ = validate(&ctx.ibc_store, &router, msg_envelope.clone()); + let _ = execute(&mut ctx.ibc_store, &mut router, msg_envelope); } // Check that latest expired consensus state is pruned. @@ -268,8 +277,14 @@ fn test_consensus_state_pruning() { expired_height.revision_number(), expired_height.revision_height(), ); - assert!(ctx.client_update_meta(&client_id, &expired_height).is_err()); - assert!(ctx.consensus_state(&client_cons_state_path).is_err()); + assert!(ctx + .ibc_store + .client_update_meta(&client_id, &expired_height) + .is_err()); + assert!(ctx + .ibc_store + .consensus_state(&client_cons_state_path) + .is_err()); // Check that latest valid consensus state exists. let earliest_valid_height = Height::new(1, 2).unwrap(); @@ -280,11 +295,15 @@ fn test_consensus_state_pruning() { ); assert!(ctx + .ibc_store .client_update_meta(&client_id, &earliest_valid_height) .is_ok()); - assert!(ctx.consensus_state(&client_cons_state_path).is_ok()); + assert!(ctx + .ibc_store + .consensus_state(&client_cons_state_path) + .is_ok()); - let end_host_timestamp = ctx.host_timestamp().unwrap(); + let end_host_timestamp = ctx.ibc_store.host_timestamp().unwrap(); assert_eq!( end_host_timestamp, @@ -306,7 +325,7 @@ fn test_update_nonexisting_client(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!(res.is_err()); } @@ -319,12 +338,16 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .build::>(); let mut ctx = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -339,7 +362,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let signer = dummy_account_id(); - let block = ctx_b.host_block(&update_height).unwrap().clone(); + let block = ctx_b.host_block(&update_height).unwrap(); let mut block = block.into_header(); block.set_trusted_height(client_height); @@ -351,16 +374,16 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { }; let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); - let res = validate(&ctx, &router, msg_envelope.clone()); + let res = validate(&ctx.ibc_store, &router, msg_envelope.clone()); assert!(res.is_ok()); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok(), "result: {res:?}"); - let client_state = ctx.client_state(&msg.client_id).unwrap(); + let client_state = ctx.ibc_store.client_state(&msg.client_id).unwrap(); assert!(client_state - .status(&ctx, &msg.client_id) + .status(&ctx.ibc_store, &msg.client_id) .unwrap() .is_active()); @@ -406,14 +429,17 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { assert_eq!(update_height.revision_height(), 22); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .max_history_size(block_params.len() as u64) .block_params_history(block_params) .build::>(); let mut ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -429,11 +455,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { let signer = dummy_account_id(); - let mut block = ctx_b - .host_block(&update_height) - .unwrap() - .clone() - .into_header(); + let mut block = ctx_b.host_block(&update_height).unwrap().into_header(); let trusted_next_validator_set = ctx_b .host_block(&client_height) @@ -453,15 +475,15 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { }; let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); - let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope.clone()); assert!(res.is_ok()); - let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + let res = execute(&mut ctx_a.ibc_store, &mut router_a, msg_envelope); assert!(res.is_ok(), "result: {res:?}"); - let client_state = ctx_a.client_state(&msg.client_id).unwrap(); + let client_state = ctx_a.ibc_store.client_state(&msg.client_id).unwrap(); assert!(client_state - .status(&ctx_a, &msg.client_id) + .status(&ctx_a.ibc_store, &msg.client_id) .unwrap() .is_active()); assert_eq!(client_state.latest_height(), latest_header_height); @@ -507,14 +529,17 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() assert_eq!(update_height.revision_height(), 22); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .max_history_size(block_params.len() as u64) .block_params_history(block_params) .build::>(); let ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -552,11 +577,7 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() trusted_next_validator_set.hash() ); - let mut block = ctx_b - .host_block(&update_height) - .unwrap() - .clone() - .into_header(); + let mut block = ctx_b.host_block(&update_height).unwrap().into_header(); // set the trusted height to height-20 block.set_trusted_height(client_height); @@ -571,7 +592,7 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx_a, &router, msg_envelope); + let res = validate(&ctx_a.ibc_store, &router, msg_envelope); assert!(res.is_err()); } @@ -616,14 +637,17 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { assert_eq!(update_height.revision_height(), 22); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .max_history_size(block_params.len() as u64) .block_params_history(block_params) .build::>(); let ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -646,11 +670,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { .next_validators .clone(); - let mut block = ctx_b - .host_block(&update_height) - .unwrap() - .clone() - .into_header(); + let mut block = ctx_b.host_block(&update_height).unwrap().into_header(); block.set_trusted_height(client_height); block.set_trusted_next_validators_set(trusted_next_validator_set); @@ -660,9 +680,9 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { client_message: block.into(), signer, }; - let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope); assert!(res.is_err()); } @@ -712,14 +732,17 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { assert_eq!(update_height.revision_height(), 22); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .max_history_size(block_params.len() as u64) .block_params_history(block_params) .build::>(); let mut ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -735,11 +758,7 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { let signer = dummy_account_id(); - let mut block = ctx_b - .host_block(&update_height) - .unwrap() - .clone() - .into_header(); + let mut block = ctx_b.host_block(&update_height).unwrap().into_header(); let trusted_next_validator_set = ctx_b .host_block(&client_height) @@ -759,15 +778,15 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { }; let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); - let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope.clone()); assert!(res.is_ok()); - let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + let res = execute(&mut ctx_a.ibc_store, &mut router_a, msg_envelope); assert!(res.is_ok(), "result: {res:?}"); - let client_state = ctx_a.client_state(&msg.client_id).unwrap(); + let client_state = ctx_a.ibc_store.client_state(&msg.client_id).unwrap(); assert!(client_state - .status(&ctx_a, &msg.client_id) + .status(&ctx_a.ibc_store, &msg.client_id) .unwrap() .is_active()); assert_eq!(client_state.latest_height(), latest_header_height); @@ -810,14 +829,17 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f assert_eq!(update_height.revision_height(), 22); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .max_history_size(block_params.len() as u64) .block_params_history(block_params) .build::>(); let ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -833,11 +855,7 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f let signer = dummy_account_id(); - let mut block = ctx_b - .host_block(&update_height) - .unwrap() - .clone() - .into_header(); + let mut block = ctx_b.host_block(&update_height).unwrap().into_header(); let trusted_next_validator_set = ctx_b .host_block(&client_height) @@ -854,9 +872,9 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f client_message: block.into(), signer, }; - let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); + let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope); assert!(res.is_err()); } @@ -869,12 +887,16 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .build::>(); let mut ctx = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -889,7 +911,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let signer = dummy_account_id(); - let block = ctx_b.host_block(&update_height).unwrap().clone(); + let block = ctx_b.host_block(&update_height).unwrap(); let mut block = block.into_header(); let trusted_height = client_height.clone().sub(1).unwrap(); block.set_trusted_height(trusted_height); @@ -903,16 +925,16 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); - let res = validate(&ctx, &router, msg_envelope.clone()); + let res = validate(&ctx.ibc_store, &router, msg_envelope.clone()); assert!(res.is_ok()); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok(), "result: {res:?}"); - let client_state = ctx.client_state(&msg.client_id).unwrap(); + let client_state = ctx.ibc_store.client_state(&msg.client_id).unwrap(); assert!(client_state - .status(&ctx, &msg.client_id) + .status(&ctx.ibc_store, &msg.client_id) .unwrap() .is_active()); @@ -929,13 +951,12 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let start_height = Height::new(1, 11).unwrap(); let ctx_b = MockContextConfig::builder() - .host_id(ctx_b_chain_id) + .host(TendermintHost::builder().chain_id(ctx_b_chain_id).build()) .latest_height(client_height) - .max_history_size(blocks_since(client_height, start_height).expect("no error") + 1) .build::>(); let mut ctx_a = MockContextConfig::builder() - .host_id(ctx_a_chain_id) + .host(MockHost::builder().chain_id(ctx_a_chain_id).build()) .latest_height(start_height) .build::>() .with_light_client( @@ -950,7 +971,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let signer = dummy_account_id(); - let block = ctx_b.host_block(&client_height).unwrap().clone(); + let block = ctx_b.host_block(&client_height).unwrap(); let mut block = block.into_header(); // Update the trusted height of the header to point to the previous height @@ -1026,19 +1047,18 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg.clone())); - let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope.clone()); assert!(res.is_ok(), "result: {res:?}"); - let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + let res = execute(&mut ctx_a.ibc_store, &mut router_a, msg_envelope); assert!(res.is_ok(), "result: {res:?}"); - let client_state = ctx_a.client_state(&msg.client_id).unwrap(); + let client_state = ctx_a.ibc_store.client_state(&msg.client_id).unwrap(); assert!(client_state - .status(&ctx_a, &msg.client_id) + .status(&ctx_a.ibc_store, &msg.client_id) .unwrap() .is_active()); assert_eq!(client_state.latest_height(), latest_header_height); - assert_eq!(client_state, ctx_a.latest_client_states(&msg.client_id)); } #[rstest] @@ -1051,12 +1071,20 @@ fn test_update_synthetic_tendermint_client_lower_height() { let chain_start_height = Height::new(1, 11).unwrap(); let ctx_b = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaB-1").unwrap()) + .host( + TendermintHost::builder() + .chain_id(ChainId::new("mockgaiaB-1").unwrap()) + .build(), + ) .latest_height(client_height) .build::>(); let ctx = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(chain_start_height) .build::>() .with_light_client( @@ -1072,13 +1100,13 @@ fn test_update_synthetic_tendermint_client_lower_height() { let msg = MsgUpdateClient { client_id, - client_message: block_ref.clone().into_header().into(), + client_message: block_ref.into_header().into(), signer, }; let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!(res.is_err()); } @@ -1102,7 +1130,7 @@ fn test_update_client_events(fixture: Fixture) { }; let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok()); let ibc_events = ctx.get_events(); @@ -1123,8 +1151,8 @@ fn test_update_client_events(fixture: Fixture) { assert_eq!(update_client_event.header(), &header.to_vec()); } -fn ensure_misbehaviour( - ctx: &MockContext, +fn ensure_misbehaviour( + ctx: &MockIbcStore, client_id: &ClientId, client_type: &ClientType, ) { @@ -1134,7 +1162,7 @@ fn ensure_misbehaviour( assert!(status.is_frozen(), "client_state status: {status}"); // check events - let ibc_events = ctx.get_events(); + let ibc_events = ctx.events.lock(); assert_eq!(ibc_events.len(), 2); assert!(matches!( ibc_events[0], @@ -1161,13 +1189,13 @@ fn test_misbehaviour_client_ok(fixture: Fixture) { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); let msg_envelope = msg_update_client(&client_id); - let res = validate(&ctx, &router, msg_envelope.clone()); + let res = validate(&ctx.ibc_store, &router, msg_envelope.clone()); assert!(res.is_ok()); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok()); - ensure_misbehaviour(&ctx, &client_id, &mock_client_type()); + ensure_misbehaviour(&ctx.ibc_store, &client_id, &mock_client_type()); } #[rstest] @@ -1182,7 +1210,7 @@ fn test_submit_misbehaviour_nonexisting_client(fixture: Fixture) { &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!(res.is_err()); } @@ -1198,7 +1226,7 @@ fn test_client_update_misbehaviour_nonexisting_client(fixture: Fixture) { &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!(res.is_err()); } @@ -1213,13 +1241,21 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { // Create a mock context for chain-B let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host( + TendermintHost::builder() + .chain_id(chain_id_b.clone()) + .build(), + ) .latest_height(misbehaviour_height) .build::>(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -1234,7 +1270,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { // Get chain-B's header at `misbehaviour_height` let header1: TmHeader = { - let block = ctx_b.host_block(&misbehaviour_height).unwrap().clone(); + let block = ctx_b.host_block(&misbehaviour_height).unwrap(); let mut block = block.into_header(); block.set_trusted_height(client_height); block.into() @@ -1242,8 +1278,11 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { // Generate an equivocal header for chain-B at `misbehaviour_height` let header2 = { - let mut tm_block = TendermintHost::with_chain_id(chain_id_b) + let mut tm_block = TendermintHost::builder() + .chain_id(chain_id_b) + .build() .generate_block( + Vec::new(), misbehaviour_height.revision_height(), Timestamp::now(), &Default::default(), @@ -1260,11 +1299,11 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { }; let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope.clone()); assert!(res.is_ok()); - let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + let res = execute(&mut ctx_a.ibc_store, &mut router_a, msg_envelope); assert!(res.is_ok()); - ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); + ensure_misbehaviour(&ctx_a.ibc_store, &client_id, &tm_client_type()); } #[rstest] @@ -1275,13 +1314,21 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host( + TendermintHost::builder() + .chain_id(chain_id_b.clone()) + .build(), + ) .latest_height(client_height) .build::>(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .build::>() .with_light_client( @@ -1293,8 +1340,11 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { // Generate `header1` for chain-B let header1 = { - let mut tm_block = TendermintHost::with_chain_id(chain_id_b.clone()) + let mut tm_block = TendermintHost::builder() + .chain_id(chain_id_b.clone()) + .build() .generate_block( + Vec::new(), misbehaviour_height.revision_height(), Timestamp::now(), &Default::default(), @@ -1309,8 +1359,11 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let header2 = { let timestamp = Timestamp::from_nanoseconds(Timestamp::now().nanoseconds() + 1_000_000_000).unwrap(); - let mut tm_block = TendermintHost::with_chain_id(chain_id_b) + let mut tm_block = TendermintHost::builder() + .chain_id(chain_id_b) + .build() .generate_block( + Vec::new(), misbehaviour_height.revision_height(), timestamp, &Default::default(), @@ -1329,11 +1382,11 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx_a, &router_a, msg_envelope.clone()); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope.clone()); assert!(res.is_ok()); - let res = execute(&mut ctx_a, &mut router_a, msg_envelope); + let res = execute(&mut ctx_a.ibc_store, &mut router_a, msg_envelope); assert!(res.is_ok()); - ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type()); + ensure_misbehaviour(&ctx_a.ibc_store, &client_id, &tm_client_type()); } #[rstest] @@ -1350,13 +1403,17 @@ fn test_expired_client() { let trusting_period = Duration::from_secs(64); let ctx_b = MockContextConfig::builder() - .host_id(chain_id_b) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) .build::>(); let mut ctx = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) .build::>() @@ -1372,14 +1429,18 @@ fn test_expired_client() { .build(), ); - while ctx.host_timestamp().expect("no error") < (timestamp + trusting_period).expect("no error") + while ctx.ibc_store.host_timestamp().expect("no error") + < (timestamp + trusting_period).expect("no error") { - ctx.advance_host_chain_height(); + ctx.advance_block(); } - let client_state = ctx.client_state(&client_id).unwrap(); + let client_state = ctx.ibc_store.client_state(&client_id).unwrap(); - assert!(client_state.status(&ctx, &client_id).unwrap().is_expired()); + assert!(client_state + .status(&ctx.ibc_store, &client_id) + .unwrap() + .is_expired()); } #[rstest] @@ -1395,14 +1456,17 @@ fn test_client_update_max_clock_drift() { let max_clock_drift = Duration::from_secs(64); let mut ctx_b = MockContextConfig::builder() - .host_id(chain_id_b.clone()) + .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) - .max_history_size(u64::MAX) .build::>(); let ctx_a = MockContextConfig::builder() - .host_id(ChainId::new("mockgaiaA-1").unwrap()) + .host( + MockHost::builder() + .chain_id(ChainId::new("mockgaiaA-1").unwrap()) + .build(), + ) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) .build::>() @@ -1420,20 +1484,20 @@ fn test_client_update_max_clock_drift() { let router_a = MockRouter::new_with_transfer(); - while ctx_b.host_timestamp().expect("no error") - < (ctx_a.host_timestamp().expect("no error") + max_clock_drift).expect("no error") + while ctx_b.ibc_store.host_timestamp().expect("no error") + < (ctx_a.ibc_store.host_timestamp().expect("no error") + max_clock_drift).expect("no error") { - ctx_b.advance_host_chain_height(); + ctx_b.advance_block(); } // include current block - ctx_b.advance_host_chain_height(); + ctx_b.advance_block(); let update_height = ctx_b.latest_height(); let signer = dummy_account_id(); - let block = ctx_b.host_block(&update_height).unwrap().clone(); + let block = ctx_b.host_block(&update_height).unwrap(); let mut block = block.into_header(); block.set_trusted_height(client_height); @@ -1454,6 +1518,6 @@ fn test_client_update_max_clock_drift() { let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); - let res = validate(&ctx_a, &router_a, msg_envelope); + let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope); assert!(res.is_err()); } diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index 620b00e9a..5f7d1af34 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -1,3 +1,4 @@ +use ibc::clients::tendermint::context::ValidationContext; use ibc::clients::tendermint::types::client_type; use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::error::{ClientError, UpgradeClientError}; @@ -8,6 +9,7 @@ use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::path::ClientConsensusStatePath; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; @@ -17,7 +19,7 @@ use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; enum Ctx { Default, @@ -39,8 +41,8 @@ fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture::with_latest_height(Height::new(0, 42).unwrap()), ); let ctx = match ctx_variant { - Ctx::Default => ctx_default, - Ctx::WithClient => ctx_with_client, + Ctx::Default => ctx_default.ibc_store, + Ctx::WithClient => ctx_with_client.ibc_store, }; let upgrade_height = Height::new(1, 26).unwrap(); @@ -95,7 +97,7 @@ fn upgrade_client_execute(fxt: &mut Fixture, expect: Expect) { } Expect::Success => { assert!(res.is_ok(), "{err_msg}"); - let ibc_events = fxt.ctx.get_events(); + let ibc_events = fxt.ctx.events.lock(); assert!(matches!( ibc_events[0], IbcEvent::Message(MessageEvent::Client) @@ -152,7 +154,7 @@ fn upgrade_client_fail_low_upgrade_height() { msg_upgrade_client_fixture(Ctx::WithClient, Msg::LowUpgradeHeight); let expected_err: ClientError = UpgradeClientError::LowUpgradeHeight { upgraded_height: Height::new(0, 26).unwrap(), - client_height: fxt.ctx.latest_height(), + client_height: fxt.ctx.host_height().unwrap(), } .into(); upgrade_client_validate( diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs index bf2d9c6d4..75b2db444 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs @@ -1,5 +1,6 @@ use core::str::FromStr; +use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore}; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::connection::types::error::ConnectionError; @@ -13,12 +14,13 @@ use ibc::core::host::types::identifiers::{ChainId, ClientId}; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_ack; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockIbcStore}; use test_log::test; enum Ctx { @@ -60,29 +62,44 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { let ctx_default = MockContext::::default(); let ctx_new = MockContextConfig::builder() - .host_id(ChainId::new(&format!("mockgaia-{}", latest_height.revision_number())).unwrap()) + .host( + MockHost::builder() + .chain_id( + ChainId::new(&format!("mockgaia-{}", latest_height.revision_number())).unwrap(), + ) + .build(), + ) .latest_height(latest_height) .build::>(); let ctx = match ctx { - Ctx::New => ctx_new, - Ctx::NewWithConnection => ctx_new - .with_light_client( - &client_id, - LightClientState::::with_latest_height(proof_height), - ) - .with_connection(conn_id, default_conn_end), - Ctx::DefaultWithConnection => ctx_default - .with_light_client( - &client_id, - LightClientState::::with_latest_height(proof_height), - ) - .with_connection(conn_id, default_conn_end), - Ctx::NewWithConnectionEndOpen => ctx_new - .with_light_client( - &client_id, - LightClientState::::with_latest_height(proof_height), - ) - .with_connection(conn_id, conn_end_open), + Ctx::New => ctx_new.ibc_store, + Ctx::NewWithConnection => { + ctx_new + .with_light_client( + &client_id, + LightClientState::::with_latest_height(proof_height), + ) + .with_connection(conn_id, default_conn_end) + .ibc_store + } + Ctx::DefaultWithConnection => { + ctx_default + .with_light_client( + &client_id, + LightClientState::::with_latest_height(proof_height), + ) + .with_connection(conn_id, default_conn_end) + .ibc_store + } + Ctx::NewWithConnectionEndOpen => { + ctx_new + .with_light_client( + &client_id, + LightClientState::::with_latest_height(proof_height), + ) + .with_connection(conn_id, conn_end_open) + .ibc_store + } }; Fixture { ctx, msg } @@ -137,7 +154,7 @@ fn conn_open_ack_execute(fxt: &mut Fixture, expect: Expect assert!(res.is_err(), "{err_msg}"); } Expect::Success => { - let ibc_events = fxt.ctx.get_events(); + let ibc_events = fxt.ctx.events.lock(); assert!(res.is_ok(), "{err_msg}"); assert_eq!(ibc_events.len(), 2); @@ -151,7 +168,7 @@ fn conn_open_ack_execute(fxt: &mut Fixture, expect: Expect let IbcEvent::OpenAckConnection(conn_open_try_event) = event else { unreachable!() }; - let conn_end = as ValidationContext>::connection_end( + let conn_end = >> as ValidationContext>::connection_end( &fxt.ctx, conn_open_try_event.conn_id_on_a(), ) diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs index fbc96574b..70a838460 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs @@ -11,11 +11,12 @@ use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_conn_open_confirm; use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use test_log::test; enum Ctx { @@ -39,7 +40,7 @@ fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { State::Init, client_id.clone(), counterparty, - ValidationContext::get_compatible_versions(&ctx_default), + ValidationContext::get_compatible_versions(&ctx_default.ibc_store), ZERO_DURATION, ) .unwrap(); @@ -48,19 +49,25 @@ fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { correct_conn_end.set_state(State::TryOpen); let ctx = match ctx { - Ctx::Default => ctx_default, - Ctx::IncorrectConnection => ctx_default - .with_light_client( - &client_id, - LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), - ) - .with_connection(msg.conn_id_on_b.clone(), incorrect_conn_end_state), - Ctx::CorrectConnection => ctx_default - .with_light_client( - &client_id, - LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), - ) - .with_connection(msg.conn_id_on_b.clone(), correct_conn_end), + Ctx::Default => ctx_default.ibc_store, + Ctx::IncorrectConnection => { + ctx_default + .with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), + ) + .with_connection(msg.conn_id_on_b.clone(), incorrect_conn_end_state) + .ibc_store + } + Ctx::CorrectConnection => { + ctx_default + .with_light_client( + &client_id, + LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), + ) + .with_connection(msg.conn_id_on_b.clone(), correct_conn_end) + .ibc_store + } }; Fixture { ctx, msg } @@ -91,7 +98,7 @@ fn conn_open_confirm_execute(fxt: &mut Fixture, expect assert!(res.is_err(), "{err_msg}"); } Expect::Success => { - let ibc_events = fxt.ctx.get_events(); + let ibc_events = fxt.ctx.events.lock(); assert!(res.is_ok(), "{err_msg}"); assert_eq!(ibc_events.len(), 2); diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs index 74b0aac1d..ded2d15e7 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs @@ -7,6 +7,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::{ dummy_msg_conn_open_init, msg_conn_open_init_with_counterparty_conn_id, msg_conn_open_with_version, @@ -14,7 +15,7 @@ use ibc_testkit::fixtures::core::connection::{ use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use test_log::test; enum Ctx { @@ -32,7 +33,7 @@ enum Msg { fn conn_open_init_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { let msg_default = dummy_msg_conn_open_init(); let msg = match msg_variant { - Msg::Default => msg_default.clone(), + Msg::Default => msg_default, Msg::NoVersion => msg_conn_open_with_version(msg_default, None), Msg::BadVersion => { msg_conn_open_with_version(msg_default, Some("random identifier 424242")) @@ -42,11 +43,15 @@ fn conn_open_init_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture::default(); let ctx = match ctx_variant { - Ctx::WithClient => ctx_default.with_light_client( - &msg.client_id_on_a, - LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), - ), - _ => ctx_default, + Ctx::WithClient => { + ctx_default + .with_light_client( + &msg.client_id_on_a, + LightClientState::::with_latest_height(Height::new(0, 10).unwrap()), + ) + .ibc_store + } + _ => ctx_default.ibc_store, }; Fixture { ctx, msg } @@ -81,7 +86,7 @@ fn conn_open_init_execute( assert!(res.is_err(), "{err_msg}") } Expect::Success => { - let ibc_events = fxt.ctx.get_events(); + let ibc_events = fxt.ctx.events.lock(); assert!(res.is_ok(), "{err_msg}"); diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs index 18de31183..b587e33f6 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -6,12 +6,13 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_try; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{DefaultIbcStore, LightClientState}; use test_log::test; enum Ctx { @@ -27,13 +28,10 @@ enum Msg { } fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { - let max_history_size = 5; + let retained_history_size = 5; let client_cons_state_height = 10; let host_chain_height = Height::new(0, 35).unwrap(); - let pruned_height = host_chain_height - .sub(max_history_size + 1) - .unwrap() - .revision_height(); + let pruned_height = host_chain_height.sub(retained_history_size + 1).unwrap(); let msg = match msg_variant { Msg::Default => dummy_msg_conn_open_try( @@ -44,7 +42,9 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture dummy_msg_conn_open_try(client_cons_state_height, pruned_height), + Msg::HeightOld => { + dummy_msg_conn_open_try(client_cons_state_height, pruned_height.revision_height()) + } Msg::ProofHeightMissing => dummy_msg_conn_open_try( client_cons_state_height - 1, host_chain_height.revision_height(), @@ -52,18 +52,23 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture>(); let ctx = match ctx_variant { - Ctx::Default => MockContext::::default(), - Ctx::WithClient => ctx_new.with_light_client( - &msg.client_id_on_b, - LightClientState::::with_latest_height( - Height::new(0, client_cons_state_height).unwrap(), - ), - ), + Ctx::Default => DefaultIbcStore::default(), + Ctx::WithClient => { + ctx_new + .with_light_client( + &msg.client_id_on_b, + LightClientState::::with_latest_height( + Height::new(0, client_cons_state_height).unwrap(), + ), + ) + .ibc_store + } }; + + ctx.prune_host_consensus_states_till(&pruned_height); Fixture { ctx, msg } } @@ -96,7 +101,7 @@ fn conn_open_try_execute(fxt: &mut Fixture, expect: Expect assert_eq!(fxt.ctx.connection_counter().unwrap(), 1); - let ibc_events = fxt.ctx.get_events(); + let ibc_events = fxt.ctx.events.lock(); assert_eq!(ibc_events.len(), 2); diff --git a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs index 2637686d0..d614c4981 100644 --- a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs +++ b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs @@ -2,7 +2,6 @@ use ibc::core::channel::types::channel::{ChannelEnd, Counterparty, Order, State} use ibc::core::channel::types::commitment::{compute_packet_commitment, PacketCommitment}; use ibc::core::channel::types::msgs::{MsgAcknowledgement, PacketMsg}; use ibc::core::channel::types::Version; -use ibc::core::client::context::ClientExecutionContext; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::connection::types::version::Version as ConnectionVersion; @@ -13,12 +12,12 @@ use ibc::core::entrypoint::{execute, validate}; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; -use ibc::core::host::ExecutionContext; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_acknowledgement; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; use test_log::test; @@ -74,7 +73,7 @@ fn fixture() -> Fixture { ConnectionState::Open, default_client_id.clone(), ConnectionCounterparty::new( - default_client_id.clone(), + default_client_id, Some(ConnectionId::zero()), CommitmentPrefix::try_from(vec![0]).expect("no error"), ), @@ -90,8 +89,8 @@ fn fixture() -> Fixture { msg, packet_commitment, conn_end_on_a, - chan_end_on_a_unordered, chan_end_on_a_ordered, + chan_end_on_a_unordered, } } @@ -103,7 +102,7 @@ fn ack_fail_no_channel(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -137,7 +136,7 @@ fn ack_success_no_packet_commitment(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -147,7 +146,6 @@ fn ack_success_no_packet_commitment(fixture: Fixture) { #[rstest] fn ack_success_happy_path(fixture: Fixture) { - let default_client_id = ClientId::new("07-tendermint", 0).expect("no error"); let Fixture { ctx, router, @@ -158,7 +156,7 @@ fn ack_success_happy_path(fixture: Fixture) { client_height, .. } = fixture; - let mut ctx = ctx + let ctx = ctx .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -175,18 +173,10 @@ fn ack_success_happy_path(fixture: Fixture) { msg.packet.seq_on_a, packet_commitment, ); - ctx.get_client_execution_context() - .store_update_meta( - default_client_id, - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - Height::new(0, 4).unwrap(), - ) - .unwrap(); let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -221,7 +211,7 @@ fn ack_unordered_chan_execute(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok()); @@ -258,7 +248,7 @@ fn ack_ordered_chan_execute(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok()); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs index 3f4e08fe9..e8e806288 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs @@ -11,19 +11,20 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; #[test] fn test_chan_close_confirm_validate() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); let default_context = MockContext::::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); + let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); let conn_end = ConnectionEnd::new( ConnectionState::Open, @@ -61,13 +62,13 @@ fn test_chan_close_confirm_validate() { .with_connection(conn_id, conn_end) .with_channel( msg_chan_close_confirm.port_id_on_b.clone(), - msg_chan_close_confirm.chan_id_on_b.clone(), + msg_chan_close_confirm.chan_id_on_b, chan_end, ); let router = MockRouter::new_with_transfer(); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -80,7 +81,7 @@ fn test_chan_close_confirm_execute() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); let default_context = MockContext::::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); + let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); let conn_end = ConnectionEnd::new( ConnectionState::Open, @@ -118,13 +119,13 @@ fn test_chan_close_confirm_execute() { .with_connection(conn_id, conn_end) .with_channel( msg_chan_close_confirm.port_id_on_b.clone(), - msg_chan_close_confirm.chan_id_on_b.clone(), + msg_chan_close_confirm.chan_id_on_b, chan_end, ); let mut router = MockRouter::new_with_transfer(); - let res = execute(&mut context, &mut router, msg_envelope); + let res = execute(&mut context.ibc_store, &mut router, msg_envelope); assert!(res.is_ok(), "Execution success: happy path"); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs index 49394f6dd..c6f102c9c 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs @@ -11,12 +11,13 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_init; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; #[test] fn test_chan_close_init_validate() { @@ -51,7 +52,7 @@ fn test_chan_close_init_validate() { let context = { let default_context = MockContext::::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); + let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); default_context .with_light_client( @@ -61,14 +62,14 @@ fn test_chan_close_init_validate() { .with_connection(conn_id, conn_end) .with_channel( msg_chan_close_init.port_id_on_a.clone(), - msg_chan_close_init.chan_id_on_a.clone(), + msg_chan_close_init.chan_id_on_a, chan_end, ) }; let router = MockRouter::new_with_transfer(); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -109,7 +110,7 @@ fn test_chan_close_init_execute() { let mut context = { let default_context = MockContext::::default(); - let client_consensus_state_height = default_context.host_height().unwrap(); + let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); default_context .with_light_client( @@ -119,14 +120,14 @@ fn test_chan_close_init_execute() { .with_connection(conn_id, conn_end) .with_channel( msg_chan_close_init.port_id_on_a.clone(), - msg_chan_close_init.chan_id_on_a.clone(), + msg_chan_close_init.chan_id_on_a, chan_end, ) }; let mut router = MockRouter::new_with_transfer(); - let res = execute(&mut context, &mut router, msg_envelope); + let res = execute(&mut context.ibc_store, &mut router, msg_envelope); assert!(res.is_ok(), "Execution happy path"); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs index 70fa5d077..608624558 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs @@ -12,12 +12,13 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; use ibc::core::primitives::*; use ibc::core::router::types::module::ModuleId; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_ack; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; use test_log::test; @@ -104,7 +105,7 @@ fn chan_open_ack_happy_path(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!(res.is_ok(), "Validation happy path") } @@ -135,9 +136,9 @@ fn chan_open_ack_execute_happy_path(fixture: Fixture) { chan_end_on_a, ); - let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg.clone())); + let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = execute(&mut context, &mut router, msg_envelope); + let res = execute(&mut context.ibc_store, &mut router, msg_envelope); assert!(res.is_ok(), "Execution happy path"); @@ -176,7 +177,7 @@ fn chan_open_ack_fail_no_connection(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -205,7 +206,7 @@ fn chan_open_ack_fail_no_channel(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -248,7 +249,7 @@ fn chan_open_ack_fail_channel_wrong_state(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs index e65b10036..441abae22 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs @@ -11,12 +11,13 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId}; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; use test_log::test; @@ -97,7 +98,7 @@ fn chan_open_confirm_validate_happy_path(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!(res.is_ok(), "Validation happy path") } @@ -126,7 +127,7 @@ fn chan_open_confirm_execute_happy_path(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = execute(&mut context, &mut router, msg_envelope); + let res = execute(&mut context.ibc_store, &mut router, msg_envelope); assert!(res.is_ok(), "Execution happy path"); @@ -163,7 +164,7 @@ fn chan_open_confirm_fail_no_channel(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -194,7 +195,7 @@ fn chan_open_confirm_fail_channel_wrong_state(fixture: Fixture) { .unwrap(); let context = context .with_light_client( - &client_id_on_b.clone(), + &client_id_on_b, LightClientState::::with_latest_height(Height::new(0, proof_height).unwrap()), ) .with_connection(conn_id_on_b, conn_end_on_b) @@ -202,7 +203,7 @@ fn chan_open_confirm_fail_channel_wrong_state(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs index 0ec6b619a..67ec6c479 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs @@ -8,11 +8,12 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_init; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_init; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::{DefaultIbcStore, LightClientState}; use rstest::*; use test_log::test; @@ -62,7 +63,7 @@ fn chan_open_init_validate_happy_path(fixture: Fixture) { ctx, router, msg, .. } = fixture; - let res = validate(&ctx, &router, msg); + let res = validate(&ctx.ibc_store, &router, msg); assert!(res.is_ok(), "Validation succeeds; good parameters") } @@ -75,7 +76,7 @@ fn chan_open_init_validate_counterparty_chan_id_set(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(ChannelMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -92,11 +93,11 @@ fn chan_open_init_execute_happy_path(fixture: Fixture) { .. } = fixture; - let res = execute(&mut ctx, &mut router, msg); + let res = execute(&mut ctx.ibc_store, &mut router, msg); assert!(res.is_ok(), "Execution succeeds; good parameters"); - assert_eq!(ctx.channel_counter().unwrap(), 1); + assert_eq!(ctx.ibc_store.channel_counter().unwrap(), 1); let ibc_events = ctx.get_events(); @@ -113,7 +114,7 @@ fn chan_open_init_execute_happy_path(fixture: Fixture) { fn chan_open_init_fail_no_connection(fixture: Fixture) { let Fixture { router, msg, .. } = fixture; - let res = validate(&MockContext::::default(), &router, msg); + let res = validate(&DefaultIbcStore::default(), &router, msg); assert!( res.is_err(), diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs index c9b7c54f6..5d6034c49 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs @@ -10,12 +10,13 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_try; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; use test_log::test; @@ -90,7 +91,7 @@ fn chan_open_try_validate_happy_path(fixture: Fixture) { ) .with_connection(conn_id_on_b, conn_end_on_b); - let res = validate(&ctx, &router, msg); + let res = validate(&ctx.ibc_store, &router, msg); assert!(res.is_ok(), "Validation success: happy path") } @@ -115,11 +116,11 @@ fn chan_open_try_execute_happy_path(fixture: Fixture) { ) .with_connection(conn_id_on_b, conn_end_on_b); - let res = execute(&mut ctx, &mut router, msg); + let res = execute(&mut ctx.ibc_store, &mut router, msg); assert!(res.is_ok(), "Execution success: happy path"); - assert_eq!(ctx.channel_counter().unwrap(), 1); + assert_eq!(ctx.ibc_store.channel_counter().unwrap(), 1); let ibc_events = ctx.get_events(); @@ -138,7 +139,7 @@ fn chan_open_try_fail_no_connection(fixture: Fixture) { ctx, router, msg, .. } = fixture; - let res = validate(&ctx, &router, msg); + let res = validate(&ctx.ibc_store, &router, msg); assert!( res.is_err(), @@ -158,7 +159,7 @@ fn chan_open_try_fail_no_client_state(fixture: Fixture) { } = fixture; let ctx = ctx.with_connection(conn_id_on_b, conn_end_on_b); - let res = validate(&ctx, &router, msg); + let res = validate(&ctx.ibc_store, &router, msg); assert!( res.is_err(), diff --git a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs index 84e1496d0..52c8e0f59 100644 --- a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs @@ -2,7 +2,6 @@ use ibc::core::channel::types::channel::{ChannelEnd, Counterparty, Order, State} use ibc::core::channel::types::msgs::{MsgRecvPacket, PacketMsg}; use ibc::core::channel::types::packet::Packet; use ibc::core::channel::types::Version; -use ibc::core::client::context::ClientExecutionContext; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::connection::types::version::Version as ConnectionVersion; @@ -13,14 +12,14 @@ use ibc::core::entrypoint::{execute, validate}; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; -use ibc::core::host::ExecutionContext; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::{dummy_msg_recv_packet, dummy_raw_msg_recv_packet}; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::hosts::MockHost; use ibc_testkit::relayer::context::RelayerContext; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; use test_log::test; @@ -97,7 +96,7 @@ fn recv_packet_fail_no_channel(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -115,12 +114,11 @@ fn recv_packet_validate_happy_path(fixture: Fixture) { chan_end_on_b, client_height, host_height, - client_id, .. } = fixture; let packet = &msg.packet; - let mut context = context + let context = context .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -136,7 +134,7 @@ fn recv_packet_validate_happy_path(fixture: Fixture) { packet.chan_id_on_b.clone(), 1.into(), ) - .with_height(host_height) + .advance_block_up_to(host_height) // This `with_recv_sequence` is required for ordered channels .with_recv_sequence( packet.port_id_on_b.clone(), @@ -144,19 +142,9 @@ fn recv_packet_validate_happy_path(fixture: Fixture) { packet.seq_on_a, ); - context - .get_client_execution_context() - .store_update_meta( - client_id, - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - Height::new(0, 5).unwrap(), - ) - .unwrap(); - let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -205,9 +193,9 @@ fn recv_packet_timeout_expired(fixture: Fixture) { .with_connection(ConnectionId::zero(), conn_end_on_b) .with_channel(PortId::transfer(), ChannelId::zero(), chan_end_on_b) .with_send_sequence(PortId::transfer(), ChannelId::zero(), 1.into()) - .with_height(host_height); + .advance_block_up_to(host_height); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -236,7 +224,7 @@ fn recv_packet_execute_happy_path(fixture: Fixture) { let msg_env = MsgEnvelope::from(PacketMsg::from(msg)); - let res = execute(&mut ctx, &mut router, msg_env); + let res = execute(&mut ctx.ibc_store, &mut router, msg_env); assert!(res.is_ok()); diff --git a/ibc-testkit/tests/core/ics04_channel/send_packet.rs b/ibc-testkit/tests/core/ics04_channel/send_packet.rs index 65286a6ce..7263f5310 100644 --- a/ibc-testkit/tests/core/ics04_channel/send_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/send_packet.rs @@ -14,9 +14,10 @@ use ibc::core::connection::types::{ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_packet; use ibc_testkit::hosts::MockHost; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use test_log::test; #[test] @@ -43,7 +44,7 @@ fn send_packet_processing() { ConnectionState::Open, default_client_id.clone(), ConnectionCounterparty::new( - default_client_id.clone(), + default_client_id, Some(ConnectionId::zero()), CommitmentPrefix::try_from(vec![0]).expect("no error"), ), @@ -147,7 +148,7 @@ fn send_packet_processing() { .collect(); for mut test in tests { - let res = send_packet(&mut test.ctx, test.packet.clone()); + let res = send_packet(&mut test.ctx.ibc_store, test.packet.clone()); // Additionally check the events and the output objects in the result. match res { Ok(()) => { diff --git a/ibc-testkit/tests/core/ics04_channel/timeout.rs b/ibc-testkit/tests/core/ics04_channel/timeout.rs index 7d2a5d2f3..315c6300b 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -13,24 +13,25 @@ use ibc::core::entrypoint::{execute, validate}; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; -use ibc::core::host::ExecutionContext; +use ibc::core::host::types::path::ClientConsensusStatePath; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; struct Fixture { ctx: MockContext, pub router: MockRouter, + client_id: ClientId, client_height: Height, msg: MsgTimeout, packet_commitment: PacketCommitment, conn_end_on_a: ConnectionEnd, chan_end_on_a_ordered: ChannelEnd, chan_end_on_a_unordered: ChannelEnd, - client_id: ClientId, } #[fixture] @@ -95,13 +96,13 @@ fn fixture() -> Fixture { Fixture { ctx, router, + client_id, client_height, msg, packet_commitment, conn_end_on_a, chan_end_on_a_ordered, chan_end_on_a_unordered, - client_id, } } @@ -119,7 +120,7 @@ fn timeout_fail_no_channel(fixture: Fixture) { LightClientState::::with_latest_height(client_height), ); let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -136,12 +137,13 @@ fn timeout_fail_no_consensus_state_for_height(fixture: Fixture) { chan_end_on_a_unordered, conn_end_on_a, packet_commitment, + client_id, .. } = fixture; let packet = msg.packet.clone(); - let ctx = ctx + let mut ctx = ctx .with_channel( PortId::transfer(), ChannelId::zero(), @@ -155,9 +157,19 @@ fn timeout_fail_no_consensus_state_for_height(fixture: Fixture) { packet_commitment, ); + let consensus_state_path = ClientConsensusStatePath::new( + client_id, + msg.proof_height_on_b.revision_number(), + msg.proof_height_on_b.revision_height(), + ); + + ctx.ibc_store + .delete_consensus_state(consensus_state_path) + .expect("consensus state exists"); + let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -174,7 +186,6 @@ fn timeout_fail_proof_timeout_not_reached(fixture: Fixture) { chan_end_on_a_unordered, conn_end_on_a, client_height, - client_id, .. } = fixture; @@ -190,7 +201,7 @@ fn timeout_fail_proof_timeout_not_reached(fixture: Fixture) { let packet = msg.packet.clone(); - let mut ctx = ctx + let ctx = ctx .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -208,17 +219,9 @@ fn timeout_fail_proof_timeout_not_reached(fixture: Fixture) { packet_commitment, ); - ctx.store_update_meta( - client_id, - client_height, - Timestamp::from_nanoseconds(5).unwrap(), - Height::new(0, 4).unwrap(), - ) - .unwrap(); - let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -247,7 +250,7 @@ fn timeout_success_no_packet_commitment(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -265,13 +268,12 @@ fn timeout_unordered_channel_validate(fixture: Fixture) { conn_end_on_a, packet_commitment, client_height, - client_id, .. } = fixture; let packet = msg.packet.clone(); - let mut ctx = ctx + let ctx = ctx .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -289,18 +291,9 @@ fn timeout_unordered_channel_validate(fixture: Fixture) { packet_commitment, ); - ctx.get_client_execution_context() - .store_update_meta( - client_id, - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - Height::new(0, 5).unwrap(), - ) - .unwrap(); - let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!(res.is_ok(), "Good parameters for unordered channels") } @@ -315,13 +308,12 @@ fn timeout_ordered_channel_validate(fixture: Fixture) { conn_end_on_a, packet_commitment, client_height, - client_id, .. } = fixture; let packet = msg.packet.clone(); - let mut ctx = ctx + let ctx = ctx .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -335,17 +327,9 @@ fn timeout_ordered_channel_validate(fixture: Fixture) { packet_commitment, ); - ctx.store_update_meta( - client_id, - client_height, - Timestamp::from_nanoseconds(1000).unwrap(), - Height::new(0, 4).unwrap(), - ) - .unwrap(); - let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&ctx, &router, msg_envelope); + let res = validate(&ctx.ibc_store, &router, msg_envelope); assert!(res.is_ok(), "Good parameters for unordered channels") } @@ -377,7 +361,7 @@ fn timeout_unordered_chan_execute(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok()); @@ -415,7 +399,7 @@ fn timeout_ordered_chan_execute(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = execute(&mut ctx, &mut router, msg_envelope); + let res = execute(&mut ctx.ibc_store, &mut router, msg_envelope); assert!(res.is_ok()); diff --git a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs index d9ed62a2c..62468bbbc 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs @@ -2,7 +2,6 @@ use ibc::core::channel::types::channel::{ChannelEnd, Counterparty, Order, State} use ibc::core::channel::types::commitment::{compute_packet_commitment, PacketCommitment}; use ibc::core::channel::types::msgs::{MsgTimeoutOnClose, PacketMsg}; use ibc::core::channel::types::Version; -use ibc::core::client::context::ClientExecutionContext; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::connection::types::version::Version as ConnectionVersion; @@ -12,12 +11,12 @@ use ibc::core::connection::types::{ use ibc::core::entrypoint::validate; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; -use ibc::core::host::ExecutionContext; use ibc::core::primitives::*; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout_on_close; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientState, MockContext}; +use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; pub struct Fixture { @@ -68,7 +67,7 @@ fn fixture() -> Fixture { ConnectionState::Open, default_client_id.clone(), ConnectionCounterparty::new( - default_client_id.clone(), + default_client_id, Some(ConnectionId::zero()), CommitmentPrefix::try_from(vec![0]).expect("no error"), ), @@ -98,7 +97,7 @@ fn timeout_on_close_fail_no_channel(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_err(), @@ -123,7 +122,7 @@ fn timeout_on_close_success_no_packet_commitment(fixture: Fixture) { let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_ok(), @@ -133,8 +132,6 @@ fn timeout_on_close_success_no_packet_commitment(fixture: Fixture) { #[rstest] fn timeout_on_close_success_happy_path(fixture: Fixture) { - let default_client_id = ClientId::new("07-tendermint", 0).expect("no error"); - let Fixture { context, router, @@ -144,7 +141,7 @@ fn timeout_on_close_success_happy_path(fixture: Fixture) { chan_end_on_a, .. } = fixture; - let mut context = context + let context = context .with_channel(PortId::transfer(), ChannelId::zero(), chan_end_on_a) .with_connection(ConnectionId::zero(), conn_end_on_a) .with_packet_commitment( @@ -154,19 +151,9 @@ fn timeout_on_close_success_happy_path(fixture: Fixture) { packet_commitment, ); - context - .get_client_execution_context() - .store_update_meta( - default_client_id, - Height::new(0, 2).unwrap(), - Timestamp::from_nanoseconds(5000).unwrap(), - Height::new(0, 5).unwrap(), - ) - .unwrap(); - let msg_envelope = MsgEnvelope::from(PacketMsg::from(msg)); - let res = validate(&context, &router, msg_envelope); + let res = validate(&context.ibc_store, &router, msg_envelope); assert!( res.is_ok(), diff --git a/ibc-testkit/tests/core/router.rs b/ibc-testkit/tests/core/router.rs index 95991a434..20344b6c5 100644 --- a/ibc-testkit/tests/core/router.rs +++ b/ibc-testkit/tests/core/router.rs @@ -1,4 +1,4 @@ -use std::ops::Add; +use core::ops::Add; use ibc::apps::transfer::handler::send_transfer; use ibc::apps::transfer::types::error::TokenTransferError; @@ -22,6 +22,7 @@ use ibc::core::host::types::path::CommitmentPath; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::applications::transfer::{ extract_transfer_packet, MsgTransferConfig, PacketDataConfig, }; @@ -43,7 +44,6 @@ use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::MockContext; use test_log::test; #[test] @@ -169,7 +169,7 @@ fn routing_module_and_keepers() { .build(); let msg_transfer_no_timeout_or_timestamp = MsgTransferConfig::builder() - .packet_data(packet_data.clone()) + .packet_data(packet_data) .timeout_timestamp_on_b(Timestamp::from_nanoseconds(0).unwrap()) .build(); @@ -193,7 +193,7 @@ fn routing_module_and_keepers() { // First, create a client.. let res = dispatch( - &mut ctx, + &mut ctx.ibc_store, &mut router, MsgEnvelope::Client(ClientMsg::CreateClient(create_client_msg.clone())), ); @@ -330,12 +330,13 @@ fn routing_module_and_keepers() { msg: MsgEnvelope::Packet(PacketMsg::Ack(msg_ack_packet.clone())).into(), want_pass: true, state_check: Some(Box::new(move |ctx| { - ctx.get_packet_commitment(&CommitmentPath::new( - &msg_ack_packet.packet.port_id_on_a, - &msg_ack_packet.packet.chan_id_on_a, - msg_ack_packet.packet.seq_on_a, - )) - .is_err() + ctx.ibc_store + .get_packet_commitment(&CommitmentPath::new( + &msg_ack_packet.packet.port_id_on_a, + &msg_ack_packet.packet.chan_id_on_a, + msg_ack_packet.packet.seq_on_a, + )) + .is_err() })), }, Test { @@ -416,8 +417,8 @@ fn routing_module_and_keepers() { for test in tests { let res = match test.msg.clone() { - TestMsg::Ics26(msg) => dispatch(&mut ctx, &mut router, msg).map(|_| ()), - TestMsg::Ics20(msg) => send_transfer(&mut ctx, &mut DummyTransferModule, msg) + TestMsg::Ics26(msg) => dispatch(&mut ctx.ibc_store, &mut router, msg), + TestMsg::Ics20(msg) => send_transfer(&mut ctx.ibc_store, &mut DummyTransferModule, msg) .map_err(|e: TokenTransferError| ChannelError::AppModule { description: e.to_string(), }) From 2acb2fa1324dd838d5165a5819a20cedaa525025 Mon Sep 17 00:00:00 2001 From: Rano | Ranadeep Date: Fri, 5 Apr 2024 04:17:50 +0200 Subject: [PATCH 04/40] imp(ibc-testkit): Tendermint proof verifications via integration test (#1146) * test tmclient proof verification * use ClientStateConfig directly * rm MockClientConfig * use basecoin store proofspec as default * update tests * use merkle storage in MockContext * fix ProofSpecs size * refactor MockIbcStore to perform begin_block and end_block * simpler proof verification test * use ValidationContext::commitment_prefix() * nits * refactor host related trait method * tendermint host client integration test with proof verification * rm raw test * use typed relayer * add todo for channel integration test * core over std * be semantically correct * add comment for TypedRelayer * integration test for all pairs * fix semantic bug * renames * add channel management * channel creation in RelayerContext * add channel creation in integration test * add test for channel close * query client_id from connection and channel * ibc_store_mut * utils functions for packet relay * add packet relay integration test * add comments * optimize integration utils functions * serde feature for integration tests * rm redundant chain_id * sync clock only on a * add comment * imp: place router under MockGenericContext * nit: add docstring for router * nits * rm redundant lint filters * imp: ditch RelayerContext * nit: simplify build_client_update_datagram * refactor integration tests * add doc strings for TypedRelayerOps * doc strings for RelayerContext * mv client_update_ping_pong to tests dir * rename main_store to multi_store * update TestHost trait * update mock and tendermint hosts * update relayer functions * nits * renames and comments * add comments for return values in relayer ops * imp: simplify into_header --------- Co-authored-by: Farhad Shabani --- ibc-testkit/src/context.rs | 139 ++- .../src/fixtures/clients/tendermint.rs | 51 +- ibc-testkit/src/fixtures/core/context.rs | 7 +- ibc-testkit/src/hosts/mock.rs | 4 + ibc-testkit/src/hosts/mod.rs | 10 +- ibc-testkit/src/hosts/tendermint.rs | 27 +- ibc-testkit/src/relayer/context.rs | 579 ++++++---- ibc-testkit/src/relayer/integration.rs | 168 +++ ibc-testkit/src/relayer/mod.rs | 5 + ibc-testkit/src/relayer/utils.rs | 1002 +++++++++++++++++ ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 14 + .../src/testapp/ibc/core/router/types.rs | 2 +- ibc-testkit/src/testapp/ibc/core/types.rs | 44 +- .../tests/core/ics02_client/create_client.rs | 92 +- .../tests/core/ics02_client/update_client.rs | 179 ++- .../tests/core/ics04_channel/recv_packet.rs | 3 +- 16 files changed, 1984 insertions(+), 342 deletions(-) create mode 100644 ibc-testkit/src/relayer/integration.rs create mode 100644 ibc-testkit/src/relayer/utils.rs diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 846c08a91..8f22a5f73 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -1,15 +1,13 @@ use core::fmt::Debug; use core::time::Duration; -use basecoin_store::avl::get_proof_spec as basecoin_proof_spec; use basecoin_store::context::ProvableStore; use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore}; use ibc::core::channel::types::channel::ChannelEnd; use ibc::core::channel::types::commitment::PacketCommitment; use ibc::core::client::context::client_state::ClientStateValidation; -use ibc::core::client::context::ClientExecutionContext; +use ibc::core::client::context::{ClientExecutionContext, ClientValidationContext}; use ibc::core::client::types::Height; -use ibc::core::commitment_types::specs::ProofSpecs; use ibc::core::connection::types::ConnectionEnd; use ibc::core::entrypoint::dispatch; use ibc::core::handler::types::events::IbcEvent; @@ -20,16 +18,15 @@ use ibc::core::host::types::path::{ SeqAckPath, SeqRecvPath, SeqSendPath, }; use ibc::core::host::{ExecutionContext, ValidationContext}; -use ibc::core::router::router::Router; use ibc::primitives::prelude::*; use ibc::primitives::Timestamp; -use typed_builder::TypedBuilder; use super::testapp::ibc::core::types::{LightClientState, MockIbcStore}; use crate::fixtures::core::context::MockContextConfig; use crate::hosts::{HostClientState, TestBlock, TestHeader, TestHost}; use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::DEFAULT_BLOCK_TIME_SECS; /// A context implementing the dependencies necessary for testing any IBC module. @@ -40,34 +37,23 @@ where H: TestHost, HostClientState: ClientStateValidation>, { + /// The multi store of the context. + /// This is where the IBC store root is stored at IBC commitment prefix. + pub multi_store: S, + /// The type of host chain underlying this mock context. pub host: H, /// An object that stores all IBC related data. pub ibc_store: MockIbcStore, + + /// A router that can route messages to the appropriate IBC application. + pub ibc_router: MockRouter, } pub type MockStore = RevertibleStore>; pub type MockContext = MockGenericContext; -#[derive(Debug, TypedBuilder)] -pub struct MockClientConfig { - #[builder(default = Duration::from_secs(64000))] - pub trusting_period: Duration, - #[builder(default = Duration::from_millis(3000))] - pub max_clock_drift: Duration, - #[builder(default = Duration::from_secs(128_000))] - pub unbonding_period: Duration, - #[builder(default = vec![basecoin_proof_spec()].into())] - pub proof_specs: ProofSpecs, -} - -impl Default for MockClientConfig { - fn default() -> Self { - Self::builder().build() - } -} - /// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the /// creation of new domain objects. @@ -94,6 +80,10 @@ where &self.ibc_store } + pub fn ibc_store_mut(&mut self) -> &mut MockIbcStore { + &mut self.ibc_store + } + pub fn host_block(&self, target_height: &Height) -> Option { self.host.get_block(target_height) } @@ -102,6 +92,13 @@ where self.host.get_block(&self.latest_height()) } + pub fn light_client_latest_height(&self, client_id: &ClientId) -> Height { + self.ibc_store + .client_state(client_id) + .expect("client state exists") + .latest_height() + } + pub fn advance_block_up_to(mut self, target_height: Height) -> Self { let latest_height = self.host.latest_height(); if target_height.revision_number() != latest_height.revision_number() { @@ -118,38 +115,79 @@ where } pub fn generate_genesis_block(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { - // commit store - let app_hash = self.ibc_store.commit().expect("no error"); + self.end_block(); - // generate and push genesis block - let genesis_block = self.host.generate_block(app_hash, 1, genesis_time, params); - self.host.push_block(genesis_block); + // commit multi store + let multi_store_commitment = self.multi_store.commit().expect("no error"); - // store it in ibc context as host consensus state - self.ibc_store.store_host_consensus_state( + // generate a genesis block + let genesis_block = self.host - .latest_block() - .into_header() - .into_consensus_state() - .into(), + .generate_block(multi_store_commitment, 1, genesis_time, params); + + // push the genesis block to the host + self.host.push_block(genesis_block); + + self.begin_block(); + } + + pub fn begin_block(&mut self) { + let consensus_state = self + .host + .latest_block() + .into_header() + .into_consensus_state() + .into(); + + let ibc_commitment_proof = self + .multi_store + .get_proof( + self.host.latest_height().revision_height().into(), + &self + .ibc_store + .commitment_prefix() + .as_bytes() + .try_into() + .expect("valid utf8 prefix"), + ) + .expect("no error"); + + self.ibc_store.begin_block( + self.host.latest_height().revision_height(), + consensus_state, + ibc_commitment_proof, ); } - pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) { - // commit store - let app_hash = self.ibc_store.commit().expect("no error"); + pub fn end_block(&mut self) { + // commit ibc store + let ibc_store_commitment = self.ibc_store.end_block().expect("no error"); + + // commit ibc store commitment in multi store + self.multi_store + .set( + self.ibc_store + .commitment_prefix() + .as_bytes() + .try_into() + .expect("valid utf8 prefix"), + ibc_store_commitment, + ) + .expect("no error"); + } + pub fn produce_block(&mut self, block_time: Duration, params: &H::BlockParams) { + // commit the multi store + let multi_store_commitment = self.multi_store.commit().expect("no error"); // generate a new block - self.host.advance_block(app_hash, block_time, params); + self.host + .advance_block(multi_store_commitment, block_time, params); + } - // store it in ibc context as host consensus state - self.ibc_store.store_host_consensus_state( - self.host - .latest_block() - .into_header() - .into_consensus_state() - .into(), - ); + pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) { + self.end_block(); + self.produce_block(block_time, params); + self.begin_block(); } pub fn advance_block(&mut self) { @@ -355,12 +393,9 @@ where /// A datagram passes from the relayer to the IBC module (on host chain). /// Alternative method to `Ics18Context::send` that does not exercise any serialization. /// Used in testing the Ics18 algorithms, hence this may return a Ics18Error. - pub fn deliver( - &mut self, - router: &mut impl Router, - msg: MsgEnvelope, - ) -> Result<(), RelayerError> { - dispatch(&mut self.ibc_store, router, msg).map_err(RelayerError::TransactionFailed)?; + pub fn deliver(&mut self, msg: MsgEnvelope) -> Result<(), RelayerError> { + dispatch(&mut self.ibc_store, &mut self.ibc_router, msg) + .map_err(RelayerError::TransactionFailed)?; // Create a new block. self.advance_block(); Ok(()) diff --git a/ibc-testkit/src/fixtures/clients/tendermint.rs b/ibc-testkit/src/fixtures/clients/tendermint.rs index e10ed576c..eab0ef94c 100644 --- a/ibc-testkit/src/fixtures/clients/tendermint.rs +++ b/ibc-testkit/src/fixtures/clients/tendermint.rs @@ -1,6 +1,7 @@ use core::str::FromStr; use core::time::Duration; +use basecoin_store::avl::get_proof_spec as basecoin_proof_spec; use ibc::clients::tendermint::client_state::ClientState as TmClientState; use ibc::clients::tendermint::types::error::{Error as ClientError, Error}; use ibc::clients::tendermint::types::proto::v1::{ClientState as RawTmClientState, Fraction}; @@ -15,6 +16,7 @@ use ibc::core::commitment_types::specs::ProofSpecs; use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use tendermint::block::Header as TmHeader; +use typed_builder::TypedBuilder; /// Returns a dummy tendermint `ClientState` by given `frozen_height`, for testing purposes only! pub fn dummy_tm_client_state_from_raw(frozen_height: RawHeight) -> Result { @@ -64,9 +66,8 @@ pub fn dummy_raw_tm_client_state(frozen_height: RawHeight) -> RawTmClientState { } } -#[derive(typed_builder::TypedBuilder, Debug)] +#[derive(TypedBuilder, Debug)] pub struct ClientStateConfig { - pub chain_id: ChainId, #[builder(default = TrustThreshold::ONE_THIRD)] pub trust_level: TrustThreshold, #[builder(default = Duration::from_secs(64000))] @@ -74,9 +75,8 @@ pub struct ClientStateConfig { #[builder(default = Duration::from_secs(128_000))] pub unbonding_period: Duration, #[builder(default = Duration::from_millis(3000))] - max_clock_drift: Duration, - pub latest_height: Height, - #[builder(default = ProofSpecs::cosmos())] + pub max_clock_drift: Duration, + #[builder(default = vec![basecoin_proof_spec(); 2].into())] pub proof_specs: ProofSpecs, #[builder(default)] pub upgrade_path: Vec, @@ -84,23 +84,30 @@ pub struct ClientStateConfig { allow_update: AllowUpdate, } -impl TryFrom for TmClientState { - type Error = ClientError; - - fn try_from(config: ClientStateConfig) -> Result { - let client_state = ClientStateType::new( - config.chain_id, - config.trust_level, - config.trusting_period, - config.unbonding_period, - config.max_clock_drift, - config.latest_height, - config.proof_specs, - config.upgrade_path, - config.allow_update, - )?; - - Ok(client_state.into()) +impl Default for ClientStateConfig { + fn default() -> Self { + Self::builder().build() + } +} + +impl ClientStateConfig { + pub fn into_client_state( + self, + chain_id: ChainId, + latest_height: Height, + ) -> Result { + Ok(ClientStateType::new( + chain_id, + self.trust_level, + self.trusting_period, + self.unbonding_period, + self.max_clock_drift, + latest_height, + self.proof_specs, + self.upgrade_path, + self.allow_update, + )? + .into()) } } diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 724a706a6..96dc31ac9 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -10,6 +10,7 @@ use typed_builder::TypedBuilder; use crate::context::MockGenericContext; use crate::hosts::{HostClientState, TestBlock, TestHost}; +use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::{MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; use crate::utils::year_2023; @@ -21,7 +22,7 @@ where H: TestHost, { #[builder(default)] - pub host: H, + host: H, #[builder(default = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS))] block_time: Duration, @@ -56,11 +57,13 @@ where .expect("no underflow"); let mut context = Self { + multi_store: Default::default(), + host: params.host, ibc_store: MockIbcStore::new( params.latest_height.revision_number(), Default::default(), ), - host: params.host, + ibc_router: MockRouter::new_with_transfer(), }; // store is a height 0; no block diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index 067d41a14..b29980b9e 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -83,6 +83,10 @@ impl TestBlock for MockHeader { fn timestamp(&self) -> Timestamp { self.timestamp } + + fn into_header_with_trusted(self, _: &Self) -> Self::Header { + self + } } impl From for MockConsensusState { diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index 387723a64..3943f9d8b 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -124,7 +124,7 @@ pub trait TestHost: Default + Debug + Sized { /// TestBlock is a trait that defines the interface for a block produced by a host blockchain. pub trait TestBlock: Clone + Debug { /// The type of header can be extracted from the block. - type Header: TestHeader + From; + type Header: TestHeader; /// The height of the block. fn height(&self) -> Height; @@ -132,9 +132,13 @@ pub trait TestBlock: Clone + Debug { /// The timestamp of the block. fn timestamp(&self) -> Timestamp; - /// Extract the header from the block. + /// Extract the IBC header using the target and trusted blocks. + fn into_header_with_trusted(self, trusted_block: &Self) -> Self::Header; + + /// Extract the IBC header only using the target block (sets the trusted + /// block to itself). fn into_header(self) -> Self::Header { - self.into() + self.clone().into_header_with_trusted(&self) } } diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 91c19b2f8..07a15fbbf 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -20,7 +20,6 @@ use tendermint_testgen::{ }; use typed_builder::TypedBuilder; -use crate::context::MockClientConfig; use crate::fixtures::clients::tendermint::ClientStateConfig; use crate::hosts::{TestBlock, TestHeader, TestHost}; @@ -42,9 +41,9 @@ impl Default for TendermintHost { impl TestHost for TendermintHost { type Block = TendermintBlock; - type ClientState = ClientState; type BlockParams = BlockParams; - type LightClientParams = MockClientConfig; + type LightClientParams = ClientStateConfig; + type ClientState = ClientState; fn history(&self) -> &VecDeque { &self.history @@ -92,19 +91,18 @@ impl TestHost for TendermintHost { latest_height: &Height, params: &Self::LightClientParams, ) -> Self::ClientState { - let client_state: ClientState = ClientStateConfig::builder() - .chain_id(self.chain_id.clone()) - .latest_height( - self.get_block(latest_height) - .expect("block exists") - .height(), - ) + let client_state = ClientStateConfig::builder() .trusting_period(params.trusting_period) .max_clock_drift(params.max_clock_drift) .unbonding_period(params.unbonding_period) .proof_specs(params.proof_specs.clone()) .build() - .try_into() + .into_client_state( + self.chain_id.clone(), + self.get_block(latest_height) + .expect("block exists") + .height(), + ) .expect("never fails"); client_state.inner().validate().expect("never fails"); @@ -138,6 +136,13 @@ impl TestBlock for TendermintBlock { fn timestamp(&self) -> Timestamp { self.0.signed_header.header.time.into() } + + fn into_header_with_trusted(self, trusted_block: &Self) -> Self::Header { + let mut header = TendermintHeader::from(self.clone()); + header.set_trusted_height(trusted_block.height()); + header.set_trusted_next_validators_set(trusted_block.inner().validators.clone()); + header + } } #[derive(Debug, TypedBuilder)] diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 7694749ed..4a2bc1f95 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -1,248 +1,359 @@ -use alloc::fmt::Debug; - -use basecoin_store::context::ProvableStore; -use ibc::clients::tendermint::context::ValidationContext; +use ibc::core::channel::types::packet::Packet; use ibc::core::client::context::client_state::ClientStateValidation; -use ibc::core::client::context::ClientValidationContext; -use ibc::core::client::types::Height; -use ibc::core::handler::types::error::ContextError; -use ibc::core::host::types::identifiers::ClientId; -use ibc::core::primitives::prelude::*; -use ibc::core::primitives::Signer; - -use crate::context::MockGenericContext; +use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::host::types::path::ChannelEndPath; +use ibc::core::host::ValidationContext; +use ibc::primitives::Signer; + +use crate::context::MockContext; use crate::hosts::{HostClientState, TestHost}; -use crate::testapp::ibc::clients::AnyClientState; -use crate::testapp::ibc::core::types::MockIbcStore; -/// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to -/// relay packets between chains. This trait comprises the dependencies towards a single chain. -/// Most of the functions in this represent wrappers over the ABCI interface. -/// This trait mimics the `Chain` trait, but at a lower level of abstraction (no networking, header -/// types, light client, RPC client, etc.) -pub trait RelayerContext { - /// Returns the latest height of the chain. - fn query_latest_height(&self) -> Result; - - /// Returns this client state for the given `client_id` on this chain. - /// Wrapper over the `/abci_query?path=..` endpoint. - fn query_client_full_state(&self, client_id: &ClientId) -> Option; - - /// Temporary solution. Similar to `CosmosSDKChain::key_and_signer()` but simpler. - fn signer(&self) -> Signer; +use crate::relayer::utils::TypedRelayerOps; +use crate::testapp::ibc::core::types::DefaultIbcStore; + +/// A relayer context that allows interaction between two [`MockContext`] instances. +pub struct RelayerContext +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + ctx_a: MockContext, + ctx_b: MockContext, } -impl RelayerContext for MockGenericContext +impl RelayerContext where - S: ProvableStore + Debug, - H: TestHost, - HostClientState: ClientStateValidation>, + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, { - fn query_latest_height(&self) -> Result { - self.ibc_store.host_height() + /// Creates a new relayer context with the given [`MockContext`] instances. + pub fn new(ctx_a: MockContext, ctx_b: MockContext) -> Self { + Self { ctx_a, ctx_b } } - fn query_client_full_state(&self, client_id: &ClientId) -> Option { - // Forward call to Ics2. - self.ibc_store.client_state(client_id).ok() + /// Returns immutable reference to the first context. + pub fn get_ctx_a(&self) -> &MockContext { + &self.ctx_a } - fn signer(&self) -> Signer { - "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C" - .to_string() - .into() + /// Returns immutable reference to the second context. + pub fn get_ctx_b(&self) -> &MockContext { + &self.ctx_b + } + + /// Returns mutable reference to the first context. + pub fn get_ctx_a_mut(&mut self) -> &mut MockContext { + &mut self.ctx_a + } + + /// Returns mutable reference to the second context. + pub fn get_ctx_b_mut(&mut self) -> &mut MockContext { + &mut self.ctx_b + } + + /// Creates a light client of second context on the first context. + /// Returns the client identifier of the created client. + pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { + TypedRelayerOps::::create_client_on_a(&mut self.ctx_a, &self.ctx_b, signer) + } + + /// Creates a light client of first context on the second context. + /// Returns the client identifier of the created client. + pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId { + TypedRelayerOps::::create_client_on_a(&mut self.ctx_b, &self.ctx_a, signer) + } + + /// Updates the client on the first context with the latest header of the second context. + pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) { + TypedRelayerOps::::update_client_on_a_with_sync( + &mut self.ctx_a, + &mut self.ctx_b, + client_id_on_a, + signer, + ) + } + + /// Updates the client on the second context with the latest header of the first context. + pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) { + TypedRelayerOps::::update_client_on_a_with_sync( + &mut self.ctx_b, + &mut self.ctx_a, + client_id_on_b, + signer, + ) + } + + /// Creates a connection between the two contexts starting from the first context. + /// Returns the connection identifiers of the created connection ends. + pub fn create_connection_on_a( + &mut self, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + TypedRelayerOps::::create_connection_on_a( + &mut self.ctx_a, + &mut self.ctx_b, + client_id_on_a, + client_id_on_b, + signer, + ) + } + + /// Creates a connection between the two contexts starting from the second context. + /// Returns the connection identifiers of the created connection ends. + pub fn create_connection_on_b( + &mut self, + client_id_on_b: ClientId, + client_id_on_a: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + TypedRelayerOps::::create_connection_on_a( + &mut self.ctx_b, + &mut self.ctx_a, + client_id_on_b, + client_id_on_a, + signer, + ) + } + + /// Creates a channel between the two contexts starting from the first context. + /// Returns the channel identifiers of the created channel ends. + pub fn create_channel_on_a( + &mut self, + conn_id_on_a: ConnectionId, + port_id_on_a: PortId, + conn_id_on_b: ConnectionId, + port_id_on_b: PortId, + signer: Signer, + ) -> (ChannelId, ChannelId) { + let client_id_on_a = self + .ctx_a + .ibc_store() + .connection_end(&conn_id_on_a) + .expect("connection exists") + .client_id() + .clone(); + + let client_id_on_b = self + .ctx_b + .ibc_store() + .connection_end(&conn_id_on_b) + .expect("connection exists") + .client_id() + .clone(); + + TypedRelayerOps::::create_channel_on_a( + &mut self.ctx_a, + &mut self.ctx_b, + client_id_on_a, + conn_id_on_a, + port_id_on_a, + client_id_on_b, + conn_id_on_b, + port_id_on_b, + signer, + ) + } + + /// Creates a channel between the two contexts starting from the second context. + /// Returns the channel identifiers of the created channel ends. + pub fn create_channel_on_b( + &mut self, + conn_id_on_b: ConnectionId, + port_id_on_b: PortId, + conn_id_on_a: ConnectionId, + port_id_on_a: PortId, + signer: Signer, + ) -> (ChannelId, ChannelId) { + let client_id_on_b = self + .ctx_b + .ibc_store() + .connection_end(&conn_id_on_b) + .expect("connection exists") + .client_id() + .clone(); + + let client_id_on_a = self + .ctx_a + .ibc_store() + .connection_end(&conn_id_on_a) + .expect("connection exists") + .client_id() + .clone(); + + TypedRelayerOps::::create_channel_on_a( + &mut self.ctx_b, + &mut self.ctx_a, + client_id_on_b, + conn_id_on_b, + port_id_on_b, + client_id_on_a, + conn_id_on_a, + port_id_on_a, + signer, + ) + } + + /// Closes a channel between the two contexts starting from the first context. + pub fn close_channel_on_a( + &mut self, + chan_id_on_a: ChannelId, + port_id_on_a: PortId, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + signer: Signer, + ) { + let conn_id_on_a = self + .ctx_a + .ibc_store() + .channel_end(&ChannelEndPath::new(&port_id_on_a, &chan_id_on_a)) + .expect("connection exists") + .connection_hops()[0] + .clone(); + + let conn_id_on_b = self + .ctx_b + .ibc_store() + .channel_end(&ChannelEndPath::new(&port_id_on_b, &chan_id_on_b)) + .expect("connection exists") + .connection_hops()[0] + .clone(); + + let client_id_on_a = self + .ctx_a + .ibc_store() + .connection_end(&conn_id_on_a) + .expect("connection exists") + .client_id() + .clone(); + + let client_id_on_b = self + .ctx_b + .ibc_store() + .connection_end(&conn_id_on_b) + .expect("connection exists") + .client_id() + .clone(); + + TypedRelayerOps::::close_channel_on_a( + &mut self.ctx_a, + &mut self.ctx_b, + client_id_on_a, + chan_id_on_a, + port_id_on_a, + client_id_on_b, + chan_id_on_b, + port_id_on_b, + signer, + ) + } + + /// Closes a channel between the two contexts starting from the second context. + pub fn close_channel_on_b( + &mut self, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + chan_id_on_a: ChannelId, + port_id_on_a: PortId, + signer: Signer, + ) { + let conn_id_on_b = self + .ctx_b + .ibc_store() + .channel_end(&ChannelEndPath::new(&port_id_on_b, &chan_id_on_b)) + .expect("connection exists") + .connection_hops()[0] + .clone(); + + let conn_id_on_a = self + .ctx_a + .ibc_store() + .channel_end(&ChannelEndPath::new(&port_id_on_a, &chan_id_on_a)) + .expect("connection exists") + .connection_hops()[0] + .clone(); + + let client_id_on_b = self + .ctx_b + .ibc_store() + .connection_end(&conn_id_on_b) + .expect("connection exists") + .client_id() + .clone(); + + let client_id_on_a = self + .ctx_a + .ibc_store() + .connection_end(&conn_id_on_a) + .expect("connection exists") + .client_id() + .clone(); + + TypedRelayerOps::::close_channel_on_a( + &mut self.ctx_b, + &mut self.ctx_a, + client_id_on_b, + chan_id_on_b, + port_id_on_b, + client_id_on_a, + chan_id_on_a, + port_id_on_a, + signer, + ) } -} -#[cfg(test)] -mod tests { - use ibc::clients::tendermint::types::client_type as tm_client_type; - use ibc::core::client::types::msgs::{ClientMsg, MsgUpdateClient}; - use ibc::core::client::types::Height; - use ibc::core::handler::types::msgs::MsgEnvelope; - use ibc::core::host::types::identifiers::ChainId; - use ibc::core::primitives::prelude::*; - use tracing::debug; - - use super::RelayerContext; - use crate::context::MockContext; - use crate::fixtures::core::context::MockContextConfig; - use crate::hosts::{MockHost, TendermintHost, TestBlock, TestHeader, TestHost}; - use crate::relayer::context::ClientId; - use crate::relayer::error::RelayerError; - use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; - use crate::testapp::ibc::core::router::MockRouter; - use crate::testapp::ibc::core::types::LightClientBuilder; - - /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` - /// context, assuming that the latest header on the source context is `src_header`. - pub(crate) fn build_client_update_datagram( - dest: &Ctx, - client_id: &ClientId, - src_header: &H, - ) -> Result - where - Ctx: RelayerContext, - { - // Check if client for ibc0 on ibc1 has been updated to latest height: - // - query client state on destination chain - let dest_client_state = dest.query_client_full_state(client_id).ok_or_else(|| { - RelayerError::ClientStateNotFound { - client_id: client_id.clone(), - } - })?; - - let dest_client_latest_height = dest_client_state.latest_height(); - - if src_header.height() == dest_client_latest_height { - return Err(RelayerError::ClientAlreadyUpToDate { - client_id: client_id.clone(), - source_height: src_header.height(), - destination_height: dest_client_latest_height, - }); - }; - - if dest_client_latest_height > src_header.height() { - return Err(RelayerError::ClientAtHigherHeight { - client_id: client_id.clone(), - source_height: src_header.height(), - destination_height: dest_client_latest_height, - }); - }; - - // Client on destination chain can be updated. - Ok(ClientMsg::UpdateClient(MsgUpdateClient { - client_id: client_id.clone(), - client_message: src_header.clone().into(), - signer: dest.signer(), - })) - } - - #[test] - /// Serves to test both ICS-26 `dispatch` & `build_client_update_datagram` functions. - /// Implements a "ping pong" of client update messages, so that two chains repeatedly - /// process a client update message and update their height in succession. - fn client_update_ping_pong() { - let chain_a_start_height = Height::new(1, 11).unwrap(); - let chain_b_start_height = Height::new(1, 20).unwrap(); - let client_on_b_for_a_height = Height::new(1, 10).unwrap(); // Should be smaller than `chain_a_start_height` - let client_on_a_for_b_height = Height::new(1, 20).unwrap(); // Should be smaller than `chain_b_start_height` - let num_iterations = 4; - - let client_on_a_for_b = tm_client_type().build_client_id(0); - let client_on_b_for_a = mock_client_type().build_client_id(0); - - let chain_id_a = ChainId::new("mockgaiaA-1").unwrap(); - let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - - // Create two mock contexts, one for each chain. - let mut ctx_a = MockContextConfig::builder() - .host(MockHost::builder().chain_id(chain_id_a).build()) - .latest_height(chain_a_start_height) - .build::>(); - - let mut ctx_b = MockContextConfig::builder() - .host(TendermintHost::builder().chain_id(chain_id_b).build()) - .latest_height(chain_b_start_height) - .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A - .build::>(); - - ctx_a = ctx_a.with_light_client( - &client_on_a_for_b, - LightClientBuilder::init() - .context(&ctx_b) - .consensus_heights([client_on_a_for_b_height]) - .build(), - ); - - ctx_b = ctx_b.with_light_client( - &client_on_b_for_a, - LightClientBuilder::init() - .context(&ctx_a) - .consensus_heights([client_on_b_for_a_height]) - .build(), - ); - - // dummy; not actually used in client updates - let mut router_a = MockRouter::new_with_transfer(); - - // dummy; not actually used in client updates - let mut router_b = MockRouter::new_with_transfer(); - - for _i in 0..num_iterations { - // Update client on chain B to latest height of A. - // - create the client update message with the latest header from A - let a_latest_header = ctx_a.query_latest_block().unwrap(); - let client_msg_b_res = build_client_update_datagram( - &ctx_b, - &client_on_b_for_a, - &a_latest_header.into_header(), - ); - - assert!( - client_msg_b_res.is_ok(), - "create_client_update failed for context destination {ctx_b:?}, error: {client_msg_b_res:?}", - ); - - let client_msg_b = client_msg_b_res.unwrap(); - - // - send the message to B. We bypass ICS18 interface and call directly into - // MockContext `recv` method (to avoid additional serialization steps). - let dispatch_res_b = ctx_b.deliver(&mut router_b, MsgEnvelope::Client(client_msg_b)); - let validation_res = ctx_b.host.validate(); - assert!( - validation_res.is_ok(), - "context validation failed with error {validation_res:?} for context {ctx_b:?}", - ); - - // Check if the update succeeded. - assert!( - dispatch_res_b.is_ok(), - "Dispatch failed for host chain b with error: {dispatch_res_b:?}" - ); - let client_height_b = ctx_b - .query_client_full_state(&client_on_b_for_a) - .unwrap() - .latest_height(); - assert_eq!(client_height_b, ctx_a.query_latest_height().unwrap()); - - // Update client on chain A to latest height of B. - // - create the client update message with the latest header from B - // The test uses LightClientBlock that does not store the trusted height - let mut b_latest_header = ctx_b.query_latest_block().unwrap().clone().into_header(); - - let th = b_latest_header.height(); - b_latest_header.set_trusted_height(th.decrement().unwrap()); - - let client_msg_a_res = - build_client_update_datagram(&ctx_a, &client_on_a_for_b, &b_latest_header); - - assert!( - client_msg_a_res.is_ok(), - "create_client_update failed for context destination {ctx_a:?}, error: {client_msg_a_res:?}", - ); - - let client_msg_a = client_msg_a_res.unwrap(); - - debug!("client_msg_a = {:?}", client_msg_a); - - // - send the message to A - let dispatch_res_a = ctx_a.deliver(&mut router_a, MsgEnvelope::Client(client_msg_a)); - let validation_res = ctx_a.host.validate(); - assert!( - validation_res.is_ok(), - "context validation failed with error {validation_res:?} for context {ctx_a:?}", - ); - - // Check if the update succeeded. - assert!( - dispatch_res_a.is_ok(), - "Dispatch failed for host chain a with error: {dispatch_res_a:?}" - ); - let client_height_a = ctx_a - .query_client_full_state(&client_on_a_for_b) - .unwrap() - .latest_height(); - assert_eq!(client_height_a, ctx_b.query_latest_height().unwrap()); - } + /// Sends a packet from the first context to the second context. + /// The IBC packet is created by an IBC application on the first context. + pub fn send_packet_on_a(&mut self, packet: Packet, signer: Signer) { + let conn_id_on_a = self + .ctx_a + .ibc_store() + .channel_end(&ChannelEndPath::new( + &packet.port_id_on_a, + &packet.chan_id_on_a, + )) + .expect("connection exists") + .connection_hops()[0] + .clone(); + + let conn_id_on_b = self + .ctx_b + .ibc_store() + .channel_end(&ChannelEndPath::new( + &packet.port_id_on_b, + &packet.chan_id_on_b, + )) + .expect("connection exists") + .connection_hops()[0] + .clone(); + + let client_id_on_a = self + .ctx_a + .ibc_store() + .connection_end(&conn_id_on_a) + .expect("connection exists") + .client_id() + .clone(); + + let client_id_on_b = self + .ctx_b + .ibc_store() + .connection_end(&conn_id_on_b) + .expect("connection exists") + .client_id() + .clone(); + + TypedRelayerOps::::send_packet_on_a( + &mut self.ctx_a, + &mut self.ctx_b, + packet, + client_id_on_a, + client_id_on_b, + signer, + ) } } diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs new file mode 100644 index 000000000..60b361048 --- /dev/null +++ b/ibc-testkit/src/relayer/integration.rs @@ -0,0 +1,168 @@ +use core::str::FromStr; + +use ibc::apps::transfer::handler::send_transfer; +use ibc::apps::transfer::types::msgs::transfer::MsgTransfer; +use ibc::apps::transfer::types::packet::PacketData; +use ibc::apps::transfer::types::PrefixedCoin; +use ibc::core::channel::types::packet::Packet; +use ibc::core::channel::types::timeout::TimeoutHeight; +use ibc::core::client::context::client_state::ClientStateValidation; +use ibc::core::handler::types::events::IbcEvent; +use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; +use ibc::primitives::Timestamp; + +use crate::context::MockContext; +use crate::fixtures::core::signer::dummy_account_id; +use crate::hosts::{HostClientState, TestHost}; +use crate::relayer::context::RelayerContext; +use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; +use crate::testapp::ibc::core::types::DefaultIbcStore; + +/// Integration test for IBC implementation. +/// This test creates clients, connections, channels, and sends packets between two [`TestHost`]s. +/// This uses [`DummyTransferModule`] to simulate the transfer of tokens between two contexts. +pub fn ibc_integration_test() +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + let ctx_a = MockContext::::default(); + let ctx_b = MockContext::::default(); + + let signer = dummy_account_id(); + + let mut relayer = RelayerContext::new(ctx_a, ctx_b); + + // client creation + let client_id_on_a = relayer.create_client_on_a(signer.clone()); + let client_id_on_b = relayer.create_client_on_b(signer.clone()); + + // connection from A to B + let (conn_id_on_a, conn_id_on_b) = relayer.create_connection_on_a( + client_id_on_a.clone(), + client_id_on_b.clone(), + signer.clone(), + ); + + assert_eq!(conn_id_on_a, ConnectionId::new(0)); + assert_eq!(conn_id_on_b, ConnectionId::new(0)); + + // connection from B to A + let (conn_id_on_b, conn_id_on_a) = relayer.create_connection_on_b( + client_id_on_b.clone(), + client_id_on_a.clone(), + signer.clone(), + ); + + assert_eq!(conn_id_on_a, ConnectionId::new(1)); + assert_eq!(conn_id_on_b, ConnectionId::new(1)); + + // channel from A to B + let (chan_id_on_a, chan_id_on_b) = relayer.create_channel_on_a( + conn_id_on_a.clone(), + PortId::transfer(), + conn_id_on_b.clone(), + PortId::transfer(), + signer.clone(), + ); + + assert_eq!(chan_id_on_a, ChannelId::new(0)); + assert_eq!(chan_id_on_b, ChannelId::new(0)); + + // close the channel from A to B + relayer.close_channel_on_a( + chan_id_on_a.clone(), + PortId::transfer(), + chan_id_on_b.clone(), + PortId::transfer(), + signer.clone(), + ); + + // channel from B to A + let (chan_id_on_b, chan_id_on_a) = relayer.create_channel_on_b( + conn_id_on_b, + PortId::transfer(), + conn_id_on_a, + PortId::transfer(), + signer.clone(), + ); + + assert_eq!(chan_id_on_a, ChannelId::new(1)); + assert_eq!(chan_id_on_b, ChannelId::new(1)); + + // send packet from A to B + + // generate packet for DummyTransferModule + let packet_data = PacketData { + token: PrefixedCoin::from_str("1000uibc").expect("valid prefixed coin"), + sender: signer.clone(), + receiver: signer.clone(), + memo: "sample memo".into(), + }; + + // packet with ibc metadata + let msg = MsgTransfer { + port_id_on_a: PortId::transfer(), + chan_id_on_a: chan_id_on_a.clone(), + packet_data, + timeout_height_on_b: TimeoutHeight::Never, + timeout_timestamp_on_b: Timestamp::none(), + }; + + // module creates the send_packet + send_transfer( + relayer.get_ctx_a_mut().ibc_store_mut(), + &mut DummyTransferModule, + msg, + ) + .expect("successfully created send_packet"); + + // send_packet wasn't committed, hence produce a block + relayer.get_ctx_a_mut().advance_block(); + + // retrieve the send_packet event + let Some(IbcEvent::SendPacket(send_packet_event)) = relayer + .get_ctx_a() + .ibc_store() + .events + .lock() + .iter() + .rev() + .nth(2) + .cloned() + else { + panic!("unexpected event") + }; + + // create the IBC packet type + let packet = Packet { + port_id_on_a: send_packet_event.port_id_on_a().clone(), + chan_id_on_a: send_packet_event.chan_id_on_a().clone(), + seq_on_a: *send_packet_event.seq_on_a(), + data: send_packet_event.packet_data().to_vec(), + timeout_height_on_b: *send_packet_event.timeout_height_on_b(), + timeout_timestamp_on_b: *send_packet_event.timeout_timestamp_on_b(), + port_id_on_b: send_packet_event.port_id_on_b().clone(), + chan_id_on_b: send_packet_event.chan_id_on_b().clone(), + }; + + // continue packet relay starting from recv_packet at B + relayer.send_packet_on_a(packet, signer); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::hosts::{MockHost, TendermintHost}; + + // tests among all the `TestHost` implementations + #[test] + fn ibc_integration_test_for_all_pairs() { + ibc_integration_test::(); + ibc_integration_test::(); + ibc_integration_test::(); + ibc_integration_test::(); + } +} diff --git a/ibc-testkit/src/relayer/mod.rs b/ibc-testkit/src/relayer/mod.rs index c2c641829..5c70430ef 100644 --- a/ibc-testkit/src/relayer/mod.rs +++ b/ibc-testkit/src/relayer/mod.rs @@ -1,2 +1,7 @@ pub mod context; pub mod error; +pub mod utils; + +// `ibc::apps::transfer::handler::send_transfer` requires `serde` +#[cfg(feature = "serde")] +pub mod integration; diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs new file mode 100644 index 000000000..40c832d3c --- /dev/null +++ b/ibc-testkit/src/relayer/utils.rs @@ -0,0 +1,1002 @@ +use alloc::string::String; +use core::marker::PhantomData; +use core::time::Duration; + +use ibc::core::channel::types::acknowledgement::Acknowledgement; +use ibc::core::channel::types::channel::Order; +use ibc::core::channel::types::msgs::{ + ChannelMsg, MsgAcknowledgement, MsgChannelCloseConfirm, MsgChannelCloseInit, MsgChannelOpenAck, + MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, MsgRecvPacket, MsgTimeout, + MsgTimeoutOnClose, PacketMsg, +}; +use ibc::core::channel::types::packet::Packet; +use ibc::core::channel::types::Version as ChannelVersion; +use ibc::core::client::context::client_state::ClientStateValidation; +use ibc::core::client::context::ClientValidationContext; +use ibc::core::client::types::msgs::{ClientMsg, MsgCreateClient, MsgUpdateClient}; +use ibc::core::connection::types::msgs::{ + ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit, + MsgConnectionOpenTry, +}; +use ibc::core::connection::types::version::Version as ConnectionVersion; +use ibc::core::connection::types::Counterparty as ConnectionCounterParty; +use ibc::core::handler::types::events::IbcEvent; +use ibc::core::handler::types::msgs::MsgEnvelope; +use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::host::types::path::{ + AckPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, + ConnectionPath, ReceiptPath, +}; +use ibc::core::host::ValidationContext; +use ibc::primitives::Signer; +use ibc_query::core::context::ProvableContext; + +use crate::context::MockContext; +use crate::hosts::{HostClientState, TestBlock, TestHost}; +use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, LightClientState}; + +/// Implements IBC relayer functions for a pair of [`TestHost`] implementations: `A` and `B`. +/// Note that, all the implementations are in one direction: from `A` to `B`. +/// This makes the variable namings be consistent with the IBC message fields, +/// making the implementation less error-prone. +/// +/// For the functions in opposite direction, use `TypedRelayerOps::` instead of TypedRelayerOps::`. +#[derive(Debug, Default)] +pub struct TypedRelayerOps(PhantomData, PhantomData) +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation; + +impl TypedRelayerOps +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + /// Creates a client on `A` with the state of `B`. + /// Returns the client identifier on `A`. + pub fn create_client_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + signer: Signer, + ) -> ClientId { + let light_client_of_b = LightClientBuilder::init() + .context(ctx_b) + .build::>(); + + let msg_for_a = MsgEnvelope::Client(ClientMsg::CreateClient(MsgCreateClient { + client_state: light_client_of_b.client_state.into(), + consensus_state: light_client_of_b + .consensus_states + .values() + .next() + .expect("at least one") + .clone() + .into() + .into(), + signer, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::CreateClient(create_client_b_event)) = + ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + + let client_id_on_a = create_client_b_event.client_id().clone(); + + assert_eq!( + ValidationContext::get_client_validation_context(ctx_a.ibc_store()) + .client_state(&client_id_on_a) + .expect("client state exists") + .latest_height(), + ctx_b.latest_height() + ); + + client_id_on_a + } + + /// Advances the block height on `A` until it catches up with the latest timestamp on `B`. + pub fn sync_clock_on_a(ctx_a: &mut MockContext, ctx_b: &MockContext) { + while ctx_b.latest_timestamp() > ctx_a.latest_timestamp() { + ctx_a.advance_block(); + } + } + + /// Updates the client on `A` with the latest header from `B`. + pub fn update_client_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + client_id_on_a: ClientId, + signer: Signer, + ) { + let trusted_height_of_b = ctx_a + .ibc_store() + .get_client_validation_context() + .client_state(&client_id_on_a) + .expect("client state exists") + .latest_height(); + + let trusted_block_of_b = ctx_b + .host + .get_block(&trusted_height_of_b) + .expect("block exists"); + + let target_height_of_b = ctx_b.latest_height(); + + let target_block_of_b = ctx_b.host_block(&target_height_of_b).expect("block exists"); + + let msg_for_a = MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id_on_a.clone(), + client_message: target_block_of_b + .into_header_with_trusted(&trusted_block_of_b) + .into(), + signer, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::UpdateClient(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// Updates the client on `A` with the latest header from `B` after syncing the timestamps. + /// + /// Timestamp sync is required, as IBC doesn't allow client update from future beyond max clock drift. + pub fn update_client_on_a_with_sync( + ctx_a: &mut MockContext, + ctx_b: &mut MockContext, + client_id_on_a: ClientId, + signer: Signer, + ) { + TypedRelayerOps::::sync_clock_on_a(ctx_a, ctx_b); + TypedRelayerOps::::update_client_on_a(ctx_a, ctx_b, client_id_on_a, signer); + } + + /// `A` initiates a connection with the other end on `B`. + /// Returns the connection identifier on `A`. + pub fn connection_open_init_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> ConnectionId { + let counterparty_b = ConnectionCounterParty::new( + client_id_on_b.clone(), + None, + ctx_b.ibc_store().commitment_prefix(), + ); + + let msg_for_a = MsgEnvelope::Connection(ConnectionMsg::OpenInit(MsgConnectionOpenInit { + client_id_on_a: client_id_on_a.clone(), + counterparty: counterparty_b, + version: None, + delay_period: Duration::from_secs(0), + signer: signer.clone(), + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::OpenInitConnection(open_init_connection_event)) = + ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + + open_init_connection_event.conn_id_on_a().clone() + } + + /// `B` receives the connection opening attempt by `A` after `A` initiates the connection. + /// Returns the connection identifier on `B`. + pub fn connection_open_try_on_b( + ctx_b: &mut MockContext, + ctx_a: &MockContext, + conn_id_on_a: ConnectionId, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> ConnectionId { + let proofs_height_on_a = ctx_a.latest_height(); + + let client_state_of_b_on_a = ctx_a + .ibc_store() + .client_state(&client_id_on_a) + .expect("client state exists"); + + let consensus_height_of_b_on_a = client_state_of_b_on_a.latest_height(); + + let counterparty_a = ConnectionCounterParty::new( + client_id_on_a.clone(), + Some(conn_id_on_a.clone()), + ctx_a.ibc_store().commitment_prefix(), + ); + + let proof_conn_end_on_a = ctx_a + .ibc_store() + .get_proof( + proofs_height_on_a, + &ConnectionPath::new(&conn_id_on_a).into(), + ) + .expect("connection end exists") + .try_into() + .expect("value merkle proof"); + + let proof_client_state_of_b_on_a = ctx_a + .ibc_store() + .get_proof( + proofs_height_on_a, + &ClientStatePath::new(client_id_on_a.clone()).into(), + ) + .expect("client state exists") + .try_into() + .expect("value merkle proof"); + + let proof_consensus_state_of_b_on_a = ctx_a + .ibc_store() + .get_proof( + proofs_height_on_a, + &ClientConsensusStatePath::new( + client_id_on_a.clone(), + consensus_height_of_b_on_a.revision_number(), + consensus_height_of_b_on_a.revision_height(), + ) + .into(), + ) + .expect("consensus state exists") + .try_into() + .expect("value merkle proof"); + + #[allow(deprecated)] + let msg_for_b = MsgEnvelope::Connection(ConnectionMsg::OpenTry(MsgConnectionOpenTry { + client_id_on_b: client_id_on_b.clone(), + client_state_of_b_on_a: client_state_of_b_on_a.into(), + counterparty: counterparty_a, + versions_on_a: ConnectionVersion::compatibles(), + proof_conn_end_on_a, + proof_client_state_of_b_on_a, + proof_consensus_state_of_b_on_a, + proofs_height_on_a, + consensus_height_of_b_on_a, + delay_period: Duration::from_secs(0), + signer: signer.clone(), + proof_consensus_state_of_b: None, + // deprecated + previous_connection_id: String::new(), + })); + + ctx_b.deliver(msg_for_b).expect("success"); + + let Some(IbcEvent::OpenTryConnection(open_try_connection_event)) = + ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + + open_try_connection_event.conn_id_on_b().clone() + } + + /// `A` receives the acknowledgement of `B` that `B` received the connection opening attempt by `A`. + /// `A` starts processing the connection at its side. + pub fn connection_open_ack_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + conn_id_on_a: ConnectionId, + conn_id_on_b: ConnectionId, + client_id_on_b: ClientId, + signer: Signer, + ) { + let proofs_height_on_b = ctx_b.latest_height(); + + let client_state_of_a_on_b = ctx_b + .ibc_store() + .client_state(&client_id_on_b) + .expect("client state exists"); + + let consensus_height_of_a_on_b = client_state_of_a_on_b.latest_height(); + + let proof_conn_end_on_b = ctx_b + .ibc_store() + .get_proof( + proofs_height_on_b, + &ConnectionPath::new(&conn_id_on_b).into(), + ) + .expect("connection end exists") + .try_into() + .expect("value merkle proof"); + + let proof_client_state_of_a_on_b = ctx_b + .ibc_store() + .get_proof( + proofs_height_on_b, + &ClientStatePath::new(client_id_on_b.clone()).into(), + ) + .expect("client state exists") + .try_into() + .expect("value merkle proof"); + + let proof_consensus_state_of_a_on_b = ctx_b + .ibc_store() + .get_proof( + proofs_height_on_b, + &ClientConsensusStatePath::new( + client_id_on_b.clone(), + consensus_height_of_a_on_b.revision_number(), + consensus_height_of_a_on_b.revision_height(), + ) + .into(), + ) + .expect("consensus state exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_a = MsgEnvelope::Connection(ConnectionMsg::OpenAck(MsgConnectionOpenAck { + conn_id_on_a: conn_id_on_a.clone(), + conn_id_on_b: conn_id_on_b.clone(), + client_state_of_a_on_b: client_state_of_a_on_b.into(), + proof_conn_end_on_b, + proof_client_state_of_a_on_b, + proof_consensus_state_of_a_on_b, + proofs_height_on_b, + consensus_height_of_a_on_b, + version: ConnectionVersion::compatibles()[0].clone(), + signer: signer.clone(), + proof_consensus_state_of_a: None, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::OpenAckConnection(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// `B` receives the confirmation from `A` that the connection creation was successful. + /// `B` also starts processing the connection at its side. + pub fn connection_open_confirm_on_b( + ctx_b: &mut MockContext, + ctx_a: &MockContext, + conn_id_on_a: ConnectionId, + conn_id_on_b: ConnectionId, + signer: Signer, + ) { + let proof_height_on_a = ctx_a.latest_height(); + + let proof_conn_end_on_a = ctx_a + .ibc_store() + .get_proof( + proof_height_on_a, + &ConnectionPath::new(&conn_id_on_a).into(), + ) + .expect("connection end exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_b = + MsgEnvelope::Connection(ConnectionMsg::OpenConfirm(MsgConnectionOpenConfirm { + conn_id_on_b: conn_id_on_b.clone(), + proof_conn_end_on_a, + proof_height_on_a, + signer: signer.clone(), + })); + + ctx_b.deliver(msg_for_b).expect("success"); + + let Some(IbcEvent::OpenConfirmConnection(_)) = ctx_b.ibc_store().events.lock().last() + else { + panic!("unexpected event") + }; + } + + /// A connection is created by `A` towards `B` using the IBC connection handshake protocol. + /// Returns the connection identifiers on `A` and `B`. + pub fn create_connection_on_a( + ctx_a: &mut MockContext, + ctx_b: &mut MockContext, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + let conn_id_on_a = TypedRelayerOps::::connection_open_init_on_a( + ctx_a, + ctx_b, + client_id_on_a.clone(), + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + let conn_id_on_b = TypedRelayerOps::::connection_open_try_on_b( + ctx_b, + ctx_a, + conn_id_on_a.clone(), + client_id_on_a.clone(), + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_a, + ctx_b, + client_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayerOps::::connection_open_ack_on_a( + ctx_a, + ctx_b, + conn_id_on_a.clone(), + conn_id_on_b.clone(), + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayerOps::::connection_open_confirm_on_b( + ctx_b, + ctx_a, + conn_id_on_b.clone(), + conn_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync(ctx_a, ctx_b, client_id_on_a, signer); + + (conn_id_on_a, conn_id_on_b) + } + + /// `A` initiates a channel with port identifier with the other end on `B`. + /// Returns the channel identifier on `A`. + pub fn channel_open_init_on_a( + ctx_a: &mut MockContext, + conn_id_on_a: ConnectionId, + port_id_on_a: PortId, + port_id_on_b: PortId, + signer: Signer, + ) -> ChannelId { + let msg_for_a = MsgEnvelope::Channel(ChannelMsg::OpenInit(MsgChannelOpenInit { + port_id_on_a, + connection_hops_on_a: [conn_id_on_a].to_vec(), + port_id_on_b, + ordering: Order::Unordered, + signer, + version_proposal: ChannelVersion::empty(), + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::OpenInitChannel(open_init_channel_event)) = + ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + + open_init_channel_event.chan_id_on_a().clone() + } + + /// `B` receives the channel opening attempt by `A` after `A` initiates the channel. + /// Returns the channel identifier on `B`. + pub fn channel_open_try_on_b( + ctx_b: &mut MockContext, + ctx_a: &MockContext, + conn_id_on_b: ConnectionId, + chan_id_on_a: ChannelId, + port_id_on_a: PortId, + signer: Signer, + ) -> ChannelId { + let proof_height_on_a = ctx_a.latest_height(); + + let proof_chan_end_on_a = ctx_a + .ibc_store() + .get_proof( + proof_height_on_a, + &ChannelEndPath::new(&port_id_on_a, &chan_id_on_a).into(), + ) + .expect("connection end exists") + .try_into() + .expect("value merkle proof"); + + #[allow(deprecated)] + let msg_for_b = MsgEnvelope::Channel(ChannelMsg::OpenTry(MsgChannelOpenTry { + port_id_on_b: PortId::transfer(), + connection_hops_on_b: [conn_id_on_b].to_vec(), + port_id_on_a: PortId::transfer(), + chan_id_on_a, + version_supported_on_a: ChannelVersion::empty(), + proof_chan_end_on_a, + proof_height_on_a, + ordering: Order::Unordered, + signer, + + version_proposal: ChannelVersion::empty(), + })); + + ctx_b.deliver(msg_for_b).expect("success"); + + let Some(IbcEvent::OpenTryChannel(open_try_channel_event)) = + ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + + open_try_channel_event.chan_id_on_b().clone() + } + + /// `A` receives the acknowledgement of `B` that `B` received the channel opening attempt by `A`. + /// `A` starts processing the channel at its side. + pub fn channel_open_ack_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + chan_id_on_a: ChannelId, + port_id_on_a: PortId, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + signer: Signer, + ) { + let proof_height_on_b = ctx_b.latest_height(); + + let proof_chan_end_on_b = ctx_b + .ibc_store() + .get_proof( + proof_height_on_b, + &ChannelEndPath::new(&port_id_on_b, &chan_id_on_b).into(), + ) + .expect("connection end exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_a = MsgEnvelope::Channel(ChannelMsg::OpenAck(MsgChannelOpenAck { + port_id_on_a, + chan_id_on_a, + chan_id_on_b, + version_on_b: ChannelVersion::empty(), + proof_chan_end_on_b, + proof_height_on_b, + signer, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::OpenAckChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// `B` receives the confirmation from `A` that the channel creation was successful. + /// `B` also starts processing the channel at its side. + pub fn channel_open_confirm_on_b( + ctx_b: &mut MockContext, + ctx_a: &MockContext, + chan_id_on_a: ChannelId, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + signer: Signer, + ) { + let proof_height_on_a = ctx_a.latest_height(); + + let proof_chan_end_on_a = ctx_a + .ibc_store() + .get_proof( + proof_height_on_a, + &ChannelEndPath::new(&PortId::transfer(), &chan_id_on_a).into(), + ) + .expect("connection end exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_b = MsgEnvelope::Channel(ChannelMsg::OpenConfirm(MsgChannelOpenConfirm { + port_id_on_b, + chan_id_on_b, + proof_chan_end_on_a, + proof_height_on_a, + signer, + })); + + ctx_b.deliver(msg_for_b).expect("success"); + + let Some(IbcEvent::OpenConfirmChannel(_)) = ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// `A` initiates the channel closing with the other end on `B`. + /// `A` stops processing the channel. + pub fn channel_close_init_on_a( + ctx_a: &mut MockContext, + chan_id_on_a: ChannelId, + port_id_on_a: PortId, + signer: Signer, + ) { + let msg_for_a = MsgEnvelope::Channel(ChannelMsg::CloseInit(MsgChannelCloseInit { + port_id_on_a, + chan_id_on_a, + signer, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::CloseInitChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// `B` receives the channel closing attempt by `A` after `A` initiates the channel closing. + /// `B` also stops processing the channel. + pub fn channel_close_confirm_on_b( + ctx_b: &mut MockContext, + ctx_a: &MockContext, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + signer: Signer, + ) { + let proof_height_on_a = ctx_a.latest_height(); + + let proof_chan_end_on_a = ctx_a + .ibc_store() + .get_proof( + proof_height_on_a, + &ChannelEndPath::new(&PortId::transfer(), &chan_id_on_b).into(), + ) + .expect("connection end exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_b = MsgEnvelope::Channel(ChannelMsg::CloseConfirm(MsgChannelCloseConfirm { + port_id_on_b, + chan_id_on_b, + proof_chan_end_on_a, + proof_height_on_a, + signer, + })); + + ctx_b.deliver(msg_for_b).expect("success"); + + let Some(IbcEvent::CloseConfirmChannel(_)) = + ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// A channel is created by `A` towards `B` using the IBC channel handshake protocol. + /// Returns the channel identifiers on `A` and `B`. + #[allow(clippy::too_many_arguments)] + pub fn create_channel_on_a( + ctx_a: &mut MockContext, + ctx_b: &mut MockContext, + client_id_on_a: ClientId, + conn_id_on_a: ConnectionId, + port_id_on_a: PortId, + client_id_on_b: ClientId, + conn_id_on_b: ConnectionId, + port_id_on_b: PortId, + signer: Signer, + ) -> (ChannelId, ChannelId) { + let chan_id_on_a = TypedRelayerOps::::channel_open_init_on_a( + ctx_a, + conn_id_on_a.clone(), + port_id_on_a.clone(), + port_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + let chan_id_on_b = TypedRelayerOps::::channel_open_try_on_b( + ctx_b, + ctx_a, + conn_id_on_b.clone(), + chan_id_on_a.clone(), + port_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_a, + ctx_b, + client_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayerOps::::channel_open_ack_on_a( + ctx_a, + ctx_b, + chan_id_on_a.clone(), + port_id_on_a.clone(), + chan_id_on_b.clone(), + port_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayerOps::::channel_open_confirm_on_b( + ctx_b, + ctx_a, + chan_id_on_a.clone(), + chan_id_on_b.clone(), + port_id_on_b, + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync(ctx_a, ctx_b, client_id_on_a, signer); + + (chan_id_on_a, chan_id_on_b) + } + + /// A channel is closed by `A` towards `B` using the IBC channel handshake protocol. + #[allow(clippy::too_many_arguments)] + pub fn close_channel_on_a( + ctx_a: &mut MockContext, + ctx_b: &mut MockContext, + client_id_on_a: ClientId, + chan_id_on_a: ChannelId, + port_id_on_a: PortId, + client_id_on_b: ClientId, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + signer: Signer, + ) { + TypedRelayerOps::::channel_close_init_on_a( + ctx_a, + chan_id_on_a.clone(), + port_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_b, + ctx_a, + client_id_on_b, + signer.clone(), + ); + + TypedRelayerOps::::channel_close_confirm_on_b( + ctx_b, + ctx_a, + chan_id_on_b, + port_id_on_b, + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync(ctx_a, ctx_b, client_id_on_a, signer); + } + + /// `B` receives a packet from a IBC module on `A`. + /// Returns the acknowledgement of `B`. + pub fn packet_recv_on_b( + ctx_b: &mut MockContext, + ctx_a: &MockContext, + packet: Packet, + signer: Signer, + ) -> Acknowledgement { + let proof_height_on_a = ctx_a.latest_height(); + + let proof_commitment_on_a = ctx_a + .ibc_store() + .get_proof( + proof_height_on_a, + &CommitmentPath::new(&packet.port_id_on_a, &packet.chan_id_on_a, packet.seq_on_a) + .into(), + ) + .expect("commitment proof exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_b = MsgEnvelope::Packet(PacketMsg::Recv(MsgRecvPacket { + packet, + proof_commitment_on_a, + proof_height_on_a, + signer, + })); + + ctx_b.deliver(msg_for_b).expect("success"); + + let Some(IbcEvent::WriteAcknowledgement(write_ack_event)) = + ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + + write_ack_event.acknowledgement().clone() + } + + /// `A` receives the acknowledgement from `B` that `B` received the packet from `A`. + pub fn packet_ack_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + packet: Packet, + acknowledgement: Acknowledgement, + signer: Signer, + ) { + let proof_height_on_b = ctx_b.latest_height(); + + let proof_acked_on_b = ctx_b + .ibc_store() + .get_proof( + proof_height_on_b, + &AckPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a).into(), + ) + .expect("acknowledgement proof exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_a = MsgEnvelope::Packet(PacketMsg::Ack(MsgAcknowledgement { + packet, + acknowledgement, + proof_acked_on_b, + proof_height_on_b, + signer, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::AcknowledgePacket(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// `A` receives the timeout packet from `B`. + /// That is, `B` has not received the packet from `A` within the timeout period. + pub fn packet_timeout_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + packet: Packet, + signer: Signer, + ) { + let proof_height_on_b = ctx_b.latest_height(); + + let proof_unreceived_on_b = ctx_b + .ibc_store() + .get_proof( + proof_height_on_b, + &ReceiptPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a) + .into(), + ) + .expect("non-membership receipt proof exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_a = MsgEnvelope::Packet(PacketMsg::Timeout(MsgTimeout { + next_seq_recv_on_b: packet.seq_on_a, + packet, + proof_unreceived_on_b, + proof_height_on_b, + signer, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::TimeoutPacket(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// `A` receives the timeout packet from `B` after closing the channel. + /// That is, `B` has not received the packet from `A` because the channel is closed. + pub fn packet_timeout_on_close_on_a( + ctx_a: &mut MockContext, + ctx_b: &MockContext, + packet: Packet, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + signer: Signer, + ) { + let proof_height_on_b = ctx_b.latest_height(); + + let proof_unreceived_on_b = ctx_b + .ibc_store() + .get_proof( + proof_height_on_b, + &ReceiptPath::new(&port_id_on_b, &chan_id_on_b, packet.seq_on_a).into(), + ) + .expect("non-membership receipt proof") + .try_into() + .expect("value merkle proof"); + + let proof_close_on_b = ctx_b + .ibc_store() + .get_proof( + proof_height_on_b, + &ChannelEndPath::new(&port_id_on_b, &chan_id_on_b).into(), + ) + .expect("channel end data exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_a = MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(MsgTimeoutOnClose { + next_seq_recv_on_b: packet.seq_on_a, + packet, + proof_unreceived_on_b, + proof_close_on_b, + proof_height_on_b, + signer, + })); + + ctx_a.deliver(msg_for_a).expect("success"); + + let Some(IbcEvent::ChannelClosed(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + /// Sends a packet from an IBC application on `A` to `B` using the IBC packet relay protocol. + pub fn send_packet_on_a( + ctx_a: &mut MockContext, + ctx_b: &mut MockContext, + packet: Packet, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) { + // packet is passed from module + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + let acknowledgement = + TypedRelayerOps::::packet_recv_on_b(ctx_b, ctx_a, packet.clone(), signer.clone()); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_a, + ctx_b, + client_id_on_a, + signer.clone(), + ); + + TypedRelayerOps::::packet_ack_on_a( + ctx_a, + ctx_b, + packet, + acknowledgement, + signer.clone(), + ); + + TypedRelayerOps::::update_client_on_a_with_sync( + ctx_b, + ctx_a, + client_id_on_b, + signer.clone(), + ); + } +} diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 37a74b20f..7af68fe97 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -13,6 +13,7 @@ use ibc::core::client::context::consensus_state::ConsensusState; use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; +use ibc::core::commitment_types::merkle::MerkleProof; use ibc::core::connection::types::error::ConnectionError; use ibc::core::connection::types::{ConnectionEnd, IdentifiedConnectionEnd}; use ibc::core::handler::types::error::ContextError; @@ -27,6 +28,7 @@ use ibc::core::host::{ClientStateRef, ConsensusStateRef, ExecutionContext, Valid use ibc::core::primitives::prelude::*; use ibc::core::primitives::{Signer, Timestamp}; use ibc::primitives::ToVec; +use ibc_proto::ibc::core::commitment::v1::MerkleProof as RawMerkleProof; use ibc_query::core::context::{ProvableContext, QueryContext}; use super::types::{MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; @@ -275,6 +277,18 @@ where fn get_proof(&self, height: Height, path: &Path) -> Option> { self.store .get_proof(height.revision_height().into(), &path.to_string().into()) + .map(|path_proof| { + let ibc_commitment_proof = self + .ibc_commiment_proofs + .lock() + .get(&height.revision_height()) + .expect("proof exists") + .clone(); + + RawMerkleProof::from(MerkleProof { + proofs: vec![path_proof, ibc_commitment_proof], + }) + }) .map(|p| p.to_vec()) } } diff --git a/ibc-testkit/src/testapp/ibc/core/router/types.rs b/ibc-testkit/src/testapp/ibc/core/router/types.rs index 00aff346f..b48cd7fbd 100644 --- a/ibc-testkit/src/testapp/ibc/core/router/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/router/types.rs @@ -8,7 +8,7 @@ use ibc::core::router::types::module::ModuleId; use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; -#[derive(Default)] +#[derive(Debug, Default)] pub struct MockRouter { pub router: BTreeMap>, diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 8fe4cddcf..a8d813222 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -25,6 +25,7 @@ use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::channel::v1::Channel as RawChannelEnd; use ibc_proto::ibc::core::client::v1::Height as RawHeight; use ibc_proto::ibc::core::connection::v1::ConnectionEnd as RawConnectionEnd; +use ibc_proto::ics23::CommitmentProof; use parking_lot::Mutex; use typed_builder::TypedBuilder; @@ -86,6 +87,8 @@ where pub packet_ack_store: BinStore, AckPath, AcknowledgementCommitment>, /// Map of host consensus states pub host_consensus_states: Arc>>, + /// Map of older ibc commitment proofs + pub ibc_commiment_proofs: Arc>>, /// IBC Events pub events: Arc>>, /// message logs @@ -123,6 +126,7 @@ where client_processed_times: TypedStore::new(shared_store.clone()), client_processed_heights: TypedStore::new(shared_store.clone()), host_consensus_states: Arc::new(Mutex::new(Default::default())), + ibc_commiment_proofs: Arc::new(Mutex::new(Default::default())), client_state_store: TypedStore::new(shared_store.clone()), consensus_state_store: TypedStore::new(shared_store.clone()), connection_end_store: TypedStore::new(shared_store.clone()), @@ -140,20 +144,37 @@ where } } - pub fn commit(&mut self) -> Result, as Store>::Error> { - self.store.commit() - } - - pub fn store_host_consensus_state(&mut self, consensus_state: AnyConsensusState) { + fn store_host_consensus_state(&mut self, height: u64, consensus_state: AnyConsensusState) { self.host_consensus_states .lock() - .insert(self.store.current_height(), consensus_state); + .insert(height, consensus_state); + } + + fn store_ibc_commitment_proof(&mut self, height: u64, proof: CommitmentProof) { + self.ibc_commiment_proofs.lock().insert(height, proof); + } + + pub fn begin_block( + &mut self, + height: u64, + consensus_state: AnyConsensusState, + proof: CommitmentProof, + ) { + assert_eq!(self.store.current_height(), height); + self.store_host_consensus_state(height, consensus_state); + self.store_ibc_commitment_proof(height, proof); + } + + pub fn end_block(&mut self) -> Result, as Store>::Error> { + self.store.commit() } pub fn prune_host_consensus_states_till(&self, height: &Height) { assert!(height.revision_number() == *self.revision_number.lock()); let mut history = self.host_consensus_states.lock(); history.retain(|h, _| h > &height.revision_height()); + let mut commitment_proofs = self.ibc_commiment_proofs.lock(); + commitment_proofs.retain(|h, _| h > &height.revision_height()); } } @@ -164,8 +185,15 @@ where fn default() -> Self { // Note: this creates a MockIbcStore which has MockConsensusState as Host ConsensusState let mut ibc_store = Self::new(0, S::default()); - ibc_store.commit().expect("no error"); - ibc_store.store_host_consensus_state(MockHeader::default().into_consensus_state().into()); + ibc_store.store.commit().expect("no error"); + ibc_store.store_host_consensus_state( + ibc_store.store.current_height(), + MockHeader::default().into_consensus_state().into(), + ); + ibc_store.store_ibc_commitment_proof( + ibc_store.store.current_height(), + CommitmentProof::default(), + ); ibc_store } } diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index d2b0dffdd..11d4a8303 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -7,21 +7,29 @@ use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::error::ClientError; use ibc::core::client::types::msgs::{ClientMsg, MsgCreateClient}; use ibc::core::client::types::Height; +use ibc::core::commitment_types::error::CommitmentError; use ibc::core::entrypoint::{execute, validate}; use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::msgs::MsgEnvelope; +use ibc::core::host::types::identifiers::ClientId; +use ibc::core::host::types::path::{ClientConsensusStatePath, NextClientSequencePath}; use ibc::core::host::{ClientStateRef, ValidationContext}; +use ibc_query::core::context::ProvableContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; +use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; +use ibc_testkit::hosts::{MockHost, TendermintHost}; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; +use ibc_testkit::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{DefaultIbcStore, MockIbcStore}; +use ibc_testkit::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, MockIbcStore}; use test_log::test; #[test] @@ -123,3 +131,85 @@ fn test_invalid_frozen_tm_client_creation() { Err(ContextError::ClientError(ClientError::ClientFrozen { .. })) )) } + +#[test] +fn test_tm_create_client_proof_verification_ok() { + let client_id = ClientId::new("07-tendermint", 0).expect("no error"); + let client_height = Height::new(0, 10).expect("no error"); + + let ctx_tm = MockContextConfig::builder() + .latest_height(client_height) + .build::>(); + + let ctx_mk = MockContext::::default().with_light_client( + &client_id, + LightClientBuilder::init().context(&ctx_tm).build(), + ); + + let client_validation_ctx_mk = ctx_mk.ibc_store().get_client_validation_context(); + + let (AnyClientState::Tendermint(tm_client_state),) = (client_validation_ctx_mk + .client_state(&client_id) + .expect("client state exists"),) + else { + panic!("client state is not valid") + }; + + let latest_client_height = tm_client_state.latest_height(); + let consensus_state_path = ClientConsensusStatePath::new( + client_id.clone(), + latest_client_height.revision_number(), + latest_client_height.revision_height(), + ); + + let AnyConsensusState::Tendermint(tm_consensus_state) = client_validation_ctx_mk + .consensus_state(&consensus_state_path) + .expect("consensus_state exists") + else { + panic!("consensus state is not valid") + }; + + let next_client_seq_path = NextClientSequencePath; + let next_client_seq_value = client_validation_ctx_mk + .client_counter() + .expect("counter exists"); + + assert_eq!( + next_client_seq_value, 0, + "client counter is not incremented" + ); + + let proof = ctx_tm + .ibc_store() + .get_proof(ctx_tm.latest_height(), &next_client_seq_path.clone().into()) + .expect("proof exists") + .try_into() + .expect("value merkle proof"); + + let root = tm_consensus_state.inner().root(); + + // correct value verification + tm_client_state + .verify_membership( + &ctx_tm.ibc_store().commitment_prefix(), + &proof, + &root, + next_client_seq_path.clone().into(), + serde_json::to_vec(&next_client_seq_value).expect("valid json serialization"), + ) + .expect("successful proof verification"); + + // incorrect value verification + assert!(matches!( + tm_client_state + .verify_membership( + &ctx_tm.ibc_store().commitment_prefix(), + &proof, + &root, + next_client_seq_path.into(), + serde_json::to_vec(&(next_client_seq_value + 1)).expect("valid json serialization"), + ) + .expect_err("proof verification fails"), + ClientError::Ics23Verification(CommitmentError::VerificationFailure) + )); +} diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 818dedfdb..002f7bef7 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -24,11 +24,15 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; use ibc::primitives::ToVec; -use ibc_testkit::context::{MockClientConfig, MockContext}; +use ibc_testkit::context::MockContext; +use ibc_testkit::fixtures::clients::tendermint::ClientStateConfig; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::hosts::tendermint::BlockParams; -use ibc_testkit::hosts::{MockHost, TendermintHost, TestBlock, TestHeader, TestHost}; +use ibc_testkit::hosts::{ + HostClientState, MockHost, TendermintHost, TestBlock, TestHeader, TestHost, +}; +use ibc_testkit::relayer::error::RelayerError; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; @@ -36,9 +40,12 @@ use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; use ibc_testkit::testapp::ibc::clients::mock::misbehaviour::Misbehaviour as MockMisbehaviour; use ibc_testkit::testapp::ibc::clients::AnyConsensusState; use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{LightClientBuilder, LightClientState, MockIbcStore}; +use ibc_testkit::testapp::ibc::core::types::{ + DefaultIbcStore, LightClientBuilder, LightClientState, MockIbcStore, +}; use rstest::*; use tendermint_testgen::Validator as TestgenValidator; +use tracing::debug; struct Fixture { ctx: MockContext, @@ -234,7 +241,7 @@ fn test_consensus_state_pruning() { LightClientBuilder::init() .context(&ctx_b) .params( - MockClientConfig::builder() + ClientStateConfig::builder() .trusting_period(Duration::from_secs(3)) .build(), ) @@ -1422,7 +1429,7 @@ fn test_expired_client() { LightClientBuilder::init() .context(&ctx_b) .params( - MockClientConfig::builder() + ClientStateConfig::builder() .trusting_period(trusting_period) .build(), ) @@ -1475,7 +1482,7 @@ fn test_client_update_max_clock_drift() { LightClientBuilder::init() .context(&ctx_b) .params( - MockClientConfig::builder() + ClientStateConfig::builder() .max_clock_drift(max_clock_drift) .build(), ) @@ -1521,3 +1528,163 @@ fn test_client_update_max_clock_drift() { let res = validate(&ctx_a.ibc_store, &router_a, msg_envelope); assert!(res.is_err()); } + +/// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` +/// context, assuming that the latest header on the source context is `src_header`. +pub(crate) fn build_client_update_datagram( + dest: &MockContext, + client_id: &ClientId, + src_header: &H, +) -> Result +where + HostClientState: ClientStateValidation, +{ + // Check if client for ibc0 on ibc1 has been updated to latest height: + // - query client state on destination chain + let dest_client_latest_height = dest.light_client_latest_height(client_id); + + if src_header.height() == dest_client_latest_height { + return Err(RelayerError::ClientAlreadyUpToDate { + client_id: client_id.clone(), + source_height: src_header.height(), + destination_height: dest_client_latest_height, + }); + }; + + if dest_client_latest_height > src_header.height() { + return Err(RelayerError::ClientAtHigherHeight { + client_id: client_id.clone(), + source_height: src_header.height(), + destination_height: dest_client_latest_height, + }); + }; + + // Client on destination chain can be updated. + Ok(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id.clone(), + client_message: src_header.clone().into(), + signer: dummy_account_id(), + })) +} + +#[rstest] +/// Serves to test both ICS-26 `dispatch` & `build_client_update_datagram` functions. +/// Implements a "ping pong" of client update messages, so that two chains repeatedly +/// process a client update message and update their height in succession. +fn client_update_ping_pong() { + let chain_a_start_height = Height::new(1, 11).unwrap(); + let chain_b_start_height = Height::new(1, 20).unwrap(); + let client_on_b_for_a_height = Height::new(1, 10).unwrap(); // Should be smaller than `chain_a_start_height` + let client_on_a_for_b_height = Height::new(1, 20).unwrap(); // Should be smaller than `chain_b_start_height` + let num_iterations = 4; + + let client_on_a_for_b = tm_client_type().build_client_id(0); + let client_on_b_for_a = mock_client_type().build_client_id(0); + + let chain_id_a = ChainId::new("mockgaiaA-1").unwrap(); + let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); + + // Create two mock contexts, one for each chain. + let mut ctx_a = MockContextConfig::builder() + .host(MockHost::builder().chain_id(chain_id_a).build()) + .latest_height(chain_a_start_height) + .build::>(); + + let mut ctx_b = MockContextConfig::builder() + .host(TendermintHost::builder().chain_id(chain_id_b).build()) + .latest_height(chain_b_start_height) + .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A + .build::>(); + + ctx_a = ctx_a.with_light_client( + &client_on_a_for_b, + LightClientBuilder::init() + .context(&ctx_b) + .consensus_heights([client_on_a_for_b_height]) + .build(), + ); + + ctx_b = ctx_b.with_light_client( + &client_on_b_for_a, + LightClientBuilder::init() + .context(&ctx_a) + .consensus_heights([client_on_b_for_a_height]) + .build(), + ); + + for _i in 0..num_iterations { + // Update client on chain B to latest height of A. + // - create the client update message with the latest header from A + let a_latest_header = ctx_a.query_latest_block().unwrap(); + let client_msg_b_res = build_client_update_datagram( + &ctx_b, + &client_on_b_for_a, + &a_latest_header.into_header(), + ); + + assert!( + client_msg_b_res.is_ok(), + "create_client_update failed for context destination {ctx_b:?}, error: {client_msg_b_res:?}", + ); + + let client_msg_b = client_msg_b_res.unwrap(); + + // - send the message to B. We bypass ICS18 interface and call directly into + // MockContext `recv` method (to avoid additional serialization steps). + let dispatch_res_b = ctx_b.deliver(MsgEnvelope::Client(client_msg_b)); + let validation_res = ctx_b.host.validate(); + assert!( + validation_res.is_ok(), + "context validation failed with error {validation_res:?} for context {ctx_b:?}", + ); + + // Check if the update succeeded. + assert!( + dispatch_res_b.is_ok(), + "Dispatch failed for host chain b with error: {dispatch_res_b:?}" + ); + + assert_eq!( + ctx_b.light_client_latest_height(&client_on_b_for_a), + ctx_a.latest_height() + ); + + // Update client on chain A to latest height of B. + // - create the client update message with the latest header from B + // The test uses LightClientBlock that does not store the trusted height + let mut b_latest_header = ctx_b.query_latest_block().unwrap().clone().into_header(); + + let th = b_latest_header.height(); + b_latest_header.set_trusted_height(th.decrement().unwrap()); + + let client_msg_a_res = + build_client_update_datagram(&ctx_a, &client_on_a_for_b, &b_latest_header); + + assert!( + client_msg_a_res.is_ok(), + "create_client_update failed for context destination {ctx_a:?}, error: {client_msg_a_res:?}", + ); + + let client_msg_a = client_msg_a_res.unwrap(); + + debug!("client_msg_a = {:?}", client_msg_a); + + // - send the message to A + let dispatch_res_a = ctx_a.deliver(MsgEnvelope::Client(client_msg_a)); + let validation_res = ctx_a.host.validate(); + assert!( + validation_res.is_ok(), + "context validation failed with error {validation_res:?} for context {ctx_a:?}", + ); + + // Check if the update succeeded. + assert!( + dispatch_res_a.is_ok(), + "Dispatch failed for host chain a with error: {dispatch_res_a:?}" + ); + assert_eq!( + ctx_a.light_client_latest_height(&client_on_a_for_b), + ctx_b.latest_height() + ); + } +} diff --git a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs index 52c8e0f59..c1ce01436 100644 --- a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs @@ -17,7 +17,6 @@ use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::{dummy_msg_recv_packet, dummy_raw_msg_recv_packet}; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::hosts::MockHost; -use ibc_testkit::relayer::context::RelayerContext; use ibc_testkit::testapp::ibc::core::router::MockRouter; use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; @@ -42,7 +41,7 @@ fn fixture() -> Fixture { let router = MockRouter::new_with_transfer(); - let host_height = context.query_latest_height().unwrap().increment(); + let host_height = context.latest_height().increment(); let client_height = host_height.increment(); From 9e5b53c5e587eb3d747b9b9d6059a2456bb6a8fb Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 11 Apr 2024 16:08:43 +0200 Subject: [PATCH 05/40] rm prune_block_till --- ibc-testkit/src/context.rs | 5 ----- ibc-testkit/src/hosts/mock.rs | 17 +++-------------- ibc-testkit/src/hosts/mod.rs | 16 ++++++---------- ibc-testkit/src/hosts/tendermint.rs | 17 +++-------------- 4 files changed, 12 insertions(+), 43 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 8f22a5f73..14bf4f1ac 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -197,11 +197,6 @@ where ) } - pub fn prune_block_till(&mut self, height: &Height) { - self.host.prune_block_till(height); - self.ibc_store.prune_host_consensus_states_till(height); - } - pub fn latest_height(&self) -> Height { let latest_ibc_height = self.ibc_store.host_height().expect("Never fails"); let latest_host_height = self.host.latest_height(); diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index b29980b9e..ff234be6d 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -1,4 +1,3 @@ -use alloc::collections::VecDeque; use alloc::vec::Vec; use ibc::core::client::types::Height; @@ -18,7 +17,7 @@ pub struct MockHost { pub chain_id: ChainId, /// The chain of blocks underlying this context. #[builder(default)] - pub history: VecDeque, + pub history: Vec, } impl Default for MockHost { @@ -33,22 +32,12 @@ impl TestHost for MockHost { type BlockParams = (); type LightClientParams = (); - fn history(&self) -> &VecDeque { + fn history(&self) -> &Vec { &self.history } fn push_block(&mut self, block: Self::Block) { - self.history.push_back(block); - } - - fn prune_block_till(&mut self, height: &Height) { - while let Some(block) = self.history.front() { - if &block.height() <= height { - self.history.pop_front(); - } else { - break; - } - } + self.history.push(block); } fn generate_block( diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index 3943f9d8b..43b2e5ab1 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -1,7 +1,6 @@ pub mod mock; pub mod tendermint; -use alloc::collections::VecDeque; use core::fmt::Debug; use core::ops::Add; use core::time::Duration; @@ -31,14 +30,14 @@ pub trait TestHost: Default + Debug + Sized { /// The type of client state produced by the host. type ClientState: Into + Debug; - /// The type of block parameters to produce a block + /// The type of block parameters to produce a block. type BlockParams: Debug + Default; - /// The type of light client parameters to produce a light client state + /// The type of light client parameters to produce a light client state. type LightClientParams: Debug + Default; /// The history of blocks produced by the host chain. - fn history(&self) -> &VecDeque; + fn history(&self) -> &Vec; /// Returns true if the host chain has no blocks. fn is_empty(&self) -> bool { @@ -52,7 +51,7 @@ pub trait TestHost: Default + Debug + Sized { /// The latest block of the host chain. fn latest_block(&self) -> Self::Block { - self.history().back().cloned().expect("no error") + self.history().last().cloned().expect("no error") } /// Get the block at the given height. @@ -65,10 +64,7 @@ pub trait TestHost: Default + Debug + Sized { /// Add a block to the host chain. fn push_block(&mut self, block: Self::Block); - /// Prune blocks until the given height. - fn prune_block_till(&mut self, height: &Height); - - /// Triggers the advancing of the host chain, by extending the history of blocks (or headers). + /// Advance the host chain, by extending the history of blocks. fn advance_block( &mut self, commitment_root: Vec, @@ -142,7 +138,7 @@ pub trait TestBlock: Clone + Debug { } } -/// TestHeader is a trait that defines the interface for a header produced by a host blockchain. +/// TestHeader is a trait that defines the interface for a header corresponding to a host blockchain. pub trait TestHeader: Clone + Debug + Into { /// The type of consensus state can be extracted from the header. type ConsensusState: ConsensusState + Into + From + Clone + Debug; diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 07a15fbbf..7a6c07b51 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -1,4 +1,3 @@ -use alloc::collections::VecDeque; use core::str::FromStr; use ibc::clients::tendermint::client_state::ClientState; @@ -30,7 +29,7 @@ pub struct TendermintHost { pub chain_id: ChainId, /// The chain of blocks underlying this context. #[builder(default)] - pub history: VecDeque, + pub history: Vec, } impl Default for TendermintHost { @@ -45,22 +44,12 @@ impl TestHost for TendermintHost { type LightClientParams = ClientStateConfig; type ClientState = ClientState; - fn history(&self) -> &VecDeque { + fn history(&self) -> &Vec { &self.history } fn push_block(&mut self, block: Self::Block) { - self.history.push_back(block); - } - - fn prune_block_till(&mut self, height: &Height) { - while let Some(block) = self.history.front() { - if &block.height() <= height { - self.history.pop_front(); - } else { - break; - } - } + self.history.push(block); } fn generate_block( From c49a2c24550c2c3f26d0de1738a5caa8e6de9fd2 Mon Sep 17 00:00:00 2001 From: Rano | Ranadeep Date: Thu, 11 Apr 2024 10:29:20 -0400 Subject: [PATCH 06/40] apply suggestions from code review Co-authored-by: Sean Chen Signed-off-by: Rano | Ranadeep --- ibc-testkit/src/context.rs | 10 ++++-- ibc-testkit/src/fixtures/core/context.rs | 4 +-- ibc-testkit/src/hosts/mock.rs | 2 +- ibc-testkit/src/hosts/tendermint.rs | 1 + ibc-testkit/src/relayer/utils.rs | 32 +++++++++---------- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 6 ++-- .../tests/core/ics02_client/update_client.rs | 2 +- 7 files changed, 32 insertions(+), 25 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 14bf4f1ac..d5c39062c 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -54,7 +54,7 @@ where pub type MockStore = RevertibleStore>; pub type MockContext = MockGenericContext; -/// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are +/// Returns a MockContext with bare minimum initialization: no clients, no connections, and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the /// creation of new domain objects. impl Default for MockGenericContext @@ -69,7 +69,7 @@ where } /// Implementation of internal interface for use in testing. The methods in this interface should -/// _not_ be accessible to any Ics handler. +/// _not_ be accessible to any ICS handler. impl MockGenericContext where S: ProvableStore + Debug, @@ -176,6 +176,9 @@ where .expect("no error"); } + /// Produces the next block for the host chain by first committing the state of + /// the host's multi store, and then generating a new block that is added to + /// the host's block history. pub fn produce_block(&mut self, block_time: Duration, params: &H::BlockParams) { // commit the multi store let multi_store_commitment = self.multi_store.commit().expect("no error"); @@ -244,6 +247,9 @@ where self } + /// Generates a light client for the host by generating a client + /// state, as well as generating consensus states for each + /// consensus height. pub fn generate_light_client( &self, mut consensus_heights: Vec, diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 96dc31ac9..f69702f93 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -66,11 +66,11 @@ where ibc_router: MockRouter::new_with_transfer(), }; - // store is a height 0; no block + // store is at height 0; no block context.generate_genesis_block(genesis_timestamp, &Default::default()); - // store is a height 1; one block + // store is at height 1; one block context = context.advance_block_up_to( params diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index ff234be6d..05363d457 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -56,7 +56,7 @@ impl TestHost for MockHost { fn generate_client_state( &self, latest_height: &Height, - _: &Self::LightClientParams, + _params: &Self::LightClientParams, ) -> Self::ClientState { MockClientState::new(self.get_block(latest_height).expect("height exists")) } diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 7a6c07b51..bf5430793 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -22,6 +22,7 @@ use typed_builder::TypedBuilder; use crate::fixtures::clients::tendermint::ClientStateConfig; use crate::hosts::{TestBlock, TestHeader, TestHost}; +/// A host that produces Tendermint blocks and interfaces with Tendermint light clients. #[derive(TypedBuilder, Debug)] pub struct TendermintHost { /// Unique identifier for the chain. diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 40c832d3c..f4bdc3c50 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -37,10 +37,10 @@ use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, Ligh /// Implements IBC relayer functions for a pair of [`TestHost`] implementations: `A` and `B`. /// Note that, all the implementations are in one direction: from `A` to `B`. -/// This makes the variable namings be consistent with the IBC message fields, -/// making the implementation less error-prone. +/// This ensures that the variable namings are consistent with the IBC message fields, +/// leading to a less error-prone implementation. /// -/// For the functions in opposite direction, use `TypedRelayerOps::` instead of TypedRelayerOps::`. +/// For the functions in the opposite direction, use `TypedRelayerOps::` instead of TypedRelayerOps::`. #[derive(Debug, Default)] pub struct TypedRelayerOps(PhantomData, PhantomData) where @@ -149,7 +149,7 @@ where /// Updates the client on `A` with the latest header from `B` after syncing the timestamps. /// - /// Timestamp sync is required, as IBC doesn't allow client update from future beyond max clock drift. + /// Timestamp sync is required, as IBC doesn't allow client updates from the future beyond max clock drift. pub fn update_client_on_a_with_sync( ctx_a: &mut MockContext, ctx_b: &mut MockContext, @@ -283,8 +283,8 @@ where open_try_connection_event.conn_id_on_b().clone() } - /// `A` receives the acknowledgement of `B` that `B` received the connection opening attempt by `A`. - /// `A` starts processing the connection at its side. + /// `A` receives `B`'s acknowledgement that `B` received the connection opening attempt by `A`. + /// `A` starts processing the connection on its side. pub fn connection_open_ack_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -360,7 +360,7 @@ where } /// `B` receives the confirmation from `A` that the connection creation was successful. - /// `B` also starts processing the connection at its side. + /// `B` also starts processing the connection on its side. pub fn connection_open_confirm_on_b( ctx_b: &mut MockContext, ctx_a: &MockContext, @@ -397,7 +397,7 @@ where } /// A connection is created by `A` towards `B` using the IBC connection handshake protocol. - /// Returns the connection identifiers on `A` and `B`. + /// Returns the connection identifiers of `A` and `B`. pub fn create_connection_on_a( ctx_a: &mut MockContext, ctx_b: &mut MockContext, @@ -466,7 +466,7 @@ where } /// `A` initiates a channel with port identifier with the other end on `B`. - /// Returns the channel identifier on `A`. + /// Returns the channel identifier of `A`. pub fn channel_open_init_on_a( ctx_a: &mut MockContext, conn_id_on_a: ConnectionId, @@ -495,7 +495,7 @@ where } /// `B` receives the channel opening attempt by `A` after `A` initiates the channel. - /// Returns the channel identifier on `B`. + /// Returns the channel identifier of `B`. pub fn channel_open_try_on_b( ctx_b: &mut MockContext, ctx_a: &MockContext, @@ -542,8 +542,8 @@ where open_try_channel_event.chan_id_on_b().clone() } - /// `A` receives the acknowledgement of `B` that `B` received the channel opening attempt by `A`. - /// `A` starts processing the channel at its side. + /// `A` receives `B`'s acknowledgement that `B` received the channel opening attempt by `A`. + /// `A` starts processing the channel on its side. pub fn channel_open_ack_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -584,7 +584,7 @@ where } /// `B` receives the confirmation from `A` that the channel creation was successful. - /// `B` also starts processing the channel at its side. + /// `B` also starts processing the channel on its side. pub fn channel_open_confirm_on_b( ctx_b: &mut MockContext, ctx_a: &MockContext, @@ -682,7 +682,7 @@ where } /// A channel is created by `A` towards `B` using the IBC channel handshake protocol. - /// Returns the channel identifiers on `A` and `B`. + /// Returns the channel identifiers of `A` and `B`. #[allow(clippy::too_many_arguments)] pub fn create_channel_on_a( ctx_a: &mut MockContext, @@ -795,8 +795,8 @@ where TypedRelayerOps::::update_client_on_a_with_sync(ctx_a, ctx_b, client_id_on_a, signer); } - /// `B` receives a packet from a IBC module on `A`. - /// Returns the acknowledgement of `B`. + /// `B` receives a packet from an IBC module on `A`. + /// Returns `B`'s acknowledgement of receipt. pub fn packet_recv_on_b( ctx_b: &mut MockContext, ctx_a: &MockContext, diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 7af68fe97..1c5f3bf6b 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -242,9 +242,9 @@ where })?) } - /// Returns a counter on the number of channel ids have been created thus far. - /// The value of this counter should increase only via method - /// `ChannelKeeper::increase_channel_counter`. + /// Returns a counter of the number of channel ids that have been created thus far. + /// The value of this counter should increase only via the + /// `ChannelKeeper::increase_channel_counter` method. fn channel_counter(&self) -> Result { Ok(self .channel_counter diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 002f7bef7..27b3ce2df 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -1567,10 +1567,10 @@ where })) } -#[rstest] /// Serves to test both ICS-26 `dispatch` & `build_client_update_datagram` functions. /// Implements a "ping pong" of client update messages, so that two chains repeatedly /// process a client update message and update their height in succession. +#[rstest] fn client_update_ping_pong() { let chain_a_start_height = Height::new(1, 11).unwrap(); let chain_b_start_height = Height::new(1, 20).unwrap(); From dedd5f6cdc49b1f6bd118d66fb3466671dfb595a Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 15 Apr 2024 15:22:47 +0200 Subject: [PATCH 07/40] rename context structs --- ibc-testkit/src/context.rs | 14 ++-- ibc-testkit/src/fixtures/core/context.rs | 4 +- ibc-testkit/src/relayer/context.rs | 16 ++-- ibc-testkit/src/relayer/integration.rs | 6 +- ibc-testkit/src/relayer/utils.rs | 84 +++++++++---------- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 2 +- ibc-testkit/src/testapp/ibc/core/types.rs | 8 +- .../tests/core/ics02_client/create_client.rs | 6 +- .../tests/core/ics02_client/update_client.rs | 76 ++++++++--------- .../tests/core/ics02_client/upgrade_client.rs | 6 +- .../core/ics03_connection/conn_open_ack.rs | 6 +- .../ics03_connection/conn_open_confirm.rs | 4 +- .../core/ics03_connection/conn_open_init.rs | 4 +- .../core/ics03_connection/conn_open_try.rs | 4 +- .../core/ics04_channel/acknowledgement.rs | 6 +- .../core/ics04_channel/chan_close_confirm.rs | 6 +- .../core/ics04_channel/chan_close_init.rs | 6 +- .../tests/core/ics04_channel/chan_open_ack.rs | 6 +- .../core/ics04_channel/chan_open_confirm.rs | 6 +- .../core/ics04_channel/chan_open_init.rs | 6 +- .../tests/core/ics04_channel/chan_open_try.rs | 6 +- .../tests/core/ics04_channel/recv_packet.rs | 6 +- .../tests/core/ics04_channel/send_packet.rs | 14 ++-- .../tests/core/ics04_channel/timeout.rs | 6 +- .../core/ics04_channel/timeout_on_close.rs | 6 +- ibc-testkit/tests/core/router.rs | 6 +- 26 files changed, 160 insertions(+), 160 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index d5c39062c..5c495ea6d 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -23,7 +23,7 @@ use ibc::primitives::Timestamp; use super::testapp::ibc::core::types::{LightClientState, MockIbcStore}; use crate::fixtures::core::context::MockContextConfig; -use crate::hosts::{HostClientState, TestBlock, TestHeader, TestHost}; +use crate::hosts::{HostClientState, MockHost, TendermintHost, TestBlock, TestHeader, TestHost}; use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; use crate::testapp::ibc::core::router::MockRouter; @@ -31,7 +31,7 @@ use crate::testapp::ibc::core::types::DEFAULT_BLOCK_TIME_SECS; /// A context implementing the dependencies necessary for testing any IBC module. #[derive(Debug)] -pub struct MockGenericContext +pub struct StoreGenericTestContext where S: ProvableStore + Debug, H: TestHost, @@ -52,12 +52,12 @@ where } pub type MockStore = RevertibleStore>; -pub type MockContext = MockGenericContext; +pub type TestContext = StoreGenericTestContext; /// Returns a MockContext with bare minimum initialization: no clients, no connections, and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the /// creation of new domain objects. -impl Default for MockGenericContext +impl Default for StoreGenericTestContext where S: ProvableStore + Debug + Default, H: TestHost, @@ -70,7 +70,7 @@ where /// Implementation of internal interface for use in testing. The methods in this interface should /// _not_ be accessible to any ICS handler. -impl MockGenericContext +impl StoreGenericTestContext where S: ProvableStore + Debug, H: TestHost, @@ -248,7 +248,7 @@ where } /// Generates a light client for the host by generating a client - /// state, as well as generating consensus states for each + /// state, as well as generating consensus states for each /// consensus height. pub fn generate_light_client( &self, @@ -428,7 +428,7 @@ mod tests { HostClientState: ClientStateValidation, { name: String, - ctx: MockContext, + ctx: TestContext, } fn run_tests(sub_title: &str) diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index f69702f93..eb1c4bdbd 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -8,7 +8,7 @@ use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use typed_builder::TypedBuilder; -use crate::context::MockGenericContext; +use crate::context::StoreGenericTestContext; use crate::hosts::{HostClientState, TestBlock, TestHost}; use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::{MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; @@ -37,7 +37,7 @@ where latest_height: Height, } -impl From> for MockGenericContext +impl From> for StoreGenericTestContext where S: ProvableStore + Debug + Default, H: TestHost, diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 4a2bc1f95..6dd268b1f 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -5,7 +5,7 @@ use ibc::core::host::types::path::ChannelEndPath; use ibc::core::host::ValidationContext; use ibc::primitives::Signer; -use crate::context::MockContext; +use crate::context::TestContext; use crate::hosts::{HostClientState, TestHost}; use crate::relayer::utils::TypedRelayerOps; use crate::testapp::ibc::core::types::DefaultIbcStore; @@ -18,8 +18,8 @@ where HostClientState: ClientStateValidation, HostClientState: ClientStateValidation, { - ctx_a: MockContext, - ctx_b: MockContext, + ctx_a: TestContext, + ctx_b: TestContext, } impl RelayerContext @@ -30,27 +30,27 @@ where HostClientState: ClientStateValidation, { /// Creates a new relayer context with the given [`MockContext`] instances. - pub fn new(ctx_a: MockContext, ctx_b: MockContext) -> Self { + pub fn new(ctx_a: TestContext, ctx_b: TestContext) -> Self { Self { ctx_a, ctx_b } } /// Returns immutable reference to the first context. - pub fn get_ctx_a(&self) -> &MockContext { + pub fn get_ctx_a(&self) -> &TestContext { &self.ctx_a } /// Returns immutable reference to the second context. - pub fn get_ctx_b(&self) -> &MockContext { + pub fn get_ctx_b(&self) -> &TestContext { &self.ctx_b } /// Returns mutable reference to the first context. - pub fn get_ctx_a_mut(&mut self) -> &mut MockContext { + pub fn get_ctx_a_mut(&mut self) -> &mut TestContext { &mut self.ctx_a } /// Returns mutable reference to the second context. - pub fn get_ctx_b_mut(&mut self) -> &mut MockContext { + pub fn get_ctx_b_mut(&mut self) -> &mut TestContext { &mut self.ctx_b } diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs index 60b361048..5d0a72b21 100644 --- a/ibc-testkit/src/relayer/integration.rs +++ b/ibc-testkit/src/relayer/integration.rs @@ -11,7 +11,7 @@ use ibc::core::handler::types::events::IbcEvent; use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; use ibc::primitives::Timestamp; -use crate::context::MockContext; +use crate::context::TestContext; use crate::fixtures::core::signer::dummy_account_id; use crate::hosts::{HostClientState, TestHost}; use crate::relayer::context::RelayerContext; @@ -28,8 +28,8 @@ where HostClientState: ClientStateValidation, HostClientState: ClientStateValidation, { - let ctx_a = MockContext::::default(); - let ctx_b = MockContext::::default(); + let ctx_a = TestContext::::default(); + let ctx_b = TestContext::::default(); let signer = dummy_account_id(); diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index f4bdc3c50..7d5fbb642 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -31,7 +31,7 @@ use ibc::core::host::ValidationContext; use ibc::primitives::Signer; use ibc_query::core::context::ProvableContext; -use crate::context::MockContext; +use crate::context::TestContext; use crate::hosts::{HostClientState, TestBlock, TestHost}; use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, LightClientState}; @@ -59,8 +59,8 @@ where /// Creates a client on `A` with the state of `B`. /// Returns the client identifier on `A`. pub fn create_client_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, signer: Signer, ) -> ClientId { let light_client_of_b = LightClientBuilder::init() @@ -102,7 +102,7 @@ where } /// Advances the block height on `A` until it catches up with the latest timestamp on `B`. - pub fn sync_clock_on_a(ctx_a: &mut MockContext, ctx_b: &MockContext) { + pub fn sync_clock_on_a(ctx_a: &mut TestContext, ctx_b: &TestContext) { while ctx_b.latest_timestamp() > ctx_a.latest_timestamp() { ctx_a.advance_block(); } @@ -110,8 +110,8 @@ where /// Updates the client on `A` with the latest header from `B`. pub fn update_client_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, client_id_on_a: ClientId, signer: Signer, ) { @@ -151,8 +151,8 @@ where /// /// Timestamp sync is required, as IBC doesn't allow client updates from the future beyond max clock drift. pub fn update_client_on_a_with_sync( - ctx_a: &mut MockContext, - ctx_b: &mut MockContext, + ctx_a: &mut TestContext, + ctx_b: &mut TestContext, client_id_on_a: ClientId, signer: Signer, ) { @@ -163,8 +163,8 @@ where /// `A` initiates a connection with the other end on `B`. /// Returns the connection identifier on `A`. pub fn connection_open_init_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, client_id_on_a: ClientId, client_id_on_b: ClientId, signer: Signer, @@ -197,8 +197,8 @@ where /// `B` receives the connection opening attempt by `A` after `A` initiates the connection. /// Returns the connection identifier on `B`. pub fn connection_open_try_on_b( - ctx_b: &mut MockContext, - ctx_a: &MockContext, + ctx_b: &mut TestContext, + ctx_a: &TestContext, conn_id_on_a: ConnectionId, client_id_on_a: ClientId, client_id_on_b: ClientId, @@ -286,8 +286,8 @@ where /// `A` receives `B`'s acknowledgement that `B` received the connection opening attempt by `A`. /// `A` starts processing the connection on its side. pub fn connection_open_ack_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, conn_id_on_a: ConnectionId, conn_id_on_b: ConnectionId, client_id_on_b: ClientId, @@ -362,8 +362,8 @@ where /// `B` receives the confirmation from `A` that the connection creation was successful. /// `B` also starts processing the connection on its side. pub fn connection_open_confirm_on_b( - ctx_b: &mut MockContext, - ctx_a: &MockContext, + ctx_b: &mut TestContext, + ctx_a: &TestContext, conn_id_on_a: ConnectionId, conn_id_on_b: ConnectionId, signer: Signer, @@ -399,8 +399,8 @@ where /// A connection is created by `A` towards `B` using the IBC connection handshake protocol. /// Returns the connection identifiers of `A` and `B`. pub fn create_connection_on_a( - ctx_a: &mut MockContext, - ctx_b: &mut MockContext, + ctx_a: &mut TestContext, + ctx_b: &mut TestContext, client_id_on_a: ClientId, client_id_on_b: ClientId, signer: Signer, @@ -468,7 +468,7 @@ where /// `A` initiates a channel with port identifier with the other end on `B`. /// Returns the channel identifier of `A`. pub fn channel_open_init_on_a( - ctx_a: &mut MockContext, + ctx_a: &mut TestContext, conn_id_on_a: ConnectionId, port_id_on_a: PortId, port_id_on_b: PortId, @@ -497,8 +497,8 @@ where /// `B` receives the channel opening attempt by `A` after `A` initiates the channel. /// Returns the channel identifier of `B`. pub fn channel_open_try_on_b( - ctx_b: &mut MockContext, - ctx_a: &MockContext, + ctx_b: &mut TestContext, + ctx_a: &TestContext, conn_id_on_b: ConnectionId, chan_id_on_a: ChannelId, port_id_on_a: PortId, @@ -545,8 +545,8 @@ where /// `A` receives `B`'s acknowledgement that `B` received the channel opening attempt by `A`. /// `A` starts processing the channel on its side. pub fn channel_open_ack_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, chan_id_on_a: ChannelId, port_id_on_a: PortId, chan_id_on_b: ChannelId, @@ -586,8 +586,8 @@ where /// `B` receives the confirmation from `A` that the channel creation was successful. /// `B` also starts processing the channel on its side. pub fn channel_open_confirm_on_b( - ctx_b: &mut MockContext, - ctx_a: &MockContext, + ctx_b: &mut TestContext, + ctx_a: &TestContext, chan_id_on_a: ChannelId, chan_id_on_b: ChannelId, port_id_on_b: PortId, @@ -624,7 +624,7 @@ where /// `A` initiates the channel closing with the other end on `B`. /// `A` stops processing the channel. pub fn channel_close_init_on_a( - ctx_a: &mut MockContext, + ctx_a: &mut TestContext, chan_id_on_a: ChannelId, port_id_on_a: PortId, signer: Signer, @@ -646,8 +646,8 @@ where /// `B` receives the channel closing attempt by `A` after `A` initiates the channel closing. /// `B` also stops processing the channel. pub fn channel_close_confirm_on_b( - ctx_b: &mut MockContext, - ctx_a: &MockContext, + ctx_b: &mut TestContext, + ctx_a: &TestContext, chan_id_on_b: ChannelId, port_id_on_b: PortId, signer: Signer, @@ -685,8 +685,8 @@ where /// Returns the channel identifiers of `A` and `B`. #[allow(clippy::too_many_arguments)] pub fn create_channel_on_a( - ctx_a: &mut MockContext, - ctx_b: &mut MockContext, + ctx_a: &mut TestContext, + ctx_b: &mut TestContext, client_id_on_a: ClientId, conn_id_on_a: ConnectionId, port_id_on_a: PortId, @@ -760,8 +760,8 @@ where /// A channel is closed by `A` towards `B` using the IBC channel handshake protocol. #[allow(clippy::too_many_arguments)] pub fn close_channel_on_a( - ctx_a: &mut MockContext, - ctx_b: &mut MockContext, + ctx_a: &mut TestContext, + ctx_b: &mut TestContext, client_id_on_a: ClientId, chan_id_on_a: ChannelId, port_id_on_a: PortId, @@ -798,8 +798,8 @@ where /// `B` receives a packet from an IBC module on `A`. /// Returns `B`'s acknowledgement of receipt. pub fn packet_recv_on_b( - ctx_b: &mut MockContext, - ctx_a: &MockContext, + ctx_b: &mut TestContext, + ctx_a: &TestContext, packet: Packet, signer: Signer, ) -> Acknowledgement { @@ -836,8 +836,8 @@ where /// `A` receives the acknowledgement from `B` that `B` received the packet from `A`. pub fn packet_ack_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, packet: Packet, acknowledgement: Acknowledgement, signer: Signer, @@ -873,8 +873,8 @@ where /// `A` receives the timeout packet from `B`. /// That is, `B` has not received the packet from `A` within the timeout period. pub fn packet_timeout_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, packet: Packet, signer: Signer, ) { @@ -910,8 +910,8 @@ where /// `A` receives the timeout packet from `B` after closing the channel. /// That is, `B` has not received the packet from `A` because the channel is closed. pub fn packet_timeout_on_close_on_a( - ctx_a: &mut MockContext, - ctx_b: &MockContext, + ctx_a: &mut TestContext, + ctx_b: &TestContext, packet: Packet, chan_id_on_b: ChannelId, port_id_on_b: PortId, @@ -958,8 +958,8 @@ where /// Sends a packet from an IBC application on `A` to `B` using the IBC packet relay protocol. pub fn send_packet_on_a( - ctx_a: &mut MockContext, - ctx_b: &mut MockContext, + ctx_a: &mut TestContext, + ctx_b: &mut TestContext, packet: Packet, client_id_on_a: ClientId, client_id_on_b: ClientId, diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 1c5f3bf6b..6d7926f63 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -243,7 +243,7 @@ where } /// Returns a counter of the number of channel ids that have been created thus far. - /// The value of this counter should increase only via the + /// The value of this counter should increase only via the /// `ChannelKeeper::increase_channel_counter` method. fn channel_counter(&self) -> Result { Ok(self diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index a8d813222..0785a8273 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -29,7 +29,7 @@ use ibc_proto::ics23::CommitmentProof; use parking_lot::Mutex; use typed_builder::TypedBuilder; -use crate::context::{MockContext, MockStore}; +use crate::context::{MockStore, TestContext}; use crate::fixtures::core::context::MockContextConfig; use crate::hosts::{HostClientState, TestBlock, TestHeader, TestHost}; use crate::testapp::ibc::clients::mock::header::MockHeader; @@ -455,7 +455,7 @@ where HostClientState: ClientStateValidation, { fn default() -> Self { - let context = MockContext::::default(); + let context = TestContext::::default(); LightClientBuilder::init().context(&context).build() } } @@ -468,7 +468,7 @@ where pub fn with_latest_height(height: Height) -> Self { let context = MockContextConfig::builder() .latest_height(height) - .build::>(); + .build::>(); LightClientBuilder::init().context(&context).build() } } @@ -480,7 +480,7 @@ where H: TestHost, HostClientState: ClientStateValidation, { - context: &'a MockContext, + context: &'a TestContext, #[builder(default, setter(into))] consensus_heights: Vec, #[builder(default)] diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 11d4a8303..058ef0f18 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -15,7 +15,7 @@ use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::types::path::{ClientConsensusStatePath, NextClientSequencePath}; use ibc::core::host::{ClientStateRef, ValidationContext}; use ibc_query::core::context::ProvableContext; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; @@ -139,9 +139,9 @@ fn test_tm_create_client_proof_verification_ok() { let ctx_tm = MockContextConfig::builder() .latest_height(client_height) - .build::>(); + .build::>(); - let ctx_mk = MockContext::::default().with_light_client( + let ctx_mk = TestContext::::default().with_light_client( &client_id, LightClientBuilder::init().context(&ctx_tm).build(), ); diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 27b3ce2df..cfd4e170d 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -24,7 +24,7 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; use ibc::primitives::ToVec; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::clients::tendermint::ClientStateConfig; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; @@ -48,7 +48,7 @@ use tendermint_testgen::Validator as TestgenValidator; use tracing::debug; struct Fixture { - ctx: MockContext, + ctx: TestContext, router: MockRouter, } @@ -56,7 +56,7 @@ struct Fixture { fn fixture() -> Fixture { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); - let ctx = MockContext::::default().with_light_client( + let ctx = TestContext::::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); @@ -136,9 +136,9 @@ fn test_update_client_with_prev_header() { .build(), ) .latest_height(latest_height) - .build::>(); + .build::>(); - let mut ctx = MockContext::::default() + let mut ctx = TestContext::::default() .with_light_client( &client_id, LightClientBuilder::init().context(&ctx_b).build(), @@ -229,13 +229,13 @@ fn test_consensus_state_pruning() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id.clone()).build()) .latest_height(client_height) - .build::>(); + .build::>(); let mut ctx = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id).build()) .latest_height(client_height) .latest_timestamp(Timestamp::now()) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init() @@ -347,7 +347,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .build::>(); + .build::>(); let mut ctx = MockContextConfig::builder() .host( @@ -356,7 +356,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init() @@ -439,7 +439,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::>(); let mut ctx_a = MockContextConfig::builder() .host( @@ -448,7 +448,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, // remote light client initialized with client_height @@ -539,7 +539,7 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::>(); let ctx_a = MockContextConfig::builder() .host( @@ -548,7 +548,7 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, // remote light client initialized with client_height @@ -647,7 +647,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::>(); let ctx_a = MockContextConfig::builder() .host( @@ -656,7 +656,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, // remote light client initialized with client_height @@ -742,7 +742,7 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::>(); let mut ctx_a = MockContextConfig::builder() .host( @@ -751,7 +751,7 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, // remote light client initialized with client_height @@ -839,7 +839,7 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::>(); let ctx_a = MockContextConfig::builder() .host( @@ -848,7 +848,7 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, // remote light client initialized with client_height @@ -896,7 +896,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .build::>(); + .build::>(); let mut ctx = MockContextConfig::builder() .host( @@ -905,7 +905,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init() @@ -960,12 +960,12 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(ctx_b_chain_id).build()) .latest_height(client_height) - .build::>(); + .build::>(); let mut ctx_a = MockContextConfig::builder() .host(MockHost::builder().chain_id(ctx_a_chain_id).build()) .latest_height(start_height) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init() @@ -1084,7 +1084,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { .build(), ) .latest_height(client_height) - .build::>(); + .build::>(); let ctx = MockContextConfig::builder() .host( @@ -1093,7 +1093,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { .build(), ) .latest_height(chain_start_height) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init().context(&ctx_b).build(), @@ -1213,7 +1213,7 @@ fn test_submit_misbehaviour_nonexisting_client(fixture: Fixture) { let msg_envelope = msg_update_client(&ClientId::from_str("nonexistingclient").unwrap()); - let ctx = MockContext::::default().with_light_client( + let ctx = TestContext::::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); @@ -1229,7 +1229,7 @@ fn test_client_update_misbehaviour_nonexisting_client(fixture: Fixture) { let msg_envelope = msg_update_client(&ClientId::from_str("nonexistingclient").unwrap()); - let ctx = MockContext::::default().with_light_client( + let ctx = TestContext::::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); @@ -1254,7 +1254,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { .build(), ) .latest_height(misbehaviour_height) - .build::>(); + .build::>(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() @@ -1264,7 +1264,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init() @@ -1327,7 +1327,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { .build(), ) .latest_height(client_height) - .build::>(); + .build::>(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() @@ -1337,7 +1337,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init().context(&ctx_b).build(), @@ -1413,7 +1413,7 @@ fn test_expired_client() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) - .build::>(); + .build::>(); let mut ctx = MockContextConfig::builder() .host( @@ -1423,7 +1423,7 @@ fn test_expired_client() { ) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init() @@ -1466,7 +1466,7 @@ fn test_client_update_max_clock_drift() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) - .build::>(); + .build::>(); let ctx_a = MockContextConfig::builder() .host( @@ -1476,7 +1476,7 @@ fn test_client_update_max_clock_drift() { ) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build::>() + .build::>() .with_light_client( &client_id, LightClientBuilder::init() @@ -1532,7 +1532,7 @@ fn test_client_update_max_clock_drift() { /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` /// context, assuming that the latest header on the source context is `src_header`. pub(crate) fn build_client_update_datagram( - dest: &MockContext, + dest: &TestContext, client_id: &ClientId, src_header: &H, ) -> Result @@ -1588,13 +1588,13 @@ fn client_update_ping_pong() { let mut ctx_a = MockContextConfig::builder() .host(MockHost::builder().chain_id(chain_id_a).build()) .latest_height(chain_a_start_height) - .build::>(); + .build::>(); let mut ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(chain_b_start_height) .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A - .build::>(); + .build::>(); ctx_a = ctx_a.with_light_client( &client_on_a_for_b, diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index 5f7d1af34..605089048 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -9,7 +9,7 @@ use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::path::ClientConsensusStatePath; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; @@ -35,8 +35,8 @@ enum Msg { fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { let client_id = mock_client_type().build_client_id(0); - let ctx_default = MockContext::::default(); - let ctx_with_client = MockContext::::default().with_light_client( + let ctx_default = TestContext::::default(); + let ctx_with_client = TestContext::::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs index 75b2db444..e64ca3152 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs @@ -14,7 +14,7 @@ use ibc::core::host::types::identifiers::{ChainId, ClientId}; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_ack; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; @@ -60,7 +60,7 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { let mut conn_end_open = default_conn_end.clone(); conn_end_open.set_state(State::Open); // incorrect field - let ctx_default = MockContext::::default(); + let ctx_default = TestContext::::default(); let ctx_new = MockContextConfig::builder() .host( MockHost::builder() @@ -70,7 +70,7 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { .build(), ) .latest_height(latest_height) - .build::>(); + .build::>(); let ctx = match ctx { Ctx::New => ctx_new.ibc_store, Ctx::NewWithConnection => { diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs index 70a838460..3347b7206 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs @@ -11,7 +11,7 @@ use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::connection::dummy_conn_open_confirm; use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; @@ -34,7 +34,7 @@ fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), ); - let ctx_default = MockContext::::default(); + let ctx_default = TestContext::::default(); let incorrect_conn_end_state = ConnectionEnd::new( State::Init, diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs index ded2d15e7..ca7f7debb 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs @@ -7,7 +7,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::connection::{ dummy_msg_conn_open_init, msg_conn_open_init_with_counterparty_conn_id, msg_conn_open_with_version, @@ -41,7 +41,7 @@ fn conn_open_init_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture msg_conn_open_init_with_counterparty_conn_id(msg_default, 2), }; - let ctx_default = MockContext::::default(); + let ctx_default = TestContext::::default(); let ctx = match ctx_variant { Ctx::WithClient => { ctx_default diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs index b587e33f6..209e63833 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -6,7 +6,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_try; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; @@ -53,7 +53,7 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture>(); + .build::>(); let ctx = match ctx_variant { Ctx::Default => DefaultIbcStore::default(), Ctx::WithClient => { diff --git a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs index d614c4981..4300f42a0 100644 --- a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs +++ b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs @@ -13,7 +13,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_acknowledgement; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -22,7 +22,7 @@ use rstest::*; use test_log::test; struct Fixture { - ctx: MockContext, + ctx: TestContext, router: MockRouter, client_height: Height, msg: MsgAcknowledgement, @@ -37,7 +37,7 @@ fn fixture() -> Fixture { let default_client_id = ClientId::new("07-tendermint", 0).expect("no error"); let client_height = Height::new(0, 2).unwrap(); - let ctx = MockContext::::default().with_light_client( + let ctx = TestContext::::default().with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), ); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs index e8e806288..028c9e1f7 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs @@ -11,7 +11,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -23,7 +23,7 @@ use ibc_testkit::testapp::ibc::core::types::LightClientState; fn test_chan_close_confirm_validate() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); - let default_context = MockContext::::default(); + let default_context = TestContext::::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); let conn_end = ConnectionEnd::new( @@ -80,7 +80,7 @@ fn test_chan_close_confirm_validate() { fn test_chan_close_confirm_execute() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); - let default_context = MockContext::::default(); + let default_context = TestContext::::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); let conn_end = ConnectionEnd::new( diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs index c6f102c9c..217f80efb 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs @@ -11,7 +11,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_init; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -51,7 +51,7 @@ fn test_chan_close_init_validate() { .unwrap(); let context = { - let default_context = MockContext::::default(); + let default_context = TestContext::::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); default_context @@ -109,7 +109,7 @@ fn test_chan_close_init_execute() { .unwrap(); let mut context = { - let default_context = MockContext::::default(); + let default_context = TestContext::::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); default_context diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs index 608624558..a11ba71cf 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs @@ -12,7 +12,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; use ibc::core::primitives::*; use ibc::core::router::types::module::ModuleId; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_ack; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -23,7 +23,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub context: MockContext, + pub context: TestContext, pub router: MockRouter, pub module_id: ModuleId, pub msg: MsgChannelOpenAck, @@ -37,7 +37,7 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { let proof_height = 10; - let context = MockContext::::default(); + let context = TestContext::::default(); let module_id = ModuleId::new(MODULE_ID_STR.to_string()); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs index 441abae22..3d560db6f 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs @@ -11,7 +11,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId}; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -22,7 +22,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub context: MockContext, + pub context: TestContext, pub router: MockRouter, pub msg: MsgChannelOpenConfirm, pub client_id_on_b: ClientId, @@ -35,7 +35,7 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { let proof_height = 10; - let context = MockContext::::default(); + let context = TestContext::::default(); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs index 67ec6c479..20395e454 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs @@ -8,7 +8,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_init; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_init; use ibc_testkit::hosts::MockHost; @@ -18,7 +18,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub ctx: MockContext, + pub ctx: TestContext, pub router: MockRouter, pub msg: MsgEnvelope, } @@ -30,7 +30,7 @@ fn fixture() -> Fixture { let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_init)); - let default_ctx = MockContext::::default(); + let default_ctx = TestContext::::default(); let router = MockRouter::new_with_transfer(); let msg_conn_init = dummy_msg_conn_open_init(); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs index 5d6034c49..2ec1c86d2 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs @@ -10,7 +10,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_try; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -21,7 +21,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub ctx: MockContext, + pub ctx: TestContext, pub router: MockRouter, pub msg: MsgEnvelope, pub client_id_on_b: ClientId, @@ -56,7 +56,7 @@ fn fixture() -> Fixture { let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_try)); - let ctx = MockContext::::default(); + let ctx = TestContext::::default(); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs index c1ce01436..8f5881930 100644 --- a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs @@ -13,7 +13,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::{dummy_msg_recv_packet, dummy_raw_msg_recv_packet}; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::hosts::MockHost; @@ -23,7 +23,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub context: MockContext, + pub context: TestContext, pub router: MockRouter, pub client_height: Height, pub host_height: Height, @@ -37,7 +37,7 @@ pub struct Fixture { fn fixture() -> Fixture { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); - let context = MockContext::::default(); + let context = TestContext::::default(); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/send_packet.rs b/ibc-testkit/tests/core/ics04_channel/send_packet.rs index 7263f5310..258bd0e4a 100644 --- a/ibc-testkit/tests/core/ics04_channel/send_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/send_packet.rs @@ -14,7 +14,7 @@ use ibc::core::connection::types::{ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_packet; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::types::LightClientState; @@ -26,7 +26,7 @@ fn send_packet_processing() { struct Test { name: String, - ctx: MockContext, + ctx: TestContext, packet: Packet, want_pass: bool, } @@ -87,13 +87,13 @@ fn send_packet_processing() { let tests: Vec = vec![ Test { name: "Processing fails because no channel exists in the context".to_string(), - ctx: MockContext::::default(), + ctx: TestContext::::default(), packet: packet.clone(), want_pass: false, }, Test { name: "Good parameters".to_string(), - ctx: MockContext::::default() + ctx: TestContext::::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -106,7 +106,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height same as destination chain height".to_string(), - ctx: MockContext::::default() + ctx: TestContext::::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -119,7 +119,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height one more than destination chain height".to_string(), - ctx: MockContext::::default() + ctx: TestContext::::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -132,7 +132,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout due to timestamp".to_string(), - ctx: MockContext::::default() + ctx: TestContext::::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), diff --git a/ibc-testkit/tests/core/ics04_channel/timeout.rs b/ibc-testkit/tests/core/ics04_channel/timeout.rs index 315c6300b..22edec15f 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -15,7 +15,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::host::types::path::ClientConsensusStatePath; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -23,7 +23,7 @@ use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; struct Fixture { - ctx: MockContext, + ctx: TestContext, pub router: MockRouter, client_id: ClientId, client_height: Height, @@ -39,7 +39,7 @@ fn fixture() -> Fixture { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); let client_height = Height::new(0, 2).unwrap(); - let ctx = MockContext::::default().with_light_client( + let ctx = TestContext::::default().with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), ); diff --git a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs index 62468bbbc..e30bfd0ac 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs @@ -12,7 +12,7 @@ use ibc::core::entrypoint::validate; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout_on_close; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -20,7 +20,7 @@ use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; pub struct Fixture { - pub context: MockContext, + pub context: TestContext, pub router: MockRouter, pub msg: MsgTimeoutOnClose, pub packet_commitment: PacketCommitment, @@ -33,7 +33,7 @@ fn fixture() -> Fixture { let default_client_id = ClientId::new("07-tendermint", 0).expect("no error"); let client_height = Height::new(0, 2).unwrap(); - let context = MockContext::::default().with_light_client( + let context = TestContext::::default().with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), ); diff --git a/ibc-testkit/tests/core/router.rs b/ibc-testkit/tests/core/router.rs index 20344b6c5..1b3ad7245 100644 --- a/ibc-testkit/tests/core/router.rs +++ b/ibc-testkit/tests/core/router.rs @@ -22,7 +22,7 @@ use ibc::core::host::types::path::CommitmentPath; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; -use ibc_testkit::context::MockContext; +use ibc_testkit::context::TestContext; use ibc_testkit::fixtures::applications::transfer::{ extract_transfer_packet, MsgTransferConfig, PacketDataConfig, }; @@ -69,7 +69,7 @@ fn routing_module_and_keepers() { } } - type StateCheckFn = dyn FnOnce(&MockContext) -> bool; + type StateCheckFn = dyn FnOnce(&TestContext) -> bool; // Test parameters struct Test { @@ -99,7 +99,7 @@ fn routing_module_and_keepers() { .add(core::time::Duration::from_secs(4)) .unwrap(), ) - .build::>(); + .build::>(); let mut router = MockRouter::new_with_transfer(); From 36eccc0174c0975ec52ca97743f0b5e7993125fc Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 15 Apr 2024 15:23:25 +0200 Subject: [PATCH 08/40] add MockContext and TendermintContext --- ibc-testkit/src/context.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 5c495ea6d..338c96b73 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -53,6 +53,8 @@ where pub type MockStore = RevertibleStore>; pub type TestContext = StoreGenericTestContext; +pub type MockContext = TestContext; +pub type TendermintContext = TestContext; /// Returns a MockContext with bare minimum initialization: no clients, no connections, and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the From 4757bdb36585398418e7031638f3ce2701d2a3ab Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 15 Apr 2024 15:37:02 +0200 Subject: [PATCH 09/40] update with MockContext and TendermintContext --- .../tests/core/ics02_client/create_client.rs | 7 +- .../tests/core/ics02_client/update_client.rs | 74 +++++++++---------- .../tests/core/ics02_client/upgrade_client.rs | 6 +- .../core/ics03_connection/conn_open_ack.rs | 6 +- .../ics03_connection/conn_open_confirm.rs | 4 +- .../core/ics03_connection/conn_open_init.rs | 4 +- .../core/ics03_connection/conn_open_try.rs | 4 +- .../core/ics04_channel/acknowledgement.rs | 6 +- .../core/ics04_channel/chan_close_confirm.rs | 6 +- .../core/ics04_channel/chan_close_init.rs | 6 +- .../tests/core/ics04_channel/chan_open_ack.rs | 6 +- .../core/ics04_channel/chan_open_confirm.rs | 6 +- .../core/ics04_channel/chan_open_init.rs | 6 +- .../tests/core/ics04_channel/chan_open_try.rs | 6 +- .../tests/core/ics04_channel/recv_packet.rs | 6 +- .../tests/core/ics04_channel/send_packet.rs | 14 ++-- .../tests/core/ics04_channel/timeout.rs | 6 +- .../core/ics04_channel/timeout_on_close.rs | 6 +- ibc-testkit/tests/core/router.rs | 7 +- 19 files changed, 92 insertions(+), 94 deletions(-) diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 058ef0f18..0e97d2c97 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -15,13 +15,12 @@ use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::types::path::{ClientConsensusStatePath, NextClientSequencePath}; use ibc::core::host::{ClientStateRef, ValidationContext}; use ibc_query::core::context::ProvableContext; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::{MockContext, TendermintContext}; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; -use ibc_testkit::hosts::{MockHost, TendermintHost}; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; @@ -139,9 +138,9 @@ fn test_tm_create_client_proof_verification_ok() { let ctx_tm = MockContextConfig::builder() .latest_height(client_height) - .build::>(); + .build::(); - let ctx_mk = TestContext::::default().with_light_client( + let ctx_mk = MockContext::default().with_light_client( &client_id, LightClientBuilder::init().context(&ctx_tm).build(), ); diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index cfd4e170d..92a00e2cb 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -24,7 +24,7 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; use ibc::primitives::ToVec; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::{MockContext, TendermintContext, TestContext}; use ibc_testkit::fixtures::clients::tendermint::ClientStateConfig; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; @@ -48,7 +48,7 @@ use tendermint_testgen::Validator as TestgenValidator; use tracing::debug; struct Fixture { - ctx: TestContext, + ctx: MockContext, router: MockRouter, } @@ -56,7 +56,7 @@ struct Fixture { fn fixture() -> Fixture { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); - let ctx = TestContext::::default().with_light_client( + let ctx = MockContext::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); @@ -136,9 +136,9 @@ fn test_update_client_with_prev_header() { .build(), ) .latest_height(latest_height) - .build::>(); + .build::(); - let mut ctx = TestContext::::default() + let mut ctx = MockContext::default() .with_light_client( &client_id, LightClientBuilder::init().context(&ctx_b).build(), @@ -229,13 +229,13 @@ fn test_consensus_state_pruning() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id.clone()).build()) .latest_height(client_height) - .build::>(); + .build::(); let mut ctx = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id).build()) .latest_height(client_height) .latest_timestamp(Timestamp::now()) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init() @@ -347,7 +347,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .build::>(); + .build::(); let mut ctx = MockContextConfig::builder() .host( @@ -356,7 +356,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init() @@ -439,7 +439,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::(); let mut ctx_a = MockContextConfig::builder() .host( @@ -448,7 +448,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, // remote light client initialized with client_height @@ -539,7 +539,7 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::(); let ctx_a = MockContextConfig::builder() .host( @@ -548,7 +548,7 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, // remote light client initialized with client_height @@ -647,7 +647,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::(); let ctx_a = MockContextConfig::builder() .host( @@ -656,7 +656,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, // remote light client initialized with client_height @@ -742,7 +742,7 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::(); let mut ctx_a = MockContextConfig::builder() .host( @@ -751,7 +751,7 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, // remote light client initialized with client_height @@ -839,7 +839,7 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) - .build::>(); + .build::(); let ctx_a = MockContextConfig::builder() .host( @@ -848,7 +848,7 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, // remote light client initialized with client_height @@ -896,7 +896,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) - .build::>(); + .build::(); let mut ctx = MockContextConfig::builder() .host( @@ -905,7 +905,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init() @@ -960,12 +960,12 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(ctx_b_chain_id).build()) .latest_height(client_height) - .build::>(); + .build::(); let mut ctx_a = MockContextConfig::builder() .host(MockHost::builder().chain_id(ctx_a_chain_id).build()) .latest_height(start_height) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init() @@ -1084,7 +1084,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { .build(), ) .latest_height(client_height) - .build::>(); + .build::(); let ctx = MockContextConfig::builder() .host( @@ -1093,7 +1093,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { .build(), ) .latest_height(chain_start_height) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init().context(&ctx_b).build(), @@ -1213,7 +1213,7 @@ fn test_submit_misbehaviour_nonexisting_client(fixture: Fixture) { let msg_envelope = msg_update_client(&ClientId::from_str("nonexistingclient").unwrap()); - let ctx = TestContext::::default().with_light_client( + let ctx = MockContext::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); @@ -1229,7 +1229,7 @@ fn test_client_update_misbehaviour_nonexisting_client(fixture: Fixture) { let msg_envelope = msg_update_client(&ClientId::from_str("nonexistingclient").unwrap()); - let ctx = TestContext::::default().with_light_client( + let ctx = MockContext::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); @@ -1254,7 +1254,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { .build(), ) .latest_height(misbehaviour_height) - .build::>(); + .build::(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() @@ -1264,7 +1264,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init() @@ -1327,7 +1327,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { .build(), ) .latest_height(client_height) - .build::>(); + .build::(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B let mut ctx_a = MockContextConfig::builder() @@ -1337,7 +1337,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { .build(), ) .latest_height(Height::new(1, 1).unwrap()) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init().context(&ctx_b).build(), @@ -1413,7 +1413,7 @@ fn test_expired_client() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) - .build::>(); + .build::(); let mut ctx = MockContextConfig::builder() .host( @@ -1423,7 +1423,7 @@ fn test_expired_client() { ) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init() @@ -1466,7 +1466,7 @@ fn test_client_update_max_clock_drift() { .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) - .build::>(); + .build::(); let ctx_a = MockContextConfig::builder() .host( @@ -1476,7 +1476,7 @@ fn test_client_update_max_clock_drift() { ) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build::>() + .build::() .with_light_client( &client_id, LightClientBuilder::init() @@ -1588,13 +1588,13 @@ fn client_update_ping_pong() { let mut ctx_a = MockContextConfig::builder() .host(MockHost::builder().chain_id(chain_id_a).build()) .latest_height(chain_a_start_height) - .build::>(); + .build::(); let mut ctx_b = MockContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(chain_b_start_height) .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A - .build::>(); + .build::(); ctx_a = ctx_a.with_light_client( &client_on_a_for_b, diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index 605089048..e5c9c268a 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -9,7 +9,7 @@ use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::path::ClientConsensusStatePath; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; @@ -35,8 +35,8 @@ enum Msg { fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture { let client_id = mock_client_type().build_client_id(0); - let ctx_default = TestContext::::default(); - let ctx_with_client = TestContext::::default().with_light_client( + let ctx_default = MockContext::default(); + let ctx_with_client = MockContext::default().with_light_client( &client_id, LightClientState::::with_latest_height(Height::new(0, 42).unwrap()), ); diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs index e64ca3152..5517b68b1 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs @@ -14,7 +14,7 @@ use ibc::core::host::types::identifiers::{ChainId, ClientId}; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_ack; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; @@ -60,7 +60,7 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { let mut conn_end_open = default_conn_end.clone(); conn_end_open.set_state(State::Open); // incorrect field - let ctx_default = TestContext::::default(); + let ctx_default = MockContext::default(); let ctx_new = MockContextConfig::builder() .host( MockHost::builder() @@ -70,7 +70,7 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { .build(), ) .latest_height(latest_height) - .build::>(); + .build::(); let ctx = match ctx { Ctx::New => ctx_new.ibc_store, Ctx::NewWithConnection => { diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs index 3347b7206..dc21dadef 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_confirm.rs @@ -11,7 +11,7 @@ use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_conn_open_confirm; use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; @@ -34,7 +34,7 @@ fn conn_open_confirm_fixture(ctx: Ctx) -> Fixture { CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), ); - let ctx_default = TestContext::::default(); + let ctx_default = MockContext::default(); let incorrect_conn_end_state = ConnectionEnd::new( State::Init, diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs index ca7f7debb..1a9218e24 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_init.rs @@ -7,7 +7,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::{ dummy_msg_conn_open_init, msg_conn_open_init_with_counterparty_conn_id, msg_conn_open_with_version, @@ -41,7 +41,7 @@ fn conn_open_init_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture msg_conn_open_init_with_counterparty_conn_id(msg_default, 2), }; - let ctx_default = TestContext::::default(); + let ctx_default = MockContext::default(); let ctx = match ctx_variant { Ctx::WithClient => { ctx_default diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs index 209e63833..2cf4d76b7 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -6,7 +6,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_try; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; @@ -53,7 +53,7 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture>(); + .build::(); let ctx = match ctx_variant { Ctx::Default => DefaultIbcStore::default(), Ctx::WithClient => { diff --git a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs index 4300f42a0..1afd22fe1 100644 --- a/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs +++ b/ibc-testkit/tests/core/ics04_channel/acknowledgement.rs @@ -13,7 +13,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_acknowledgement; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -22,7 +22,7 @@ use rstest::*; use test_log::test; struct Fixture { - ctx: TestContext, + ctx: MockContext, router: MockRouter, client_height: Height, msg: MsgAcknowledgement, @@ -37,7 +37,7 @@ fn fixture() -> Fixture { let default_client_id = ClientId::new("07-tendermint", 0).expect("no error"); let client_height = Height::new(0, 2).unwrap(); - let ctx = TestContext::::default().with_light_client( + let ctx = MockContext::default().with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), ); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs index 028c9e1f7..f3491f9ca 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_confirm.rs @@ -11,7 +11,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -23,7 +23,7 @@ use ibc_testkit::testapp::ibc::core::types::LightClientState; fn test_chan_close_confirm_validate() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); - let default_context = TestContext::::default(); + let default_context = MockContext::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); let conn_end = ConnectionEnd::new( @@ -80,7 +80,7 @@ fn test_chan_close_confirm_validate() { fn test_chan_close_confirm_execute() { let client_id = mock_client_type().build_client_id(24); let conn_id = ConnectionId::new(2); - let default_context = TestContext::::default(); + let default_context = MockContext::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); let conn_end = ConnectionEnd::new( diff --git a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs index 217f80efb..63fd30d65 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_close_init.rs @@ -11,7 +11,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_close_init; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -51,7 +51,7 @@ fn test_chan_close_init_validate() { .unwrap(); let context = { - let default_context = TestContext::::default(); + let default_context = MockContext::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); default_context @@ -109,7 +109,7 @@ fn test_chan_close_init_execute() { .unwrap(); let mut context = { - let default_context = TestContext::::default(); + let default_context = MockContext::default(); let client_consensus_state_height = default_context.ibc_store.host_height().unwrap(); default_context diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs index a11ba71cf..e4250dd83 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_ack.rs @@ -12,7 +12,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; use ibc::core::primitives::*; use ibc::core::router::types::module::ModuleId; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_ack; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -23,7 +23,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub context: TestContext, + pub context: MockContext, pub router: MockRouter, pub module_id: ModuleId, pub msg: MsgChannelOpenAck, @@ -37,7 +37,7 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { let proof_height = 10; - let context = TestContext::::default(); + let context = MockContext::default(); let module_id = ModuleId::new(MODULE_ID_STR.to_string()); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs index 3d560db6f..0b399aa40 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_confirm.rs @@ -11,7 +11,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId}; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_confirm; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -22,7 +22,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub context: TestContext, + pub context: MockContext, pub router: MockRouter, pub msg: MsgChannelOpenConfirm, pub client_id_on_b: ClientId, @@ -35,7 +35,7 @@ pub struct Fixture { #[fixture] fn fixture() -> Fixture { let proof_height = 10; - let context = TestContext::::default(); + let context = MockContext::default(); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs index 20395e454..844af3d65 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_init.rs @@ -8,7 +8,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ConnectionId; use ibc::core::host::ValidationContext; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_init; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_init; use ibc_testkit::hosts::MockHost; @@ -18,7 +18,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub ctx: TestContext, + pub ctx: MockContext, pub router: MockRouter, pub msg: MsgEnvelope, } @@ -30,7 +30,7 @@ fn fixture() -> Fixture { let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_init)); - let default_ctx = TestContext::::default(); + let default_ctx = MockContext::default(); let router = MockRouter::new_with_transfer(); let msg_conn_init = dummy_msg_conn_open_init(); diff --git a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs index 2ec1c86d2..3a43d0dfb 100644 --- a/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs +++ b/ibc-testkit/tests/core/ics04_channel/chan_open_try.rs @@ -10,7 +10,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; use ibc::core::host::ValidationContext; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_chan_open_try; use ibc_testkit::fixtures::core::connection::dummy_raw_counterparty_conn; use ibc_testkit::hosts::MockHost; @@ -21,7 +21,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub ctx: TestContext, + pub ctx: MockContext, pub router: MockRouter, pub msg: MsgEnvelope, pub client_id_on_b: ClientId, @@ -56,7 +56,7 @@ fn fixture() -> Fixture { let msg = MsgEnvelope::from(ChannelMsg::from(msg_chan_open_try)); - let ctx = TestContext::::default(); + let ctx = MockContext::default(); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs index 8f5881930..f7c4deb13 100644 --- a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs @@ -13,7 +13,7 @@ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::{dummy_msg_recv_packet, dummy_raw_msg_recv_packet}; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::hosts::MockHost; @@ -23,7 +23,7 @@ use rstest::*; use test_log::test; pub struct Fixture { - pub context: TestContext, + pub context: MockContext, pub router: MockRouter, pub client_height: Height, pub host_height: Height, @@ -37,7 +37,7 @@ pub struct Fixture { fn fixture() -> Fixture { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); - let context = TestContext::::default(); + let context = MockContext::default(); let router = MockRouter::new_with_transfer(); diff --git a/ibc-testkit/tests/core/ics04_channel/send_packet.rs b/ibc-testkit/tests/core/ics04_channel/send_packet.rs index 258bd0e4a..51d973a03 100644 --- a/ibc-testkit/tests/core/ics04_channel/send_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/send_packet.rs @@ -14,7 +14,7 @@ use ibc::core::connection::types::{ use ibc::core::handler::types::events::{IbcEvent, MessageEvent}; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_packet; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::types::LightClientState; @@ -26,7 +26,7 @@ fn send_packet_processing() { struct Test { name: String, - ctx: TestContext, + ctx: MockContext, packet: Packet, want_pass: bool, } @@ -87,13 +87,13 @@ fn send_packet_processing() { let tests: Vec = vec![ Test { name: "Processing fails because no channel exists in the context".to_string(), - ctx: TestContext::::default(), + ctx: MockContext::default(), packet: packet.clone(), want_pass: false, }, Test { name: "Good parameters".to_string(), - ctx: TestContext::::default() + ctx: MockContext::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -106,7 +106,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height same as destination chain height".to_string(), - ctx: TestContext::::default() + ctx: MockContext::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -119,7 +119,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height one more than destination chain height".to_string(), - ctx: TestContext::::default() + ctx: MockContext::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), @@ -132,7 +132,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout due to timestamp".to_string(), - ctx: TestContext::::default() + ctx: MockContext::default() .with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), diff --git a/ibc-testkit/tests/core/ics04_channel/timeout.rs b/ibc-testkit/tests/core/ics04_channel/timeout.rs index 22edec15f..240ddd4a8 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -15,7 +15,7 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::host::types::path::ClientConsensusStatePath; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -23,7 +23,7 @@ use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; struct Fixture { - ctx: TestContext, + ctx: MockContext, pub router: MockRouter, client_id: ClientId, client_height: Height, @@ -39,7 +39,7 @@ fn fixture() -> Fixture { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); let client_height = Height::new(0, 2).unwrap(); - let ctx = TestContext::::default().with_light_client( + let ctx = MockContext::default().with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), ); diff --git a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs index e30bfd0ac..a454261fd 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout_on_close.rs @@ -12,7 +12,7 @@ use ibc::core::entrypoint::validate; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::core::primitives::*; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::channel::dummy_raw_msg_timeout_on_close; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -20,7 +20,7 @@ use ibc_testkit::testapp::ibc::core::types::LightClientState; use rstest::*; pub struct Fixture { - pub context: TestContext, + pub context: MockContext, pub router: MockRouter, pub msg: MsgTimeoutOnClose, pub packet_commitment: PacketCommitment, @@ -33,7 +33,7 @@ fn fixture() -> Fixture { let default_client_id = ClientId::new("07-tendermint", 0).expect("no error"); let client_height = Height::new(0, 2).unwrap(); - let context = TestContext::::default().with_light_client( + let context = MockContext::default().with_light_client( &ClientId::new("07-tendermint", 0).expect("no error"), LightClientState::::with_latest_height(client_height), ); diff --git a/ibc-testkit/tests/core/router.rs b/ibc-testkit/tests/core/router.rs index 1b3ad7245..f9bfd8b3d 100644 --- a/ibc-testkit/tests/core/router.rs +++ b/ibc-testkit/tests/core/router.rs @@ -22,7 +22,7 @@ use ibc::core::host::types::path::CommitmentPath; use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; -use ibc_testkit::context::TestContext; +use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::applications::transfer::{ extract_transfer_packet, MsgTransferConfig, PacketDataConfig, }; @@ -38,7 +38,6 @@ use ibc_testkit::fixtures::core::connection::{ }; use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; -use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; @@ -69,7 +68,7 @@ fn routing_module_and_keepers() { } } - type StateCheckFn = dyn FnOnce(&TestContext) -> bool; + type StateCheckFn = dyn FnOnce(&MockContext) -> bool; // Test parameters struct Test { @@ -99,7 +98,7 @@ fn routing_module_and_keepers() { .add(core::time::Duration::from_secs(4)) .unwrap(), ) - .build::>(); + .build::(); let mut router = MockRouter::new_with_transfer(); From 06963b93a8af5fde1f0a544c61a235810deb2559 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 15 Apr 2024 15:37:50 +0200 Subject: [PATCH 10/40] MockContextConfig to TestContextConfig --- ibc-testkit/src/context.rs | 12 ++-- ibc-testkit/src/fixtures/core/context.rs | 6 +- ibc-testkit/src/testapp/ibc/core/types.rs | 4 +- .../tests/core/ics02_client/create_client.rs | 4 +- .../tests/core/ics02_client/update_client.rs | 64 +++++++++---------- .../core/ics03_connection/conn_open_ack.rs | 4 +- .../core/ics03_connection/conn_open_try.rs | 4 +- ibc-testkit/tests/core/router.rs | 4 +- 8 files changed, 51 insertions(+), 51 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 338c96b73..685b6c85a 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -22,7 +22,7 @@ use ibc::primitives::prelude::*; use ibc::primitives::Timestamp; use super::testapp::ibc::core::types::{LightClientState, MockIbcStore}; -use crate::fixtures::core::context::MockContextConfig; +use crate::fixtures::core::context::TestContextConfig; use crate::hosts::{HostClientState, MockHost, TendermintHost, TestBlock, TestHeader, TestHost}; use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; @@ -66,7 +66,7 @@ where HostClientState: ClientStateValidation>, { fn default() -> Self { - MockContextConfig::builder().build() + TestContextConfig::builder().build() } } @@ -444,25 +444,25 @@ mod tests { let tests: Vec> = vec![ Test { name: "Empty history, small pruning window".to_string(), - ctx: MockContextConfig::builder() + ctx: TestContextConfig::builder() .latest_height(Height::new(cv, 1).expect("Never fails")) .build(), }, Test { name: "Large pruning window".to_string(), - ctx: MockContextConfig::builder() + ctx: TestContextConfig::builder() .latest_height(Height::new(cv, 2).expect("Never fails")) .build(), }, Test { name: "Small pruning window".to_string(), - ctx: MockContextConfig::builder() + ctx: TestContextConfig::builder() .latest_height(Height::new(cv, 30).expect("Never fails")) .build(), }, Test { name: "Small pruning window, small starting height".to_string(), - ctx: MockContextConfig::builder() + ctx: TestContextConfig::builder() .latest_height(Height::new(cv, 2).expect("Never fails")) .build(), }, diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index eb1c4bdbd..2afa755fa 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -17,7 +17,7 @@ use crate::utils::year_2023; /// Configuration of the `MockContext` type for generating dummy contexts. #[derive(Debug, TypedBuilder)] #[builder(build_method(into))] -pub struct MockContextConfig +pub struct TestContextConfig where H: TestHost, { @@ -37,13 +37,13 @@ where latest_height: Height, } -impl From> for StoreGenericTestContext +impl From> for StoreGenericTestContext where S: ProvableStore + Debug + Default, H: TestHost, HostClientState: ClientStateValidation>, { - fn from(params: MockContextConfig) -> Self { + fn from(params: TestContextConfig) -> Self { assert_ne!( params.latest_height.revision_height(), 0, diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 0785a8273..a1296cf6a 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -30,7 +30,7 @@ use parking_lot::Mutex; use typed_builder::TypedBuilder; use crate::context::{MockStore, TestContext}; -use crate::fixtures::core::context::MockContextConfig; +use crate::fixtures::core::context::TestContextConfig; use crate::hosts::{HostClientState, TestBlock, TestHeader, TestHost}; use crate::testapp::ibc::clients::mock::header::MockHeader; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; @@ -466,7 +466,7 @@ where HostClientState: ClientStateValidation, { pub fn with_latest_height(height: Height) -> Self { - let context = MockContextConfig::builder() + let context = TestContextConfig::builder() .latest_height(height) .build::>(); LightClientBuilder::init().context(&context).build() diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 0e97d2c97..3f8bd4bc8 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -19,7 +19,7 @@ use ibc_testkit::context::{MockContext, TendermintContext}; use ibc_testkit::fixtures::clients::tendermint::{ dummy_tendermint_header, dummy_tm_client_state_from_header, }; -use ibc_testkit::fixtures::core::context::MockContextConfig; +use ibc_testkit::fixtures::core::context::TestContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, @@ -136,7 +136,7 @@ fn test_tm_create_client_proof_verification_ok() { let client_id = ClientId::new("07-tendermint", 0).expect("no error"); let client_height = Height::new(0, 10).expect("no error"); - let ctx_tm = MockContextConfig::builder() + let ctx_tm = TestContextConfig::builder() .latest_height(client_height) .build::(); diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 92a00e2cb..0f83552f0 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -26,7 +26,7 @@ use ibc::primitives::proto::Any; use ibc::primitives::ToVec; use ibc_testkit::context::{MockContext, TendermintContext, TestContext}; use ibc_testkit::fixtures::clients::tendermint::ClientStateConfig; -use ibc_testkit::fixtures::core::context::MockContextConfig; +use ibc_testkit::fixtures::core::context::TestContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::hosts::tendermint::BlockParams; use ibc_testkit::hosts::{ @@ -129,7 +129,7 @@ fn test_update_client_with_prev_header() { let height_1 = Height::new(0, 43).unwrap(); let height_2 = Height::new(0, 44).unwrap(); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host( TendermintHost::builder() .chain_id(chain_id_b.clone()) @@ -226,12 +226,12 @@ fn test_consensus_state_pruning() { let client_id = tm_client_type().build_client_id(0); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id.clone()).build()) .latest_height(client_height) .build::(); - let mut ctx = MockContextConfig::builder() + let mut ctx = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id).build()) .latest_height(client_height) .latest_timestamp(Timestamp::now()) @@ -344,12 +344,12 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let update_height = Height::new(1, 21).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .build::(); - let mut ctx = MockContextConfig::builder() + let mut ctx = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -435,13 +435,13 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { assert_eq!(update_height.revision_height(), 22); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) .build::(); - let mut ctx_a = MockContextConfig::builder() + let mut ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -535,13 +535,13 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() assert_eq!(update_height.revision_height(), 22); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) .build::(); - let ctx_a = MockContextConfig::builder() + let ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -643,13 +643,13 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { assert_eq!(update_height.revision_height(), 22); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) .build::(); - let ctx_a = MockContextConfig::builder() + let ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -738,13 +738,13 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { assert_eq!(update_height.revision_height(), 22); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) .build::(); - let mut ctx_a = MockContextConfig::builder() + let mut ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -835,13 +835,13 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f assert_eq!(update_height.revision_height(), 22); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .block_params_history(block_params) .build::(); - let ctx_a = MockContextConfig::builder() + let ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -893,12 +893,12 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let update_height = Height::new(1, 21).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(update_height) .build::(); - let mut ctx = MockContextConfig::builder() + let mut ctx = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -957,12 +957,12 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let ctx_b_chain_id = ChainId::new("mockgaiaB-1").unwrap(); let start_height = Height::new(1, 11).unwrap(); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(ctx_b_chain_id).build()) .latest_height(client_height) .build::(); - let mut ctx_a = MockContextConfig::builder() + let mut ctx_a = TestContextConfig::builder() .host(MockHost::builder().chain_id(ctx_a_chain_id).build()) .latest_height(start_height) .build::() @@ -1077,7 +1077,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { let chain_start_height = Height::new(1, 11).unwrap(); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host( TendermintHost::builder() .chain_id(ChainId::new("mockgaiaB-1").unwrap()) @@ -1086,7 +1086,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { .latest_height(client_height) .build::(); - let ctx = MockContextConfig::builder() + let ctx = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -1247,7 +1247,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); // Create a mock context for chain-B - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host( TendermintHost::builder() .chain_id(chain_id_b.clone()) @@ -1257,7 +1257,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { .build::(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B - let mut ctx_a = MockContextConfig::builder() + let mut ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -1320,7 +1320,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let misbehaviour_height = Height::new(1, 21).unwrap(); let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host( TendermintHost::builder() .chain_id(chain_id_b.clone()) @@ -1330,7 +1330,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { .build::(); // Create a mock context for chain-A with a synthetic tendermint light client for chain-B - let mut ctx_a = MockContextConfig::builder() + let mut ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -1409,13 +1409,13 @@ fn test_expired_client() { let trusting_period = Duration::from_secs(64); - let ctx_b = MockContextConfig::builder() + let ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) .build::(); - let mut ctx = MockContextConfig::builder() + let mut ctx = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -1462,13 +1462,13 @@ fn test_client_update_max_clock_drift() { let max_clock_drift = Duration::from_secs(64); - let mut ctx_b = MockContextConfig::builder() + let mut ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(client_height) .latest_timestamp(timestamp) .build::(); - let ctx_a = MockContextConfig::builder() + let ctx_a = TestContextConfig::builder() .host( MockHost::builder() .chain_id(ChainId::new("mockgaiaA-1").unwrap()) @@ -1585,12 +1585,12 @@ fn client_update_ping_pong() { let chain_id_b = ChainId::new("mockgaiaB-1").unwrap(); // Create two mock contexts, one for each chain. - let mut ctx_a = MockContextConfig::builder() + let mut ctx_a = TestContextConfig::builder() .host(MockHost::builder().chain_id(chain_id_a).build()) .latest_height(chain_a_start_height) .build::(); - let mut ctx_b = MockContextConfig::builder() + let mut ctx_b = TestContextConfig::builder() .host(TendermintHost::builder().chain_id(chain_id_b).build()) .latest_height(chain_b_start_height) .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs index 5517b68b1..37733b60c 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs @@ -16,7 +16,7 @@ use ibc::core::primitives::prelude::*; use ibc::core::primitives::ZERO_DURATION; use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_ack; -use ibc_testkit::fixtures::core::context::MockContextConfig; +use ibc_testkit::fixtures::core::context::TestContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -61,7 +61,7 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { conn_end_open.set_state(State::Open); // incorrect field let ctx_default = MockContext::default(); - let ctx_new = MockContextConfig::builder() + let ctx_new = TestContextConfig::builder() .host( MockHost::builder() .chain_id( diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs index 2cf4d76b7..e466b403d 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -8,7 +8,7 @@ use ibc::core::host::ValidationContext; use ibc::core::primitives::prelude::*; use ibc_testkit::context::MockContext; use ibc_testkit::fixtures::core::connection::dummy_msg_conn_open_try; -use ibc_testkit::fixtures::core::context::MockContextConfig; +use ibc_testkit::fixtures::core::context::TestContextConfig; use ibc_testkit::fixtures::{Expect, Fixture}; use ibc_testkit::hosts::MockHost; use ibc_testkit::testapp::ibc::core::router::MockRouter; @@ -51,7 +51,7 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture(); let ctx = match ctx_variant { diff --git a/ibc-testkit/tests/core/router.rs b/ibc-testkit/tests/core/router.rs index f9bfd8b3d..b2e88ebca 100644 --- a/ibc-testkit/tests/core/router.rs +++ b/ibc-testkit/tests/core/router.rs @@ -36,7 +36,7 @@ use ibc_testkit::fixtures::core::connection::{ dummy_msg_conn_open_ack, dummy_msg_conn_open_init, dummy_msg_conn_open_init_with_client_id, dummy_msg_conn_open_try, msg_conn_open_try_with_client_id, }; -use ibc_testkit::fixtures::core::context::MockContextConfig; +use ibc_testkit::fixtures::core::context::TestContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; @@ -90,7 +90,7 @@ fn routing_module_and_keepers() { let upgrade_client_height_second = Height::new(1, 1).unwrap(); // We reuse this same context across all tests. Nothing in particular needs parametrizing. - let mut ctx = MockContextConfig::builder() + let mut ctx = TestContextConfig::builder() // a future timestamp, so that submitted packets are considered from past // not more than 5 secs, as later dummy_raw_msg_timeout_on_close(*, 5) is used .latest_timestamp( From b6f9622978dd68a044b21d91efe50e3c6ae6664e Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 15 Apr 2024 15:43:49 +0200 Subject: [PATCH 11/40] update comments with new namings --- ibc-testkit/src/context.rs | 8 ++++++-- ibc-testkit/src/fixtures/core/context.rs | 2 +- ibc-testkit/src/relayer/context.rs | 4 ++-- ibc-testkit/src/utils/mod.rs | 2 +- ibc-testkit/tests/core/ics02_client/update_client.rs | 10 +++++----- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 685b6c85a..267e39dc7 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -51,12 +51,16 @@ where pub ibc_router: MockRouter, } +/// A mock store type using basecoin-storage implementations. pub type MockStore = RevertibleStore>; +/// A [`StoreGenericTestContext`] using [`MockStore`]. pub type TestContext = StoreGenericTestContext; +/// A [`StoreGenericTestContext`] using [`MockStore`] and [`MockHost`]. pub type MockContext = TestContext; +/// A [`StoreGenericTestContext`] using [`MockStore`] and [`TendermintHost`]. pub type TendermintContext = TestContext; -/// Returns a MockContext with bare minimum initialization: no clients, no connections, and no channels are +/// Returns a [`StoreGenericTestContext`] with bare minimum initialization: no clients, no connections, and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the /// creation of new domain objects. impl Default for StoreGenericTestContext @@ -470,7 +474,7 @@ mod tests { // Generating 2000 Tendermint blocks is slow. // Test { // name: "Large pruning window, large starting height".to_string(), - // ctx: MockContextConfig::builder() + // ctx: TestContextConfig::builder() // .latest_height(Height::new(cv, 2000).expect("Never fails")) // .build(), // }, diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 2afa755fa..45ac363f2 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -14,7 +14,7 @@ use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::{MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; use crate::utils::year_2023; -/// Configuration of the `MockContext` type for generating dummy contexts. +/// Configuration of the [`StoreGenericTestContext`] type for generating dummy contexts. #[derive(Debug, TypedBuilder)] #[builder(build_method(into))] pub struct TestContextConfig diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 6dd268b1f..232fb6074 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -10,7 +10,7 @@ use crate::hosts::{HostClientState, TestHost}; use crate::relayer::utils::TypedRelayerOps; use crate::testapp::ibc::core::types::DefaultIbcStore; -/// A relayer context that allows interaction between two [`MockContext`] instances. +/// A relayer context that allows interaction between two [`TestContext`] instances. pub struct RelayerContext where A: TestHost, @@ -29,7 +29,7 @@ where HostClientState: ClientStateValidation, HostClientState: ClientStateValidation, { - /// Creates a new relayer context with the given [`MockContext`] instances. + /// Creates a new relayer context with the given [`TestContext`] instances. pub fn new(ctx_a: TestContext, ctx_b: TestContext) -> Self { Self { ctx_a, ctx_b } } diff --git a/ibc-testkit/src/utils/mod.rs b/ibc-testkit/src/utils/mod.rs index 502eff987..1e7813f99 100644 --- a/ibc-testkit/src/utils/mod.rs +++ b/ibc-testkit/src/utils/mod.rs @@ -8,7 +8,7 @@ use tendermint::Time; /// If two [`MockGenericContext`](crate::context::MockGenericContext) /// are initialized using [`Time::now()`], second one will have a greater timestamp than the first one. /// So, the latest header of the second context can not be submitted to first one. -/// We can still set a custom timestamp via [`MockContextConfig`](crate::fixtures::core::context::MockContextConfig). +/// We can still set a custom timestamp via [`TestContextConfig`](crate::fixtures::core::context::TestContextConfig). pub fn year_2023() -> Timestamp { // TODO(rano): can we turn this into a fixture? diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 0f83552f0..aaca3b46f 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -209,8 +209,8 @@ fn test_update_client_with_prev_header() { /// Tests that the Tendermint client consensus state pruning logic /// functions correctly. /// -/// This test sets up a MockContext with host height 1 and a trusting -/// period of 3 seconds. It then advances the state of the MockContext +/// This test sets up a `TendermintContext` with host height 1 and a trusting +/// period of 3 seconds. It then advances the state of the `TendermintContext` /// by 2 heights, and thus 6 seconds, due to the DEFAULT_BLOCK_TIME_SECS /// constant being set to 3 seconds. At this point, the chain is at height /// 3. Any consensus states associated with a block more than 3 seconds @@ -984,7 +984,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { // Update the trusted height of the header to point to the previous height // (`start_height` in this case). // - // Note: The current MockContext interface doesn't allow us to + // Note: The current `TestContext` interface doesn't allow us to // do this without a major redesign. // current problem: the timestamp of the new header doesn't match the timestamp of @@ -995,7 +995,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { // Update the client height to `client_height` // - // Note: The current MockContext interface doesn't allow us to + // Note: The current `TestContext` interface doesn't allow us to // do this without a major redesign. { // FIXME: idea: we need to update the light client with the latest block from @@ -1630,7 +1630,7 @@ fn client_update_ping_pong() { let client_msg_b = client_msg_b_res.unwrap(); // - send the message to B. We bypass ICS18 interface and call directly into - // MockContext `recv` method (to avoid additional serialization steps). + // `TestContext` `recv` method (to avoid additional serialization steps). let dispatch_res_b = ctx_b.deliver(MsgEnvelope::Client(client_msg_b)); let validation_res = ctx_b.host.validate(); assert!( From 13ec2f0d7df15e6dac6a093d7d4ddd4ede4b6e21 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:13:07 +0200 Subject: [PATCH 12/40] add util methods for StoreGenericTestContext --- ibc-testkit/src/context.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 267e39dc7..048abc0a1 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -9,7 +9,8 @@ use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::context::{ClientExecutionContext, ClientValidationContext}; use ibc::core::client::types::Height; use ibc::core::connection::types::ConnectionEnd; -use ibc::core::entrypoint::dispatch; +use ibc::core::entrypoint::{dispatch, execute, validate}; +use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::events::IbcEvent; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId, Sequence}; @@ -90,6 +91,14 @@ where &mut self.ibc_store } + pub fn ibc_router(&self) -> &MockRouter { + &self.ibc_router + } + + pub fn ibc_router_mut(&mut self) -> &mut MockRouter { + &mut self.ibc_router + } + pub fn host_block(&self, target_height: &Height) -> Option { self.host.get_block(target_height) } @@ -397,11 +406,23 @@ where self } + pub fn validate(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { + validate(&self.ibc_store, &self.ibc_router, msg) + } + + pub fn execute(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { + execute(&mut self.ibc_store, &mut self.ibc_router, msg) + } + + pub fn dispatch(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { + dispatch(&mut self.ibc_store, &mut self.ibc_router, msg) + } + /// A datagram passes from the relayer to the IBC module (on host chain). /// Alternative method to `Ics18Context::send` that does not exercise any serialization. /// Used in testing the Ics18 algorithms, hence this may return a Ics18Error. pub fn deliver(&mut self, msg: MsgEnvelope) -> Result<(), RelayerError> { - dispatch(&mut self.ibc_store, &mut self.ibc_router, msg) + self.dispatch(msg) .map_err(RelayerError::TransactionFailed)?; // Create a new block. self.advance_block(); From 934288ac16ead5c7b4af553a394ce1225698730c Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:14:13 +0200 Subject: [PATCH 13/40] fix failing client recovery tests --- .../tests/core/ics02_client/recover_client.rs | 182 +++++++++++++----- 1 file changed, 129 insertions(+), 53 deletions(-) diff --git a/ibc-testkit/tests/core/ics02_client/recover_client.rs b/ibc-testkit/tests/core/ics02_client/recover_client.rs index 22fc227a7..656858da9 100644 --- a/ibc-testkit/tests/core/ics02_client/recover_client.rs +++ b/ibc-testkit/tests/core/ics02_client/recover_client.rs @@ -1,26 +1,27 @@ use core::time::Duration; +use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::context::ClientValidationContext; use ibc::core::client::handler::recover_client; use ibc::core::client::types::msgs::{ClientMsg, MsgCreateClient, MsgRecoverClient}; -use ibc::core::client::types::Height; -use ibc::core::entrypoint::{execute, validate}; +use ibc::core::client::types::{Height, Status as ClientStatus}; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::ClientId; use ibc::core::host::ValidationContext; -use ibc::core::primitives::{Signer, Timestamp}; +use ibc::core::primitives::Signer; +use ibc_testkit::context::{MockContext, TendermintContext}; +use ibc_testkit::fixtures::core::context::TestContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; +use ibc_testkit::hosts::{TestBlock, TestHost}; use ibc_testkit::testapp::ibc::clients::mock::client_state::{ client_type as mock_client_type, MockClientState, }; use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; -use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader; -use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::MockContext; +use ibc_testkit::testapp::ibc::core::types::DEFAULT_BLOCK_TIME_SECS; use rstest::*; struct Fixture { - ctx: MockContext, + ctx: TendermintContext, subject_client_id: ClientId, substitute_client_id: ClientId, signer: Signer, @@ -40,50 +41,70 @@ fn setup_client_recovery_fixture( substitute_trusting_period: Duration, substitute_height: Height, ) -> Fixture { - let mut ctx = MockContext::default(); - let mut router = MockRouter::new_with_transfer(); - let signer = dummy_account_id(); + let mut ctx = TendermintContext::default(); + + // create a ctx_b + let ctx_b: MockContext = TestContextConfig::builder() + .latest_height(substitute_height) + .build(); - let subject_timestamp = (Timestamp::now() - subject_trusting_period).unwrap(); + let signer = dummy_account_id(); - // Create the subject client state such that it will be in an expired state by initializing it with - // a timestamp that is of duration `subject_trusting_period` in the past - let subject_client_state = - MockClientState::new(MockHeader::new(subject_height).with_timestamp(subject_timestamp)) - .with_trusting_period(subject_trusting_period); + let subject_client_header = ctx_b + .host + .get_block(&subject_height) + .expect("block exists") + .into_header(); - // Create the subject client let msg = MsgCreateClient::new( - subject_client_state.into(), - MockConsensusState::new(MockHeader::new(subject_height).with_current_timestamp()).into(), + MockClientState::new(subject_client_header) + .with_trusting_period(subject_trusting_period) + .into(), + MockConsensusState::new(subject_client_header).into(), signer.clone(), ); - let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + let subject_msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); let client_type = mock_client_type(); - let subject_client_id = client_type.build_client_id(ctx.client_counter().unwrap()); + let subject_client_id = client_type.build_client_id(ctx.ibc_store().client_counter().unwrap()); - validate(&ctx, &router, msg_envelope.clone()).expect("create subject client validation"); - execute(&mut ctx, &mut router, msg_envelope).expect("create subject client execution"); + ctx.dispatch(subject_msg_envelope) + .expect("create subject client execution"); - // Create the substitute client - let substitute_client_state = - MockClientState::new(MockHeader::new(substitute_height).with_current_timestamp()) - .with_trusting_period(substitute_trusting_period); + let substitute_client_header = ctx_b + .host + .get_block(&substitute_height) + .expect("block exists") + .into_header(); let msg = MsgCreateClient::new( - substitute_client_state.into(), - MockConsensusState::new(MockHeader::new(substitute_height).with_current_timestamp()).into(), + MockClientState::new(substitute_client_header) + .with_trusting_period(substitute_trusting_period) + .into(), + MockConsensusState::new(substitute_client_header).into(), signer.clone(), ); - let msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + let substitute_msg_envelope = MsgEnvelope::from(ClientMsg::from(msg)); + + let substitute_client_id = + client_type.build_client_id(ctx.ibc_store().client_counter().unwrap()); - let substitute_client_id = client_type.build_client_id(ctx.client_counter().unwrap()); + ctx.dispatch(substitute_msg_envelope) + .expect("create substitute client execution"); - validate(&ctx, &router, msg_envelope.clone()).expect("create substitute client validation"); - execute(&mut ctx, &mut router, msg_envelope).expect("create substitute client execution"); + let subject_client_trusted_timestamp = + (subject_client_header.timestamp() + subject_trusting_period).expect("no error"); + + // Let the subject client state expire. + while ctx.latest_timestamp() <= subject_client_trusted_timestamp { + ctx.advance_block(); + } + + // at this point, the subject client should be expired. + // and the substitute client should be active or expired according + // to the fixture arguments Fixture { ctx, @@ -95,8 +116,8 @@ fn setup_client_recovery_fixture( #[rstest] fn test_recover_client_ok() { - let subject_trusting_period = Duration::from_nanos(100); - let substitute_trusting_period = Duration::from_secs(3); + let subject_trusting_period = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS); + let substitute_trusting_period = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS) * 10; let subject_height = Height::new(0, 42).unwrap(); let substitute_height = Height::new(0, 43).unwrap(); @@ -112,30 +133,51 @@ fn test_recover_client_ok() { substitute_height, ); + assert_eq!( + ctx.ibc_store() + .client_state(&subject_client_id) + .expect("substitute client state exists") + .status(ctx.ibc_store(), &subject_client_id) + .expect("no error"), + ClientStatus::Expired + ); + + assert_eq!( + ctx.ibc_store() + .client_state(&substitute_client_id) + .expect("substitute client state exists") + .status(ctx.ibc_store(), &substitute_client_id) + .expect("no error"), + ClientStatus::Active + ); + let msg = MsgRecoverClient { subject_client_id, substitute_client_id, signer, }; - let res = recover_client::validate(&ctx, msg.clone()); - - assert!(res.is_ok(), "client recovery validation happy path"); - - let res = recover_client::execute(&mut ctx, msg.clone()); + recover_client::validate(ctx.ibc_store_mut(), msg.clone()) + .expect("client recovery execution happy path"); - assert!(res.is_ok(), "client recovery execution happy path"); + recover_client::execute(ctx.ibc_store_mut(), msg.clone()) + .expect("client recovery execution happy path"); assert_eq!( - ctx.client_state(&msg.subject_client_id).unwrap(), - ctx.client_state(&msg.substitute_client_id).unwrap(), + ctx.ibc_store() + .client_state(&msg.subject_client_id) + .unwrap(), + ctx.ibc_store() + .client_state(&msg.substitute_client_id) + .unwrap(), ); } #[rstest] fn test_recover_client_with_expired_substitute() { - let subject_trusting_period = Duration::from_nanos(100); - let substitute_trusting_period = Duration::from_nanos(100); + // twice of DEFAULT_BLOCK_TIME_SECS to make sure the substitute client is expired as well + let subject_trusting_period = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS) * 2; + let substitute_trusting_period = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS); let subject_height = Height::new(0, 42).unwrap(); let substitute_height = Height::new(0, 43).unwrap(); @@ -151,21 +193,38 @@ fn test_recover_client_with_expired_substitute() { substitute_height, ); + assert_eq!( + ctx.ibc_store() + .client_state(&subject_client_id) + .expect("substitute client state exists") + .status(ctx.ibc_store(), &subject_client_id) + .expect("no error"), + ClientStatus::Expired + ); + + assert_eq!( + ctx.ibc_store() + .client_state(&substitute_client_id) + .expect("substitute client state exists") + .status(ctx.ibc_store(), &substitute_client_id) + .expect("no error"), + ClientStatus::Expired + ); + let msg = MsgRecoverClient { subject_client_id, substitute_client_id, signer, }; - let res = recover_client::validate(&ctx, msg); - - assert!(res.is_err(), "expected client recovery validation to fail"); + recover_client::validate(ctx.ibc_store(), msg) + .expect_err("expected client recovery validation to fail"); } #[rstest] fn test_recover_client_with_matching_heights() { - let subject_trusting_period = Duration::from_nanos(100); - let substitute_trusting_period = Duration::from_secs(3); + let subject_trusting_period = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS); + let substitute_trusting_period = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS) * 10; let subject_height = Height::new(0, 42).unwrap(); let substitute_height = Height::new(0, 42).unwrap(); @@ -181,13 +240,30 @@ fn test_recover_client_with_matching_heights() { substitute_height, ); + assert_eq!( + ctx.ibc_store() + .client_state(&subject_client_id) + .expect("substitute client state exists") + .status(ctx.ibc_store(), &subject_client_id) + .expect("no error"), + ClientStatus::Expired + ); + + assert_eq!( + ctx.ibc_store() + .client_state(&substitute_client_id) + .expect("substitute client state exists") + .status(ctx.ibc_store(), &substitute_client_id) + .expect("no error"), + ClientStatus::Active + ); + let msg = MsgRecoverClient { subject_client_id, substitute_client_id, signer, }; - let res = recover_client::validate(&ctx, msg); - - assert!(res.is_err(), "expected client recovery validation to fail"); + dbg!(recover_client::validate(ctx.ibc_store(), msg)) + .expect_err("expected client recovery validation to fail"); } From 0c80b62ff72f43c8f6d78c28ce3c4df5d0225746 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:14:46 +0200 Subject: [PATCH 14/40] increase default trusting period of MockClientState --- ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 7d6768698..6825b991c 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -44,7 +44,7 @@ impl MockClientState { pub fn new(header: MockHeader) -> Self { Self { header, - trusting_period: Duration::from_secs(10), + trusting_period: Duration::from_secs(64000), frozen: false, } } From 1b6dc16c4588903d8e774d98c252d9599433cfd0 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:29:11 +0200 Subject: [PATCH 15/40] fallible ProofSpecs conversion --- ibc-testkit/src/fixtures/clients/tendermint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/src/fixtures/clients/tendermint.rs b/ibc-testkit/src/fixtures/clients/tendermint.rs index eab0ef94c..47b885a52 100644 --- a/ibc-testkit/src/fixtures/clients/tendermint.rs +++ b/ibc-testkit/src/fixtures/clients/tendermint.rs @@ -76,7 +76,7 @@ pub struct ClientStateConfig { pub unbonding_period: Duration, #[builder(default = Duration::from_millis(3000))] pub max_clock_drift: Duration, - #[builder(default = vec![basecoin_proof_spec(); 2].into())] + #[builder(default = vec![basecoin_proof_spec(); 2].try_into().expect("no error"))] pub proof_specs: ProofSpecs, #[builder(default)] pub upgrade_path: Vec, From 4d1f38cbe8d695afe26ec5eb7ec7c54ae2127844 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:32:15 +0200 Subject: [PATCH 16/40] update with new ClientStateConfig --- ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs b/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs index 96ec1434a..4745dab08 100644 --- a/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs +++ b/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs @@ -82,11 +82,9 @@ impl Fixture { // freeze for the `happy_cw_client_recovery` test. let tm_client_state: TmClientState = ClientStateConfig::builder() - .chain_id("test-chain".parse().unwrap()) .trusting_period(Duration::from_secs(1)) - .latest_height(self.trusted_height) .build() - .try_into() + .into_client_state("test-chain".parse().unwrap(), self.trusted_height) .expect("never fails"); let tm_consensus_state = dummy_sov_consensus_state(self.trusted_timestamp); From 17706ddc59bfcff3ebd9a7f7c72cc55caa300706 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:32:25 +0200 Subject: [PATCH 17/40] use ExtClientValidationContext --- ibc-testkit/tests/core/ics02_client/upgrade_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index e5c9c268a..03468ee11 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -1,6 +1,6 @@ -use ibc::clients::tendermint::context::ValidationContext; use ibc::clients::tendermint::types::client_type; use ibc::core::client::context::ClientValidationContext; +use ibc::core::client::context::ExtClientValidationContext; use ibc::core::client::types::error::{ClientError, UpgradeClientError}; use ibc::core::client::types::msgs::{ClientMsg, MsgUpgradeClient}; use ibc::core::client::types::Height; From 1367864e5a997492a2a6da5f371a686df9f82959 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:39:43 +0200 Subject: [PATCH 18/40] fix links in doc comments --- ibc-testkit/src/utils/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ibc-testkit/src/utils/mod.rs b/ibc-testkit/src/utils/mod.rs index 1e7813f99..80eb0bad4 100644 --- a/ibc-testkit/src/utils/mod.rs +++ b/ibc-testkit/src/utils/mod.rs @@ -3,15 +3,13 @@ use tendermint::Time; /// Returns a `Timestamp` representation of beginning of year 2023. /// -/// This is introduced to initialize [`MockGenericContext`](crate::context::MockGenericContext)s +/// This is introduced to initialize [`StoreGenericTestContext`](crate::context::StoreGenericTestContext)s /// with the same latest timestamp by default. -/// If two [`MockGenericContext`](crate::context::MockGenericContext) +/// If two [`StoreGenericTestContext`](crate::context::StoreGenericTestContext) /// are initialized using [`Time::now()`], second one will have a greater timestamp than the first one. /// So, the latest header of the second context can not be submitted to first one. /// We can still set a custom timestamp via [`TestContextConfig`](crate::fixtures::core::context::TestContextConfig). pub fn year_2023() -> Timestamp { - // TODO(rano): can we turn this into a fixture? - // Sun Jan 01 2023 00:00:00 GMT+0000 Time::from_unix_timestamp(1_672_531_200, 0) .expect("should be a valid time") From 0ada6237332a5d6723be359331f72637c60f9a36 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 18 Apr 2024 11:47:27 +0200 Subject: [PATCH 19/40] fix import order --- ibc-testkit/tests/core/ics02_client/upgrade_client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index 03468ee11..015bf8921 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -1,6 +1,5 @@ use ibc::clients::tendermint::types::client_type; -use ibc::core::client::context::ClientValidationContext; -use ibc::core::client::context::ExtClientValidationContext; +use ibc::core::client::context::{ClientValidationContext, ExtClientValidationContext}; use ibc::core::client::types::error::{ClientError, UpgradeClientError}; use ibc::core::client::types::msgs::{ClientMsg, MsgUpgradeClient}; use ibc::core::client::types::Height; From 142f8f0adcb5587721718042aabf8ad7e4fea868 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Sat, 27 Apr 2024 11:40:24 +0200 Subject: [PATCH 20/40] fix semantic conflicts --- .../cw-contract/src/tests/fixture.rs | 2 +- ibc-testkit/Cargo.toml | 2 +- ibc-testkit/src/relayer/utils.rs | 1 - .../tests/core/ics02_client/recover_client.rs | 26 ++++++++++--------- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs b/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs index e1365122d..b9b646eec 100644 --- a/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs +++ b/ibc-clients/ics07-tendermint/cw-contract/src/tests/fixture.rs @@ -91,7 +91,7 @@ impl Fixture { let tm_client_state: TmClientState = ClientStateConfig::builder() .trusting_period(Duration::from_secs(1)) .build() - .into_client_state("test-chain".parse().unwrap(), self.trusted_height) + .into_client_state(self.chain_id.clone(), self.trusted_height) .expect("never fails"); let tm_consensus_state = dummy_sov_consensus_state(self.trusted_timestamp); diff --git a/ibc-testkit/Cargo.toml b/ibc-testkit/Cargo.toml index e26ae12b7..6c3d64805 100644 --- a/ibc-testkit/Cargo.toml +++ b/ibc-testkit/Cargo.toml @@ -31,7 +31,7 @@ typed-builder = { version = "0.18.0" } # ibc dependencies ibc = { workspace = true, features = ["std"] } ibc-proto = { workspace = true } -ibc-query = { version = "0.51.0", path = "../ibc-query" } +ibc-query = { version = "0.52.0", path = "../ibc-query" } basecoin-store = { git = "https://github.com/informalsystems/basecoin-rs", rev = "ce0d6c7" } # cosmos dependencies diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 7d5fbb642..02f0181d4 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -75,7 +75,6 @@ where .next() .expect("at least one") .clone() - .into() .into(), signer, })); diff --git a/ibc-testkit/tests/core/ics02_client/recover_client.rs b/ibc-testkit/tests/core/ics02_client/recover_client.rs index 03fe9407c..afed94eee 100644 --- a/ibc-testkit/tests/core/ics02_client/recover_client.rs +++ b/ibc-testkit/tests/core/ics02_client/recover_client.rs @@ -176,18 +176,20 @@ fn test_recover_client_ok() { // latest consensus state is copied. assert_eq!( - ctx.consensus_state(&ClientConsensusStatePath::new( - msg.subject_client_id, - substitute_height.revision_number(), - substitute_height.revision_height(), - )) - .unwrap(), - ctx.consensus_state(&ClientConsensusStatePath::new( - msg.substitute_client_id, - substitute_height.revision_number(), - substitute_height.revision_height(), - )) - .unwrap(), + ctx.ibc_store() + .consensus_state(&ClientConsensusStatePath::new( + msg.subject_client_id, + substitute_height.revision_number(), + substitute_height.revision_height(), + )) + .unwrap(), + ctx.ibc_store() + .consensus_state(&ClientConsensusStatePath::new( + msg.substitute_client_id, + substitute_height.revision_number(), + substitute_height.revision_height(), + )) + .unwrap(), ); } From 03552cee61e2f72e5054ebc172dc01bb360ce858 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Sun, 28 Apr 2024 15:34:24 +0200 Subject: [PATCH 21/40] Convertible over ConsensusStateConverter --- ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 1c592db17..54c8114ed 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -238,7 +238,7 @@ impl ClientStateCommon for MockClientState { impl ClientStateValidation for MockClientState where V: ClientValidationContext + MockClientContext, - V::ConsensusStateRef: ConsensusStateConverter, + V::ConsensusStateRef: Convertible, { fn verify_client_message( &self, @@ -324,7 +324,7 @@ impl ClientStateExecution for MockClientState where E: ClientExecutionContext + MockClientContext, E::ClientStateRef: From, - E::ConsensusStateRef: ConsensusStateConverter, + E::ConsensusStateRef: Convertible, { fn initialise( &self, From 950c43cf8cd05b7f185e04cf1a54d17aec462661 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Sun, 28 Apr 2024 15:34:06 +0200 Subject: [PATCH 22/40] rm ConsensusStateConverter --- .../src/testapp/ibc/clients/mock/client_state.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 54c8114ed..46564fa75 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -151,16 +151,6 @@ impl From for Any { } } -pub trait ConsensusStateConverter: - TryInto + From -{ -} - -impl ConsensusStateConverter for C where - C: TryInto + From -{ -} - pub trait MockClientContext { /// Returns the current timestamp of the local chain. fn host_timestamp(&self) -> Result; From d77cd526df8f5a42504864cbc7fb0d752f11e817 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 20:25:32 +0200 Subject: [PATCH 23/40] ibc-query as workspace deps --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 184204497..c879eca85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,7 @@ ibc-core-channel = { version = "0.52.0", path = "./ibc-core/ics04-channel", d ibc-core-host = { version = "0.52.0", path = "./ibc-core/ics24-host", default-features = false } ibc-core-handler = { version = "0.52.0", path = "./ibc-core/ics25-handler", default-features = false } ibc-core-router = { version = "0.52.0", path = "./ibc-core/ics26-routing", default-features = false } +ibc-query = { version = "0.52.0", path = "./ibc-query", default-features = false } ibc-client-cw = { version = "0.52.0", path = "./ibc-clients/cw-context", default-features = false } ibc-client-tendermint = { version = "0.52.0", path = "./ibc-clients/ics07-tendermint", default-features = false } From fc0626b21a26a8fdeae43af9b620d47a41d7dfe9 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 20:34:57 +0200 Subject: [PATCH 24/40] update Convertible --- .../src/testapp/ibc/clients/mock/client_state.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index a72f1c87e..8b02a83fe 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -228,7 +228,8 @@ impl ClientStateCommon for MockClientState { impl ClientStateValidation for MockClientState where V: ClientValidationContext + MockClientContext, - V::ConsensusStateRef: Convertible, + MockConsensusState: Convertible, + >::Error: Into, { fn verify_client_message( &self, @@ -278,13 +279,13 @@ where return Ok(Status::Frozen); } - let latest_consensus_state = { + let latest_consensus_state: MockConsensusState = { match ctx.consensus_state(&ClientConsensusStatePath::new( client_id.clone(), self.latest_height().revision_number(), self.latest_height().revision_height(), )) { - Ok(cs) => cs.try_into()?, + Ok(cs) => cs.try_into().map_err(Into::into)?, // if the client state does not have an associated consensus state for its latest height // then it must be expired Err(_) => return Ok(Status::Expired), @@ -314,7 +315,8 @@ impl ClientStateExecution for MockClientState where E: ClientExecutionContext + MockClientContext, E::ClientStateRef: From, - E::ConsensusStateRef: Convertible, + MockConsensusState: Convertible, + >::Error: Into, { fn initialise( &self, @@ -322,7 +324,7 @@ where client_id: &ClientId, consensus_state: Any, ) -> Result<(), ClientError> { - let mock_consensus_state = MockConsensusState::try_from(consensus_state)?; + let mock_consensus_state: MockConsensusState = consensus_state.try_into()?; ctx.store_client_state(ClientStatePath::new(client_id.clone()), (*self).into())?; ctx.store_consensus_state( @@ -401,7 +403,7 @@ where upgraded_consensus_state: Any, ) -> Result { let new_client_state = Self::try_from(upgraded_client_state)?; - let new_consensus_state = MockConsensusState::try_from(upgraded_consensus_state)?; + let new_consensus_state: MockConsensusState = upgraded_consensus_state.try_into()?; let latest_height = new_client_state.latest_height(); @@ -450,7 +452,7 @@ where let host_timestamp = ctx.host_timestamp()?; let host_height = ctx.host_height()?; - let mock_consensus_state = MockConsensusState::try_from(substitute_consensus_state)?; + let mock_consensus_state: MockConsensusState = substitute_consensus_state.try_into()?; ctx.store_consensus_state( ClientConsensusStatePath::new( From 07e8db1898701ef9d03f59c575db46629c6f8f51 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 20:35:06 +0200 Subject: [PATCH 25/40] fix failing test --- ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 8b02a83fe..4352ac22f 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -495,7 +495,7 @@ mod test { use super::{MockClientState, MockHeader}; let client_state = MockClientState::new(MockHeader::default()); - let expected = r#"{"typeUrl":"/ibc.mock.ClientState","value":"CgQKAhABEIDIr6Al"}"#; + let expected = r#"{"typeUrl":"/ibc.mock.ClientState","value":"CgQKAhABEICAkMrSxg4="}"#; let json = serde_json::to_string(&Any::from(client_state)).unwrap(); assert_eq!(json, expected); From b18b732d859990d497d16ef10fc5f8cea06aebc9 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 20:46:13 +0200 Subject: [PATCH 26/40] update changelog entry --- .../unreleased/improvements/1044-introduce-host-trait.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changelog/unreleased/improvements/1044-introduce-host-trait.md b/.changelog/unreleased/improvements/1044-introduce-host-trait.md index d2d1b2bfa..d8b95983e 100644 --- a/.changelog/unreleased/improvements/1044-introduce-host-trait.md +++ b/.changelog/unreleased/improvements/1044-introduce-host-trait.md @@ -1,2 +1,3 @@ -- [ibc-testkit] Refactor `HostBlock` enum to `Host` trait +- [ibc-testkit] Replace `HostBlock` and `HostType` enums with a `Host` trait to + eliminate manual delegations by utilizing monomorphization. ([\#1044](https://github.com/cosmos/ibc-rs/issues/1044)) From d8e65c613a1dc37484c6daf42e052a077dbd0daa Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:16:04 +0200 Subject: [PATCH 27/40] add comments --- ibc-testkit/src/context.rs | 61 ++++++++++++++++++++++-- ibc-testkit/src/fixtures/core/context.rs | 2 +- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 048abc0a1..c81768b8a 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -83,30 +83,37 @@ where H: TestHost, HostClientState: ClientStateValidation>, { + /// Returns a immutable reference to the IBC store. pub fn ibc_store(&self) -> &MockIbcStore { &self.ibc_store } + /// Returns a mutable reference to the IBC store. pub fn ibc_store_mut(&mut self) -> &mut MockIbcStore { &mut self.ibc_store } + /// Returns a immutable reference to the IBC router. pub fn ibc_router(&self) -> &MockRouter { &self.ibc_router } + /// Returns a mutable reference to the IBC router. pub fn ibc_router_mut(&mut self) -> &mut MockRouter { &mut self.ibc_router } + /// Returns the block at the given height from the host chain, if exists. pub fn host_block(&self, target_height: &Height) -> Option { self.host.get_block(target_height) } + /// Returns the latest block from the host chain. pub fn query_latest_block(&self) -> Option { self.host.get_block(&self.latest_height()) } + /// Returns the latest height of client state for the given [`ClientId`]. pub fn light_client_latest_height(&self, client_id: &ClientId) -> Height { self.ibc_store .client_state(client_id) @@ -114,6 +121,7 @@ where .latest_height() } + /// Advances the host chain height to the given target height. pub fn advance_block_up_to(mut self, target_height: Height) -> Self { let latest_height = self.host.latest_height(); if target_height.revision_number() != latest_height.revision_number() { @@ -129,13 +137,23 @@ where self } - pub fn generate_genesis_block(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { + /// Advance the first height of the host chain by generating a genesis block. + /// + /// This method is exactly the same as [`Self::advance_with_block_params`]. + /// But it bootstraps the genesis block by height 1 and `genesis_time`. + /// + /// The method starts and ends with [`Self::end_block`] and [`Self::begin_block`], just + /// like the [`Self::advance_with_block_params`], so that it can advance to next height + /// i.e. height 2 - just by calling [`Self::advance_with_block_params`]. + pub fn advance_with_genesis_block(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { self.end_block(); // commit multi store let multi_store_commitment = self.multi_store.commit().expect("no error"); // generate a genesis block + // this is basically self.host.produce_block() but with + // block height 1 and block timestamp `genesis_time`. let genesis_block = self.host .generate_block(multi_store_commitment, 1, genesis_time, params); @@ -146,6 +164,10 @@ where self.begin_block(); } + /// Begin a new block on the context. + /// + /// This method book keeps the data from last block, + /// and prepares the context for the next block. pub fn begin_block(&mut self) { let consensus_state = self .host @@ -174,6 +196,9 @@ where ); } + /// End the current block on the context. + /// + /// This method commits the state of the IBC store and the host's multi store. pub fn end_block(&mut self) { // commit ibc store let ibc_store_commitment = self.ibc_store.end_block().expect("no error"); @@ -202,12 +227,15 @@ where .advance_block(multi_store_commitment, block_time, params); } + /// Advances the host chain by ending the current block, producing a new block, and + /// beginning the next block. pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) { self.end_block(); self.produce_block(block_time, params); self.begin_block(); } + /// Convenience method to advance the host chain using default parameters. pub fn advance_block(&mut self) { self.advance_with_block_params( Duration::from_secs(DEFAULT_BLOCK_TIME_SECS), @@ -215,6 +243,7 @@ where ) } + /// Returns the latest height of the host chain. pub fn latest_height(&self) -> Height { let latest_ibc_height = self.ibc_store.host_height().expect("Never fails"); let latest_host_height = self.host.latest_height(); @@ -225,10 +254,12 @@ where latest_ibc_height } + /// Returns the latest timestamp of the host chain. pub fn latest_timestamp(&self) -> Timestamp { self.host.latest_block().timestamp() } + /// Returns the timestamp at the given height. pub fn timestamp_at(&self, height: Height) -> Timestamp { self.host .get_block(&height) @@ -236,6 +267,7 @@ where .timestamp() } + /// Bootstraps the context with a client state and its corresponding [`ClientId`]. pub fn with_client_state(mut self, client_id: &ClientId, client_state: AnyClientState) -> Self { let client_state_path = ClientStatePath::new(client_id.clone()); self.ibc_store @@ -244,6 +276,7 @@ where self } + /// Bootstraps the context with a consensus state and its corresponding [`ClientId`] and [`Height`]. pub fn with_consensus_state( mut self, client_id: &ClientId, @@ -300,6 +333,7 @@ where } } + /// Bootstrap a light client with ClientState and its ConsensusState(s) to this context. pub fn with_light_client( mut self, client_id: &ClientId, @@ -326,7 +360,9 @@ where self } - /// Associates a connection to this context. + /// Bootstraps a IBC connection to this context. + /// + /// This does not bootstrap any light client. pub fn with_connection( mut self, connection_id: ConnectionId, @@ -339,7 +375,9 @@ where self } - /// Associates a channel (in an arbitrary state) to this context. + /// Bootstraps a IBC channel to this context. + /// + /// This does not bootstrap any corresponding IBC connection or light client. pub fn with_channel( mut self, port_id: PortId, @@ -353,6 +391,9 @@ where self } + /// Bootstraps a send sequence to this context. + /// + /// This does not bootstrap any corresponding IBC channel, connection or light client. pub fn with_send_sequence( mut self, port_id: PortId, @@ -366,6 +407,9 @@ where self } + /// Bootstraps a receive sequence to this context. + /// + /// This does not bootstrap any corresponding IBC channel, connection or light client. pub fn with_recv_sequence( mut self, port_id: PortId, @@ -379,6 +423,9 @@ where self } + /// Bootstraps a ack sequence to this context. + /// + /// This does not bootstrap any corresponding IBC channel, connection or light client. pub fn with_ack_sequence( mut self, port_id: PortId, @@ -392,6 +439,9 @@ where self } + /// Bootstraps a packet commitment to this context. + /// + /// This does not bootstrap any corresponding IBC channel, connection or light client. pub fn with_packet_commitment( mut self, port_id: PortId, @@ -406,14 +456,17 @@ where self } + /// Calls [`validate`] function on [`MsgEnvelope`] using the context's IBC store and router. pub fn validate(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { validate(&self.ibc_store, &self.ibc_router, msg) } + /// Calls [`execute`] function on [`MsgEnvelope`] using the context's IBC store and router. pub fn execute(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { execute(&mut self.ibc_store, &mut self.ibc_router, msg) } + /// Calls [`dispatch`] function on [`MsgEnvelope`] using the context's IBC store and router. pub fn dispatch(&mut self, msg: MsgEnvelope) -> Result<(), ContextError> { dispatch(&mut self.ibc_store, &mut self.ibc_router, msg) } @@ -429,10 +482,12 @@ where Ok(()) } + /// Returns all the events that have been emitted by the context's IBC store. pub fn get_events(&self) -> Vec { self.ibc_store.events.lock().clone() } + /// Returns all the logs that have been emitted by the context's IBC store. pub fn get_logs(&self) -> Vec { self.ibc_store.logs.lock().clone() } diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 45ac363f2..d12fea7be 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -68,7 +68,7 @@ where // store is at height 0; no block - context.generate_genesis_block(genesis_timestamp, &Default::default()); + context.advance_with_genesis_block(genesis_timestamp, &Default::default()); // store is at height 1; one block From 8d47a2a8a1239282e1bf4c30b8b8fc592a5a6b61 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:19:39 +0200 Subject: [PATCH 28/40] rm absolute import --- ibc-testkit/src/testapp/ibc/clients/mock/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs b/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs index d26c2865c..ec880383c 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/mod.rs @@ -6,5 +6,5 @@ pub mod misbehaviour; /// Re-exports mock proto types from the `ibc-proto` crate pub mod proto { - pub use ::ibc_proto::ibc::mock::*; + pub use ibc_proto::ibc::mock::*; } From 20dd42c003f15c4b184349c8ea82acdf74970fa3 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:21:41 +0200 Subject: [PATCH 29/40] use expect over comment for safety --- ibc-testkit/src/testapp/ibc/core/client_ctx.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index bdf0b239f..839a00ce1 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -92,7 +92,7 @@ where ) -> Result, ContextError> { let path = format!("clients/{client_id}/consensusStates") .try_into() - .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers + .expect("Must be valid path: ClientId and height are valid Identifiers"); let keys = self.store.get_keys(&path); let found_path = keys.into_iter().find_map(|path| { @@ -127,7 +127,7 @@ where ) -> Result, ContextError> { let path = format!("clients/{client_id}/consensusStates") .try_into() - .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers + .expect("Must be valid path: ClientId and height are valid Identifiers"); let keys = self.store.get_keys(&path); let found_path = keys.into_iter().rev().find_map(|path| { From b0d39433d70b37060f9a5a0ff01db2698f73bcd2 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:22:57 +0200 Subject: [PATCH 30/40] infallible try_into --- ibc-testkit/src/testapp/ibc/core/client_ctx.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index 839a00ce1..8c7bd8dd2 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -90,9 +90,7 @@ where client_id: &ClientId, height: &Height, ) -> Result, ContextError> { - let path = format!("clients/{client_id}/consensusStates") - .try_into() - .expect("Must be valid path: ClientId and height are valid Identifiers"); + let path = format!("clients/{client_id}/consensusStates").into(); let keys = self.store.get_keys(&path); let found_path = keys.into_iter().find_map(|path| { @@ -125,9 +123,7 @@ where client_id: &ClientId, height: &Height, ) -> Result, ContextError> { - let path = format!("clients/{client_id}/consensusStates") - .try_into() - .expect("Must be valid path: ClientId and height are valid Identifiers"); + let path = format!("clients/{client_id}/consensusStates").into(); let keys = self.store.get_keys(&path); let found_path = keys.into_iter().rev().find_map(|path| { From e09235965713088ce92e2be11be33203dbab0039 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:35:09 +0200 Subject: [PATCH 31/40] update method names --- ibc-testkit/src/context.rs | 41 ++++++++++--------- ibc-testkit/src/fixtures/core/context.rs | 6 +-- ibc-testkit/src/hosts/mod.rs | 4 +- ibc-testkit/src/relayer/integration.rs | 2 +- ibc-testkit/src/relayer/utils.rs | 2 +- .../tests/core/ics02_client/recover_client.rs | 2 +- .../tests/core/ics02_client/update_client.rs | 8 ++-- .../tests/core/ics04_channel/recv_packet.rs | 4 +- 8 files changed, 35 insertions(+), 34 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index c81768b8a..846182e8c 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -122,7 +122,7 @@ where } /// Advances the host chain height to the given target height. - pub fn advance_block_up_to(mut self, target_height: Height) -> Self { + pub fn advance_block_up_to_height(mut self, target_height: Height) -> Self { let latest_height = self.host.latest_height(); if target_height.revision_number() != latest_height.revision_number() { panic!("Cannot advance history of the chain to a different revision number!") @@ -131,7 +131,7 @@ where } else { // Repeatedly advance the host chain height till we hit the desired height while self.host.latest_height().revision_height() < target_height.revision_height() { - self.advance_block() + self.advance_height() } } self @@ -139,13 +139,13 @@ where /// Advance the first height of the host chain by generating a genesis block. /// - /// This method is exactly the same as [`Self::advance_with_block_params`]. + /// This method is exactly the same as [`Self::advance_genesis_height`]. /// But it bootstraps the genesis block by height 1 and `genesis_time`. /// /// The method starts and ends with [`Self::end_block`] and [`Self::begin_block`], just - /// like the [`Self::advance_with_block_params`], so that it can advance to next height - /// i.e. height 2 - just by calling [`Self::advance_with_block_params`]. - pub fn advance_with_genesis_block(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { + /// like the [`Self::advance_height_with_params`], so that it can advance to next height + /// i.e. height 2 - just by calling [`Self::advance_height_with_params`]. + pub fn advance_genesis_height(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { self.end_block(); // commit multi store @@ -216,28 +216,29 @@ where .expect("no error"); } - /// Produces the next block for the host chain by first committing the state of - /// the host's multi store, and then generating a new block that is added to - /// the host's block history. - pub fn produce_block(&mut self, block_time: Duration, params: &H::BlockParams) { + /// Commit store state to the current block of the host chain by: + /// - Committing the state to the context's multi store. + /// - Generating a new block with the commitment. + /// - Adding the generated block to the host's block history. + pub fn commit_state_to_host(&mut self, block_time: Duration, params: &H::BlockParams) { // commit the multi store let multi_store_commitment = self.multi_store.commit().expect("no error"); - // generate a new block + // generate a new block and add it to the block history self.host - .advance_block(multi_store_commitment, block_time, params); + .commit_block(multi_store_commitment, block_time, params); } - /// Advances the host chain by ending the current block, producing a new block, and + /// Advances the host chain height by ending the current block, producing a new block, and /// beginning the next block. - pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) { + pub fn advance_height_with_params(&mut self, block_time: Duration, params: &H::BlockParams) { self.end_block(); - self.produce_block(block_time, params); + self.commit_state_to_host(block_time, params); self.begin_block(); } - /// Convenience method to advance the host chain using default parameters. - pub fn advance_block(&mut self) { - self.advance_with_block_params( + /// Convenience method to advance the host chain height using default parameters. + pub fn advance_height(&mut self) { + self.advance_height_with_params( Duration::from_secs(DEFAULT_BLOCK_TIME_SECS), &Default::default(), ) @@ -478,7 +479,7 @@ where self.dispatch(msg) .map_err(RelayerError::TransactionFailed)?; // Create a new block. - self.advance_block(); + self.advance_height(); Ok(()) } @@ -569,7 +570,7 @@ mod tests { let current_height = test.ctx.latest_height(); // After advancing the chain's height, the context should still be valid. - test.ctx.advance_block(); + test.ctx.advance_height(); assert!( test.ctx.host.validate().is_ok(), "failed in test [{}] {} while validating context {:?}", diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index d12fea7be..7810c6b77 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -68,11 +68,11 @@ where // store is at height 0; no block - context.advance_with_genesis_block(genesis_timestamp, &Default::default()); + context.advance_genesis_height(genesis_timestamp, &Default::default()); // store is at height 1; one block - context = context.advance_block_up_to( + context = context.advance_block_up_to_height( params .latest_height .sub(params.block_params_history.len() as u64) @@ -80,7 +80,7 @@ where ); for block_params in params.block_params_history { - context.advance_with_block_params(params.block_time, &block_params); + context.advance_height_with_params(params.block_time, &block_params); } assert_eq!( diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index 43b2e5ab1..3a4183401 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -64,8 +64,8 @@ pub trait TestHost: Default + Debug + Sized { /// Add a block to the host chain. fn push_block(&mut self, block: Self::Block); - /// Advance the host chain, by extending the history of blocks. - fn advance_block( + /// Commit a block with commitment root to the blockchain, by extending the history of blocks. + fn commit_block( &mut self, commitment_root: Vec, block_time: Duration, diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs index 5d0a72b21..766a21f75 100644 --- a/ibc-testkit/src/relayer/integration.rs +++ b/ibc-testkit/src/relayer/integration.rs @@ -120,7 +120,7 @@ where .expect("successfully created send_packet"); // send_packet wasn't committed, hence produce a block - relayer.get_ctx_a_mut().advance_block(); + relayer.get_ctx_a_mut().advance_height(); // retrieve the send_packet event let Some(IbcEvent::SendPacket(send_packet_event)) = relayer diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 02f0181d4..af93f6085 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -103,7 +103,7 @@ where /// Advances the block height on `A` until it catches up with the latest timestamp on `B`. pub fn sync_clock_on_a(ctx_a: &mut TestContext, ctx_b: &TestContext) { while ctx_b.latest_timestamp() > ctx_a.latest_timestamp() { - ctx_a.advance_block(); + ctx_a.advance_height(); } } diff --git a/ibc-testkit/tests/core/ics02_client/recover_client.rs b/ibc-testkit/tests/core/ics02_client/recover_client.rs index afed94eee..8f6f804b0 100644 --- a/ibc-testkit/tests/core/ics02_client/recover_client.rs +++ b/ibc-testkit/tests/core/ics02_client/recover_client.rs @@ -100,7 +100,7 @@ fn setup_client_recovery_fixture( // Let the subject client state expire. while ctx.latest_timestamp() <= subject_client_trusted_timestamp { - ctx.advance_block(); + ctx.advance_height(); } // at this point, the subject client should be expired. diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index aaca3b46f..88d511233 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -258,7 +258,7 @@ fn test_consensus_state_pruning() { let update_height = ctx.latest_height(); - ctx.advance_block(); + ctx.advance_height(); let block = ctx.host_block(&update_height).unwrap().clone(); let mut block = block.into_header(); @@ -1439,7 +1439,7 @@ fn test_expired_client() { while ctx.ibc_store.host_timestamp().expect("no error") < (timestamp + trusting_period).expect("no error") { - ctx.advance_block(); + ctx.advance_height(); } let client_state = ctx.ibc_store.client_state(&client_id).unwrap(); @@ -1494,11 +1494,11 @@ fn test_client_update_max_clock_drift() { while ctx_b.ibc_store.host_timestamp().expect("no error") < (ctx_a.ibc_store.host_timestamp().expect("no error") + max_clock_drift).expect("no error") { - ctx_b.advance_block(); + ctx_b.advance_height(); } // include current block - ctx_b.advance_block(); + ctx_b.advance_height(); let update_height = ctx_b.latest_height(); diff --git a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs index f7c4deb13..df946f1e3 100644 --- a/ibc-testkit/tests/core/ics04_channel/recv_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/recv_packet.rs @@ -133,7 +133,7 @@ fn recv_packet_validate_happy_path(fixture: Fixture) { packet.chan_id_on_b.clone(), 1.into(), ) - .advance_block_up_to(host_height) + .advance_block_up_to_height(host_height) // This `with_recv_sequence` is required for ordered channels .with_recv_sequence( packet.port_id_on_b.clone(), @@ -192,7 +192,7 @@ fn recv_packet_timeout_expired(fixture: Fixture) { .with_connection(ConnectionId::zero(), conn_end_on_b) .with_channel(PortId::transfer(), ChannelId::zero(), chan_end_on_b) .with_send_sequence(PortId::transfer(), ChannelId::zero(), 1.into()) - .advance_block_up_to(host_height); + .advance_block_up_to_height(host_height); let res = validate(&context.ibc_store, &router, msg_envelope); From 5cb831b03ec1e736b3d5c8037ea97600ad8b91c6 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:42:19 +0200 Subject: [PATCH 32/40] add unused variable names --- ibc-testkit/src/hosts/mock.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index 05363d457..266f69f53 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -42,10 +42,10 @@ impl TestHost for MockHost { fn generate_block( &self, - _: Vec, + _commitment_root: Vec, height: u64, timestamp: Timestamp, - _: &Self::BlockParams, + _params: &Self::BlockParams, ) -> Self::Block { MockHeader { height: Height::new(self.chain_id.revision_number(), height).expect("Never fails"), @@ -73,7 +73,7 @@ impl TestBlock for MockHeader { self.timestamp } - fn into_header_with_trusted(self, _: &Self) -> Self::Header { + fn into_header_with_trusted(self, _trusted_block: &Self) -> Self::Header { self } } From 0deecd37fd4e7bfe218d931724b331b9df570198 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:53:19 +0200 Subject: [PATCH 33/40] rm redundant TendermintBlock wrapper type --- ibc-testkit/src/hosts/tendermint.rs | 53 ++++++++----------- .../tests/core/ics02_client/update_client.rs | 7 --- 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index bf5430793..7b0f7d1bc 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -30,7 +30,7 @@ pub struct TendermintHost { pub chain_id: ChainId, /// The chain of blocks underlying this context. #[builder(default)] - pub history: Vec, + pub history: Vec, } impl Default for TendermintHost { @@ -40,7 +40,7 @@ impl Default for TendermintHost { } impl TestHost for TendermintHost { - type Block = TendermintBlock; + type Block = TmLightBlock; type BlockParams = BlockParams; type LightClientParams = ClientStateConfig; type ClientState = ClientState; @@ -60,20 +60,18 @@ impl TestHost for TendermintHost { timestamp: Timestamp, params: &Self::BlockParams, ) -> Self::Block { - TendermintBlock( - TestgenLightBlock::new_default_with_header( - TestgenHeader::new(¶ms.validators) - .app_hash(commitment_root.try_into().expect("infallible")) - .height(height) - .chain_id(self.chain_id.as_str()) - .next_validators(¶ms.next_validators) - .time(timestamp.into_tm_time().expect("Never fails")), - ) - .validators(¶ms.validators) - .next_validators(¶ms.next_validators) - .generate() - .expect("Never fails"), + TestgenLightBlock::new_default_with_header( + TestgenHeader::new(¶ms.validators) + .app_hash(commitment_root.try_into().expect("infallible")) + .height(height) + .chain_id(self.chain_id.as_str()) + .next_validators(¶ms.next_validators) + .time(timestamp.into_tm_time().expect("Never fails")), ) + .validators(¶ms.validators) + .next_validators(¶ms.next_validators) + .generate() + .expect("Never fails") } fn generate_client_state( @@ -101,36 +99,27 @@ impl TestHost for TendermintHost { } } -#[derive(Debug, Clone)] -pub struct TendermintBlock(TmLightBlock); - -impl TendermintBlock { - pub fn inner(&self) -> &TmLightBlock { - &self.0 - } -} - -impl TestBlock for TendermintBlock { +impl TestBlock for TmLightBlock { type Header = TendermintHeader; fn height(&self) -> Height { Height::new( - ChainId::from_str(self.0.signed_header.header.chain_id.as_str()) + ChainId::from_str(self.signed_header.header.chain_id.as_str()) .expect("Never fails") .revision_number(), - self.0.signed_header.header.height.value(), + self.signed_header.header.height.value(), ) .expect("Never fails") } fn timestamp(&self) -> Timestamp { - self.0.signed_header.header.time.into() + self.signed_header.header.time.into() } fn into_header_with_trusted(self, trusted_block: &Self) -> Self::Header { let mut header = TendermintHeader::from(self.clone()); header.set_trusted_height(trusted_block.height()); - header.set_trusted_next_validators_set(trusted_block.inner().validators.clone()); + header.set_trusted_next_validators_set(trusted_block.validators.clone()); header } } @@ -217,15 +206,15 @@ impl From for ConsensusState { } } -impl From for TendermintHeader { - fn from(block: TendermintBlock) -> Self { +impl From for TendermintHeader { + fn from(block: TmLightBlock) -> Self { let trusted_height = block.height(); let TmLightBlock { signed_header, validators: validator_set, .. - } = block.0; + } = block; let trusted_next_validator_set = validator_set.clone(); diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 88d511233..e74f10fa9 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -467,7 +467,6 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { let trusted_next_validator_set = ctx_b .host_block(&client_height) .expect("no error") - .inner() .next_validators .clone(); @@ -566,7 +565,6 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() let trusted_next_validator_set = ctx_b .host_block(&client_height) .expect("no error") - .inner() .next_validators .clone(); @@ -574,7 +572,6 @@ fn test_update_synthetic_tendermint_client_wrong_trusted_validator_change_fail() let mistrusted_next_validator_set = ctx_b .host_block(&client_height.increment()) .expect("no error") - .inner() .next_validators .clone(); @@ -673,7 +670,6 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { let trusted_next_validator_set = ctx_b .host_block(&client_height) .expect("no error") - .inner() .next_validators .clone(); @@ -770,7 +766,6 @@ fn test_update_synthetic_tendermint_client_malicious_validator_change_pass() { let trusted_next_validator_set = ctx_b .host_block(&client_height) .expect("no error") - .inner() .next_validators .clone(); @@ -867,7 +862,6 @@ fn test_update_synthetic_tendermint_client_adjacent_malicious_validator_change_f let trusted_next_validator_set = ctx_b .host_block(&client_height) .expect("no error") - .inner() .next_validators .clone(); @@ -1511,7 +1505,6 @@ fn test_client_update_max_clock_drift() { let trusted_next_validator_set = ctx_b .host_block(&client_height) .expect("no error") - .inner() .next_validators .clone(); From 59004348ccdb880e8116830412364480a8b315ef Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 21:55:46 +0200 Subject: [PATCH 34/40] add comments --- ibc-testkit/src/hosts/tendermint.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 7b0f7d1bc..a3b8af534 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -159,6 +159,8 @@ impl Default for BlockParams { } } +/// This wrapper type is needed to implement +/// [`From`] traits [`Header`] to foreign types. #[derive(Debug, Clone)] pub struct TendermintHeader(Header); From b8db190df5c2619fa108c92322253aa246cdefd0 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 22:07:01 +0200 Subject: [PATCH 35/40] update docstrings --- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 25 ++++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 6d7926f63..4d5628e14 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -391,7 +391,7 @@ where .collect::, _>>() } - /// Connections queries all the IBC connections of a chain. + /// Returns all the IBC connection ends of a chain. fn connection_ends(&self) -> Result, ContextError> { let path = "connections".to_owned().into(); @@ -420,7 +420,7 @@ where .collect() } - /// ClientConnections queries all the connection paths associated with a client. + /// Returns all the IBC connection ends associated with a client. fn client_connection_ends( &self, client_id: &ClientId, @@ -433,7 +433,7 @@ where .unwrap_or_default()) } - /// Channels queries all the IBC channels of a chain. + /// Returns all the IBC channel ends of a chain. fn channel_ends(&self) -> Result, ContextError> { let path = "channelEnds".to_owned().into(); @@ -464,7 +464,7 @@ where .collect() } - /// PacketCommitments returns all the packet commitments associated with a channel. + /// Returns all the packet commitments associated with a channel. fn packet_commitments( &self, channel_end_path: &ChannelEndPath, @@ -505,8 +505,10 @@ where .collect::, _>>() } - /// PacketAcknowledgements returns all the packet acknowledgements associated with a channel. - /// Returns all the packet acknowledgements if sequences is empty. + /// Returns the acknowledged packets associated with a channel. + /// + /// Takes a sequence list as an argument. + /// If the list set is empty, it returns all acknowledged packets. fn packet_acknowledgements( &self, channel_end_path: &ChannelEndPath, @@ -560,8 +562,9 @@ where .collect::, _>>() } - /// UnreceivedPackets returns all the unreceived IBC packets associated with - /// a channel and sequences. + /// Returns the unreceived IBC packets associated with a channel and sequences. + /// + /// Takes a sequence list as an argument. fn unreceived_packets( &self, channel_end_path: &ChannelEndPath, @@ -583,8 +586,10 @@ where .collect()) } - /// UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a channel and sequences. - /// Returns all the unreceived acks if sequences is empty. + /// Returns all the unreceived IBC acknowledgements associated with a channel and sequences. + /// + /// Takes a sequence list as an argument. + /// If the list is empty, it Returns all the unreceived acks. fn unreceived_acks( &self, channel_end_path: &ChannelEndPath, From d8fe3d4b3eb64bd803a053e952dad84415bd30aa Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 22:15:48 +0200 Subject: [PATCH 36/40] rm should never fail comment --- ibc-core/ics24-host/src/context.rs | 3 --- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 2 -- 2 files changed, 5 deletions(-) diff --git a/ibc-core/ics24-host/src/context.rs b/ibc-core/ics24-host/src/context.rs index b390d6b83..612768a32 100644 --- a/ibc-core/ics24-host/src/context.rs +++ b/ibc-core/ics24-host/src/context.rs @@ -151,7 +151,6 @@ pub trait ExecutionContext: ValidationContext { /// Called upon client creation. /// Increases the counter which keeps track of how many clients have been created. - /// Should never fail. fn increase_client_counter(&mut self) -> Result<(), ContextError>; /// Stores the given connection_end at path @@ -170,7 +169,6 @@ pub trait ExecutionContext: ValidationContext { /// Called upon connection identifier creation (Init or Try process). /// Increases the counter which keeps track of how many connections have been created. - /// Should never fail. fn increase_connection_counter(&mut self) -> Result<(), ContextError>; /// Stores the given packet commitment at the given store path @@ -233,7 +231,6 @@ pub trait ExecutionContext: ValidationContext { /// Called upon channel identifier creation (Init or Try message processing). /// Increases the counter which keeps track of how many channels have been created. - /// Should never fail. fn increase_channel_counter(&mut self) -> Result<(), ContextError>; /// Emit the given IBC event diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 4d5628e14..386a83efb 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -648,7 +648,6 @@ where /// Called upon client creation. /// Increases the counter which keeps track of how many clients have been created. - /// Should never fail. fn increase_client_counter(&mut self) -> Result<(), ContextError> { let current_sequence = self .client_counter @@ -701,7 +700,6 @@ where /// Called upon connection identifier creation (Init or Try process). /// Increases the counter which keeps track of how many connections have been created. - /// Should never fail. fn increase_connection_counter(&mut self) -> Result<(), ContextError> { let current_sequence = self .conn_counter From 6e7172d915682fcb37402ea57ba0c25d0eee468b Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Apr 2024 22:16:40 +0200 Subject: [PATCH 37/40] update adr --- docs/architecture/adr-005-handlers-redesign.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/architecture/adr-005-handlers-redesign.md b/docs/architecture/adr-005-handlers-redesign.md index 855a2b668..e808f2892 100644 --- a/docs/architecture/adr-005-handlers-redesign.md +++ b/docs/architecture/adr-005-handlers-redesign.md @@ -421,7 +421,6 @@ trait ExecutionContext { /// Called upon client creation. /// Increases the counter which keeps track of how many clients have been created. - /// Should never fail. fn increase_client_counter(&mut self); /// Called upon successful client update. @@ -452,7 +451,6 @@ trait ExecutionContext { /// Called upon connection identifier creation (Init or Try process). /// Increases the counter which keeps track of how many connections have been created. - /// Should never fail. fn increase_connection_counter(&mut self); fn store_packet_commitment( @@ -514,7 +512,6 @@ trait ExecutionContext { /// Called upon channel identifier creation (Init or Try message processing). /// Increases the counter which keeps track of how many channels have been created. - /// Should never fail. fn increase_channel_counter(&mut self); /// Ibc events From c264ae8600cb518d3636a6c015bacdb5005f7154 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 2 May 2024 13:47:50 +0200 Subject: [PATCH 38/40] fix compilation --- ibc-testkit/tests/core/ics04_channel/send_packet.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ibc-testkit/tests/core/ics04_channel/send_packet.rs b/ibc-testkit/tests/core/ics04_channel/send_packet.rs index 3ba25b274..3a2bace44 100644 --- a/ibc-testkit/tests/core/ics04_channel/send_packet.rs +++ b/ibc-testkit/tests/core/ics04_channel/send_packet.rs @@ -140,12 +140,10 @@ fn send_packet_processing() { }, Test { name: "Packet without height and timestamp timeout".to_string(), - ctx: context - .clone() - .with_client_config( - MockClientConfig::builder() - .latest_height(client_height) - .build(), + ctx: MockContext::default() + .with_light_client( + &ClientId::new("07-tendermint", 0).expect("no error"), + LightClientState::::with_latest_height(client_height), ) .with_connection(ConnectionId::zero(), conn_end_on_a.clone()) .with_channel(PortId::transfer(), ChannelId::zero(), chan_end_on_a.clone()) From 7704550bf423a8bcf7d38d705b0370e4fd1e91a8 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 2 May 2024 13:47:59 +0200 Subject: [PATCH 39/40] fix failing test --- ibc-testkit/src/relayer/integration.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs index 766a21f75..2a29feb16 100644 --- a/ibc-testkit/src/relayer/integration.rs +++ b/ibc-testkit/src/relayer/integration.rs @@ -5,7 +5,6 @@ use ibc::apps::transfer::types::msgs::transfer::MsgTransfer; use ibc::apps::transfer::types::packet::PacketData; use ibc::apps::transfer::types::PrefixedCoin; use ibc::core::channel::types::packet::Packet; -use ibc::core::channel::types::timeout::TimeoutHeight; use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::handler::types::events::IbcEvent; use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; @@ -103,11 +102,14 @@ where }; // packet with ibc metadata + // either height timeout or timestamp timeout must be set let msg = MsgTransfer { port_id_on_a: PortId::transfer(), chan_id_on_a: chan_id_on_a.clone(), packet_data, - timeout_height_on_b: TimeoutHeight::Never, + // setting timeout height to 10 blocks from B's current height. + timeout_height_on_b: relayer.get_ctx_b().latest_height().add(10).into(), + // not setting timeout timestamp. timeout_timestamp_on_b: Timestamp::none(), }; From e42acc9a4140625694cd8d3aa644e9c3a1388f2a Mon Sep 17 00:00:00 2001 From: Rano | Ranadeep Date: Thu, 2 May 2024 19:07:47 +0200 Subject: [PATCH 40/40] docs: ADR-009 to revamp testing framework (#1157) * draft adr 009 * update adr-009 * apply suggestions from pr review Co-authored-by: Sean Chen Signed-off-by: Rano | Ranadeep * use numbers list for sub-proposal titles Co-authored-by: Sean Chen Signed-off-by: Rano | Ranadeep * links to validation and execute context Co-authored-by: Sean Chen Signed-off-by: Rano | Ranadeep * apply suggestion from pr review * add links to impls * update comment * markdown format * update adr * use TestContext * update comment * apply suggestions from code review Co-authored-by: Sean Chen Signed-off-by: Rano | Ranadeep * markdown format * update method names * fix cargo doc error --------- Signed-off-by: Rano | Ranadeep Co-authored-by: Sean Chen --- docs/architecture/adr-009-revamp-testkit.md | 349 ++++++++++++++++++ ibc-testkit/src/context.rs | 25 +- ibc-testkit/src/fixtures/core/context.rs | 2 +- ibc-testkit/src/hosts/mod.rs | 3 +- ibc-testkit/src/relayer/integration.rs | 2 +- ibc-testkit/src/relayer/utils.rs | 2 +- .../tests/core/ics02_client/recover_client.rs | 2 +- .../tests/core/ics02_client/update_client.rs | 8 +- 8 files changed, 374 insertions(+), 19 deletions(-) create mode 100644 docs/architecture/adr-009-revamp-testkit.md diff --git a/docs/architecture/adr-009-revamp-testkit.md b/docs/architecture/adr-009-revamp-testkit.md new file mode 100644 index 000000000..8bf5af3e2 --- /dev/null +++ b/docs/architecture/adr-009-revamp-testkit.md @@ -0,0 +1,349 @@ +# ADR 009: Revamp IBC integration test framework + +## Changelog + +- 04-03-2024: Initial draft + +## Context + +The current framework in the IBC testkit uses +[existing types and injects state and/or dependencies +manually](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/tests/core/ics02_client/update_client.rs#L574-L578). +Sometimes, it uses +[semantically incorrect data as mock data](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/src/testapp/ibc/core/types.rs#L320). +Because of this, tests with customizable steps and fixed fixtures became ad-hoc +and unmaintainable. + +To overcome this, we need to improve our test framework such that it allows: + +- testing different implementations and traits. +- composition of succinct tests with useful `util` methods. +- validating code paths that were not easily testable before, i.e. Merkle proof + generation. +- simulating a more realistic IBC workflow, using real IBC and relayer + interfaces. + +## Decision + +The main goal of this proposal is to create a test framework that is modular and +closer to a real blockchain environment. This should also make the existing +tests more succinct and readable. Instead of bootstrapping the mock data that +tests use, we should use valid steps to generate it - so that we know the exact +steps to reach a state to reproduce in a real environment. + +To achieve this, we have broken down the proposal into sub-proposals: + +### 1. Adopt a Merkle store for the test framework + +The current framework uses `HashMap` and `HashSet` to store data. This works for +many test scenarios, but it fails to test proof-sensitive scenarios. Because of +this, we don't have any connection, channel handshake, or packet relay tests +that cover the Tendermint light client. + +We generalize +[`MockContext`](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/src/testapp/ibc/core/types.rs#L103) +to use a Merkle store which is used for IBC Context's store. For concrete or +default implementations, we can use the IAVL Merkle store implementation from +`informalsystems/basecoin-rs`. + +### 2. Modularize the host environment + +Currently, we are using `Mock` and `SyntheticTendermint` variants of the +[`HostType`](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/src/hosts/block.rs#L33-L36) +enum as host environments. To manage these two different environments, we also +introduced +[`HostBlocks`](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/src/hosts/block.rs#L72-75) +for encapsulating the possible host variants that need to be covered by +`ibc-testkit`. + +However, this creates friction in the case when we need to add new host +variants. It creates the same problem that the `ibc-derive` crate was designed +to solve for `ClientState` and `ConsensusState` types, namely: dispatching +methods to underlying variants of a top-level enum. But, a concrete +`MockContext` will always have a unique `HostType` and its corresponding +`HostBlocks`. So we can refactor `HostTypes` and `HockBlocks` with a generic +`TestHost` trait that maintains its corresponding types e.g. `Block` types, via +associated types. Finally, we generalize the `MockContext` once more to use this +`TestHost` trait instead of a concrete enum variant. + +This `TestHost` trait should be responsible for generating blocks, headers, +client, and consensus states specific to that host environment. + +```rs +/// TestHost is a trait that defines the interface for a host blockchain. +pub trait TestHost: Default + Debug + Sized { + /// The type of block produced by the host. + type Block: TestBlock; + + /// The type of client state produced by the host. + type ClientState: Into + Debug; + + /// The type of block parameters to produce a block. + type BlockParams: Debug + Default; + + /// The type of light client parameters to produce a light client state. + type LightClientParams: Debug + Default; + + /// The history of blocks produced by the host chain. + fn history(&self) -> &Vec; + + /// Commit a block with commitment root to the blockchain, by extending the history of blocks. + fn commit_block( + &mut self, + commitment_root: Vec, + block_time: Duration, + params: &Self::BlockParams, + ); + + /// Generate a block at the given height and timestamp, using the provided parameters. + fn generate_block( + &self, + commitment_root: Vec, + height: u64, + timestamp: Timestamp, + params: &Self::BlockParams, + ) -> Self::Block; + + /// Generate a client state using the block at the given height and the provided parameters. + fn generate_client_state( + &self, + latest_height: &Height, + params: &Self::LightClientParams, + ) -> Self::ClientState; +} + +/// TestBlock is a trait that defines the interface for a block produced by a host blockchain. +pub trait TestBlock: Clone + Debug { + /// The type of header that can be extracted from the block. + type Header: TestHeader; + + /// The height of the block. + fn height(&self) -> Height; + + /// The timestamp of the block. + fn timestamp(&self) -> Timestamp; + + /// Extract the header from the block. + fn into_header(self) -> Self::Header; +} + +/// TestHeader is a trait that defines the interface for a header +/// submitted by relayer from the host blockchain. +pub trait TestHeader: Clone + Debug + Into { + /// The type of consensus state can be extracted from the header. + type ConsensusState: ConsensusState + Into + From + Clone + Debug; + + /// The height of the block, as recorded in the header. + fn height(&self) -> Height; + + /// The timestamp of the block, as recorded in the header. + fn timestamp(&self) -> Timestamp; + + /// Extract the consensus state from the header. + fn into_consensus_state(self) -> Self::ConsensusState; +} +``` + +### 3. Decoupling IbcContext and Host environment + +Currently, `MockContext` implements the top-level +[validation](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-core/ics25-handler/src/entrypoint.rs#L45) +and +[execution](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-core/ics25-handler/src/entrypoint.rs#L112) +contexts of `ibc-rs`, as opposed to the more granular contexts of each of the +individual handlers. It contains other host-specific data e.g. `host_chain_id`, +`block_time` - that are not directly relevant to the IBC context. If we think of +`MockContext` as a real blockchain context, the `MockContext` represents the +top-level runtime; it contains `MockIbcStore`, which is a more appropriate +candidate to implement the validation and execution contexts for than the +`MockContext` itself. + +With this, the `MockContext` contains two decoupled parts - the host and the IBC +module. + +### 4. Chain-like interface for `MockContext` + +With the above changes, we can refactor the `MockContext` to have +blockchain-like interfaces. + +The `MockContext` should have `end_block`, `produce_block`, and `begin_block` to +mimic real blockchain environments, such as the Cosmos-SDK. + +```rs +impl MockContext +where + S: ProvableStore + Debug, + H: TestHost, +{ + // Access ibc module store + pub fn ibc_store_mut(&mut self) -> &mut MockIbcStore; + + // Advance the first block height. + pub fn advance_genesis_height(&mut self, genesis_time: Timestamp); + // Routine procedures at the beginning of a block + // Just after committing last block state. + pub fn begin_block(&mut self); + // Advance the current block height. + pub fn advance_block_height(&mut self, block_time: Duration); + // Routine procedures at the end of a block + // Just before committing current block state. + pub fn end_block(&mut self); +} +``` + +### 5. ICS23 compatible proof generation + +With the new proof generation capabilities, we can now test the Tendermint light +clients. But we need our proofs to be ICS23 compatible. ICS23 expects the IBC +store root to be committed at a commitment prefix at a top-level store in the +host environment. + +For this, we add an extra store in `MockContext` where the `MockIbcStore` +commits its storage root at its +[commitment prefix](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/src/testapp/ibc/core/core_ctx.rs#L127-L129) +key. + +So the `MockContext` is finalized as: + +```rs +pub struct MockContext +where + S: ProvableStore + Debug, + H: TestHost +{ + pub main_store: S, + pub host: H, + pub ibc_store: MockIbcStore, +} +``` + +Now the `MockIbcStore` can generate proofs that contain the proofs in its store +and commitment prefix. But it has to know the proofs of its commitment prefix of +the previous heights. + +So we add an extra store in `MockIbcStore` to store the proofs from previous +heights. This is similar to storing `HostConsensusState`s of previous heights. + +```rs +#[derive(Debug)] +pub struct MockIbcStore +where + S: ProvableStore + Debug, +{ + ... + /// Map of host consensus states. + pub host_consensus_states: Arc>>, + /// Map of proofs of ibc commitment prefix. + pub ibc_commiment_proofs: Arc>>, +} +``` + +The storing of the IBC store root at the IBC commitment prefix happens in the +`end_block` procedure. `produce_block` commits the main store, produces a block +with its latest root, and pushes the block to the blockchain. The storing of +proofs and host consensus states happens in the `begin_block` of the +`MockContext`. + +### 6. Integration Tests via `RelayerContext` + +With all the above changes, we can now write an integration test that tests the +IBC workflow - client creation, connection handshake, channel handshake, and +packet relaying for any host environment that implements `TestHost`. + +This can be done by reading the +[IBC events](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/src/testapp/ibc/core/types.rs#L95) +from `MockIbcStore` and creating and sending the IBC messages via +[`MockContext::deliver`](https://github.com/cosmos/ibc-rs/blob/v0.51.0/ibc-testkit/src/testapp/ibc/core/types.rs#L696). + +### Miscellaneous + +To achieve blockchain-like interfaces, we removed `max_history_size` and +`host_chain_id` from `MockContext`. + +- `max_history_size`: We generate all the blocks till a block height. This gives + us reproducibility. If we need to prune some older block data, we use a + dedicated `prune_block_till` to prune older blocks. This makes our tests more + descriptive about the assumption of the test scenarios. +- `host_chain_id`: The IBC runtime does not depend on `host_chain_id` directly. + The `TestHost` trait implementation is responsible for generating the blocks + with the necessary data. + +Also to minimize verbosity while writing tests (as Rust doesn't support default +arguments to function parameters), we want to use some parameter builders. For +that, we can use the [`TypedBuilder`](https://crates.io/crates/typed-builder) +crate. + +## Status + +Proposed + +## Consequences + +This ADR pays the technical debt of the existing testing framework. + +### Positive + +Future tests will be more readable and maintainable. The test framework becomes +modular and leverages Rust's trait system. `ibc-rs` users may benefit from this +framework, which allows them to test their host implementations of `ibc-rs` +components. + +### Negative + +This requires a significant refactoring of the existing tests. Since this may +take some time, the parallel development on the `main` branch may conflict with +this work. + +## References + +This work is being tracked at +[cosmos/ibc-rs#1109](https://github.com/cosmos/ibc-rs/pull/1109). + +The following provides the concrete implementations of the proposed changes: + +#### MockIbcStore + +The modified `MockIbcStore` with Merkle store lives at +[`testapp/ibc/core/types.rs`](https://github.com/cosmos/ibc-rs/blob/feat/refactor-testkit/ibc-testkit/src/testapp/ibc/core/types.rs#L43-L96). + +#### TestHost + +The Rust trait lives at +[`hosts/mod.rs`](https://github.com/cosmos/ibc-rs/blob/feat/refactor-testkit/ibc-testkit/src/hosts/mod.rs#L27). +The `Mock` and `Tendermint` host implementations live in +[`hosts/mock.rs`](https://github.com/cosmos/ibc-rs/blob/feat/refactor-testkit/ibc-testkit/src/hosts/mock.rs#L30) +and +[`hosts/tendermint.rs`](https://github.com/cosmos/ibc-rs/blob/feat/refactor-testkit/ibc-testkit/src/hosts/tendermint.rs#L42) +respectively. + +#### Renaming `MockContext` to `StoreGenericTestContext` + +There was confusion about what is a _Test_ component and what is a _Mock_ +component. We have `MockContext` with `MockClient` and `TendermintClient`. + +To avoid this confusion, we renamed `MockContext` to `StoreGenericTestContext`. +This means that going forward all our general frameworks and traits should have +`Test` in their name. But a dummy concrete implementation of these traits may +have `Mock` in their name. + +#### StoreGenericTestContext + +[`StoreGenericTestContext`](https://github.com/cosmos/ibc-rs/blob/feat/refactor-testkit/ibc-testkit/src/context.rs#L34-L52) +is actually what is described as `MockContext` in the ADR. For convenience, we +defined `TestContext` to have a concrete store implementation - +[`MockStore`](https://github.com/cosmos/ibc-rs/blob/feat/refactor-testkit/ibc-testkit/src/context.rs#L55-L56). + +```rs +// A mock store type using basecoin-storage implementations. +pub type MockStore = RevertibleStore>; + +pub type TestContext = StoreGenericTestContext; +``` + +With this, we can now define `MockContext` which uses `MockStore` and `MockHost` +and `TendermintContext` which uses `MockStore` and `TendermintHost`. + +```rs +pub type MockContext = TestContext; +pub type TendermintContext = TestContext; +``` diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 846182e8c..74d141f2e 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -131,7 +131,7 @@ where } else { // Repeatedly advance the host chain height till we hit the desired height while self.host.latest_height().revision_height() < target_height.revision_height() { - self.advance_height() + self.advance_block_height() } } self @@ -143,8 +143,8 @@ where /// But it bootstraps the genesis block by height 1 and `genesis_time`. /// /// The method starts and ends with [`Self::end_block`] and [`Self::begin_block`], just - /// like the [`Self::advance_height_with_params`], so that it can advance to next height - /// i.e. height 2 - just by calling [`Self::advance_height_with_params`]. + /// like the [`Self::advance_block_height_with_params`], so that it can advance to next height + /// i.e. height 2 - just by calling [`Self::advance_block_height_with_params`]. pub fn advance_genesis_height(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { self.end_block(); @@ -166,8 +166,9 @@ where /// Begin a new block on the context. /// - /// This method book keeps the data from last block, - /// and prepares the context for the next block. + /// This method commits the required metadata from the last block generation + /// and consensus, and prepares the context for the next block. This includes + /// the latest consensus state and the latest IBC commitment proof. pub fn begin_block(&mut self) { let consensus_state = self .host @@ -230,15 +231,19 @@ where /// Advances the host chain height by ending the current block, producing a new block, and /// beginning the next block. - pub fn advance_height_with_params(&mut self, block_time: Duration, params: &H::BlockParams) { + pub fn advance_block_height_with_params( + &mut self, + block_time: Duration, + params: &H::BlockParams, + ) { self.end_block(); self.commit_state_to_host(block_time, params); self.begin_block(); } /// Convenience method to advance the host chain height using default parameters. - pub fn advance_height(&mut self) { - self.advance_height_with_params( + pub fn advance_block_height(&mut self) { + self.advance_block_height_with_params( Duration::from_secs(DEFAULT_BLOCK_TIME_SECS), &Default::default(), ) @@ -479,7 +484,7 @@ where self.dispatch(msg) .map_err(RelayerError::TransactionFailed)?; // Create a new block. - self.advance_height(); + self.advance_block_height(); Ok(()) } @@ -570,7 +575,7 @@ mod tests { let current_height = test.ctx.latest_height(); // After advancing the chain's height, the context should still be valid. - test.ctx.advance_height(); + test.ctx.advance_block_height(); assert!( test.ctx.host.validate().is_ok(), "failed in test [{}] {} while validating context {:?}", diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 7810c6b77..99304f0e0 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -80,7 +80,7 @@ where ); for block_params in params.block_params_history { - context.advance_height_with_params(params.block_time, &block_params); + context.advance_block_height_with_params(params.block_time, &block_params); } assert_eq!( diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index 3a4183401..426b69066 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -138,7 +138,8 @@ pub trait TestBlock: Clone + Debug { } } -/// TestHeader is a trait that defines the interface for a header corresponding to a host blockchain. +/// TestHeader is a trait that defines the interface for a header +/// submitted by relayer from the host blockchain. pub trait TestHeader: Clone + Debug + Into { /// The type of consensus state can be extracted from the header. type ConsensusState: ConsensusState + Into + From + Clone + Debug; diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs index 2a29feb16..85df3cd9c 100644 --- a/ibc-testkit/src/relayer/integration.rs +++ b/ibc-testkit/src/relayer/integration.rs @@ -122,7 +122,7 @@ where .expect("successfully created send_packet"); // send_packet wasn't committed, hence produce a block - relayer.get_ctx_a_mut().advance_height(); + relayer.get_ctx_a_mut().advance_block_height(); // retrieve the send_packet event let Some(IbcEvent::SendPacket(send_packet_event)) = relayer diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index af93f6085..9d4c96bd6 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -103,7 +103,7 @@ where /// Advances the block height on `A` until it catches up with the latest timestamp on `B`. pub fn sync_clock_on_a(ctx_a: &mut TestContext, ctx_b: &TestContext) { while ctx_b.latest_timestamp() > ctx_a.latest_timestamp() { - ctx_a.advance_height(); + ctx_a.advance_block_height(); } } diff --git a/ibc-testkit/tests/core/ics02_client/recover_client.rs b/ibc-testkit/tests/core/ics02_client/recover_client.rs index 8f6f804b0..28f1de076 100644 --- a/ibc-testkit/tests/core/ics02_client/recover_client.rs +++ b/ibc-testkit/tests/core/ics02_client/recover_client.rs @@ -100,7 +100,7 @@ fn setup_client_recovery_fixture( // Let the subject client state expire. while ctx.latest_timestamp() <= subject_client_trusted_timestamp { - ctx.advance_height(); + ctx.advance_block_height(); } // at this point, the subject client should be expired. diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index e74f10fa9..9dbc49030 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -258,7 +258,7 @@ fn test_consensus_state_pruning() { let update_height = ctx.latest_height(); - ctx.advance_height(); + ctx.advance_block_height(); let block = ctx.host_block(&update_height).unwrap().clone(); let mut block = block.into_header(); @@ -1433,7 +1433,7 @@ fn test_expired_client() { while ctx.ibc_store.host_timestamp().expect("no error") < (timestamp + trusting_period).expect("no error") { - ctx.advance_height(); + ctx.advance_block_height(); } let client_state = ctx.ibc_store.client_state(&client_id).unwrap(); @@ -1488,11 +1488,11 @@ fn test_client_update_max_clock_drift() { while ctx_b.ibc_store.host_timestamp().expect("no error") < (ctx_a.ibc_store.host_timestamp().expect("no error") + max_clock_drift).expect("no error") { - ctx_b.advance_height(); + ctx_b.advance_block_height(); } // include current block - ctx_b.advance_height(); + ctx_b.advance_block_height(); let update_height = ctx_b.latest_height();