From 012403ea157afbd8e34339e668a5d2a932167ff1 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 15:23:14 +0100 Subject: [PATCH 01/54] test tmclient proof verification --- .../tests/core/ics02_client/create_client.rs | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index d2b0dffdd..0968109a2 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::context::ProvableStore; use basecoin_store::impls::InMemoryStore; use ibc::clients::tendermint::types::{ client_type as tm_client_type, ConsensusState as TmConsensusState, @@ -7,21 +8,30 @@ 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::commitment_types::merkle::MerkleProof; 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::{ChainId, ClientId}; +use ibc::core::host::types::path::{ClientConsensusStatePath, NextClientSequencePath}; use ibc::core::host::{ClientStateRef, ValidationContext}; +use ibc_proto::ibc::core::commitment::v1::MerklePath; +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 +133,91 @@ 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 ctx_tm = MockContextConfig::builder() + .host_id(ChainId::new("tendermint-0").unwrap()) + .latest_height(Height::new(0, 10).expect("no error")) + .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 consensus_state_path = ClientConsensusStatePath::new(client_id.clone(), 0, 10); + + let ( + AnyClientState::Tendermint(tm_client_state), + AnyConsensusState::Tendermint(tm_consensus_state), + ) = ( + client_validation_ctx_mk + .client_state(&client_id) + .expect("client state exists"), + client_validation_ctx_mk + .consensus_state(&consensus_state_path) + .expect("consensus_state exists"), + ) + else { + panic!("client and consensus state are 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() + .store + .get_proof( + ctx_tm.latest_height().revision_height().into(), + &next_client_seq_path.to_string().into(), + ) + .expect("proof exists"); + + let root = tm_consensus_state.inner().root(); + + let merkle_path = MerklePath { + key_path: vec![next_client_seq_path.to_string()], + }; + + let merkle_proof = MerkleProof { + proofs: vec![proof], + }; + + // with correct value + merkle_proof + .verify_membership( + &tm_client_state.inner().proof_specs, + root.clone().into(), + merkle_path.clone(), + serde_json::to_vec(&next_client_seq_value).expect("valid json serialization"), + 0, + ) + .expect("proof verification is successful"); + + // with incorrect value + assert!(matches!( + merkle_proof + .verify_membership( + &tm_client_state.inner().proof_specs, + root.into(), + merkle_path, + serde_json::to_vec(&1).expect("valid json serialization"), + 0, + ) + .expect_err("proof verification fails"), + CommitmentError::VerificationFailure + )); +} From dd010fe8f191e0abc8d66a2bb4c524b9c12d72d7 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 16:42:04 +0100 Subject: [PATCH 02/54] use ClientStateConfig directly --- .../src/fixtures/clients/tendermint.rs | 48 +++++++++++-------- ibc-testkit/src/hosts/tendermint.rs | 20 ++++---- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/ibc-testkit/src/fixtures/clients/tendermint.rs b/ibc-testkit/src/fixtures/clients/tendermint.rs index e10ed576c..943bea832 100644 --- a/ibc-testkit/src/fixtures/clients/tendermint.rs +++ b/ibc-testkit/src/fixtures/clients/tendermint.rs @@ -15,6 +15,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 +65,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,8 +74,7 @@ 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, + pub max_clock_drift: Duration, #[builder(default = ProofSpecs::cosmos())] pub proof_specs: ProofSpecs, #[builder(default)] @@ -84,23 +83,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/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 91c19b2f8..051baac3e 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"); From f3dd05e5f7ebc7f9b4676cea927ab0ff378558e1 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 16:42:16 +0100 Subject: [PATCH 03/54] rm MockClientConfig --- ibc-testkit/src/context.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 846c08a91..a9651f6ac 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -1,7 +1,5 @@ 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; @@ -9,7 +7,6 @@ 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; @@ -23,7 +20,6 @@ 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; @@ -50,24 +46,6 @@ where 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. From e19c085938a17562f127a23c2347a987c26faca4 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 16:43:45 +0100 Subject: [PATCH 04/54] use basecoin store proofspec as default --- ibc-testkit/src/fixtures/clients/tendermint.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ibc-testkit/src/fixtures/clients/tendermint.rs b/ibc-testkit/src/fixtures/clients/tendermint.rs index 943bea832..030259d7f 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}; @@ -75,7 +76,7 @@ pub struct ClientStateConfig { pub unbonding_period: Duration, #[builder(default = Duration::from_millis(3000))] pub max_clock_drift: Duration, - #[builder(default = ProofSpecs::cosmos())] + #[builder(default = vec![basecoin_proof_spec()].into())] pub proof_specs: ProofSpecs, #[builder(default)] pub upgrade_path: Vec, From 14201eef408177535709c1a7f68aa8ae933f3730 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 16:44:03 +0100 Subject: [PATCH 05/54] update tests --- ibc-testkit/tests/core/ics02_client/update_client.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 818dedfdb..9389a99c2 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -24,7 +24,8 @@ 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; @@ -234,7 +235,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 +1423,7 @@ fn test_expired_client() { LightClientBuilder::init() .context(&ctx_b) .params( - MockClientConfig::builder() + ClientStateConfig::builder() .trusting_period(trusting_period) .build(), ) @@ -1475,7 +1476,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(), ) From 8a15b70b059852cd22551f41d5f33a31b2a8a629 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 17:49:01 +0100 Subject: [PATCH 06/54] use merkle storage in MockContext --- ibc-testkit/src/context.rs | 46 +++++++++++++++++++- ibc-testkit/src/fixtures/core/context.rs | 2 + ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 14 ++++++ ibc-testkit/src/testapp/ibc/core/types.rs | 25 +++++++++-- 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index a9651f6ac..4d3edc003 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -7,6 +7,7 @@ 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::commitment::CommitmentPrefix; use ibc::core::connection::types::ConnectionEnd; use ibc::core::entrypoint::dispatch; use ibc::core::handler::types::events::IbcEvent; @@ -36,9 +37,15 @@ where H: TestHost, HostClientState: ClientStateValidation>, { + /// The main store of the context. + pub main_store: S, + /// The type of host chain underlying this mock context. pub host: H, + /// CommitmentPrefix of the ibc store + pub ibc_commitment_prefix: CommitmentPrefix, + /// An object that stores all IBC related data. pub ibc_store: MockIbcStore, } @@ -105,6 +112,7 @@ where // store it in ibc context as host consensus state self.ibc_store.store_host_consensus_state( + 1, self.host .latest_block() .into_header() @@ -115,19 +123,53 @@ where 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"); + let ibc_store_commitment = self.ibc_store.commit().expect("no error"); + + // commit ibc store commitment in main store + self.main_store + .set( + self.ibc_commitment_prefix + .as_bytes() + .try_into() + .expect("valid utf8 prefix"), + ibc_store_commitment, + ) + .expect("no error"); + + // commit main store + let main_store_commitment = self.main_store.commit().expect("no error"); // generate a new block - self.host.advance_block(app_hash, block_time, params); + self.host + .advance_block(main_store_commitment, block_time, params); // store it in ibc context as host consensus state self.ibc_store.store_host_consensus_state( + self.host.latest_height().revision_height(), self.host .latest_block() .into_header() .into_consensus_state() .into(), ); + + let ibc_commitment_proof = self + .main_store + .get_proof( + self.host.latest_height().revision_height().into(), + &self + .ibc_commitment_prefix + .as_bytes() + .try_into() + .expect("valid utf8 prefix"), + ) + .expect("no error"); + + // store ibc commitment prefix + self.ibc_store.store_ibc_commitment_proof( + self.host.latest_height().revision_height(), + ibc_commitment_proof, + ); } pub fn advance_block(&mut self) { diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 724a706a6..f770c4d98 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -56,6 +56,8 @@ where .expect("no underflow"); let mut context = Self { + main_store: Default::default(), + ibc_commitment_prefix: b"ibc".to_vec().try_into().expect("valid commitment prefix"), ibc_store: MockIbcStore::new( params.latest_height.revision_number(), Default::default(), 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/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 8fe4cddcf..57ef156b3 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()), @@ -144,16 +148,22 @@ where self.store.commit() } - pub fn store_host_consensus_state(&mut self, consensus_state: AnyConsensusState) { + pub 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); + } + + pub fn store_ibc_commitment_proof(&mut self, height: u64, proof: CommitmentProof) { + self.ibc_commiment_proofs.lock().insert(height, proof); } 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 +174,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 } } From edbc25cf0eb5e0b129650036fd2c65136962fb53 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 17:49:28 +0100 Subject: [PATCH 07/54] fix ProofSpecs size --- 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 030259d7f..eab0ef94c 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()].into())] + #[builder(default = vec![basecoin_proof_spec(); 2].into())] pub proof_specs: ProofSpecs, #[builder(default)] pub upgrade_path: Vec, From 2ef512c6cd789e56367cdb79d42f8b0dcc3af2d6 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 17:50:12 +0100 Subject: [PATCH 08/54] refactor MockIbcStore to perform begin_block and end_block --- ibc-testkit/src/context.rs | 91 ++++++++++++----------- ibc-testkit/src/testapp/ibc/core/types.rs | 23 ++++-- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 4d3edc003..ec8161354 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -1,4 +1,5 @@ use core::fmt::Debug; +use core::time::Duration; use basecoin_store::context::ProvableStore; use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore}; @@ -103,27 +104,52 @@ 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 main store + let main_store_commitment = self.main_store.commit().expect("no error"); - // store it in ibc context as host consensus state - self.ibc_store.store_host_consensus_state( - 1, + // generate a genesis block + let genesis_block = self.host - .latest_block() - .into_header() - .into_consensus_state() - .into(), + .generate_block(main_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 + .main_store + .get_proof( + self.host.latest_height().revision_height().into(), + &self + .ibc_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 ibc_store_commitment = 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 main store self.main_store @@ -135,41 +161,20 @@ where ibc_store_commitment, ) .expect("no error"); + } + pub fn produce_block(&mut self, block_time: Duration, params: &H::BlockParams) { // commit main store let main_store_commitment = self.main_store.commit().expect("no error"); - // generate a new block self.host .advance_block(main_store_commitment, block_time, params); + } - // store it in ibc context as host consensus state - self.ibc_store.store_host_consensus_state( - self.host.latest_height().revision_height(), - self.host - .latest_block() - .into_header() - .into_consensus_state() - .into(), - ); - - let ibc_commitment_proof = self - .main_store - .get_proof( - self.host.latest_height().revision_height().into(), - &self - .ibc_commitment_prefix - .as_bytes() - .try_into() - .expect("valid utf8 prefix"), - ) - .expect("no error"); - - // store ibc commitment prefix - self.ibc_store.store_ibc_commitment_proof( - self.host.latest_height().revision_height(), - ibc_commitment_proof, - ); + 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) { diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 57ef156b3..a8d813222 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -144,20 +144,31 @@ where } } - pub fn commit(&mut self) -> Result, as Store>::Error> { - self.store.commit() - } - - pub fn store_host_consensus_state(&mut self, height: u64, consensus_state: AnyConsensusState) { + fn store_host_consensus_state(&mut self, height: u64, consensus_state: AnyConsensusState) { self.host_consensus_states .lock() .insert(height, consensus_state); } - pub fn store_ibc_commitment_proof(&mut self, height: u64, proof: CommitmentProof) { + 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(); From 50c304f7f24850ffd2a26d631e8e3dc7d38f764c Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 17:51:41 +0100 Subject: [PATCH 09/54] simpler proof verification test --- .../tests/core/ics02_client/create_client.rs | 87 +++++++++---------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 0968109a2..99a78c624 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -1,4 +1,3 @@ -use basecoin_store::context::ProvableStore; use basecoin_store::impls::InMemoryStore; use ibc::clients::tendermint::types::{ client_type as tm_client_type, ConsensusState as TmConsensusState, @@ -9,14 +8,13 @@ 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::commitment_types::merkle::MerkleProof; 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::{ChainId, ClientId}; use ibc::core::host::types::path::{ClientConsensusStatePath, NextClientSequencePath}; use ibc::core::host::{ClientStateRef, ValidationContext}; -use ibc_proto::ibc::core::commitment::v1::MerklePath; +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, @@ -137,10 +135,11 @@ fn test_invalid_frozen_tm_client_creation() { #[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() .host_id(ChainId::new("tendermint-0").unwrap()) - .latest_height(Height::new(0, 10).expect("no error")) + .latest_height(client_height) .build::>(); let ctx_mk = MockContext::::default().with_light_client( @@ -150,21 +149,25 @@ fn test_tm_create_client_proof_verification_ok() { let client_validation_ctx_mk = ctx_mk.ibc_store().get_client_validation_context(); - let consensus_state_path = ClientConsensusStatePath::new(client_id.clone(), 0, 10); - - let ( - AnyClientState::Tendermint(tm_client_state), - AnyConsensusState::Tendermint(tm_consensus_state), - ) = ( - client_validation_ctx_mk - .client_state(&client_id) - .expect("client state exists"), - client_validation_ctx_mk - .consensus_state(&consensus_state_path) - .expect("consensus_state exists"), - ) + let (AnyClientState::Tendermint(tm_client_state),) = (client_validation_ctx_mk + .client_state(&client_id) + .expect("client state exists"),) else { - panic!("client and consensus state are not valid") + 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; @@ -179,45 +182,35 @@ fn test_tm_create_client_proof_verification_ok() { let proof = ctx_tm .ibc_store() - .store - .get_proof( - ctx_tm.latest_height().revision_height().into(), - &next_client_seq_path.to_string().into(), - ) - .expect("proof exists"); + .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(); - let merkle_path = MerklePath { - key_path: vec![next_client_seq_path.to_string()], - }; - - let merkle_proof = MerkleProof { - proofs: vec![proof], - }; - - // with correct value - merkle_proof + // correct value verification + tm_client_state .verify_membership( - &tm_client_state.inner().proof_specs, - root.clone().into(), - merkle_path.clone(), + &ctx_tm.ibc_commitment_prefix, + &proof, + &root, + next_client_seq_path.clone().into(), serde_json::to_vec(&next_client_seq_value).expect("valid json serialization"), - 0, ) - .expect("proof verification is successful"); + .expect("successful proof verification"); - // with incorrect value + // incorrect value verification assert!(matches!( - merkle_proof + tm_client_state .verify_membership( - &tm_client_state.inner().proof_specs, - root.into(), - merkle_path, - serde_json::to_vec(&1).expect("valid json serialization"), - 0, + &ctx_tm.ibc_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"), - CommitmentError::VerificationFailure + ClientError::Ics23Verification(CommitmentError::VerificationFailure) )); } From 50d4ac71cc29ebb9d2dac65ab730299a5a31aaa1 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 18:23:54 +0100 Subject: [PATCH 10/54] use ValidationContext::commitment_prefix() --- ibc-testkit/src/context.rs | 10 ++++------ ibc-testkit/src/fixtures/core/context.rs | 11 +++++------ ibc-testkit/tests/core/ics02_client/create_client.rs | 4 ++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index ec8161354..d41b6e425 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -8,7 +8,6 @@ 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::commitment::CommitmentPrefix; use ibc::core::connection::types::ConnectionEnd; use ibc::core::entrypoint::dispatch; use ibc::core::handler::types::events::IbcEvent; @@ -44,9 +43,6 @@ where /// The type of host chain underlying this mock context. pub host: H, - /// CommitmentPrefix of the ibc store - pub ibc_commitment_prefix: CommitmentPrefix, - /// An object that stores all IBC related data. pub ibc_store: MockIbcStore, } @@ -133,7 +129,8 @@ where .get_proof( self.host.latest_height().revision_height().into(), &self - .ibc_commitment_prefix + .ibc_store + .commitment_prefix() .as_bytes() .try_into() .expect("valid utf8 prefix"), @@ -154,7 +151,8 @@ where // commit ibc store commitment in main store self.main_store .set( - self.ibc_commitment_prefix + self.ibc_store + .commitment_prefix() .as_bytes() .try_into() .expect("valid utf8 prefix"), diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index f770c4d98..7929122dc 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -55,14 +55,13 @@ where * u32::try_from(params.latest_height.revision_height() - 1).expect("no overflow"))) .expect("no underflow"); + let ibc_store = + MockIbcStore::new(params.latest_height.revision_number(), Default::default()); + let mut context = Self { - main_store: Default::default(), - ibc_commitment_prefix: b"ibc".to_vec().try_into().expect("valid commitment prefix"), - ibc_store: MockIbcStore::new( - params.latest_height.revision_number(), - Default::default(), - ), host: params.host, + main_store: Default::default(), + ibc_store, }; // store is a height 0; no block diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 99a78c624..4f37ee75d 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -192,7 +192,7 @@ fn test_tm_create_client_proof_verification_ok() { // correct value verification tm_client_state .verify_membership( - &ctx_tm.ibc_commitment_prefix, + &ctx_tm.ibc_store().commitment_prefix(), &proof, &root, next_client_seq_path.clone().into(), @@ -204,7 +204,7 @@ fn test_tm_create_client_proof_verification_ok() { assert!(matches!( tm_client_state .verify_membership( - &ctx_tm.ibc_commitment_prefix, + &ctx_tm.ibc_store().commitment_prefix(), &proof, &root, next_client_seq_path.into(), From d6e9dbfd04476c0d64a940333b8563b7d776934c Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 20 Mar 2024 18:26:01 +0100 Subject: [PATCH 11/54] nits --- ibc-testkit/src/fixtures/core/context.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 7929122dc..ec9a1d294 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -55,13 +55,13 @@ where * u32::try_from(params.latest_height.revision_height() - 1).expect("no overflow"))) .expect("no underflow"); - let ibc_store = - MockIbcStore::new(params.latest_height.revision_number(), Default::default()); - let mut context = Self { - host: params.host, main_store: Default::default(), - ibc_store, + ibc_store: MockIbcStore::new( + params.latest_height.revision_number(), + Default::default(), + ), + host: params.host, }; // store is a height 0; no block From 230e79a2afa03a46ac4bc1b78970b4784ad1809e Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 15:51:24 +0100 Subject: [PATCH 12/54] refactor host related trait method --- ibc-testkit/src/hosts/mock.rs | 4 ++++ ibc-testkit/src/hosts/mod.rs | 7 +++++-- ibc-testkit/src/hosts/tendermint.rs | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index 067d41a14..4423d5669 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_previous_block(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..2204b828a 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; @@ -133,8 +133,11 @@ pub trait TestBlock: Clone + Debug { fn timestamp(&self) -> Timestamp; /// Extract the header from the block. + fn into_header_with_previous_block(self, previous_block: &Self) -> Self::Header; + fn into_header(self) -> Self::Header { - self.into() + let prev_block = self.clone(); + self.into_header_with_previous_block(&prev_block) } } diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 051baac3e..60e437bf3 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -136,6 +136,13 @@ impl TestBlock for TendermintBlock { fn timestamp(&self) -> Timestamp { self.0.signed_header.header.time.into() } + + fn into_header_with_previous_block(self, previous_block: &Self) -> Self::Header { + let mut header = TendermintHeader::from(self); + header.set_trusted_height(previous_block.height()); + header.set_trusted_next_validators_set(previous_block.inner().validators.clone()); + header + } } #[derive(Debug, TypedBuilder)] From ef369bec3ddd268a1b6909e2887a5bacd3b3797c Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 15:52:22 +0100 Subject: [PATCH 13/54] tendermint host client integration test with proof verification --- ibc-testkit/tests/core/mod.rs | 496 ++++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) diff --git a/ibc-testkit/tests/core/mod.rs b/ibc-testkit/tests/core/mod.rs index 10f45250e..99896b618 100644 --- a/ibc-testkit/tests/core/mod.rs +++ b/ibc-testkit/tests/core/mod.rs @@ -1,5 +1,501 @@ +use std::time::Duration; + +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::client::types::Height; +use ibc::core::connection::types::msgs::{ + ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit, + MsgConnectionOpenTry, +}; +use ibc::core::connection::types::version::Version; +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::{ClientId, ConnectionId}; +use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath}; +use ibc::core::host::ValidationContext; +use ibc::primitives::Signer; +use ibc_query::core::context::ProvableContext; +use ibc_testkit::context::MockContext; +use ibc_testkit::fixtures::core::signer::dummy_account_id; +use ibc_testkit::hosts::{HostClientState, TendermintHost, TestBlock, TestHost}; +use ibc_testkit::testapp::ibc::core::router::MockRouter; +use ibc_testkit::testapp::ibc::core::types::{ + DefaultIbcStore, LightClientBuilder, LightClientState, +}; + pub mod ics02_client; pub mod ics03_connection; pub mod ics04_channel; #[cfg(feature = "serde")] pub mod router; + +pub struct RelayerContext +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + ctx_a: MockContext, + router_a: MockRouter, + ctx_b: MockContext, + router_b: MockRouter, +} + +impl RelayerContext +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + pub fn new( + ctx_a: MockContext, + router_a: MockRouter, + ctx_b: MockContext, + router_b: MockRouter, + ) -> Self { + Self { + ctx_a, + router_a, + ctx_b, + router_b, + } + } + + pub fn get_ctx_a(&self) -> &MockContext { + &self.ctx_a + } + + pub fn get_ctx_b(&self) -> &MockContext { + &self.ctx_b + } + + pub fn get_ctx_a_mut(&mut self) -> &mut MockContext { + &mut self.ctx_a + } + + pub fn get_ctx_b_mut(&mut self) -> &mut MockContext { + &mut self.ctx_b + } + + pub fn reverse(self) -> RelayerContext { + RelayerContext::new(self.ctx_b, self.router_b, self.ctx_a, self.router_a) + } + + pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { + let light_client_of_b = LightClientBuilder::init() + .context(&self.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, + })); + + self.ctx_a + .deliver(&mut self.router_a, msg_for_a) + .expect("success"); + + let Some(IbcEvent::CreateClient(create_client_b_event)) = + self.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(self.ctx_a.ibc_store()) + .client_state(&client_id_on_a) + .expect("client state exists") + .latest_height(), + self.ctx_b.latest_height() + ); + + client_id_on_a + } + + pub fn sync_latest_timestamp(&mut self) { + if self.ctx_a.latest_timestamp() > self.ctx_b.latest_timestamp() { + while self.ctx_a.latest_timestamp() > self.ctx_b.latest_timestamp() { + self.ctx_b.advance_block(); + } + } else { + while self.ctx_b.latest_timestamp() > self.ctx_a.latest_timestamp() { + self.ctx_a.advance_block(); + } + } + } + + pub fn update_client_on_a(&mut self, client_id_on_a: ClientId, signer: Signer) { + let latest_client_height_on_a = self + .ctx_a + .ibc_store() + .get_client_validation_context() + .client_state(&client_id_on_a) + .unwrap() + .latest_height(); + + let latest_height_of_b = self.ctx_b.latest_height(); + + let msg_for_a = MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id_on_a.clone(), + client_message: self + .ctx_b + .host_block(&latest_height_of_b) + .expect("block exists") + .into_header_with_previous_block( + &self + .ctx_b + .host_block(&latest_client_height_on_a) + .expect("block exists"), + ) + .into(), + signer, + })); + + self.sync_latest_timestamp(); + + self.ctx_a + .deliver(&mut self.router_a, msg_for_a) + .expect("success"); + + let Some(IbcEvent::UpdateClient(_)) = self.ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn update_client_on_b(&mut self, client_id_on_b: ClientId, signer: Signer) { + let latest_client_height_on_b = self + .ctx_b + .ibc_store() + .get_client_validation_context() + .client_state(&client_id_on_b) + .unwrap() + .latest_height(); + + let latest_height_of_a = self.ctx_a.latest_height(); + + let msg_for_b = MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id_on_b.clone(), + client_message: self + .ctx_a + .host_block(&latest_height_of_a) + .expect("block exists") + .into_header_with_previous_block( + &self + .ctx_a + .host_block(&latest_client_height_on_b) + .expect("block exists"), + ) + .into(), + signer, + })); + + self.sync_latest_timestamp(); + + self.ctx_b + .deliver(&mut self.router_b, msg_for_b) + .expect("success"); + + let Some(IbcEvent::UpdateClient(_)) = self.ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn create_connection_on_b( + &mut self, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + let counterparty_b = ConnectionCounterParty::new( + client_id_on_b.clone(), + None, + self.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(), + })); + + self.ctx_a + .deliver(&mut self.router_a, msg_for_a) + .expect("success"); + + let Some(IbcEvent::OpenInitConnection(open_init_connection_event)) = + self.ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + self.update_client_on_b(client_id_on_b.clone(), signer.clone()); + + let conn_id_on_a = open_init_connection_event.conn_id_on_a().clone(); + + let proofs_height_on_a = self.ctx_a.latest_height(); + + let client_state_of_b_on_a = self + .ctx_a + .ibc_store() + .client_state(&client_id_on_a) + .unwrap(); + + 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()), + self.ctx_a.ibc_store().commitment_prefix(), + ); + + let proof_conn_end_on_a = self + .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 = self + .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 = self + .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: Version::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(), + })); + + self.ctx_b + .deliver(&mut self.router_b, msg_for_b) + .expect("success"); + + let Some(IbcEvent::OpenTryConnection(open_try_connection_event)) = + self.ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + self.update_client_on_a(client_id_on_a.clone(), signer.clone()); + + let conn_id_on_b = open_try_connection_event.conn_id_on_b().clone(); + let proofs_height_on_b = self.ctx_b.latest_height(); + + let client_state_of_a_on_b = self + .ctx_b + .ibc_store() + .client_state(&client_id_on_b) + .unwrap(); + + let consensus_height_of_a_on_b = client_state_of_a_on_b.latest_height(); + + let proof_conn_end_on_b = self + .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 = self + .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 = self + .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: Version::compatibles()[0].clone(), + signer: signer.clone(), + proof_consensus_state_of_a: None, + })); + + self.ctx_a + .deliver(&mut self.router_a, msg_for_a) + .expect("success"); + + let Some(IbcEvent::OpenAckConnection(_)) = + self.ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + self.update_client_on_b(client_id_on_b.clone(), signer.clone()); + + let proof_height_on_a = self.ctx_a.latest_height(); + + let proof_conn_end_on_a = self + .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(), + })); + + self.ctx_b + .deliver(&mut self.router_b, msg_for_b) + .expect("success"); + + let Some(IbcEvent::OpenConfirmConnection(_)) = self.ctx_b.ibc_store().events.lock().last() + else { + panic!("unexpected event") + }; + self.update_client_on_a(client_id_on_a.clone(), signer.clone()); + + (conn_id_on_a, conn_id_on_b) + } +} + +pub fn integration_test_between_host_pair() +where + P: TestHost, + Q: TestHost, + HostClientState

: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + let ctx_a = MockContext::

: 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, MockRouter::default(), ctx_b, MockRouter::default()); - - assert_eq!( - relayer.get_ctx_a().latest_height(), - Height::new(0, 5).expect("no error") - ); - assert_eq!( - relayer.get_ctx_b().latest_height(), - Height::new(0, 5).expect("no error") - ); - - // client q on context p - let client_id_on_a = relayer.create_client_on_a(signer.clone()); - - let mut relayer = relayer.reverse(); - - // client p on context q - let client_id_on_b = relayer.create_client_on_a(signer.clone()); - - let mut relayer = relayer.reverse(); - - // asserts - - assert_eq!( - relayer.get_ctx_a().latest_height(), - Height::new(0, 6).expect("no error") - ); - assert_eq!( - relayer.get_ctx_b().latest_height(), - Height::new(0, 6).expect("no error") - ); - - // connection - let (_conn_id_on_a, _conn_id_on_b) = - relayer.create_connection_on_b(client_id_on_a, client_id_on_b, signer); - - // channel - - // channel/packet timeout -} - -#[test] -fn tendermint_integration_test() { - integration_test_between_host_pair::(); -} From 8a9cab4d6ac34ca2f2056e36643972cca7a77ae5 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 16:55:50 +0100 Subject: [PATCH 15/54] use typed relayer --- ibc-testkit/src/integration/mod.rs | 187 +++++++++++ ibc-testkit/src/integration/utils.rs | 460 +++++++++++++++++++++++++++ ibc-testkit/src/lib.rs | 1 + 3 files changed, 648 insertions(+) create mode 100644 ibc-testkit/src/integration/mod.rs create mode 100644 ibc-testkit/src/integration/utils.rs diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs new file mode 100644 index 000000000..f644a6447 --- /dev/null +++ b/ibc-testkit/src/integration/mod.rs @@ -0,0 +1,187 @@ +use ibc::core::client::context::client_state::ClientStateValidation; +use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; +use ibc::primitives::Signer; + +use self::utils::TypedRelayer; +use crate::context::MockContext; +use crate::fixtures::core::signer::dummy_account_id; +use crate::hosts::{HostClientState, TestHost}; +use crate::testapp::ibc::core::router::MockRouter; +use crate::testapp::ibc::core::types::DefaultIbcStore; + +pub mod utils; + +pub struct RelayerContext +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + ctx_a: MockContext, + router_a: MockRouter, + ctx_b: MockContext, + router_b: MockRouter, +} + +impl RelayerContext +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + pub fn new( + ctx_a: MockContext, + router_a: MockRouter, + ctx_b: MockContext, + router_b: MockRouter, + ) -> Self { + Self { + ctx_a, + router_a, + ctx_b, + router_b, + } + } + + pub fn get_ctx_a(&self) -> &MockContext { + &self.ctx_a + } + + pub fn get_ctx_b(&self) -> &MockContext { + &self.ctx_b + } + + pub fn get_ctx_a_mut(&mut self) -> &mut MockContext { + &mut self.ctx_a + } + + pub fn get_ctx_b_mut(&mut self) -> &mut MockContext { + &mut self.ctx_b + } + + pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { + TypedRelayer::::create_client_on_a( + &mut self.ctx_a, + &mut self.router_a, + &self.ctx_b, + signer, + ) + } + + pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId { + TypedRelayer::::create_client_on_a( + &mut self.ctx_b, + &mut self.router_b, + &self.ctx_a, + signer, + ) + } + + pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) { + TypedRelayer::::update_client_on_a_with_sync( + &mut self.ctx_a, + &mut self.router_a, + &mut self.ctx_b, + client_id_on_a, + signer, + ) + } + + pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) { + TypedRelayer::::update_client_on_a_with_sync( + &mut self.ctx_b, + &mut self.router_b, + &mut self.ctx_a, + client_id_on_b, + signer, + ) + } + + pub fn create_connection_on_a( + &mut self, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + TypedRelayer::::create_connection_on_a( + &mut self.ctx_a, + &mut self.router_a, + &mut self.ctx_b, + &mut self.router_b, + client_id_on_a, + client_id_on_b, + signer, + ) + } + + pub fn create_connection_on_b( + &mut self, + client_id_on_b: ClientId, + client_id_on_a: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + TypedRelayer::::create_connection_on_a( + &mut self.ctx_b, + &mut self.router_b, + &mut self.ctx_a, + &mut self.router_a, + client_id_on_a, + client_id_on_b, + signer, + ) + } +} + +pub fn integration_test_between_host_pair() +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, MockRouter::default(), ctx_b, MockRouter::default()); + + // 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_a, conn_id_on_b) = + relayer.create_connection_on_b(client_id_on_b, client_id_on_a, signer); + + assert_eq!(conn_id_on_a, ConnectionId::new(1)); + assert_eq!(conn_id_on_b, ConnectionId::new(1)); + + // channel + + // channel/packet timeout +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::hosts::TendermintHost; + + #[test] + fn tendermint_integration_test() { + integration_test_between_host_pair::(); + } +} diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs new file mode 100644 index 000000000..b693feea8 --- /dev/null +++ b/ibc-testkit/src/integration/utils.rs @@ -0,0 +1,460 @@ +use alloc::string::String; +use core::marker::PhantomData; +use std::time::Duration; + +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; +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::{ClientId, ConnectionId}; +use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath}; +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::router::MockRouter; +use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, LightClientState}; + +#[derive(Debug, Default)] +pub struct TypedRelayer(PhantomData, PhantomData) +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation; + +impl TypedRelayer +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + pub fn create_client_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + 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(router_a, 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 + } + + pub fn sync_latest_timestamp(ctx_a: &mut MockContext, ctx_b: &mut MockContext) { + if ctx_a.latest_timestamp() > ctx_b.latest_timestamp() { + while ctx_a.latest_timestamp() > ctx_b.latest_timestamp() { + ctx_b.advance_block(); + } + } else { + while ctx_b.latest_timestamp() > ctx_a.latest_timestamp() { + ctx_a.advance_block(); + } + } + } + + pub fn update_client_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &MockContext, + client_id_on_a: ClientId, + signer: Signer, + ) { + let latest_client_height_on_a = ctx_a + .ibc_store() + .get_client_validation_context() + .client_state(&client_id_on_a) + .expect("client state exists") + .latest_height(); + + let latest_height_of_b = ctx_b.latest_height(); + + let msg_for_a = MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { + client_id: client_id_on_a.clone(), + client_message: ctx_b + .host_block(&latest_height_of_b) + .expect("block exists") + .into_header_with_previous_block( + &ctx_b + .host_block(&latest_client_height_on_a) + .expect("block exists"), + ) + .into(), + signer, + })); + + ctx_a.deliver(router_a, msg_for_a).expect("success"); + + let Some(IbcEvent::UpdateClient(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn update_client_on_a_with_sync( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &mut MockContext, + client_id_on_a: ClientId, + signer: Signer, + ) { + TypedRelayer::::sync_latest_timestamp(ctx_a, ctx_b); + TypedRelayer::::update_client_on_a(ctx_a, router_a, ctx_b, client_id_on_a, signer); + } + + pub fn connection_open_init_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + 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(router_a, 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() + } + + pub fn connection_open_try_on_b( + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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: Version::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(router_b, 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() + } + + pub fn connection_open_ack_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + 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: Version::compatibles()[0].clone(), + signer: signer.clone(), + proof_consensus_state_of_a: None, + })); + + ctx_a.deliver(router_a, msg_for_a).expect("success"); + + let Some(IbcEvent::OpenAckConnection(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn connection_open_confirm_on_b( + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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(router_b, msg_for_b).expect("success"); + + let Some(IbcEvent::OpenConfirmConnection(_)) = ctx_b.ibc_store().events.lock().last() + else { + panic!("unexpected event") + }; + } + + pub fn create_connection_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + let conn_id_on_a = TypedRelayer::::connection_open_init_on_a( + ctx_a, + router_a, + ctx_b, + client_id_on_a.clone(), + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_b, + router_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + let conn_id_on_b = TypedRelayer::::connection_open_try_on_b( + ctx_b, + router_b, + ctx_a, + conn_id_on_a.clone(), + client_id_on_a.clone(), + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_a, + router_a, + ctx_b, + client_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayer::::connection_open_ack_on_a( + ctx_a, + router_a, + ctx_b, + conn_id_on_a.clone(), + conn_id_on_b.clone(), + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_b, + router_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayer::::connection_open_confirm_on_b( + ctx_b, + router_b, + ctx_a, + conn_id_on_b.clone(), + conn_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_a, + router_a, + ctx_b, + client_id_on_a, + signer, + ); + + (conn_id_on_a, conn_id_on_b) + } +} diff --git a/ibc-testkit/src/lib.rs b/ibc-testkit/src/lib.rs index f4326ba1d..7ae8d5182 100644 --- a/ibc-testkit/src/lib.rs +++ b/ibc-testkit/src/lib.rs @@ -18,6 +18,7 @@ extern crate std; pub mod context; pub mod fixtures; pub mod hosts; +pub mod integration; pub mod relayer; pub mod testapp; pub mod utils; From 18830165ca1b11aa4fe4e7773761e1da379b8a51 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 17:02:34 +0100 Subject: [PATCH 16/54] add todo for channel integration test --- ibc-testkit/src/integration/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index f644a6447..19df275ec 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -170,9 +170,9 @@ where assert_eq!(conn_id_on_a, ConnectionId::new(1)); assert_eq!(conn_id_on_b, ConnectionId::new(1)); - // channel - - // channel/packet timeout + // channel/packet integration; test timeouts + // TODO(rano): test channel and packet messages require a module + // create integration with ics-20 module } #[cfg(test)] From 3735db10f59fb94239edf1b646aa5065a487629f Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 17:04:16 +0100 Subject: [PATCH 17/54] core over std --- ibc-testkit/src/integration/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index b693feea8..f90ba35d1 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -1,6 +1,6 @@ use alloc::string::String; use core::marker::PhantomData; -use std::time::Duration; +use core::time::Duration; use ibc::core::client::context::client_state::ClientStateValidation; use ibc::core::client::context::ClientValidationContext; From 3b88e2984fcdf7a7a527d2352a2ad67b2b8245b3 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 17:09:08 +0100 Subject: [PATCH 18/54] be semantically correct --- ibc-testkit/src/integration/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 19df275ec..8512c376c 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -164,7 +164,7 @@ where assert_eq!(conn_id_on_b, ConnectionId::new(0)); // connection from B to A - let (conn_id_on_a, conn_id_on_b) = + let (conn_id_on_b, conn_id_on_a) = relayer.create_connection_on_b(client_id_on_b, client_id_on_a, signer); assert_eq!(conn_id_on_a, ConnectionId::new(1)); From 2f638b9458c180dc4c0b036c53615403bf4c49d4 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 17:09:20 +0100 Subject: [PATCH 19/54] add comment for TypedRelayer --- ibc-testkit/src/integration/utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index f90ba35d1..7275ef3f2 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -24,6 +24,9 @@ use crate::hosts::{HostClientState, TestBlock, TestHost}; use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, LightClientState}; +/// Implements relayer methods for a pair of hosts +/// Note that, all the implementations are in one direction, from A to B +/// For the methods in opposite direction, use `TypedRelayer::` instead of TypedRelayer::` #[derive(Debug, Default)] pub struct TypedRelayer(PhantomData, PhantomData) where From 27695c978c84bc3e27e62c9baa00c0d16ba4b8c3 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 17:18:25 +0100 Subject: [PATCH 20/54] integration test for all pairs --- ibc-testkit/src/integration/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 8512c376c..ae803a6f9 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -178,10 +178,13 @@ where #[cfg(test)] mod tests { use super::*; - use crate::hosts::TendermintHost; + use crate::hosts::{MockHost, TendermintHost}; #[test] - fn tendermint_integration_test() { + fn integration_test_for_all() { + integration_test_between_host_pair::(); + integration_test_between_host_pair::(); + integration_test_between_host_pair::(); integration_test_between_host_pair::(); } } From e370bcf48e21ce0d82ee934073ec9d42b1c24367 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 17:18:47 +0100 Subject: [PATCH 21/54] fix semantic bug --- ibc-testkit/src/integration/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index ae803a6f9..1b941c4eb 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -127,8 +127,8 @@ where &mut self.router_b, &mut self.ctx_a, &mut self.router_a, - client_id_on_a, client_id_on_b, + client_id_on_a, signer, ) } From 135188af958e8f020b18cb19ea3f14fec29f0fa7 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 17:21:12 +0100 Subject: [PATCH 22/54] renames --- ibc-testkit/src/integration/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 1b941c4eb..f72ee22d2 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -134,7 +134,7 @@ where } } -pub fn integration_test_between_host_pair() +pub fn ibc_integration_test() where A: TestHost, B: TestHost, @@ -181,10 +181,10 @@ mod tests { use crate::hosts::{MockHost, TendermintHost}; #[test] - fn integration_test_for_all() { - integration_test_between_host_pair::(); - integration_test_between_host_pair::(); - integration_test_between_host_pair::(); - integration_test_between_host_pair::(); + fn ibc_integration_test_for_all_pairs() { + ibc_integration_test::(); + ibc_integration_test::(); + ibc_integration_test::(); + ibc_integration_test::(); } } From 5739e23299a015fbe5e27a02cb10521e5dd4a553 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 25 Mar 2024 15:25:19 +0100 Subject: [PATCH 23/54] add channel management --- ibc-testkit/src/integration/utils.rs | 318 ++++++++++++++++++++++++++- 1 file changed, 313 insertions(+), 5 deletions(-) diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index 7275ef3f2..a8590a21d 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -2,6 +2,12 @@ use alloc::string::String; use core::marker::PhantomData; use core::time::Duration; +use ibc::core::channel::types::channel::Order; +use ibc::core::channel::types::msgs::{ + ChannelMsg, MsgChannelCloseConfirm, MsgChannelCloseInit, MsgChannelOpenAck, + MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, +}; +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}; @@ -9,12 +15,14 @@ use ibc::core::connection::types::msgs::{ ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit, MsgConnectionOpenTry, }; -use ibc::core::connection::types::version::Version; +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::{ClientId, ConnectionId}; -use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath}; +use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; +use ibc::core::host::types::path::{ + ChannelEndPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, +}; use ibc::core::host::ValidationContext; use ibc::primitives::Signer; use ibc_query::core::context::ProvableContext; @@ -244,7 +252,7 @@ where 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: Version::compatibles(), + versions_on_a: ConnectionVersion::compatibles(), proof_conn_end_on_a, proof_client_state_of_b_on_a, proof_consensus_state_of_b_on_a, @@ -330,7 +338,7 @@ where proof_consensus_state_of_a_on_b, proofs_height_on_b, consensus_height_of_a_on_b, - version: Version::compatibles()[0].clone(), + version: ConnectionVersion::compatibles()[0].clone(), signer: signer.clone(), proof_consensus_state_of_a: None, })); @@ -460,4 +468,304 @@ where (conn_id_on_a, conn_id_on_b) } + + pub fn channel_open_init_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + 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(router_a, 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() + } + + pub fn channel_open_try_on_b( + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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(router_b, 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() + } + + #[allow(clippy::too_many_arguments)] + pub fn channel_open_ack_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + 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(router_a, msg_for_a).expect("success"); + + let Some(IbcEvent::OpenAckChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn channel_open_confirm_on_b( + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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(router_b, msg_for_b).expect("success"); + + let Some(IbcEvent::OpenConfirmChannel(_)) = ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn channel_close_init_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + 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(router_a, msg_for_a).expect("success"); + + let Some(IbcEvent::CloseInitChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn channel_close_confirm_on_b( + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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(router_b, msg_for_b).expect("success"); + + let Some(IbcEvent::CloseConfirmChannel(_)) = + ctx_b.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + #[allow(clippy::too_many_arguments)] + pub fn create_channel_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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 = TypedRelayer::::channel_open_init_on_a( + ctx_a, + router_a, + conn_id_on_a.clone(), + port_id_on_a.clone(), + port_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_b, + router_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + let chan_id_on_b = TypedRelayer::::channel_open_try_on_b( + ctx_b, + router_b, + ctx_a, + conn_id_on_b.clone(), + chan_id_on_a.clone(), + port_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_a, + router_a, + ctx_b, + client_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayer::::channel_open_ack_on_a( + ctx_a, + router_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(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_b, + router_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + TypedRelayer::::channel_open_confirm_on_b( + ctx_b, + router_b, + ctx_a, + chan_id_on_a.clone(), + chan_id_on_b.clone(), + port_id_on_b, + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_a, + router_a, + ctx_b, + client_id_on_a, + signer, + ); + + (chan_id_on_a, chan_id_on_b) + } } From ccd4063970de877123974fc32357565a6e64c436 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 25 Mar 2024 15:25:57 +0100 Subject: [PATCH 24/54] channel creation in RelayerContext --- ibc-testkit/src/integration/mod.rs | 54 +++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index f72ee22d2..543d8cf5b 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -1,5 +1,5 @@ use ibc::core::client::context::client_state::ClientStateValidation; -use ibc::core::host::types::identifiers::{ClientId, ConnectionId}; +use ibc::core::host::types::identifiers::{ChannelId, ClientId, ConnectionId, PortId}; use ibc::primitives::Signer; use self::utils::TypedRelayer; @@ -132,6 +132,58 @@ where signer, ) } + + #[allow(clippy::too_many_arguments)] + pub fn create_channel_on_a( + &mut self, + 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) { + TypedRelayer::::create_channel_on_a( + &mut self.ctx_a, + &mut self.router_a, + &mut self.ctx_b, + &mut self.router_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, + ) + } + + #[allow(clippy::too_many_arguments)] + pub fn create_channel_on_b( + &mut self, + client_id_on_b: ClientId, + conn_id_on_b: ConnectionId, + port_id_on_b: PortId, + client_id_on_a: ClientId, + conn_id_on_a: ConnectionId, + port_id_on_a: PortId, + signer: Signer, + ) -> (ChannelId, ChannelId) { + TypedRelayer::::create_channel_on_a( + &mut self.ctx_b, + &mut self.router_b, + &mut self.ctx_a, + &mut self.router_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, + ) + } } pub fn ibc_integration_test() From b74263a0b0b632e9fada854a7206b24173877254 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 25 Mar 2024 15:26:18 +0100 Subject: [PATCH 25/54] add channel creation in integration test --- ibc-testkit/src/integration/mod.rs | 43 +++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 543d8cf5b..1e8362b1b 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -198,8 +198,12 @@ where let signer = dummy_account_id(); - let mut relayer = - RelayerContext::new(ctx_a, MockRouter::default(), ctx_b, MockRouter::default()); + let mut relayer = RelayerContext::new( + ctx_a, + MockRouter::new_with_transfer(), + ctx_b, + MockRouter::new_with_transfer(), + ); // client creation let client_id_on_a = relayer.create_client_on_a(signer.clone()); @@ -216,15 +220,40 @@ where 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, client_id_on_a, signer); + 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/packet integration; test timeouts - // TODO(rano): test channel and packet messages require a module - // create integration with ics-20 module + let (chan_end_on_a, chan_end_on_b) = relayer.create_channel_on_a( + client_id_on_a.clone(), + conn_id_on_a.clone(), + PortId::transfer(), + client_id_on_b.clone(), + conn_id_on_b.clone(), + PortId::transfer(), + signer.clone(), + ); + + assert_eq!(chan_end_on_a, ChannelId::new(0)); + assert_eq!(chan_end_on_b, ChannelId::new(0)); + + let (chan_end_on_b, chan_end_on_a) = relayer.create_channel_on_b( + client_id_on_b, + conn_id_on_b, + PortId::transfer(), + client_id_on_a, + conn_id_on_a, + PortId::transfer(), + signer, + ); + + assert_eq!(chan_end_on_a, ChannelId::new(1)); + assert_eq!(chan_end_on_b, ChannelId::new(1)); } #[cfg(test)] From aa99304576f5f36e6b3b3c60c93186c1e9e6298c Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 25 Mar 2024 15:47:40 +0100 Subject: [PATCH 26/54] add test for channel close --- ibc-testkit/src/integration/mod.rs | 84 +++++++++++++++++++++++++--- ibc-testkit/src/integration/utils.rs | 48 ++++++++++++++++ 2 files changed, 123 insertions(+), 9 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 1e8362b1b..0c7346422 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -184,6 +184,60 @@ where signer, ) } + + #[allow(clippy::too_many_arguments)] + pub fn close_channel_on_a( + &mut self, + 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, + ) { + TypedRelayer::::close_channel_on_a( + &mut self.ctx_a, + &mut self.router_a, + &mut self.ctx_b, + &mut self.router_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, + ) + } + + #[allow(clippy::too_many_arguments)] + pub fn close_channel_on_b( + &mut self, + client_id_on_b: ClientId, + chan_id_on_b: ChannelId, + port_id_on_b: PortId, + client_id_on_a: ClientId, + chan_id_on_a: ChannelId, + port_id_on_a: PortId, + + signer: Signer, + ) { + TypedRelayer::::close_channel_on_a( + &mut self.ctx_b, + &mut self.router_b, + &mut self.ctx_a, + &mut self.router_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, + ) + } } pub fn ibc_integration_test() @@ -229,7 +283,7 @@ where assert_eq!(conn_id_on_a, ConnectionId::new(1)); assert_eq!(conn_id_on_b, ConnectionId::new(1)); - let (chan_end_on_a, chan_end_on_b) = relayer.create_channel_on_a( + let (chan_id_on_a, chan_id_on_b) = relayer.create_channel_on_a( client_id_on_a.clone(), conn_id_on_a.clone(), PortId::transfer(), @@ -239,21 +293,33 @@ where signer.clone(), ); - assert_eq!(chan_end_on_a, ChannelId::new(0)); - assert_eq!(chan_end_on_b, ChannelId::new(0)); + assert_eq!(chan_id_on_a, ChannelId::new(0)); + assert_eq!(chan_id_on_b, ChannelId::new(0)); + + relayer.close_channel_on_a( + client_id_on_a.clone(), + chan_id_on_a.clone(), + PortId::transfer(), + client_id_on_b.clone(), + chan_id_on_b.clone(), + PortId::transfer(), + signer.clone(), + ); - let (chan_end_on_b, chan_end_on_a) = relayer.create_channel_on_b( - client_id_on_b, + let (chan_id_on_b, chan_id_on_a) = relayer.create_channel_on_b( + client_id_on_b.clone(), conn_id_on_b, PortId::transfer(), - client_id_on_a, + client_id_on_a.clone(), conn_id_on_a, PortId::transfer(), - signer, + signer.clone(), ); - assert_eq!(chan_end_on_a, ChannelId::new(1)); - assert_eq!(chan_end_on_b, ChannelId::new(1)); + assert_eq!(chan_id_on_a, ChannelId::new(1)); + assert_eq!(chan_id_on_b, ChannelId::new(1)); + + // TODO(rano): add steps for packets } #[cfg(test)] diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index a8590a21d..78db02a2b 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -768,4 +768,52 @@ where (chan_id_on_a, chan_id_on_b) } + + #[allow(clippy::too_many_arguments)] + pub fn close_channel_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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, + ) { + TypedRelayer::::channel_close_init_on_a( + ctx_a, + router_a, + chan_id_on_a.clone(), + port_id_on_a.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_b, + router_b, + ctx_a, + client_id_on_b, + signer.clone(), + ); + + TypedRelayer::::channel_close_confirm_on_b( + ctx_b, + router_b, + ctx_a, + chan_id_on_b, + port_id_on_b, + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_a, + router_a, + ctx_b, + client_id_on_a, + signer, + ); + } } From 186909462b5f1e557542ce8c15fd1d40f0d988dc Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Mon, 25 Mar 2024 19:29:25 +0100 Subject: [PATCH 27/54] query client_id from connection and channel --- ibc-testkit/src/integration/mod.rs | 113 +++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 15 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 0c7346422..18cfd2c48 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -1,5 +1,7 @@ use ibc::core::client::context::client_state::ClientStateValidation; 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 self::utils::TypedRelayer; @@ -133,17 +135,30 @@ where ) } - #[allow(clippy::too_many_arguments)] pub fn create_channel_on_a( &mut self, - 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 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(); + TypedRelayer::::create_channel_on_a( &mut self.ctx_a, &mut self.router_a, @@ -162,14 +177,28 @@ where #[allow(clippy::too_many_arguments)] pub fn create_channel_on_b( &mut self, - client_id_on_b: ClientId, conn_id_on_b: ConnectionId, port_id_on_b: PortId, - client_id_on_a: ClientId, 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(); + TypedRelayer::::create_channel_on_a( &mut self.ctx_b, &mut self.router_b, @@ -188,15 +217,45 @@ where #[allow(clippy::too_many_arguments)] pub fn close_channel_on_a( &mut self, - 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, ) { + 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(); + TypedRelayer::::close_channel_on_a( &mut self.ctx_a, &mut self.router_a, @@ -215,15 +274,45 @@ where #[allow(clippy::too_many_arguments)] pub fn close_channel_on_b( &mut self, - client_id_on_b: ClientId, chan_id_on_b: ChannelId, port_id_on_b: PortId, - client_id_on_a: ClientId, 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(); + TypedRelayer::::close_channel_on_a( &mut self.ctx_b, &mut self.router_b, @@ -284,10 +373,8 @@ where assert_eq!(conn_id_on_b, ConnectionId::new(1)); let (chan_id_on_a, chan_id_on_b) = relayer.create_channel_on_a( - client_id_on_a.clone(), conn_id_on_a.clone(), PortId::transfer(), - client_id_on_b.clone(), conn_id_on_b.clone(), PortId::transfer(), signer.clone(), @@ -297,20 +384,16 @@ where assert_eq!(chan_id_on_b, ChannelId::new(0)); relayer.close_channel_on_a( - client_id_on_a.clone(), chan_id_on_a.clone(), PortId::transfer(), - client_id_on_b.clone(), chan_id_on_b.clone(), PortId::transfer(), signer.clone(), ); let (chan_id_on_b, chan_id_on_a) = relayer.create_channel_on_b( - client_id_on_b.clone(), conn_id_on_b, PortId::transfer(), - client_id_on_a.clone(), conn_id_on_a, PortId::transfer(), signer.clone(), From c67cf6225bc885f4b5977223ef1b74fa2ac71050 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 26 Mar 2024 13:56:15 +0100 Subject: [PATCH 28/54] ibc_store_mut --- ibc-testkit/src/context.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index d41b6e425..4c8ba5e94 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -76,6 +76,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) } From 2b9ea761a7d63e6bddfdc68185cad83ddd337b1c Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 26 Mar 2024 13:57:01 +0100 Subject: [PATCH 29/54] utils functions for packet relay --- ibc-testkit/src/integration/utils.rs | 239 ++++++++++++++++++++++++++- 1 file changed, 236 insertions(+), 3 deletions(-) diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index 78db02a2b..76200c1df 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -2,11 +2,14 @@ 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, MsgChannelCloseConfirm, MsgChannelCloseInit, MsgChannelOpenAck, - MsgChannelOpenConfirm, MsgChannelOpenInit, MsgChannelOpenTry, + 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; @@ -21,7 +24,8 @@ 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::{ - ChannelEndPath, ClientConsensusStatePath, ClientStatePath, ConnectionPath, + AckPath, ChannelEndPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, + ConnectionPath, ReceiptPath, SeqRecvPath, }; use ibc::core::host::ValidationContext; use ibc::primitives::Signer; @@ -816,4 +820,233 @@ where signer, ); } + + pub fn packet_recv_on_b( + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + 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(router_b, 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() + } + + #[allow(clippy::too_many_arguments)] + pub fn packet_ack_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &MockContext, + packet: Packet, + acknowledgement: Acknowledgement, + signer: Signer, + ) { + let proof_height_on_b = ctx_b.latest_height(); + + // let sequence = ctx_b + // .ibc_store() + // .get_next_sequence_ack(&SeqAckPath::new(&port_id_on_b, &chan_id_on_b)) + // .expect("sequence exists"); + + 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("connection end 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(router_a, msg_for_a).expect("success"); + + let Some(IbcEvent::AcknowledgePacket(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn packet_timeout_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &MockContext, + packet: Packet, + signer: Signer, + ) { + let proof_height_on_b = ctx_b.latest_height(); + + // let next_seq_recv_on_b = ctx_b + // .ibc_store() + // .get_next_sequence_recv(&SeqRecvPath::new(&port_id_on_b, &chan_id_on_b)) + // .expect("sequence exists"); + + 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("connection end 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(router_a, msg_for_a).expect("success"); + + let Some(IbcEvent::TimeoutPacket(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + pub fn packet_timeout_on_close_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + 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 next_seq_recv_on_b = ctx_b + .ibc_store() + .get_next_sequence_recv(&SeqRecvPath::new(&port_id_on_b, &chan_id_on_b)) + .expect("sequence exists"); + + 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, next_seq_recv_on_b).into(), + ) + .expect("connection end exists") + .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("connection end exists") + .try_into() + .expect("value merkle proof"); + + let msg_for_a = MsgEnvelope::Packet(PacketMsg::TimeoutOnClose(MsgTimeoutOnClose { + packet, + next_seq_recv_on_b, + proof_unreceived_on_b, + proof_close_on_b, + proof_height_on_b, + signer, + })); + + ctx_a.deliver(router_a, msg_for_a).expect("success"); + + let Some(IbcEvent::ChannelClosed(_)) = ctx_a.ibc_store().events.lock().last().cloned() + else { + panic!("unexpected event") + }; + } + + #[allow(clippy::too_many_arguments)] + pub fn send_packet_on_a( + ctx_a: &mut MockContext, + router_a: &mut MockRouter, + ctx_b: &mut MockContext, + router_b: &mut MockRouter, + packet: Packet, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) { + // packet is passed from module + + TypedRelayer::::update_client_on_a_with_sync( + ctx_b, + router_b, + ctx_a, + client_id_on_b.clone(), + signer.clone(), + ); + + let acknowledgement = TypedRelayer::::packet_recv_on_b( + ctx_b, + router_b, + ctx_a, + packet.clone(), + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_a, + router_a, + ctx_b, + client_id_on_a, + signer.clone(), + ); + + TypedRelayer::::packet_ack_on_a( + ctx_a, + router_a, + ctx_b, + packet, + acknowledgement, + signer.clone(), + ); + + TypedRelayer::::update_client_on_a_with_sync( + ctx_b, + router_b, + ctx_a, + client_id_on_b, + signer.clone(), + ); + } } From acbb3bd6b5423d21e7c449c3c3cff014e5336f61 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 26 Mar 2024 13:57:21 +0100 Subject: [PATCH 30/54] add packet relay integration test --- ibc-testkit/src/integration/mod.rs | 116 ++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 18cfd2c48..3de1e6eb1 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -1,13 +1,23 @@ +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, ClientId, ConnectionId, PortId}; use ibc::core::host::types::path::ChannelEndPath; use ibc::core::host::ValidationContext; -use ibc::primitives::Signer; +use ibc::primitives::{Signer, Timestamp}; use self::utils::TypedRelayer; use crate::context::MockContext; use crate::fixtures::core::signer::dummy_account_id; use crate::hosts::{HostClientState, TestHost}; +use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::DefaultIbcStore; @@ -327,6 +337,57 @@ where signer, ) } + + 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(); + + TypedRelayer::::send_packet_on_a( + &mut self.ctx_a, + &mut self.router_a, + &mut self.ctx_b, + &mut self.router_b, + packet, + client_id_on_a, + client_id_on_b, + signer, + ) + } } pub fn ibc_integration_test() @@ -402,7 +463,58 @@ where assert_eq!(chan_id_on_a, ChannelId::new(1)); assert_eq!(chan_id_on_b, ChannelId::new(1)); - // TODO(rano): add steps for packets + // module packets + + let packet_data = PacketData { + token: PrefixedCoin::from_str("1000uibc").expect("valid prefixed coin"), + sender: signer.clone(), + receiver: signer.clone(), + memo: "sample memo".into(), + }; + + 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(), + }; + + 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(); + + let Some(IbcEvent::SendPacket(send_packet_event)) = relayer + .get_ctx_a() + .ibc_store() + .events + .lock() + .iter() + .rev() + .nth(2) + .cloned() + else { + panic!("unexpected event") + }; + + 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(), + }; + + relayer.send_packet_on_a(packet, signer); } #[cfg(test)] From 555a4b592be327fa88d5ba7cbaa4f9951fd94a71 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 26 Mar 2024 14:00:52 +0100 Subject: [PATCH 31/54] add comments --- ibc-testkit/src/integration/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 3de1e6eb1..3442865ed 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -433,6 +433,7 @@ where 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(), @@ -444,6 +445,7 @@ where 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(), @@ -452,6 +454,7 @@ where 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(), @@ -463,8 +466,9 @@ where assert_eq!(chan_id_on_a, ChannelId::new(1)); assert_eq!(chan_id_on_b, ChannelId::new(1)); - // module packets + // 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(), @@ -472,6 +476,7 @@ where 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(), @@ -480,6 +485,7 @@ where timeout_timestamp_on_b: Timestamp::none(), }; + // module creates the send_packet send_transfer( relayer.get_ctx_a_mut().ibc_store_mut(), &mut DummyTransferModule, @@ -490,6 +496,7 @@ where // 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() @@ -503,6 +510,7 @@ where 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(), @@ -514,6 +522,7 @@ where 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); } From 6f4d13b04ba677cbba8f8573df77479ff8a2d031 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 26 Mar 2024 14:06:55 +0100 Subject: [PATCH 32/54] optimize integration utils functions --- ibc-testkit/src/integration/utils.rs | 29 +++++++--------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index 76200c1df..10b196de7 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -25,7 +25,7 @@ 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, SeqRecvPath, + ConnectionPath, ReceiptPath, }; use ibc::core::host::ValidationContext; use ibc::primitives::Signer; @@ -870,18 +870,13 @@ where ) { let proof_height_on_b = ctx_b.latest_height(); - // let sequence = ctx_b - // .ibc_store() - // .get_next_sequence_ack(&SeqAckPath::new(&port_id_on_b, &chan_id_on_b)) - // .expect("sequence exists"); - 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("connection end exists") + .expect("acknowledgement proof exists") .try_into() .expect("value merkle proof"); @@ -910,11 +905,6 @@ where ) { let proof_height_on_b = ctx_b.latest_height(); - // let next_seq_recv_on_b = ctx_b - // .ibc_store() - // .get_next_sequence_recv(&SeqRecvPath::new(&port_id_on_b, &chan_id_on_b)) - // .expect("sequence exists"); - let proof_unreceived_on_b = ctx_b .ibc_store() .get_proof( @@ -922,7 +912,7 @@ where &ReceiptPath::new(&packet.port_id_on_b, &packet.chan_id_on_b, packet.seq_on_a) .into(), ) - .expect("connection end exists") + .expect("non-membership receipt proof exists") .try_into() .expect("value merkle proof"); @@ -953,18 +943,13 @@ where ) { let proof_height_on_b = ctx_b.latest_height(); - let next_seq_recv_on_b = ctx_b - .ibc_store() - .get_next_sequence_recv(&SeqRecvPath::new(&port_id_on_b, &chan_id_on_b)) - .expect("sequence exists"); - 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, next_seq_recv_on_b).into(), + &ReceiptPath::new(&port_id_on_b, &chan_id_on_b, packet.seq_on_a).into(), ) - .expect("connection end exists") + .expect("non-membership receipt proof") .try_into() .expect("value merkle proof"); @@ -974,13 +959,13 @@ where proof_height_on_b, &ChannelEndPath::new(&port_id_on_b, &chan_id_on_b).into(), ) - .expect("connection end exists") + .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, - next_seq_recv_on_b, proof_unreceived_on_b, proof_close_on_b, proof_height_on_b, From a8b7a98a9d2db7b080ce18eac5fdaeb1dee31a79 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 26 Mar 2024 14:32:51 +0100 Subject: [PATCH 33/54] serde feature for integration tests --- ibc-testkit/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ibc-testkit/src/lib.rs b/ibc-testkit/src/lib.rs index 7ae8d5182..68d3157db 100644 --- a/ibc-testkit/src/lib.rs +++ b/ibc-testkit/src/lib.rs @@ -18,7 +18,10 @@ extern crate std; pub mod context; pub mod fixtures; pub mod hosts; -pub mod integration; pub mod relayer; pub mod testapp; pub mod utils; + +// `ibc::apps::transfer::handler::send_transfer` requires `serde` +#[cfg(feature = "serde")] +pub mod integration; From 393462d5292a61810113ddcc7a13f584db814613 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 29 Mar 2024 13:50:29 +0100 Subject: [PATCH 34/54] rm redundant chain_id --- ibc-testkit/tests/core/ics02_client/create_client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ibc-testkit/tests/core/ics02_client/create_client.rs b/ibc-testkit/tests/core/ics02_client/create_client.rs index 4f37ee75d..11d4a8303 100644 --- a/ibc-testkit/tests/core/ics02_client/create_client.rs +++ b/ibc-testkit/tests/core/ics02_client/create_client.rs @@ -11,7 +11,7 @@ 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::{ChainId, ClientId}; +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; @@ -138,7 +138,6 @@ fn test_tm_create_client_proof_verification_ok() { let client_height = Height::new(0, 10).expect("no error"); let ctx_tm = MockContextConfig::builder() - .host_id(ChainId::new("tendermint-0").unwrap()) .latest_height(client_height) .build::>(); From 98a4b7f8c6eb0bdb95fbceaf3d0e23388a661198 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 29 Mar 2024 14:08:08 +0100 Subject: [PATCH 35/54] sync clock only on a --- ibc-testkit/src/integration/mod.rs | 30 +++++------ ibc-testkit/src/integration/utils.rs | 80 ++++++++++++++-------------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 3442865ed..11673b48b 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -13,7 +13,7 @@ use ibc::core::host::types::path::ChannelEndPath; use ibc::core::host::ValidationContext; use ibc::primitives::{Signer, Timestamp}; -use self::utils::TypedRelayer; +use self::utils::TypedIntegration; use crate::context::MockContext; use crate::fixtures::core::signer::dummy_account_id; use crate::hosts::{HostClientState, TestHost}; @@ -23,7 +23,7 @@ use crate::testapp::ibc::core::types::DefaultIbcStore; pub mod utils; -pub struct RelayerContext +pub struct IntegrationContext where A: TestHost, B: TestHost, @@ -36,7 +36,7 @@ where router_b: MockRouter, } -impl RelayerContext +impl IntegrationContext where A: TestHost, B: TestHost, @@ -74,7 +74,7 @@ where } pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { - TypedRelayer::::create_client_on_a( + TypedIntegration::::create_client_on_a( &mut self.ctx_a, &mut self.router_a, &self.ctx_b, @@ -83,7 +83,7 @@ where } pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId { - TypedRelayer::::create_client_on_a( + TypedIntegration::::create_client_on_a( &mut self.ctx_b, &mut self.router_b, &self.ctx_a, @@ -92,7 +92,7 @@ where } pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) { - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( &mut self.ctx_a, &mut self.router_a, &mut self.ctx_b, @@ -102,7 +102,7 @@ where } pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) { - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( &mut self.ctx_b, &mut self.router_b, &mut self.ctx_a, @@ -117,7 +117,7 @@ where client_id_on_b: ClientId, signer: Signer, ) -> (ConnectionId, ConnectionId) { - TypedRelayer::::create_connection_on_a( + TypedIntegration::::create_connection_on_a( &mut self.ctx_a, &mut self.router_a, &mut self.ctx_b, @@ -134,7 +134,7 @@ where client_id_on_a: ClientId, signer: Signer, ) -> (ConnectionId, ConnectionId) { - TypedRelayer::::create_connection_on_a( + TypedIntegration::::create_connection_on_a( &mut self.ctx_b, &mut self.router_b, &mut self.ctx_a, @@ -169,7 +169,7 @@ where .client_id() .clone(); - TypedRelayer::::create_channel_on_a( + TypedIntegration::::create_channel_on_a( &mut self.ctx_a, &mut self.router_a, &mut self.ctx_b, @@ -209,7 +209,7 @@ where .client_id() .clone(); - TypedRelayer::::create_channel_on_a( + TypedIntegration::::create_channel_on_a( &mut self.ctx_b, &mut self.router_b, &mut self.ctx_a, @@ -266,7 +266,7 @@ where .client_id() .clone(); - TypedRelayer::::close_channel_on_a( + TypedIntegration::::close_channel_on_a( &mut self.ctx_a, &mut self.router_a, &mut self.ctx_b, @@ -323,7 +323,7 @@ where .client_id() .clone(); - TypedRelayer::::close_channel_on_a( + TypedIntegration::::close_channel_on_a( &mut self.ctx_b, &mut self.router_b, &mut self.ctx_a, @@ -377,7 +377,7 @@ where .client_id() .clone(); - TypedRelayer::::send_packet_on_a( + TypedIntegration::::send_packet_on_a( &mut self.ctx_a, &mut self.router_a, &mut self.ctx_b, @@ -402,7 +402,7 @@ where let signer = dummy_account_id(); - let mut relayer = RelayerContext::new( + let mut relayer = IntegrationContext::new( ctx_a, MockRouter::new_with_transfer(), ctx_b, diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index 10b196de7..1f6d9c7eb 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -38,16 +38,16 @@ use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, Ligh /// Implements relayer methods for a pair of hosts /// Note that, all the implementations are in one direction, from A to B -/// For the methods in opposite direction, use `TypedRelayer::` instead of TypedRelayer::` +/// For the methods in opposite direction, use `TypedIntegration::` instead of TypedIntegration::` #[derive(Debug, Default)] -pub struct TypedRelayer(PhantomData, PhantomData) +pub struct TypedIntegration(PhantomData, PhantomData) where A: TestHost, B: TestHost, HostClientState: ClientStateValidation, HostClientState: ClientStateValidation; -impl TypedRelayer +impl TypedIntegration where A: TestHost, B: TestHost, @@ -98,15 +98,9 @@ where client_id_on_a } - pub fn sync_latest_timestamp(ctx_a: &mut MockContext, ctx_b: &mut MockContext) { - if ctx_a.latest_timestamp() > ctx_b.latest_timestamp() { - while ctx_a.latest_timestamp() > ctx_b.latest_timestamp() { - ctx_b.advance_block(); - } - } else { - while ctx_b.latest_timestamp() > ctx_a.latest_timestamp() { - ctx_a.advance_block(); - } + 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(); } } @@ -155,8 +149,16 @@ where client_id_on_a: ClientId, signer: Signer, ) { - TypedRelayer::::sync_latest_timestamp(ctx_a, ctx_b); - TypedRelayer::::update_client_on_a(ctx_a, router_a, ctx_b, client_id_on_a, signer); + // this is required, as IBC doesn't allow client update + // from future beyond max clock drift + TypedIntegration::::sync_clock_on_a(ctx_a, ctx_b); + TypedIntegration::::update_client_on_a( + ctx_a, + router_a, + ctx_b, + client_id_on_a, + signer, + ); } pub fn connection_open_init_on_a( @@ -400,7 +402,7 @@ where client_id_on_b: ClientId, signer: Signer, ) -> (ConnectionId, ConnectionId) { - let conn_id_on_a = TypedRelayer::::connection_open_init_on_a( + let conn_id_on_a = TypedIntegration::::connection_open_init_on_a( ctx_a, router_a, ctx_b, @@ -409,7 +411,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_b, router_b, ctx_a, @@ -417,7 +419,7 @@ where signer.clone(), ); - let conn_id_on_b = TypedRelayer::::connection_open_try_on_b( + let conn_id_on_b = TypedIntegration::::connection_open_try_on_b( ctx_b, router_b, ctx_a, @@ -427,7 +429,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_a, router_a, ctx_b, @@ -435,7 +437,7 @@ where signer.clone(), ); - TypedRelayer::::connection_open_ack_on_a( + TypedIntegration::::connection_open_ack_on_a( ctx_a, router_a, ctx_b, @@ -445,7 +447,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_b, router_b, ctx_a, @@ -453,7 +455,7 @@ where signer.clone(), ); - TypedRelayer::::connection_open_confirm_on_b( + TypedIntegration::::connection_open_confirm_on_b( ctx_b, router_b, ctx_a, @@ -462,7 +464,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_a, router_a, ctx_b, @@ -698,7 +700,7 @@ where port_id_on_b: PortId, signer: Signer, ) -> (ChannelId, ChannelId) { - let chan_id_on_a = TypedRelayer::::channel_open_init_on_a( + let chan_id_on_a = TypedIntegration::::channel_open_init_on_a( ctx_a, router_a, conn_id_on_a.clone(), @@ -707,7 +709,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_b, router_b, ctx_a, @@ -715,7 +717,7 @@ where signer.clone(), ); - let chan_id_on_b = TypedRelayer::::channel_open_try_on_b( + let chan_id_on_b = TypedIntegration::::channel_open_try_on_b( ctx_b, router_b, ctx_a, @@ -725,7 +727,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_a, router_a, ctx_b, @@ -733,7 +735,7 @@ where signer.clone(), ); - TypedRelayer::::channel_open_ack_on_a( + TypedIntegration::::channel_open_ack_on_a( ctx_a, router_a, ctx_b, @@ -744,7 +746,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_b, router_b, ctx_a, @@ -752,7 +754,7 @@ where signer.clone(), ); - TypedRelayer::::channel_open_confirm_on_b( + TypedIntegration::::channel_open_confirm_on_b( ctx_b, router_b, ctx_a, @@ -762,7 +764,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_a, router_a, ctx_b, @@ -787,7 +789,7 @@ where port_id_on_b: PortId, signer: Signer, ) { - TypedRelayer::::channel_close_init_on_a( + TypedIntegration::::channel_close_init_on_a( ctx_a, router_a, chan_id_on_a.clone(), @@ -795,7 +797,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_b, router_b, ctx_a, @@ -803,7 +805,7 @@ where signer.clone(), ); - TypedRelayer::::channel_close_confirm_on_b( + TypedIntegration::::channel_close_confirm_on_b( ctx_b, router_b, ctx_a, @@ -812,7 +814,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_a, router_a, ctx_b, @@ -993,7 +995,7 @@ where ) { // packet is passed from module - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_b, router_b, ctx_a, @@ -1001,7 +1003,7 @@ where signer.clone(), ); - let acknowledgement = TypedRelayer::::packet_recv_on_b( + let acknowledgement = TypedIntegration::::packet_recv_on_b( ctx_b, router_b, ctx_a, @@ -1009,7 +1011,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_a, router_a, ctx_b, @@ -1017,7 +1019,7 @@ where signer.clone(), ); - TypedRelayer::::packet_ack_on_a( + TypedIntegration::::packet_ack_on_a( ctx_a, router_a, ctx_b, @@ -1026,7 +1028,7 @@ where signer.clone(), ); - TypedRelayer::::update_client_on_a_with_sync( + TypedIntegration::::update_client_on_a_with_sync( ctx_b, router_b, ctx_a, From fd8e08b1a737826a64a17440f4cf6894f3a9052e Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 29 Mar 2024 15:47:50 +0100 Subject: [PATCH 36/54] add comment --- 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 60e437bf3..c5ee5b856 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -138,6 +138,8 @@ impl TestBlock for TendermintBlock { } fn into_header_with_previous_block(self, previous_block: &Self) -> Self::Header { + // this method is used to inject the trusted height and validators set + // given the previous_block is trusted, Tendermint light client validates the current (untrusted) block let mut header = TendermintHeader::from(self); header.set_trusted_height(previous_block.height()); header.set_trusted_next_validators_set(previous_block.inner().validators.clone()); From 3e3c9accc0d8a31c9ba8bef3b4a0d116560a2a47 Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Fri, 29 Mar 2024 10:06:19 -0700 Subject: [PATCH 37/54] imp: place router under MockGenericContext --- ibc-testkit/src/context.rs | 13 ++- ibc-testkit/src/fixtures/core/context.rs | 2 + ibc-testkit/src/integration/mod.rs | 54 +---------- ibc-testkit/src/integration/utils.rs | 91 ++++--------------- ibc-testkit/src/relayer/context.rs | 11 +-- .../src/testapp/ibc/core/router/types.rs | 2 +- 6 files changed, 33 insertions(+), 140 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 4c8ba5e94..70ce684aa 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -18,7 +18,6 @@ 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; @@ -27,6 +26,7 @@ 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. @@ -43,6 +43,8 @@ where /// The type of host chain underlying this mock context. pub host: H, + pub router: MockRouter, + /// An object that stores all IBC related data. pub ibc_store: MockIbcStore, } @@ -382,12 +384,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.router, msg) + .map_err(RelayerError::TransactionFailed)?; // Create a new block. self.advance_block(); Ok(()) diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index ec9a1d294..683500f57 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; @@ -61,6 +62,7 @@ where params.latest_height.revision_number(), Default::default(), ), + router: MockRouter::new_with_transfer(), host: params.host, }; diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 11673b48b..094a4f7ef 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -18,7 +18,6 @@ use crate::context::MockContext; use crate::fixtures::core::signer::dummy_account_id; use crate::hosts::{HostClientState, TestHost}; use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; -use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::DefaultIbcStore; pub mod utils; @@ -31,9 +30,7 @@ where HostClientState: ClientStateValidation, { ctx_a: MockContext, - router_a: MockRouter, ctx_b: MockContext, - router_b: MockRouter, } impl IntegrationContext @@ -43,18 +40,8 @@ where HostClientState: ClientStateValidation, HostClientState: ClientStateValidation, { - pub fn new( - ctx_a: MockContext, - router_a: MockRouter, - ctx_b: MockContext, - router_b: MockRouter, - ) -> Self { - Self { - ctx_a, - router_a, - ctx_b, - router_b, - } + pub fn new(ctx_a: MockContext, ctx_b: MockContext) -> Self { + Self { ctx_a, ctx_b } } pub fn get_ctx_a(&self) -> &MockContext { @@ -74,27 +61,16 @@ where } pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { - TypedIntegration::::create_client_on_a( - &mut self.ctx_a, - &mut self.router_a, - &self.ctx_b, - signer, - ) + TypedIntegration::::create_client_on_a(&mut self.ctx_a, &self.ctx_b, signer) } pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId { - TypedIntegration::::create_client_on_a( - &mut self.ctx_b, - &mut self.router_b, - &self.ctx_a, - signer, - ) + TypedIntegration::::create_client_on_a(&mut self.ctx_b, &self.ctx_a, signer) } pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) { TypedIntegration::::update_client_on_a_with_sync( &mut self.ctx_a, - &mut self.router_a, &mut self.ctx_b, client_id_on_a, signer, @@ -104,7 +80,6 @@ where pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) { TypedIntegration::::update_client_on_a_with_sync( &mut self.ctx_b, - &mut self.router_b, &mut self.ctx_a, client_id_on_b, signer, @@ -119,9 +94,7 @@ where ) -> (ConnectionId, ConnectionId) { TypedIntegration::::create_connection_on_a( &mut self.ctx_a, - &mut self.router_a, &mut self.ctx_b, - &mut self.router_b, client_id_on_a, client_id_on_b, signer, @@ -136,9 +109,7 @@ where ) -> (ConnectionId, ConnectionId) { TypedIntegration::::create_connection_on_a( &mut self.ctx_b, - &mut self.router_b, &mut self.ctx_a, - &mut self.router_a, client_id_on_b, client_id_on_a, signer, @@ -171,9 +142,7 @@ where TypedIntegration::::create_channel_on_a( &mut self.ctx_a, - &mut self.router_a, &mut self.ctx_b, - &mut self.router_b, client_id_on_a, conn_id_on_a, port_id_on_a, @@ -211,9 +180,7 @@ where TypedIntegration::::create_channel_on_a( &mut self.ctx_b, - &mut self.router_b, &mut self.ctx_a, - &mut self.router_a, client_id_on_b, conn_id_on_b, port_id_on_b, @@ -268,9 +235,7 @@ where TypedIntegration::::close_channel_on_a( &mut self.ctx_a, - &mut self.router_a, &mut self.ctx_b, - &mut self.router_b, client_id_on_a, chan_id_on_a, port_id_on_a, @@ -325,9 +290,7 @@ where TypedIntegration::::close_channel_on_a( &mut self.ctx_b, - &mut self.router_b, &mut self.ctx_a, - &mut self.router_a, client_id_on_b, chan_id_on_b, port_id_on_b, @@ -379,9 +342,7 @@ where TypedIntegration::::send_packet_on_a( &mut self.ctx_a, - &mut self.router_a, &mut self.ctx_b, - &mut self.router_b, packet, client_id_on_a, client_id_on_b, @@ -402,12 +363,7 @@ where let signer = dummy_account_id(); - let mut relayer = IntegrationContext::new( - ctx_a, - MockRouter::new_with_transfer(), - ctx_b, - MockRouter::new_with_transfer(), - ); + let mut relayer = IntegrationContext::new(ctx_a, ctx_b); // client creation let client_id_on_a = relayer.create_client_on_a(signer.clone()); diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index 1f6d9c7eb..bc46c2e4a 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -33,7 +33,6 @@ use ibc_query::core::context::ProvableContext; use crate::context::MockContext; use crate::hosts::{HostClientState, TestBlock, TestHost}; -use crate::testapp::ibc::core::router::MockRouter; use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, LightClientState}; /// Implements relayer methods for a pair of hosts @@ -56,7 +55,6 @@ where { pub fn create_client_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, signer: Signer, ) -> ClientId { @@ -77,7 +75,7 @@ where signer, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::CreateClient(create_client_b_event)) = ctx_a.ibc_store().events.lock().last().cloned() @@ -106,7 +104,6 @@ where pub fn update_client_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, client_id_on_a: ClientId, signer: Signer, @@ -134,7 +131,7 @@ where signer, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::UpdateClient(_)) = ctx_a.ibc_store().events.lock().last().cloned() else { @@ -144,7 +141,6 @@ where pub fn update_client_on_a_with_sync( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &mut MockContext, client_id_on_a: ClientId, signer: Signer, @@ -152,18 +148,11 @@ where // this is required, as IBC doesn't allow client update // from future beyond max clock drift TypedIntegration::::sync_clock_on_a(ctx_a, ctx_b); - TypedIntegration::::update_client_on_a( - ctx_a, - router_a, - ctx_b, - client_id_on_a, - signer, - ); + TypedIntegration::::update_client_on_a(ctx_a, ctx_b, client_id_on_a, signer); } pub fn connection_open_init_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, client_id_on_a: ClientId, client_id_on_b: ClientId, @@ -183,7 +172,7 @@ where signer: signer.clone(), })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::OpenInitConnection(open_init_connection_event)) = ctx_a.ibc_store().events.lock().last().cloned() @@ -196,7 +185,6 @@ where pub fn connection_open_try_on_b( ctx_b: &mut MockContext, - router_b: &mut MockRouter, ctx_a: &MockContext, conn_id_on_a: ConnectionId, client_id_on_a: ClientId, @@ -271,7 +259,7 @@ where previous_connection_id: String::new(), })); - ctx_b.deliver(router_b, msg_for_b).expect("success"); + ctx_b.deliver(msg_for_b).expect("success"); let Some(IbcEvent::OpenTryConnection(open_try_connection_event)) = ctx_b.ibc_store().events.lock().last().cloned() @@ -284,7 +272,6 @@ where pub fn connection_open_ack_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, conn_id_on_a: ConnectionId, conn_id_on_b: ConnectionId, @@ -349,7 +336,7 @@ where proof_consensus_state_of_a: None, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::OpenAckConnection(_)) = ctx_a.ibc_store().events.lock().last().cloned() else { @@ -359,7 +346,6 @@ where pub fn connection_open_confirm_on_b( ctx_b: &mut MockContext, - router_b: &mut MockRouter, ctx_a: &MockContext, conn_id_on_a: ConnectionId, conn_id_on_b: ConnectionId, @@ -385,7 +371,7 @@ where signer: signer.clone(), })); - ctx_b.deliver(router_b, msg_for_b).expect("success"); + ctx_b.deliver(msg_for_b).expect("success"); let Some(IbcEvent::OpenConfirmConnection(_)) = ctx_b.ibc_store().events.lock().last() else { @@ -395,16 +381,13 @@ where pub fn create_connection_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &mut MockContext, - router_b: &mut MockRouter, client_id_on_a: ClientId, client_id_on_b: ClientId, signer: Signer, ) -> (ConnectionId, ConnectionId) { let conn_id_on_a = TypedIntegration::::connection_open_init_on_a( ctx_a, - router_a, ctx_b, client_id_on_a.clone(), client_id_on_b.clone(), @@ -413,7 +396,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_b, - router_b, ctx_a, client_id_on_b.clone(), signer.clone(), @@ -421,7 +403,6 @@ where let conn_id_on_b = TypedIntegration::::connection_open_try_on_b( ctx_b, - router_b, ctx_a, conn_id_on_a.clone(), client_id_on_a.clone(), @@ -431,7 +412,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_a, - router_a, ctx_b, client_id_on_a.clone(), signer.clone(), @@ -439,7 +419,6 @@ where TypedIntegration::::connection_open_ack_on_a( ctx_a, - router_a, ctx_b, conn_id_on_a.clone(), conn_id_on_b.clone(), @@ -449,7 +428,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_b, - router_b, ctx_a, client_id_on_b.clone(), signer.clone(), @@ -457,7 +435,6 @@ where TypedIntegration::::connection_open_confirm_on_b( ctx_b, - router_b, ctx_a, conn_id_on_b.clone(), conn_id_on_a.clone(), @@ -466,7 +443,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_a, - router_a, ctx_b, client_id_on_a, signer, @@ -477,7 +453,6 @@ where pub fn channel_open_init_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, conn_id_on_a: ConnectionId, port_id_on_a: PortId, port_id_on_b: PortId, @@ -492,7 +467,7 @@ where version_proposal: ChannelVersion::empty(), })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::OpenInitChannel(open_init_channel_event)) = ctx_a.ibc_store().events.lock().last().cloned() @@ -505,7 +480,6 @@ where pub fn channel_open_try_on_b( ctx_b: &mut MockContext, - router_b: &mut MockRouter, ctx_a: &MockContext, conn_id_on_b: ConnectionId, chan_id_on_a: ChannelId, @@ -539,7 +513,7 @@ where version_proposal: ChannelVersion::empty(), })); - ctx_b.deliver(router_b, msg_for_b).expect("success"); + ctx_b.deliver(msg_for_b).expect("success"); let Some(IbcEvent::OpenTryChannel(open_try_channel_event)) = ctx_b.ibc_store().events.lock().last().cloned() @@ -553,7 +527,6 @@ where #[allow(clippy::too_many_arguments)] pub fn channel_open_ack_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, chan_id_on_a: ChannelId, port_id_on_a: PortId, @@ -583,7 +556,7 @@ where signer, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::OpenAckChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned() else { @@ -593,7 +566,6 @@ where pub fn channel_open_confirm_on_b( ctx_b: &mut MockContext, - router_b: &mut MockRouter, ctx_a: &MockContext, chan_id_on_a: ChannelId, chan_id_on_b: ChannelId, @@ -620,7 +592,7 @@ where signer, })); - ctx_b.deliver(router_b, msg_for_b).expect("success"); + ctx_b.deliver(msg_for_b).expect("success"); let Some(IbcEvent::OpenConfirmChannel(_)) = ctx_b.ibc_store().events.lock().last().cloned() else { @@ -630,7 +602,6 @@ where pub fn channel_close_init_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, chan_id_on_a: ChannelId, port_id_on_a: PortId, signer: Signer, @@ -641,7 +612,7 @@ where signer, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::CloseInitChannel(_)) = ctx_a.ibc_store().events.lock().last().cloned() else { @@ -651,7 +622,6 @@ where pub fn channel_close_confirm_on_b( ctx_b: &mut MockContext, - router_b: &mut MockRouter, ctx_a: &MockContext, chan_id_on_b: ChannelId, port_id_on_b: PortId, @@ -677,7 +647,7 @@ where signer, })); - ctx_b.deliver(router_b, msg_for_b).expect("success"); + ctx_b.deliver(msg_for_b).expect("success"); let Some(IbcEvent::CloseConfirmChannel(_)) = ctx_b.ibc_store().events.lock().last().cloned() @@ -689,9 +659,7 @@ where #[allow(clippy::too_many_arguments)] pub fn create_channel_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &mut MockContext, - router_b: &mut MockRouter, client_id_on_a: ClientId, conn_id_on_a: ConnectionId, port_id_on_a: PortId, @@ -702,7 +670,6 @@ where ) -> (ChannelId, ChannelId) { let chan_id_on_a = TypedIntegration::::channel_open_init_on_a( ctx_a, - router_a, conn_id_on_a.clone(), port_id_on_a.clone(), port_id_on_b.clone(), @@ -711,7 +678,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_b, - router_b, ctx_a, client_id_on_b.clone(), signer.clone(), @@ -719,7 +685,6 @@ where let chan_id_on_b = TypedIntegration::::channel_open_try_on_b( ctx_b, - router_b, ctx_a, conn_id_on_b.clone(), chan_id_on_a.clone(), @@ -729,7 +694,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_a, - router_a, ctx_b, client_id_on_a.clone(), signer.clone(), @@ -737,7 +701,6 @@ where TypedIntegration::::channel_open_ack_on_a( ctx_a, - router_a, ctx_b, chan_id_on_a.clone(), port_id_on_a.clone(), @@ -748,7 +711,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_b, - router_b, ctx_a, client_id_on_b.clone(), signer.clone(), @@ -756,7 +718,6 @@ where TypedIntegration::::channel_open_confirm_on_b( ctx_b, - router_b, ctx_a, chan_id_on_a.clone(), chan_id_on_b.clone(), @@ -766,7 +727,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_a, - router_a, ctx_b, client_id_on_a, signer, @@ -778,9 +738,7 @@ where #[allow(clippy::too_many_arguments)] pub fn close_channel_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &mut MockContext, - router_b: &mut MockRouter, client_id_on_a: ClientId, chan_id_on_a: ChannelId, port_id_on_a: PortId, @@ -791,7 +749,6 @@ where ) { TypedIntegration::::channel_close_init_on_a( ctx_a, - router_a, chan_id_on_a.clone(), port_id_on_a.clone(), signer.clone(), @@ -799,7 +756,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_b, - router_b, ctx_a, client_id_on_b, signer.clone(), @@ -807,7 +763,6 @@ where TypedIntegration::::channel_close_confirm_on_b( ctx_b, - router_b, ctx_a, chan_id_on_b, port_id_on_b, @@ -816,7 +771,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_a, - router_a, ctx_b, client_id_on_a, signer, @@ -825,7 +779,6 @@ where pub fn packet_recv_on_b( ctx_b: &mut MockContext, - router_b: &mut MockRouter, ctx_a: &MockContext, packet: Packet, signer: Signer, @@ -850,7 +803,7 @@ where signer, })); - ctx_b.deliver(router_b, msg_for_b).expect("success"); + ctx_b.deliver(msg_for_b).expect("success"); let Some(IbcEvent::WriteAcknowledgement(write_ack_event)) = ctx_b.ibc_store().events.lock().last().cloned() @@ -864,7 +817,6 @@ where #[allow(clippy::too_many_arguments)] pub fn packet_ack_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, packet: Packet, acknowledgement: Acknowledgement, @@ -890,7 +842,7 @@ where signer, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::AcknowledgePacket(_)) = ctx_a.ibc_store().events.lock().last().cloned() else { @@ -900,7 +852,6 @@ where pub fn packet_timeout_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, packet: Packet, signer: Signer, @@ -926,7 +877,7 @@ where signer, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::TimeoutPacket(_)) = ctx_a.ibc_store().events.lock().last().cloned() else { @@ -936,7 +887,6 @@ where pub fn packet_timeout_on_close_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &MockContext, packet: Packet, chan_id_on_b: ChannelId, @@ -974,7 +924,7 @@ where signer, })); - ctx_a.deliver(router_a, msg_for_a).expect("success"); + ctx_a.deliver(msg_for_a).expect("success"); let Some(IbcEvent::ChannelClosed(_)) = ctx_a.ibc_store().events.lock().last().cloned() else { @@ -985,9 +935,7 @@ where #[allow(clippy::too_many_arguments)] pub fn send_packet_on_a( ctx_a: &mut MockContext, - router_a: &mut MockRouter, ctx_b: &mut MockContext, - router_b: &mut MockRouter, packet: Packet, client_id_on_a: ClientId, client_id_on_b: ClientId, @@ -997,7 +945,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_b, - router_b, ctx_a, client_id_on_b.clone(), signer.clone(), @@ -1005,7 +952,6 @@ where let acknowledgement = TypedIntegration::::packet_recv_on_b( ctx_b, - router_b, ctx_a, packet.clone(), signer.clone(), @@ -1013,7 +959,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_a, - router_a, ctx_b, client_id_on_a, signer.clone(), @@ -1021,7 +966,6 @@ where TypedIntegration::::packet_ack_on_a( ctx_a, - router_a, ctx_b, packet, acknowledgement, @@ -1030,7 +974,6 @@ where TypedIntegration::::update_client_on_a_with_sync( ctx_b, - router_b, ctx_a, client_id_on_b, signer.clone(), diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 7694749ed..16fa4d7d3 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -70,7 +70,6 @@ mod tests { 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` @@ -162,12 +161,6 @@ mod tests { .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 @@ -187,7 +180,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 dispatch_res_b = ctx_b.deliver(MsgEnvelope::Client(client_msg_b)); let validation_res = ctx_b.host.validate(); assert!( validation_res.is_ok(), @@ -226,7 +219,7 @@ mod tests { 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 dispatch_res_a = ctx_a.deliver(MsgEnvelope::Client(client_msg_a)); let validation_res = ctx_a.host.validate(); assert!( validation_res.is_ok(), 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>, From d2e4ba54928bffee37b2ca0fd8f63ddd3ee10e94 Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Fri, 29 Mar 2024 10:10:22 -0700 Subject: [PATCH 38/54] nit: add docstring for router --- ibc-testkit/src/context.rs | 7 ++++--- ibc-testkit/src/fixtures/core/context.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 70ce684aa..880a1859e 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -43,10 +43,11 @@ where /// The type of host chain underlying this mock context. pub host: H, - pub router: MockRouter, - /// 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>; @@ -385,7 +386,7 @@ where /// 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.router, msg) + dispatch(&mut self.ibc_store, &mut self.ibc_router, msg) .map_err(RelayerError::TransactionFailed)?; // Create a new block. self.advance_block(); diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 683500f57..f7913d06e 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -62,7 +62,7 @@ where params.latest_height.revision_number(), Default::default(), ), - router: MockRouter::new_with_transfer(), + ibc_router: MockRouter::new_with_transfer(), host: params.host, }; From ef23ac1b6f3e7c71eff34a095282d7e080926a93 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 29 Mar 2024 18:22:25 +0100 Subject: [PATCH 39/54] nits --- ibc-testkit/src/fixtures/core/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index f7913d06e..424ab1100 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -22,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, @@ -58,12 +58,12 @@ where let mut context = Self { main_store: Default::default(), + host: params.host, ibc_store: MockIbcStore::new( params.latest_height.revision_number(), Default::default(), ), ibc_router: MockRouter::new_with_transfer(), - host: params.host, }; // store is a height 0; no block From 3538f2692e0321c4e48f683efb493d6049f7a5aa Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 29 Mar 2024 18:26:35 +0100 Subject: [PATCH 40/54] rm redundant lint filters --- ibc-testkit/src/integration/mod.rs | 5 ----- ibc-testkit/src/integration/utils.rs | 3 --- 2 files changed, 8 deletions(-) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs index 094a4f7ef..99286df73 100644 --- a/ibc-testkit/src/integration/mod.rs +++ b/ibc-testkit/src/integration/mod.rs @@ -153,7 +153,6 @@ where ) } - #[allow(clippy::too_many_arguments)] pub fn create_channel_on_b( &mut self, conn_id_on_b: ConnectionId, @@ -191,14 +190,12 @@ where ) } - #[allow(clippy::too_many_arguments)] 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 @@ -246,14 +243,12 @@ where ) } - #[allow(clippy::too_many_arguments)] 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 diff --git a/ibc-testkit/src/integration/utils.rs b/ibc-testkit/src/integration/utils.rs index bc46c2e4a..45ab468e9 100644 --- a/ibc-testkit/src/integration/utils.rs +++ b/ibc-testkit/src/integration/utils.rs @@ -524,7 +524,6 @@ where open_try_channel_event.chan_id_on_b().clone() } - #[allow(clippy::too_many_arguments)] pub fn channel_open_ack_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -814,7 +813,6 @@ where write_ack_event.acknowledgement().clone() } - #[allow(clippy::too_many_arguments)] pub fn packet_ack_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -932,7 +930,6 @@ where }; } - #[allow(clippy::too_many_arguments)] pub fn send_packet_on_a( ctx_a: &mut MockContext, ctx_b: &mut MockContext, From e645b7036a15f354e8def2ace2bd57b861f63ce7 Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Fri, 29 Mar 2024 11:03:03 -0700 Subject: [PATCH 41/54] imp: ditch RelayerContext --- ibc-testkit/src/context.rs | 9 +- ibc-testkit/src/relayer/context.rs | 97 +++++-------------- .../tests/core/ics04_channel/recv_packet.rs | 3 +- 3 files changed, 31 insertions(+), 78 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index 880a1859e..f5b6866dc 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -6,7 +6,7 @@ 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::connection::types::ConnectionEnd; use ibc::core::entrypoint::dispatch; @@ -91,6 +91,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() { diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 16fa4d7d3..00c90e6fb 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -1,90 +1,38 @@ -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::primitives::prelude::*; -use ibc::core::primitives::Signer; - -use crate::context::MockGenericContext; -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; -} - -impl RelayerContext for MockGenericContext -where - S: ProvableStore + Debug, - H: TestHost, - HostClientState: ClientStateValidation>, -{ - fn query_latest_height(&self) -> Result { - self.ibc_store.host_height() - } - - fn query_client_full_state(&self, client_id: &ClientId) -> Option { - // Forward call to Ics2. - self.ibc_store.client_state(client_id).ok() - } - - fn signer(&self) -> Signer { - "0CDA3F47EF3C4906693B170EF650EB968C5F4B2C" - .to_string() - .into() - } -} - #[cfg(test)] mod tests { use ibc::clients::tendermint::types::client_type as tm_client_type; + 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::Height; use ibc::core::handler::types::msgs::MsgEnvelope; - use ibc::core::host::types::identifiers::ChainId; + use ibc::core::host::types::identifiers::{ChainId, ClientId}; 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::fixtures::core::signer::dummy_account_id; + use crate::hosts::{ + HostClientState, MockHost, TendermintHost, TestBlock, TestHeader, TestHost, + }; use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; - use crate::testapp::ibc::core::types::LightClientBuilder; + use crate::testapp::ibc::core::types::{DefaultIbcStore, 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, + pub(crate) fn build_client_update_datagram( + dest: &MockContext, client_id: &ClientId, src_header: &H, ) -> Result where - Ctx: RelayerContext, + HostClientState: ClientStateValidation, { // 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(|| { + let dest_client_state = dest.ibc_store.client_state(client_id).map_err(|_| { RelayerError::ClientStateNotFound { client_id: client_id.clone(), } @@ -112,7 +60,7 @@ mod tests { Ok(ClientMsg::UpdateClient(MsgUpdateClient { client_id: client_id.clone(), client_message: src_header.clone().into(), - signer: dest.signer(), + signer: dummy_account_id(), })) } @@ -192,11 +140,11 @@ mod tests { 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()); + + 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 @@ -231,11 +179,10 @@ mod tests { 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()); + 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 6f3d32854262ba99bc3e54568c8ca0a87f00d263 Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Fri, 29 Mar 2024 11:18:01 -0700 Subject: [PATCH 42/54] nit: simplify build_client_update_datagram --- ibc-testkit/src/relayer/context.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 00c90e6fb..8a64b0b43 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -2,7 +2,6 @@ mod tests { use ibc::clients::tendermint::types::client_type as tm_client_type; 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::Height; use ibc::core::handler::types::msgs::MsgEnvelope; @@ -32,13 +31,7 @@ mod tests { { // Check if client for ibc0 on ibc1 has been updated to latest height: // - query client state on destination chain - let dest_client_state = dest.ibc_store.client_state(client_id).map_err(|_| { - RelayerError::ClientStateNotFound { - client_id: client_id.clone(), - } - })?; - - let dest_client_latest_height = dest_client_state.latest_height(); + let dest_client_latest_height = dest.light_client_latest_height(client_id); if src_header.height() == dest_client_latest_height { return Err(RelayerError::ClientAlreadyUpToDate { From 8150c80c193dd98695fc69157f13e8accb1dbc43 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 3 Apr 2024 15:00:25 +0200 Subject: [PATCH 43/54] refactor integration tests --- ibc-testkit/src/integration/mod.rs | 492 ------------------ ibc-testkit/src/lib.rs | 4 - ibc-testkit/src/relayer/context.rs | 336 ++++++++++++ ibc-testkit/src/relayer/integration.rs | 164 ++++++ ibc-testkit/src/relayer/mod.rs | 5 + .../src/{integration => relayer}/utils.rs | 0 6 files changed, 505 insertions(+), 496 deletions(-) delete mode 100644 ibc-testkit/src/integration/mod.rs create mode 100644 ibc-testkit/src/relayer/integration.rs rename ibc-testkit/src/{integration => relayer}/utils.rs (100%) diff --git a/ibc-testkit/src/integration/mod.rs b/ibc-testkit/src/integration/mod.rs deleted file mode 100644 index 99286df73..000000000 --- a/ibc-testkit/src/integration/mod.rs +++ /dev/null @@ -1,492 +0,0 @@ -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, ClientId, ConnectionId, PortId}; -use ibc::core::host::types::path::ChannelEndPath; -use ibc::core::host::ValidationContext; -use ibc::primitives::{Signer, Timestamp}; - -use self::utils::TypedIntegration; -use crate::context::MockContext; -use crate::fixtures::core::signer::dummy_account_id; -use crate::hosts::{HostClientState, TestHost}; -use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; -use crate::testapp::ibc::core::types::DefaultIbcStore; - -pub mod utils; - -pub struct IntegrationContext -where - A: TestHost, - B: TestHost, - HostClientState: ClientStateValidation, - HostClientState: ClientStateValidation, -{ - ctx_a: MockContext, - ctx_b: MockContext, -} - -impl IntegrationContext -where - A: TestHost, - B: TestHost, - HostClientState: ClientStateValidation, - HostClientState: ClientStateValidation, -{ - pub fn new(ctx_a: MockContext, ctx_b: MockContext) -> Self { - Self { ctx_a, ctx_b } - } - - pub fn get_ctx_a(&self) -> &MockContext { - &self.ctx_a - } - - pub fn get_ctx_b(&self) -> &MockContext { - &self.ctx_b - } - - pub fn get_ctx_a_mut(&mut self) -> &mut MockContext { - &mut self.ctx_a - } - - pub fn get_ctx_b_mut(&mut self) -> &mut MockContext { - &mut self.ctx_b - } - - pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { - TypedIntegration::::create_client_on_a(&mut self.ctx_a, &self.ctx_b, signer) - } - - pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId { - TypedIntegration::::create_client_on_a(&mut self.ctx_b, &self.ctx_a, signer) - } - - pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) { - TypedIntegration::::update_client_on_a_with_sync( - &mut self.ctx_a, - &mut self.ctx_b, - client_id_on_a, - signer, - ) - } - - pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) { - TypedIntegration::::update_client_on_a_with_sync( - &mut self.ctx_b, - &mut self.ctx_a, - client_id_on_b, - signer, - ) - } - - pub fn create_connection_on_a( - &mut self, - client_id_on_a: ClientId, - client_id_on_b: ClientId, - signer: Signer, - ) -> (ConnectionId, ConnectionId) { - TypedIntegration::::create_connection_on_a( - &mut self.ctx_a, - &mut self.ctx_b, - client_id_on_a, - client_id_on_b, - signer, - ) - } - - pub fn create_connection_on_b( - &mut self, - client_id_on_b: ClientId, - client_id_on_a: ClientId, - signer: Signer, - ) -> (ConnectionId, ConnectionId) { - TypedIntegration::::create_connection_on_a( - &mut self.ctx_b, - &mut self.ctx_a, - client_id_on_b, - client_id_on_a, - signer, - ) - } - - 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(); - - TypedIntegration::::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, - ) - } - - 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(); - - TypedIntegration::::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, - ) - } - - 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(); - - TypedIntegration::::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, - ) - } - - 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(); - - TypedIntegration::::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, - ) - } - - 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(); - - TypedIntegration::::send_packet_on_a( - &mut self.ctx_a, - &mut self.ctx_b, - packet, - client_id_on_a, - client_id_on_b, - signer, - ) - } -} - -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 = IntegrationContext::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}; - - #[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/lib.rs b/ibc-testkit/src/lib.rs index 68d3157db..f4326ba1d 100644 --- a/ibc-testkit/src/lib.rs +++ b/ibc-testkit/src/lib.rs @@ -21,7 +21,3 @@ pub mod hosts; pub mod relayer; pub mod testapp; pub mod utils; - -// `ibc::apps::transfer::handler::send_transfer` requires `serde` -#[cfg(feature = "serde")] -pub mod integration; diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 8a64b0b43..64ed606e9 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -1,3 +1,339 @@ +use ibc::core::channel::types::packet::Packet; +use ibc::core::client::context::client_state::ClientStateValidation; +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::relayer::utils::TypedIntegration; +use crate::testapp::ibc::core::types::DefaultIbcStore; + +pub struct IntegrationContext +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + ctx_a: MockContext, + ctx_b: MockContext, +} + +impl IntegrationContext +where + A: TestHost, + B: TestHost, + HostClientState: ClientStateValidation, + HostClientState: ClientStateValidation, +{ + pub fn new(ctx_a: MockContext, ctx_b: MockContext) -> Self { + Self { ctx_a, ctx_b } + } + + pub fn get_ctx_a(&self) -> &MockContext { + &self.ctx_a + } + + pub fn get_ctx_b(&self) -> &MockContext { + &self.ctx_b + } + + pub fn get_ctx_a_mut(&mut self) -> &mut MockContext { + &mut self.ctx_a + } + + pub fn get_ctx_b_mut(&mut self) -> &mut MockContext { + &mut self.ctx_b + } + + pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { + TypedIntegration::::create_client_on_a(&mut self.ctx_a, &self.ctx_b, signer) + } + + pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId { + TypedIntegration::::create_client_on_a(&mut self.ctx_b, &self.ctx_a, signer) + } + + pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) { + TypedIntegration::::update_client_on_a_with_sync( + &mut self.ctx_a, + &mut self.ctx_b, + client_id_on_a, + signer, + ) + } + + pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) { + TypedIntegration::::update_client_on_a_with_sync( + &mut self.ctx_b, + &mut self.ctx_a, + client_id_on_b, + signer, + ) + } + + pub fn create_connection_on_a( + &mut self, + client_id_on_a: ClientId, + client_id_on_b: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + TypedIntegration::::create_connection_on_a( + &mut self.ctx_a, + &mut self.ctx_b, + client_id_on_a, + client_id_on_b, + signer, + ) + } + + pub fn create_connection_on_b( + &mut self, + client_id_on_b: ClientId, + client_id_on_a: ClientId, + signer: Signer, + ) -> (ConnectionId, ConnectionId) { + TypedIntegration::::create_connection_on_a( + &mut self.ctx_b, + &mut self.ctx_a, + client_id_on_b, + client_id_on_a, + signer, + ) + } + + 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(); + + TypedIntegration::::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, + ) + } + + 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(); + + TypedIntegration::::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, + ) + } + + 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(); + + TypedIntegration::::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, + ) + } + + 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(); + + TypedIntegration::::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, + ) + } + + 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(); + + TypedIntegration::::send_packet_on_a( + &mut self.ctx_a, + &mut self.ctx_b, + packet, + client_id_on_a, + client_id_on_b, + signer, + ) + } +} + #[cfg(test)] mod tests { use ibc::clients::tendermint::types::client_type as tm_client_type; diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs new file mode 100644 index 000000000..d3ec9f88f --- /dev/null +++ b/ibc-testkit/src/relayer/integration.rs @@ -0,0 +1,164 @@ +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::IntegrationContext; +use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; +use crate::testapp::ibc::core::types::DefaultIbcStore; + +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 = IntegrationContext::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}; + + #[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/integration/utils.rs b/ibc-testkit/src/relayer/utils.rs similarity index 100% rename from ibc-testkit/src/integration/utils.rs rename to ibc-testkit/src/relayer/utils.rs From 5a047f536953194347373e2c5d95eb250428ddf4 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 3 Apr 2024 15:38:42 +0200 Subject: [PATCH 44/54] add doc strings for TypedRelayerOps --- ibc-testkit/src/relayer/context.rs | 28 +++--- ibc-testkit/src/relayer/integration.rs | 4 +- ibc-testkit/src/relayer/utils.rs | 122 ++++++++++++++----------- 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 64ed606e9..1081f2ef1 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -7,10 +7,10 @@ use ibc::primitives::Signer; use crate::context::MockContext; use crate::hosts::{HostClientState, TestHost}; -use crate::relayer::utils::TypedIntegration; +use crate::relayer::utils::TypedRelayerOps; use crate::testapp::ibc::core::types::DefaultIbcStore; -pub struct IntegrationContext +pub struct RelayerContext where A: TestHost, B: TestHost, @@ -21,7 +21,7 @@ where ctx_b: MockContext, } -impl IntegrationContext +impl RelayerContext where A: TestHost, B: TestHost, @@ -49,15 +49,15 @@ where } pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { - TypedIntegration::::create_client_on_a(&mut self.ctx_a, &self.ctx_b, signer) + TypedRelayerOps::::create_client_on_a(&mut self.ctx_a, &self.ctx_b, signer) } pub fn create_client_on_b(&mut self, signer: Signer) -> ClientId { - TypedIntegration::::create_client_on_a(&mut self.ctx_b, &self.ctx_a, signer) + TypedRelayerOps::::create_client_on_a(&mut self.ctx_b, &self.ctx_a, signer) } pub fn update_client_on_a_with_sync(&mut self, client_id_on_a: ClientId, signer: Signer) { - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( &mut self.ctx_a, &mut self.ctx_b, client_id_on_a, @@ -66,7 +66,7 @@ where } pub fn update_client_on_b_with_sync(&mut self, client_id_on_b: ClientId, signer: Signer) { - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( &mut self.ctx_b, &mut self.ctx_a, client_id_on_b, @@ -80,7 +80,7 @@ where client_id_on_b: ClientId, signer: Signer, ) -> (ConnectionId, ConnectionId) { - TypedIntegration::::create_connection_on_a( + TypedRelayerOps::::create_connection_on_a( &mut self.ctx_a, &mut self.ctx_b, client_id_on_a, @@ -95,7 +95,7 @@ where client_id_on_a: ClientId, signer: Signer, ) -> (ConnectionId, ConnectionId) { - TypedIntegration::::create_connection_on_a( + TypedRelayerOps::::create_connection_on_a( &mut self.ctx_b, &mut self.ctx_a, client_id_on_b, @@ -128,7 +128,7 @@ where .client_id() .clone(); - TypedIntegration::::create_channel_on_a( + TypedRelayerOps::::create_channel_on_a( &mut self.ctx_a, &mut self.ctx_b, client_id_on_a, @@ -165,7 +165,7 @@ where .client_id() .clone(); - TypedIntegration::::create_channel_on_a( + TypedRelayerOps::::create_channel_on_a( &mut self.ctx_b, &mut self.ctx_a, client_id_on_b, @@ -218,7 +218,7 @@ where .client_id() .clone(); - TypedIntegration::::close_channel_on_a( + TypedRelayerOps::::close_channel_on_a( &mut self.ctx_a, &mut self.ctx_b, client_id_on_a, @@ -271,7 +271,7 @@ where .client_id() .clone(); - TypedIntegration::::close_channel_on_a( + TypedRelayerOps::::close_channel_on_a( &mut self.ctx_b, &mut self.ctx_a, client_id_on_b, @@ -323,7 +323,7 @@ where .client_id() .clone(); - TypedIntegration::::send_packet_on_a( + TypedRelayerOps::::send_packet_on_a( &mut self.ctx_a, &mut self.ctx_b, packet, diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs index d3ec9f88f..0a205a21d 100644 --- a/ibc-testkit/src/relayer/integration.rs +++ b/ibc-testkit/src/relayer/integration.rs @@ -14,7 +14,7 @@ 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::IntegrationContext; +use crate::relayer::context::RelayerContext; use crate::testapp::ibc::applications::transfer::types::DummyTransferModule; use crate::testapp::ibc::core::types::DefaultIbcStore; @@ -30,7 +30,7 @@ where let signer = dummy_account_id(); - let mut relayer = IntegrationContext::new(ctx_a, ctx_b); + let mut relayer = RelayerContext::new(ctx_a, ctx_b); // client creation let client_id_on_a = relayer.create_client_on_a(signer.clone()); diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 45ab468e9..6bf9bc18f 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -35,24 +35,28 @@ use crate::context::MockContext; use crate::hosts::{HostClientState, TestBlock, TestHost}; use crate::testapp::ibc::core::types::{DefaultIbcStore, LightClientBuilder, LightClientState}; -/// Implements relayer methods for a pair of hosts -/// Note that, all the implementations are in one direction, from A to B -/// For the methods in opposite direction, use `TypedIntegration::` instead of TypedIntegration::` +/// 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 TypedIntegration(PhantomData, PhantomData) +pub struct TypedRelayerOps(PhantomData, PhantomData) where A: TestHost, B: TestHost, HostClientState: ClientStateValidation, HostClientState: ClientStateValidation; -impl TypedIntegration +impl TypedRelayerOps where A: TestHost, B: TestHost, HostClientState: ClientStateValidation, HostClientState: ClientStateValidation, { + /// Creates a client on `A` with the state of `B`. pub fn create_client_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -96,12 +100,14 @@ where 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, @@ -139,18 +145,20 @@ 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. pub fn update_client_on_a_with_sync( ctx_a: &mut MockContext, ctx_b: &mut MockContext, client_id_on_a: ClientId, signer: Signer, ) { - // this is required, as IBC doesn't allow client update - // from future beyond max clock drift - TypedIntegration::::sync_clock_on_a(ctx_a, ctx_b); - TypedIntegration::::update_client_on_a(ctx_a, ctx_b, client_id_on_a, 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`. pub fn connection_open_init_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -183,6 +191,7 @@ where open_init_connection_event.conn_id_on_a().clone() } + /// `B` receives the connection opening attempt by `A` after `A` initiates the connection. pub fn connection_open_try_on_b( ctx_b: &mut MockContext, ctx_a: &MockContext, @@ -270,6 +279,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. pub fn connection_open_ack_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -344,6 +355,8 @@ where }; } + /// `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, @@ -379,6 +392,7 @@ where }; } + /// A connection is created by `A` towards `B` using the IBC connection handshake protocol. pub fn create_connection_on_a( ctx_a: &mut MockContext, ctx_b: &mut MockContext, @@ -386,7 +400,7 @@ where client_id_on_b: ClientId, signer: Signer, ) -> (ConnectionId, ConnectionId) { - let conn_id_on_a = TypedIntegration::::connection_open_init_on_a( + let conn_id_on_a = TypedRelayerOps::::connection_open_init_on_a( ctx_a, ctx_b, client_id_on_a.clone(), @@ -394,14 +408,14 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_b, ctx_a, client_id_on_b.clone(), signer.clone(), ); - let conn_id_on_b = TypedIntegration::::connection_open_try_on_b( + let conn_id_on_b = TypedRelayerOps::::connection_open_try_on_b( ctx_b, ctx_a, conn_id_on_a.clone(), @@ -410,14 +424,14 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_a, ctx_b, client_id_on_a.clone(), signer.clone(), ); - TypedIntegration::::connection_open_ack_on_a( + TypedRelayerOps::::connection_open_ack_on_a( ctx_a, ctx_b, conn_id_on_a.clone(), @@ -426,14 +440,14 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_b, ctx_a, client_id_on_b.clone(), signer.clone(), ); - TypedIntegration::::connection_open_confirm_on_b( + TypedRelayerOps::::connection_open_confirm_on_b( ctx_b, ctx_a, conn_id_on_b.clone(), @@ -441,16 +455,12 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( - ctx_a, - ctx_b, - client_id_on_a, - signer, - ); + 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`. pub fn channel_open_init_on_a( ctx_a: &mut MockContext, conn_id_on_a: ConnectionId, @@ -478,6 +488,7 @@ where open_init_channel_event.chan_id_on_a().clone() } + /// `B` receives the channel opening attempt by `A` after `A` initiates the channel. pub fn channel_open_try_on_b( ctx_b: &mut MockContext, ctx_a: &MockContext, @@ -524,6 +535,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. pub fn channel_open_ack_on_a( ctx_a: &mut MockContext, ctx_b: &MockContext, @@ -563,6 +576,8 @@ where }; } + /// `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, @@ -599,6 +614,8 @@ 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, chan_id_on_a: ChannelId, @@ -619,6 +636,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, @@ -655,6 +674,7 @@ where }; } + /// A channel is created by `A` towards `B` using the IBC channel handshake protocol. #[allow(clippy::too_many_arguments)] pub fn create_channel_on_a( ctx_a: &mut MockContext, @@ -667,7 +687,7 @@ where port_id_on_b: PortId, signer: Signer, ) -> (ChannelId, ChannelId) { - let chan_id_on_a = TypedIntegration::::channel_open_init_on_a( + let chan_id_on_a = TypedRelayerOps::::channel_open_init_on_a( ctx_a, conn_id_on_a.clone(), port_id_on_a.clone(), @@ -675,14 +695,14 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_b, ctx_a, client_id_on_b.clone(), signer.clone(), ); - let chan_id_on_b = TypedIntegration::::channel_open_try_on_b( + let chan_id_on_b = TypedRelayerOps::::channel_open_try_on_b( ctx_b, ctx_a, conn_id_on_b.clone(), @@ -691,14 +711,14 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_a, ctx_b, client_id_on_a.clone(), signer.clone(), ); - TypedIntegration::::channel_open_ack_on_a( + TypedRelayerOps::::channel_open_ack_on_a( ctx_a, ctx_b, chan_id_on_a.clone(), @@ -708,14 +728,14 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_b, ctx_a, client_id_on_b.clone(), signer.clone(), ); - TypedIntegration::::channel_open_confirm_on_b( + TypedRelayerOps::::channel_open_confirm_on_b( ctx_b, ctx_a, chan_id_on_a.clone(), @@ -724,16 +744,12 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( - ctx_a, - ctx_b, - client_id_on_a, - signer, - ); + 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, @@ -746,21 +762,21 @@ where port_id_on_b: PortId, signer: Signer, ) { - TypedIntegration::::channel_close_init_on_a( + TypedRelayerOps::::channel_close_init_on_a( ctx_a, chan_id_on_a.clone(), port_id_on_a.clone(), signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_b, ctx_a, client_id_on_b, signer.clone(), ); - TypedIntegration::::channel_close_confirm_on_b( + TypedRelayerOps::::channel_close_confirm_on_b( ctx_b, ctx_a, chan_id_on_b, @@ -768,14 +784,10 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( - ctx_a, - ctx_b, - client_id_on_a, - signer, - ); + 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`. pub fn packet_recv_on_b( ctx_b: &mut MockContext, ctx_a: &MockContext, @@ -813,6 +825,7 @@ where 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, @@ -848,6 +861,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, @@ -883,6 +898,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, @@ -930,6 +947,7 @@ where }; } + /// Sends a packet from a module on `A` to `B` using the IBC packet relay protocol. pub fn send_packet_on_a( ctx_a: &mut MockContext, ctx_b: &mut MockContext, @@ -940,28 +958,24 @@ where ) { // packet is passed from module - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_b, ctx_a, client_id_on_b.clone(), signer.clone(), ); - let acknowledgement = TypedIntegration::::packet_recv_on_b( - ctx_b, - ctx_a, - packet.clone(), - signer.clone(), - ); + let acknowledgement = + TypedRelayerOps::::packet_recv_on_b(ctx_b, ctx_a, packet.clone(), signer.clone()); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_a, ctx_b, client_id_on_a, signer.clone(), ); - TypedIntegration::::packet_ack_on_a( + TypedRelayerOps::::packet_ack_on_a( ctx_a, ctx_b, packet, @@ -969,7 +983,7 @@ where signer.clone(), ); - TypedIntegration::::update_client_on_a_with_sync( + TypedRelayerOps::::update_client_on_a_with_sync( ctx_b, ctx_a, client_id_on_b, From 5c1229a44200c91a3119862ba1c3011ec457fd41 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Wed, 3 Apr 2024 15:48:13 +0200 Subject: [PATCH 45/54] doc strings for RelayerContext --- ibc-testkit/src/relayer/context.rs | 18 ++++++++++++++++++ ibc-testkit/src/relayer/integration.rs | 4 ++++ ibc-testkit/src/relayer/utils.rs | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 1081f2ef1..69a99c416 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -10,6 +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. pub struct RelayerContext where A: TestHost, @@ -28,34 +29,42 @@ where HostClientState: ClientStateValidation, HostClientState: ClientStateValidation, { + /// 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 } } + /// Returns immutable reference to the first context. pub fn get_ctx_a(&self) -> &MockContext { &self.ctx_a } + /// 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. 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. 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, @@ -65,6 +74,7 @@ where ) } + /// 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, @@ -74,6 +84,7 @@ where ) } + /// Creates a connection between the two contexts starting from the first context. pub fn create_connection_on_a( &mut self, client_id_on_a: ClientId, @@ -89,6 +100,7 @@ where ) } + /// Creates a connection between the two contexts starting from the second context. pub fn create_connection_on_b( &mut self, client_id_on_b: ClientId, @@ -104,6 +116,7 @@ where ) } + /// Creates a channel between the two contexts starting from the first context. pub fn create_channel_on_a( &mut self, conn_id_on_a: ConnectionId, @@ -141,6 +154,7 @@ where ) } + /// Creates a channel between the two contexts starting from the second context. pub fn create_channel_on_b( &mut self, conn_id_on_b: ConnectionId, @@ -178,6 +192,7 @@ where ) } + /// 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, @@ -231,6 +246,7 @@ where ) } + /// 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, @@ -284,6 +300,8 @@ where ) } + /// 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 diff --git a/ibc-testkit/src/relayer/integration.rs b/ibc-testkit/src/relayer/integration.rs index 0a205a21d..60b361048 100644 --- a/ibc-testkit/src/relayer/integration.rs +++ b/ibc-testkit/src/relayer/integration.rs @@ -18,6 +18,9 @@ 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, @@ -154,6 +157,7 @@ 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::(); diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 6bf9bc18f..264e57958 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -947,7 +947,7 @@ where }; } - /// Sends a packet from a module on `A` to `B` using the IBC packet relay protocol. + /// 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, From 66ad234be17562f5a23276a5712bb80e80af9cab Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 12:58:52 +0200 Subject: [PATCH 46/54] mv client_update_ping_pong to tests dir --- ibc-testkit/src/relayer/context.rs | 182 ------------------ .../tests/core/ics02_client/update_client.rs | 170 +++++++++++++++- 2 files changed, 168 insertions(+), 184 deletions(-) diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 69a99c416..3d4b5b1f6 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -351,185 +351,3 @@ where ) } } - -#[cfg(test)] -mod tests { - use ibc::clients::tendermint::types::client_type as tm_client_type; - use ibc::core::client::context::client_state::ClientStateValidation; - 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, ClientId}; - use ibc::core::primitives::prelude::*; - use tracing::debug; - - use crate::context::MockContext; - use crate::fixtures::core::context::MockContextConfig; - use crate::fixtures::core::signer::dummy_account_id; - use crate::hosts::{ - HostClientState, MockHost, TendermintHost, TestBlock, TestHeader, TestHost, - }; - use crate::relayer::error::RelayerError; - use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; - use crate::testapp::ibc::core::types::{DefaultIbcStore, 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: &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(), - })) - } - - #[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(), - ); - - 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/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 9389a99c2..002f7bef7 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -29,7 +29,10 @@ 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, }; @@ -37,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, @@ -1522,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() + ); + } +} From def992d573e9b246f643ef0c4d761b46ff57b207 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 13:55:35 +0200 Subject: [PATCH 47/54] rename main_store to multi_store --- ibc-testkit/src/context.rs | 23 ++++++++++++----------- ibc-testkit/src/fixtures/core/context.rs | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ibc-testkit/src/context.rs b/ibc-testkit/src/context.rs index f5b6866dc..8f22a5f73 100644 --- a/ibc-testkit/src/context.rs +++ b/ibc-testkit/src/context.rs @@ -37,8 +37,9 @@ where H: TestHost, HostClientState: ClientStateValidation>, { - /// The main store of the context. - pub main_store: S, + /// 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, @@ -116,13 +117,13 @@ where pub fn generate_genesis_block(&mut self, genesis_time: Timestamp, params: &H::BlockParams) { self.end_block(); - // commit main store - let main_store_commitment = self.main_store.commit().expect("no error"); + // commit multi store + let multi_store_commitment = self.multi_store.commit().expect("no error"); // generate a genesis block let genesis_block = self.host - .generate_block(main_store_commitment, 1, genesis_time, params); + .generate_block(multi_store_commitment, 1, genesis_time, params); // push the genesis block to the host self.host.push_block(genesis_block); @@ -139,7 +140,7 @@ where .into(); let ibc_commitment_proof = self - .main_store + .multi_store .get_proof( self.host.latest_height().revision_height().into(), &self @@ -162,8 +163,8 @@ where // commit ibc store let ibc_store_commitment = self.ibc_store.end_block().expect("no error"); - // commit ibc store commitment in main store - self.main_store + // commit ibc store commitment in multi store + self.multi_store .set( self.ibc_store .commitment_prefix() @@ -176,11 +177,11 @@ where } pub fn produce_block(&mut self, block_time: Duration, params: &H::BlockParams) { - // commit main store - let main_store_commitment = self.main_store.commit().expect("no error"); + // commit the multi store + let multi_store_commitment = self.multi_store.commit().expect("no error"); // generate a new block self.host - .advance_block(main_store_commitment, block_time, params); + .advance_block(multi_store_commitment, block_time, params); } pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) { diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 424ab1100..96dc31ac9 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -57,7 +57,7 @@ where .expect("no underflow"); let mut context = Self { - main_store: Default::default(), + multi_store: Default::default(), host: params.host, ibc_store: MockIbcStore::new( params.latest_height.revision_number(), From 53a8db2dd1f17958ddd2776e526b49606dd84c8a Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 14:24:26 +0200 Subject: [PATCH 48/54] update TestHost trait --- ibc-testkit/src/hosts/mod.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index 2204b828a..2855eeb66 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -7,7 +7,9 @@ use core::ops::Add; use core::time::Duration; use ibc::core::client::context::consensus_state::ConsensusState; +use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::ClientId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; @@ -21,6 +23,7 @@ pub type HostBlock = ::Block; pub type HostBlockParams = ::BlockParams; pub type HostLightClientParams = ::LightClientParams; pub type HostHeader = as TestBlock>::Header; +pub type HostLightClientHeaderParams = as TestBlock>::HeaderParams; pub type HostConsensusState = as TestHeader>::ConsensusState; /// TestHost is a trait that defines the interface for a host blockchain. @@ -119,6 +122,14 @@ pub trait TestHost: Default + Debug + Sized { } Ok(()) } + + fn header_params( + &self, + client_id: &ClientId, + client_context: &C, + ) -> HostLightClientHeaderParams + where + C: ClientValidationContext; } /// TestBlock is a trait that defines the interface for a block produced by a host blockchain. @@ -126,6 +137,9 @@ pub trait TestBlock: Clone + Debug { /// The type of header can be extracted from the block. type Header: TestHeader; + /// The type of parameters required to extract the header from the block. + type HeaderParams: Debug + Default; + /// The height of the block. fn height(&self) -> Height; @@ -133,11 +147,10 @@ pub trait TestBlock: Clone + Debug { fn timestamp(&self) -> Timestamp; /// Extract the header from the block. - fn into_header_with_previous_block(self, previous_block: &Self) -> Self::Header; + fn into_header_with_params(self, params: &Self::HeaderParams) -> Self::Header; fn into_header(self) -> Self::Header { - let prev_block = self.clone(); - self.into_header_with_previous_block(&prev_block) + self.into_header_with_params(&Default::default()) } } From e711fce90ac943617a4d7521b3d84f2781ecfb12 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 14:25:02 +0200 Subject: [PATCH 49/54] update mock and tendermint hosts --- ibc-testkit/src/hosts/mock.rs | 12 +++++++++-- ibc-testkit/src/hosts/tendermint.rs | 31 ++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index 4423d5669..954f3db3f 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -1,8 +1,9 @@ use alloc::collections::VecDeque; use alloc::vec::Vec; +use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::ChainId; +use ibc::core::host::types::identifiers::{ChainId, ClientId}; use ibc::core::primitives::Timestamp; use typed_builder::TypedBuilder; @@ -71,10 +72,17 @@ impl TestHost for MockHost { ) -> Self::ClientState { MockClientState::new(self.get_block(latest_height).expect("height exists")) } + + fn header_params(&self, _: &ClientId, _: &C) + where + C: ClientValidationContext, + { + } } impl TestBlock for MockHeader { type Header = Self; + type HeaderParams = (); fn height(&self) -> Height { self.height @@ -84,7 +92,7 @@ impl TestBlock for MockHeader { self.timestamp } - fn into_header_with_previous_block(self, _: &Self) -> Self::Header { + fn into_header_with_params(self, _: &Self::HeaderParams) -> Self::Header { self } } diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index c5ee5b856..93955bf06 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -5,8 +5,10 @@ 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::context::client_state::ClientStateCommon; +use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::ChainId; +use ibc::core::host::types::identifiers::{ChainId, ClientId}; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; @@ -109,6 +111,20 @@ impl TestHost for TendermintHost { client_state } + + fn header_params(&self, client_id: &ClientId, client_context: &C) -> Option + where + C: ClientValidationContext, + { + let latest_client_height = client_context + .client_state(client_id) + .expect("client state exists") + .latest_height(); + + let header_params = self.get_block(&latest_client_height).expect("block exists"); + + Some(header_params) + } } #[derive(Debug, Clone)] @@ -122,6 +138,7 @@ impl TendermintBlock { impl TestBlock for TendermintBlock { type Header = TendermintHeader; + type HeaderParams = Option; fn height(&self) -> Height { Height::new( @@ -137,12 +154,12 @@ impl TestBlock for TendermintBlock { self.0.signed_header.header.time.into() } - fn into_header_with_previous_block(self, previous_block: &Self) -> Self::Header { - // this method is used to inject the trusted height and validators set - // given the previous_block is trusted, Tendermint light client validates the current (untrusted) block - let mut header = TendermintHeader::from(self); - header.set_trusted_height(previous_block.height()); - header.set_trusted_next_validators_set(previous_block.inner().validators.clone()); + fn into_header_with_params(self, params: &Self::HeaderParams) -> Self::Header { + let trusted_block = params.as_ref().unwrap_or(&self); + + 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 } } From a19c925677e5a6f61802bcbe6cf83df2e82a2d7d Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 14:26:29 +0200 Subject: [PATCH 50/54] update relayer functions --- ibc-testkit/src/relayer/utils.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 264e57958..5899cde18 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -114,12 +114,10 @@ where client_id_on_a: ClientId, signer: Signer, ) { - let latest_client_height_on_a = ctx_a - .ibc_store() - .get_client_validation_context() - .client_state(&client_id_on_a) - .expect("client state exists") - .latest_height(); + let header_params = ctx_b.host.header_params( + &client_id_on_a, + ctx_a.ibc_store().get_client_validation_context(), + ); let latest_height_of_b = ctx_b.latest_height(); @@ -128,11 +126,7 @@ where client_message: ctx_b .host_block(&latest_height_of_b) .expect("block exists") - .into_header_with_previous_block( - &ctx_b - .host_block(&latest_client_height_on_a) - .expect("block exists"), - ) + .into_header_with_params(&header_params) .into(), signer, })); From cfdf142318b0179e22539d784b9ab7441dbc7071 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 14:29:02 +0200 Subject: [PATCH 51/54] nits --- ibc-testkit/src/hosts/mock.rs | 6 +----- ibc-testkit/src/hosts/mod.rs | 6 ++---- ibc-testkit/src/hosts/tendermint.rs | 9 +++++---- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index 954f3db3f..fca71a943 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -73,11 +73,7 @@ impl TestHost for MockHost { MockClientState::new(self.get_block(latest_height).expect("height exists")) } - fn header_params(&self, _: &ClientId, _: &C) - where - C: ClientValidationContext, - { - } + fn header_params(&self, _: &ClientId, _: &C) {} } impl TestBlock for MockHeader { diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index 2855eeb66..a44280f56 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -123,13 +123,11 @@ pub trait TestHost: Default + Debug + Sized { Ok(()) } - fn header_params( + fn header_params( &self, client_id: &ClientId, client_context: &C, - ) -> HostLightClientHeaderParams - where - C: ClientValidationContext; + ) -> HostLightClientHeaderParams; } /// TestBlock is a trait that defines the interface for a block produced by a host blockchain. diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index 93955bf06..ad76ba4b6 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -112,10 +112,11 @@ impl TestHost for TendermintHost { client_state } - fn header_params(&self, client_id: &ClientId, client_context: &C) -> Option - where - C: ClientValidationContext, - { + fn header_params( + &self, + client_id: &ClientId, + client_context: &C, + ) -> Option { let latest_client_height = client_context .client_state(client_id) .expect("client state exists") From 320805e57b03daea39d4607dbecaceb666597fda Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 14:33:28 +0200 Subject: [PATCH 52/54] renames and comments --- ibc-testkit/src/hosts/tendermint.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index ad76ba4b6..c38cd37c8 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -117,14 +117,17 @@ impl TestHost for TendermintHost { client_id: &ClientId, client_context: &C, ) -> Option { - let latest_client_height = client_context + let trusted_height = client_context .client_state(client_id) .expect("client state exists") .latest_height(); - let header_params = self.get_block(&latest_client_height).expect("block exists"); + // `.expect` is called because this block should exist + let trusted_block = self.get_block(&trusted_height).expect("block exists"); - Some(header_params) + // wraps with `Some`, as `None` corresponds to the case where + // the block bootstrap itself as trusted block + Some(trusted_block) } } From 6693800f416d613ce8ec2ebddbcb9937d8e5d878 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 4 Apr 2024 14:48:32 +0200 Subject: [PATCH 53/54] add comments for return values in relayer ops --- ibc-testkit/src/relayer/context.rs | 6 ++++++ ibc-testkit/src/relayer/utils.rs | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index 3d4b5b1f6..4a2bc1f95 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -55,11 +55,13 @@ where } /// 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) } @@ -85,6 +87,7 @@ where } /// 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, @@ -101,6 +104,7 @@ where } /// 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, @@ -117,6 +121,7 @@ where } /// 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, @@ -155,6 +160,7 @@ where } /// 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, diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 5899cde18..8f71b4ff2 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -57,6 +57,7 @@ where 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, @@ -153,6 +154,7 @@ 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, @@ -186,6 +188,7 @@ 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, @@ -387,6 +390,7 @@ where } /// 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, @@ -455,6 +459,7 @@ where } /// `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, @@ -483,6 +488,7 @@ where } /// `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, @@ -669,6 +675,7 @@ where } /// 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, @@ -782,6 +789,7 @@ where } /// `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, From 410b8adf535c6247b6f8cc90ab869edf6b61b65f Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Thu, 4 Apr 2024 19:08:03 -0700 Subject: [PATCH 54/54] imp: simplify into_header --- ibc-testkit/src/hosts/mock.rs | 8 ++------ ibc-testkit/src/hosts/mod.rs | 20 +++++--------------- ibc-testkit/src/hosts/tendermint.rs | 27 ++------------------------- ibc-testkit/src/relayer/utils.rs | 25 ++++++++++++++++--------- 4 files changed, 25 insertions(+), 55 deletions(-) diff --git a/ibc-testkit/src/hosts/mock.rs b/ibc-testkit/src/hosts/mock.rs index fca71a943..b29980b9e 100644 --- a/ibc-testkit/src/hosts/mock.rs +++ b/ibc-testkit/src/hosts/mock.rs @@ -1,9 +1,8 @@ use alloc::collections::VecDeque; use alloc::vec::Vec; -use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::{ChainId, ClientId}; +use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::Timestamp; use typed_builder::TypedBuilder; @@ -72,13 +71,10 @@ impl TestHost for MockHost { ) -> Self::ClientState { MockClientState::new(self.get_block(latest_height).expect("height exists")) } - - fn header_params(&self, _: &ClientId, _: &C) {} } impl TestBlock for MockHeader { type Header = Self; - type HeaderParams = (); fn height(&self) -> Height { self.height @@ -88,7 +84,7 @@ impl TestBlock for MockHeader { self.timestamp } - fn into_header_with_params(self, _: &Self::HeaderParams) -> Self::Header { + fn into_header_with_trusted(self, _: &Self) -> Self::Header { self } } diff --git a/ibc-testkit/src/hosts/mod.rs b/ibc-testkit/src/hosts/mod.rs index a44280f56..3943f9d8b 100644 --- a/ibc-testkit/src/hosts/mod.rs +++ b/ibc-testkit/src/hosts/mod.rs @@ -7,9 +7,7 @@ use core::ops::Add; use core::time::Duration; use ibc::core::client::context::consensus_state::ConsensusState; -use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::ClientId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; @@ -23,7 +21,6 @@ pub type HostBlock = ::Block; pub type HostBlockParams = ::BlockParams; pub type HostLightClientParams = ::LightClientParams; pub type HostHeader = as TestBlock>::Header; -pub type HostLightClientHeaderParams = as TestBlock>::HeaderParams; pub type HostConsensusState = as TestHeader>::ConsensusState; /// TestHost is a trait that defines the interface for a host blockchain. @@ -122,12 +119,6 @@ pub trait TestHost: Default + Debug + Sized { } Ok(()) } - - fn header_params( - &self, - client_id: &ClientId, - client_context: &C, - ) -> HostLightClientHeaderParams; } /// TestBlock is a trait that defines the interface for a block produced by a host blockchain. @@ -135,20 +126,19 @@ pub trait TestBlock: Clone + Debug { /// The type of header can be extracted from the block. type Header: TestHeader; - /// The type of parameters required to extract the header from the block. - type HeaderParams: Debug + Default; - /// 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_with_params(self, params: &Self::HeaderParams) -> Self::Header; + /// 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_header_with_params(&Default::default()) + self.clone().into_header_with_trusted(&self) } } diff --git a/ibc-testkit/src/hosts/tendermint.rs b/ibc-testkit/src/hosts/tendermint.rs index c38cd37c8..07a15fbbf 100644 --- a/ibc-testkit/src/hosts/tendermint.rs +++ b/ibc-testkit/src/hosts/tendermint.rs @@ -5,10 +5,8 @@ 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::context::client_state::ClientStateCommon; -use ibc::core::client::context::ClientValidationContext; use ibc::core::client::types::Height; -use ibc::core::host::types::identifiers::{ChainId, ClientId}; +use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::primitives::proto::Any; @@ -111,24 +109,6 @@ impl TestHost for TendermintHost { client_state } - - fn header_params( - &self, - client_id: &ClientId, - client_context: &C, - ) -> Option { - let trusted_height = client_context - .client_state(client_id) - .expect("client state exists") - .latest_height(); - - // `.expect` is called because this block should exist - let trusted_block = self.get_block(&trusted_height).expect("block exists"); - - // wraps with `Some`, as `None` corresponds to the case where - // the block bootstrap itself as trusted block - Some(trusted_block) - } } #[derive(Debug, Clone)] @@ -142,7 +122,6 @@ impl TendermintBlock { impl TestBlock for TendermintBlock { type Header = TendermintHeader; - type HeaderParams = Option; fn height(&self) -> Height { Height::new( @@ -158,9 +137,7 @@ impl TestBlock for TendermintBlock { self.0.signed_header.header.time.into() } - fn into_header_with_params(self, params: &Self::HeaderParams) -> Self::Header { - let trusted_block = params.as_ref().unwrap_or(&self); - + 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()); diff --git a/ibc-testkit/src/relayer/utils.rs b/ibc-testkit/src/relayer/utils.rs index 8f71b4ff2..40c832d3c 100644 --- a/ibc-testkit/src/relayer/utils.rs +++ b/ibc-testkit/src/relayer/utils.rs @@ -115,19 +115,26 @@ where client_id_on_a: ClientId, signer: Signer, ) { - let header_params = ctx_b.host.header_params( - &client_id_on_a, - ctx_a.ibc_store().get_client_validation_context(), - ); + 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 latest_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: ctx_b - .host_block(&latest_height_of_b) - .expect("block exists") - .into_header_with_params(&header_params) + client_message: target_block_of_b + .into_header_with_trusted(&trusted_block_of_b) .into(), signer, }));

::default(); + let ctx_b = MockContext::::default(); + + let signer = dummy_account_id(); + + let mut relayer = + RelayerContext::new(ctx_a, MockRouter::default(), ctx_b, MockRouter::default()); + + assert_eq!( + relayer.get_ctx_a().latest_height(), + Height::new(0, 5).expect("no error") + ); + assert_eq!( + relayer.get_ctx_b().latest_height(), + Height::new(0, 5).expect("no error") + ); + + // client q on context p + let client_id_on_a = relayer.create_client_on_a(signer.clone()); + + let mut relayer = relayer.reverse(); + + // client p on context q + let client_id_on_b = relayer.create_client_on_a(signer.clone()); + + let mut relayer = relayer.reverse(); + + // asserts + + assert_eq!( + relayer.get_ctx_a().latest_height(), + Height::new(0, 6).expect("no error") + ); + assert_eq!( + relayer.get_ctx_b().latest_height(), + Height::new(0, 6).expect("no error") + ); + + // connection + let (_conn_id_on_a, _conn_id_on_b) = + relayer.create_connection_on_b(client_id_on_a, client_id_on_b, signer); + + // channel + + // channel/packet timeout +} + +#[test] +fn tendermint_integration_test() { + integration_test_between_host_pair::(); +} From 60b8271c0d555b7913f826c9b6c01d512581e74f Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 21 Mar 2024 16:54:58 +0100 Subject: [PATCH 14/54] rm raw test --- ibc-testkit/tests/core/mod.rs | 496 ---------------------------------- 1 file changed, 496 deletions(-) diff --git a/ibc-testkit/tests/core/mod.rs b/ibc-testkit/tests/core/mod.rs index 99896b618..10f45250e 100644 --- a/ibc-testkit/tests/core/mod.rs +++ b/ibc-testkit/tests/core/mod.rs @@ -1,501 +1,5 @@ -use std::time::Duration; - -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::client::types::Height; -use ibc::core::connection::types::msgs::{ - ConnectionMsg, MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenInit, - MsgConnectionOpenTry, -}; -use ibc::core::connection::types::version::Version; -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::{ClientId, ConnectionId}; -use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath, ConnectionPath}; -use ibc::core::host::ValidationContext; -use ibc::primitives::Signer; -use ibc_query::core::context::ProvableContext; -use ibc_testkit::context::MockContext; -use ibc_testkit::fixtures::core::signer::dummy_account_id; -use ibc_testkit::hosts::{HostClientState, TendermintHost, TestBlock, TestHost}; -use ibc_testkit::testapp::ibc::core::router::MockRouter; -use ibc_testkit::testapp::ibc::core::types::{ - DefaultIbcStore, LightClientBuilder, LightClientState, -}; - pub mod ics02_client; pub mod ics03_connection; pub mod ics04_channel; #[cfg(feature = "serde")] pub mod router; - -pub struct RelayerContext -where - A: TestHost, - B: TestHost, - HostClientState: ClientStateValidation, - HostClientState: ClientStateValidation, -{ - ctx_a: MockContext, - router_a: MockRouter, - ctx_b: MockContext, - router_b: MockRouter, -} - -impl RelayerContext -where - A: TestHost, - B: TestHost, - HostClientState: ClientStateValidation, - HostClientState: ClientStateValidation, -{ - pub fn new( - ctx_a: MockContext, - router_a: MockRouter, - ctx_b: MockContext, - router_b: MockRouter, - ) -> Self { - Self { - ctx_a, - router_a, - ctx_b, - router_b, - } - } - - pub fn get_ctx_a(&self) -> &MockContext { - &self.ctx_a - } - - pub fn get_ctx_b(&self) -> &MockContext { - &self.ctx_b - } - - pub fn get_ctx_a_mut(&mut self) -> &mut MockContext { - &mut self.ctx_a - } - - pub fn get_ctx_b_mut(&mut self) -> &mut MockContext { - &mut self.ctx_b - } - - pub fn reverse(self) -> RelayerContext { - RelayerContext::new(self.ctx_b, self.router_b, self.ctx_a, self.router_a) - } - - pub fn create_client_on_a(&mut self, signer: Signer) -> ClientId { - let light_client_of_b = LightClientBuilder::init() - .context(&self.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, - })); - - self.ctx_a - .deliver(&mut self.router_a, msg_for_a) - .expect("success"); - - let Some(IbcEvent::CreateClient(create_client_b_event)) = - self.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(self.ctx_a.ibc_store()) - .client_state(&client_id_on_a) - .expect("client state exists") - .latest_height(), - self.ctx_b.latest_height() - ); - - client_id_on_a - } - - pub fn sync_latest_timestamp(&mut self) { - if self.ctx_a.latest_timestamp() > self.ctx_b.latest_timestamp() { - while self.ctx_a.latest_timestamp() > self.ctx_b.latest_timestamp() { - self.ctx_b.advance_block(); - } - } else { - while self.ctx_b.latest_timestamp() > self.ctx_a.latest_timestamp() { - self.ctx_a.advance_block(); - } - } - } - - pub fn update_client_on_a(&mut self, client_id_on_a: ClientId, signer: Signer) { - let latest_client_height_on_a = self - .ctx_a - .ibc_store() - .get_client_validation_context() - .client_state(&client_id_on_a) - .unwrap() - .latest_height(); - - let latest_height_of_b = self.ctx_b.latest_height(); - - let msg_for_a = MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { - client_id: client_id_on_a.clone(), - client_message: self - .ctx_b - .host_block(&latest_height_of_b) - .expect("block exists") - .into_header_with_previous_block( - &self - .ctx_b - .host_block(&latest_client_height_on_a) - .expect("block exists"), - ) - .into(), - signer, - })); - - self.sync_latest_timestamp(); - - self.ctx_a - .deliver(&mut self.router_a, msg_for_a) - .expect("success"); - - let Some(IbcEvent::UpdateClient(_)) = self.ctx_a.ibc_store().events.lock().last().cloned() - else { - panic!("unexpected event") - }; - } - - pub fn update_client_on_b(&mut self, client_id_on_b: ClientId, signer: Signer) { - let latest_client_height_on_b = self - .ctx_b - .ibc_store() - .get_client_validation_context() - .client_state(&client_id_on_b) - .unwrap() - .latest_height(); - - let latest_height_of_a = self.ctx_a.latest_height(); - - let msg_for_b = MsgEnvelope::Client(ClientMsg::UpdateClient(MsgUpdateClient { - client_id: client_id_on_b.clone(), - client_message: self - .ctx_a - .host_block(&latest_height_of_a) - .expect("block exists") - .into_header_with_previous_block( - &self - .ctx_a - .host_block(&latest_client_height_on_b) - .expect("block exists"), - ) - .into(), - signer, - })); - - self.sync_latest_timestamp(); - - self.ctx_b - .deliver(&mut self.router_b, msg_for_b) - .expect("success"); - - let Some(IbcEvent::UpdateClient(_)) = self.ctx_b.ibc_store().events.lock().last().cloned() - else { - panic!("unexpected event") - }; - } - - pub fn create_connection_on_b( - &mut self, - client_id_on_a: ClientId, - client_id_on_b: ClientId, - signer: Signer, - ) -> (ConnectionId, ConnectionId) { - let counterparty_b = ConnectionCounterParty::new( - client_id_on_b.clone(), - None, - self.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(), - })); - - self.ctx_a - .deliver(&mut self.router_a, msg_for_a) - .expect("success"); - - let Some(IbcEvent::OpenInitConnection(open_init_connection_event)) = - self.ctx_a.ibc_store().events.lock().last().cloned() - else { - panic!("unexpected event") - }; - self.update_client_on_b(client_id_on_b.clone(), signer.clone()); - - let conn_id_on_a = open_init_connection_event.conn_id_on_a().clone(); - - let proofs_height_on_a = self.ctx_a.latest_height(); - - let client_state_of_b_on_a = self - .ctx_a - .ibc_store() - .client_state(&client_id_on_a) - .unwrap(); - - 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()), - self.ctx_a.ibc_store().commitment_prefix(), - ); - - let proof_conn_end_on_a = self - .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 = self - .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 = self - .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: Version::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(), - })); - - self.ctx_b - .deliver(&mut self.router_b, msg_for_b) - .expect("success"); - - let Some(IbcEvent::OpenTryConnection(open_try_connection_event)) = - self.ctx_b.ibc_store().events.lock().last().cloned() - else { - panic!("unexpected event") - }; - self.update_client_on_a(client_id_on_a.clone(), signer.clone()); - - let conn_id_on_b = open_try_connection_event.conn_id_on_b().clone(); - let proofs_height_on_b = self.ctx_b.latest_height(); - - let client_state_of_a_on_b = self - .ctx_b - .ibc_store() - .client_state(&client_id_on_b) - .unwrap(); - - let consensus_height_of_a_on_b = client_state_of_a_on_b.latest_height(); - - let proof_conn_end_on_b = self - .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 = self - .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 = self - .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: Version::compatibles()[0].clone(), - signer: signer.clone(), - proof_consensus_state_of_a: None, - })); - - self.ctx_a - .deliver(&mut self.router_a, msg_for_a) - .expect("success"); - - let Some(IbcEvent::OpenAckConnection(_)) = - self.ctx_a.ibc_store().events.lock().last().cloned() - else { - panic!("unexpected event") - }; - self.update_client_on_b(client_id_on_b.clone(), signer.clone()); - - let proof_height_on_a = self.ctx_a.latest_height(); - - let proof_conn_end_on_a = self - .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(), - })); - - self.ctx_b - .deliver(&mut self.router_b, msg_for_b) - .expect("success"); - - let Some(IbcEvent::OpenConfirmConnection(_)) = self.ctx_b.ibc_store().events.lock().last() - else { - panic!("unexpected event") - }; - self.update_client_on_a(client_id_on_a.clone(), signer.clone()); - - (conn_id_on_a, conn_id_on_b) - } -} - -pub fn integration_test_between_host_pair() -where - P: TestHost, - Q: TestHost, - HostClientState