From 0d592b7e5af3430f8a85c4aca7c8e2a2ece31022 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 10:38:05 +0100 Subject: [PATCH 01/22] non empty default CommitmentPrefix --- ibc-core/ics23-commitment/types/src/commitment.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ibc-core/ics23-commitment/types/src/commitment.rs b/ibc-core/ics23-commitment/types/src/commitment.rs index b24b60a88..da5580f86 100644 --- a/ibc-core/ics23-commitment/types/src/commitment.rs +++ b/ibc-core/ics23-commitment/types/src/commitment.rs @@ -144,11 +144,17 @@ impl<'a> TryFrom<&'a CommitmentProofBytes> for MerkleProof { )] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[derive(Clone, PartialEq, Eq, Hash, Default)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct CommitmentPrefix { bytes: Vec, } +impl Default for CommitmentPrefix { + fn default() -> Self { + Self { bytes: vec![0x00] } + } +} + impl CommitmentPrefix { pub fn as_bytes(&self) -> &[u8] { &self.bytes From 9bc3187dddd8b9cce162f93821e12c81b574319b Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 11:20:56 +0100 Subject: [PATCH 02/22] update ibc mock client types --- Cargo.toml | 3 ++ ibc-testkit/Cargo.toml | 1 + .../testapp/ibc/clients/mock/client_state.rs | 39 ++++++++++++------- .../ibc/clients/mock/consensus_state.rs | 12 ++---- .../src/testapp/ibc/clients/mock/header.rs | 18 ++++++--- ibc-testkit/src/testapp/ibc/mod.rs | 1 + ibc-testkit/src/testapp/ibc/utils.rs | 30 ++++++++++++++ 7 files changed, 77 insertions(+), 27 deletions(-) create mode 100644 ibc-testkit/src/testapp/ibc/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 6007c4f27..9c47412a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,3 +105,6 @@ tendermint-testgen = { version = "0.34.0", default-features = fals # parity dependencies parity-scale-codec = { version = "3.6.5", default-features = false, features = ["full"] } scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } + +[patch.crates-io] +ibc-proto = { path = "../ibc-proto-rs" } diff --git a/ibc-testkit/Cargo.toml b/ibc-testkit/Cargo.toml index 032f03cdb..c5343396d 100644 --- a/ibc-testkit/Cargo.toml +++ b/ibc-testkit/Cargo.toml @@ -35,6 +35,7 @@ ibc-proto = { workspace = true } # cosmos dependencies tendermint = { workspace = true } tendermint-testgen = { workspace = true } +tendermint-proto = { workspace = true } [dev-dependencies] env_logger = "0.11.0" diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 34a387a34..84b04f2ff 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -21,9 +21,8 @@ use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use crate::testapp::ibc::clients::mock::header::MockHeader; use crate::testapp::ibc::clients::mock::misbehaviour::Misbehaviour; -use crate::testapp::ibc::clients::mock::proto::{ - ClientState as RawMockClientState, Header as RawMockHeader, -}; +use crate::testapp::ibc::clients::mock::proto::ClientState as RawMockClientState; +use crate::testapp::ibc::utils::{duration_gpb_to_ibc, duration_ibc_to_gbp}; pub const MOCK_CLIENT_STATE_TYPE_URL: &str = "/ibc.mock.ClientState"; pub const MOCK_CLIENT_TYPE: &str = "9999-mock"; @@ -39,14 +38,18 @@ pub fn client_type() -> ClientType { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct MockClientState { pub header: MockHeader, - pub frozen_height: Option, + pub max_clock_drift: Option, + pub trusing_period: Option, + pub frozen: bool, } impl MockClientState { pub fn new(header: MockHeader) -> Self { Self { header, - frozen_height: None, + max_clock_drift: None, + trusing_period: None, + frozen: false, } } @@ -58,15 +61,15 @@ impl MockClientState { None } - pub fn with_frozen_height(self, frozen_height: Height) -> Self { + pub fn with_frozen_height(self, _frozen_height: Height) -> Self { Self { - frozen_height: Some(frozen_height), + frozen: true, ..self } } pub fn is_frozen(&self) -> bool { - self.frozen_height.is_some() + self.frozen } fn expired(&self, _elapsed: Duration) -> bool { @@ -80,17 +83,27 @@ impl TryFrom for MockClientState { type Error = ClientError; fn try_from(raw: RawMockClientState) -> Result { - Ok(Self::new(raw.header.expect("Never fails").try_into()?)) + Ok(Self { + header: raw + .header + .ok_or(ClientError::Other { + description: "header is not present".into(), + })? + .try_into()?, + max_clock_drift: raw.max_clock_drift.map(duration_gpb_to_ibc), + trusing_period: raw.trusing_period.map(duration_gpb_to_ibc), + frozen: raw.frozen, + }) } } impl From for RawMockClientState { fn from(value: MockClientState) -> Self { RawMockClientState { - header: Some(RawMockHeader { - height: Some(value.header.height().into()), - timestamp: value.header.timestamp.nanoseconds(), - }), + header: Some(value.header.into()), + max_clock_drift: value.max_clock_drift.map(duration_ibc_to_gbp), + trusing_period: value.trusing_period.map(duration_ibc_to_gbp), + frozen: value.frozen, } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs index 8a2e31e58..ca6f33e09 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs @@ -6,9 +6,7 @@ use ibc::core::primitives::Timestamp; use ibc::primitives::proto::{Any, Protobuf}; use crate::testapp::ibc::clients::mock::header::MockHeader; -use crate::testapp::ibc::clients::mock::proto::{ - ConsensusState as RawMockConsensusState, Header as RawMockHeader, -}; +use crate::testapp::ibc::clients::mock::proto::ConsensusState as RawMockConsensusState; pub const MOCK_CONSENSUS_STATE_TYPE_URL: &str = "/ibc.mock.ConsensusState"; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -40,7 +38,7 @@ impl TryFrom for MockConsensusState { let raw_header = raw.header.ok_or(ClientError::MissingRawConsensusState)?; Ok(Self { - header: MockHeader::try_from(raw_header)?, + header: raw_header.try_into()?, root: CommitmentRoot::from(vec![0]), }) } @@ -49,10 +47,8 @@ impl TryFrom for MockConsensusState { impl From for RawMockConsensusState { fn from(value: MockConsensusState) -> Self { RawMockConsensusState { - header: Some(RawMockHeader { - height: Some(value.header.height().into()), - timestamp: value.header.timestamp.nanoseconds(), - }), + header: Some(value.header.into()), + bytes: value.root.into_vec(), } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index 072b7bba7..75f97b316 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -7,6 +7,7 @@ use ibc::core::primitives::Timestamp; use ibc::primitives::proto::{Any, Protobuf}; use crate::testapp::ibc::clients::mock::proto::Header as RawMockHeader; +use crate::testapp::ibc::utils::{timestamp_gpb_to_ibc, timestamp_ibc_to_gpb}; pub const MOCK_HEADER_TYPE_URL: &str = "/ibc.mock.Header"; @@ -45,11 +46,16 @@ impl TryFrom for MockHeader { Ok(MockHeader { height: raw .height - .and_then(|raw_height| raw_height.try_into().ok()) - .ok_or(ClientError::MissingClientMessage)?, - - timestamp: Timestamp::from_nanoseconds(raw.timestamp) - .map_err(ClientError::InvalidPacketTimestamp)?, + .ok_or(ClientError::Other { + description: "missing height".into(), + })? + .try_into()?, + timestamp: raw + .timestamp + .map(timestamp_gpb_to_ibc) + .ok_or(ClientError::Other { + description: "missing timestamp".into(), + })?, }) } } @@ -58,7 +64,7 @@ impl From for RawMockHeader { fn from(value: MockHeader) -> Self { RawMockHeader { height: Some(value.height.into()), - timestamp: value.timestamp.nanoseconds(), + timestamp: Some(timestamp_ibc_to_gpb(value.timestamp)), } } } diff --git a/ibc-testkit/src/testapp/ibc/mod.rs b/ibc-testkit/src/testapp/ibc/mod.rs index f13a3c645..fb9937e58 100644 --- a/ibc-testkit/src/testapp/ibc/mod.rs +++ b/ibc-testkit/src/testapp/ibc/mod.rs @@ -1,3 +1,4 @@ pub mod applications; pub mod clients; pub mod core; +pub mod utils; diff --git a/ibc-testkit/src/testapp/ibc/utils.rs b/ibc-testkit/src/testapp/ibc/utils.rs new file mode 100644 index 000000000..de04df3ef --- /dev/null +++ b/ibc-testkit/src/testapp/ibc/utils.rs @@ -0,0 +1,30 @@ +use core::time::Duration; + +use ibc::primitives::Timestamp; +use ibc_proto::google::protobuf::{Duration as GDuration, Timestamp as GTimestamp}; + +pub fn timestamp_gpb_to_ibc(gpb_timestamp: GTimestamp) -> Timestamp { + let GTimestamp { seconds, nanos } = gpb_timestamp; + Timestamp::from_nanoseconds(seconds as u64 * 1_000_000_000 + nanos as u64) + .expect("no hmm overflow") +} + +pub fn timestamp_ibc_to_gpb(ibc_timestamp: Timestamp) -> GTimestamp { + let tendermint_proto::google::protobuf::Timestamp { seconds, nanos } = ibc_timestamp + .into_tm_time() + .unwrap_or_else(|| tendermint::Time::from_unix_timestamp(0, 0).expect("no overflow")) + .into(); + GTimestamp { seconds, nanos } +} + +pub fn duration_gpb_to_ibc(gbp_duration: GDuration) -> Duration { + let GDuration { seconds, nanos } = gbp_duration; + Duration::from_nanos(seconds as u64 * 1_000_000_000 + nanos as u64) +} + +pub fn duration_ibc_to_gbp(ibc_duration: Duration) -> GDuration { + GDuration { + seconds: ibc_duration.as_secs() as i64, + nanos: ibc_duration.subsec_nanos() as i32, + } +} From e389a9fda0cedce3f5be59e916968db7c6bbbd09 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 11:28:47 +0100 Subject: [PATCH 03/22] fix tests for updated mock types --- ibc-testkit/src/fixtures/core/client/mod.rs | 2 +- ibc-testkit/src/testapp/ibc/clients/mock/header.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/fixtures/core/client/mod.rs b/ibc-testkit/src/fixtures/core/client/mod.rs index a536d778c..f55b9eae7 100644 --- a/ibc-testkit/src/fixtures/core/client/mod.rs +++ b/ibc-testkit/src/fixtures/core/client/mod.rs @@ -52,7 +52,7 @@ mod tests { "07-tendermint", "0-5", "0-5,0-7", - "0a102f6962632e6d6f636b2e48656164657212040a021005", + "0a102f6962632e6d6f636b2e48656164657212060a0210051200", ]; let tests: Vec = vec![ diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index 75f97b316..ac23f5ef7 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -136,7 +136,7 @@ mod tests { &bytes, &[ 10, 16, 47, 105, 98, 99, 46, 109, 111, 99, 107, 46, 72, 101, 97, 100, 101, 114, 18, - 6, 10, 4, 8, 1, 16, 10 + 8, 10, 4, 8, 1, 16, 10, 18, 0 ] ); } From 8dc3299f382b3e7eba9254a2db00566606a8cdde Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 11:40:00 +0100 Subject: [PATCH 04/22] generalize MockContextConfig::into --- ibc-testkit/src/fixtures/core/context.rs | 2 +- ibc-testkit/src/relayer/context.rs | 6 +-- .../tests/core/ics02_client/update_client.rs | 38 +++++++++---------- .../core/ics03_connection/conn_open_ack.rs | 2 +- .../core/ics03_connection/conn_open_try.rs | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index a886777e7..6ee78f116 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -16,7 +16,7 @@ use crate::testapp::ibc::core::types::{MockContext, MockIbcStore, DEFAULT_BLOCK_ /// Configuration of the `MockContext` type for generating dummy contexts. #[derive(Debug, TypedBuilder)] -#[builder(build_method(into = MockContext))] +#[builder(build_method(into))] pub struct MockContextConfig { #[builder(default = HostType::Mock)] host_type: HostType, diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index d41331345..c8e8d3bcc 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -59,7 +59,7 @@ mod tests { use crate::relayer::error::RelayerError; use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type; use crate::testapp::ibc::core::router::MockRouter; - use crate::testapp::ibc::core::types::MockClientConfig; + use crate::testapp::ibc::core::types::{MockClientConfig, MockContext}; /// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` /// context, assuming that the latest header on the source context is `src_header`. @@ -126,7 +126,7 @@ mod tests { let mut ctx_a = MockContextConfig::builder() .host_id(chain_id_a.clone()) .latest_height(chain_a_start_height) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -142,7 +142,7 @@ mod tests { .host_id(chain_id_b) .host_type(HostType::SyntheticTendermint) .latest_height(chain_b_start_height) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_a) diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 8d9707566..22eb77471 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -101,7 +101,7 @@ fn test_consensus_state_pruning() { .latest_height(client_height) .latest_timestamp(Timestamp::now()) .max_history_size(u64::MAX) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id.clone()) @@ -217,7 +217,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { let mut ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -233,7 +233,7 @@ fn test_update_synthetic_tendermint_client_adjacent_ok() { .host_id(chain_id_b) .host_type(HostType::SyntheticTendermint) .latest_height(update_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -273,7 +273,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( // client state initialized with client_height, and // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. @@ -320,7 +320,7 @@ fn test_update_synthetic_tendermint_client_validator_change_ok() { .latest_height(update_height) .max_history_size(ctx_b_val_history.len() as u64 - 1) .validator_set_history(ctx_b_val_history) - .build(); + .build::(); let signer = dummy_account_id(); @@ -365,7 +365,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { let ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( // client state initialized with client_height, and // [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set. @@ -413,7 +413,7 @@ fn test_update_synthetic_tendermint_client_validator_change_fail() { .latest_height(update_height) .max_history_size(ctx_b_val_history.len() as u64 - 1) .validator_set_history(ctx_b_val_history) - .build(); + .build::(); let signer = dummy_account_id(); @@ -450,7 +450,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { let mut ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -470,7 +470,7 @@ fn test_update_synthetic_tendermint_client_non_adjacent_ok() { .host_id(chain_id_b) .host_type(HostType::SyntheticTendermint) .latest_height(update_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -515,7 +515,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { let mut ctx_a = MockContextConfig::builder() .host_id(ctx_a_chain_id) .latest_height(start_height) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(ctx_b_chain_id.clone()) @@ -532,7 +532,7 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { .host_id(ctx_b_chain_id) .host_type(HostType::SyntheticTendermint) .latest_height(client_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -648,7 +648,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { let ctx = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(chain_start_height) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_id(client_id.clone()) @@ -663,7 +663,7 @@ fn test_update_synthetic_tendermint_client_lower_height() { .host_id(ChainId::new("mockgaiaB-1").unwrap()) .host_type(HostType::SyntheticTendermint) .latest_height(client_height) - .build(); + .build::(); let signer = dummy_account_id(); @@ -818,7 +818,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -835,7 +835,7 @@ fn test_misbehaviour_synthetic_tendermint_equivocation() { .host_id(chain_id_b.clone()) .host_type(HostType::SyntheticTendermint) .latest_height(misbehaviour_height) - .build(); + .build::(); // Get chain-B's header at `misbehaviour_height` let header1: TmHeader = { @@ -880,7 +880,7 @@ fn test_misbehaviour_synthetic_tendermint_bft_time() { let mut ctx_a = MockContextConfig::builder() .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -949,7 +949,7 @@ fn test_expired_client() { .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -987,7 +987,7 @@ fn test_client_update_max_clock_drift() { .host_id(ChainId::new("mockgaiaA-1").unwrap()) .latest_height(Height::new(1, 1).unwrap()) .latest_timestamp(timestamp) - .build() + .build::() .with_client_config( MockClientConfig::builder() .client_chain_id(chain_id_b.clone()) @@ -1007,7 +1007,7 @@ fn test_client_update_max_clock_drift() { .latest_height(client_height) .latest_timestamp(timestamp) .max_history_size(u64::MAX) - .build(); + .build::(); while ctx_b.host_timestamp().expect("no error") < (ctx_a.host_timestamp().expect("no error") + max_clock_drift).expect("no error") diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs index 9bfa34469..de2386e8d 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_ack.rs @@ -61,7 +61,7 @@ fn conn_open_ack_fixture(ctx: Ctx) -> Fixture { let ctx_new = MockContextConfig::builder() .host_id(ChainId::new(&format!("mockgaia-{}", latest_height.revision_number())).unwrap()) .latest_height(latest_height) - .build(); + .build::(); let ctx = match ctx { Ctx::New => ctx_new, Ctx::NewWithConnection => ctx_new diff --git a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs index 5afca6581..9800aa789 100644 --- a/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs +++ b/ibc-testkit/tests/core/ics03_connection/conn_open_try.rs @@ -53,7 +53,7 @@ fn conn_open_try_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture(); let ctx = match ctx_variant { Ctx::Default => MockContext::default(), Ctx::WithClient => ctx_new.with_client_config( From e9d07226a38f0584ec003cf2b13a1bbbb2a74e04 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 11:56:35 +0100 Subject: [PATCH 05/22] rm clone from MockContext --- ibc-testkit/src/fixtures/mod.rs | 2 +- ibc-testkit/src/testapp/ibc/core/types.rs | 22 +------------------ .../tests/core/ics02_client/upgrade_client.rs | 2 +- .../core/ics03_connection/conn_open_init.rs | 2 +- .../tests/core/ics04_channel/send_packet.rs | 19 ++++++---------- 5 files changed, 11 insertions(+), 36 deletions(-) diff --git a/ibc-testkit/src/fixtures/mod.rs b/ibc-testkit/src/fixtures/mod.rs index af14cadb9..709cb4206 100644 --- a/ibc-testkit/src/fixtures/mod.rs +++ b/ibc-testkit/src/fixtures/mod.rs @@ -12,7 +12,7 @@ pub enum Expect { Failure(Option), } -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Fixture { pub ctx: MockContext, pub msg: M, diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 603d6d00d..0c8365a0f 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -42,7 +42,7 @@ use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; pub const DEFAULT_BLOCK_TIME_SECS: u64 = 3; /// An object that stores all IBC related data. -#[derive(Clone, Debug, Default)] +#[derive(Debug, Default)] pub struct MockIbcStore { /// The set of all clients, indexed by their id. pub clients: BTreeMap, @@ -153,26 +153,6 @@ impl Default for MockContext { } } -/// A manual clone impl is provided because the tests are oblivious to the fact that the `ibc_store` -/// is a shared ptr. -impl Clone for MockContext { - fn clone(&self) -> Self { - let ibc_store = { - let ibc_store = self.ibc_store.lock().clone(); - Arc::new(Mutex::new(ibc_store)) - }; - - Self { - host_chain_type: self.host_chain_type, - host_chain_id: self.host_chain_id.clone(), - max_history_size: self.max_history_size, - history: self.history.clone(), - block_time: self.block_time, - ibc_store, - } - } -} - /// Implementation of internal interface for use in testing. The methods in this interface should /// _not_ be accessible to any Ics handler. impl MockContext { diff --git a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs index 84d5b1fbd..7b58f98e6 100644 --- a/ibc-testkit/tests/core/ics02_client/upgrade_client.rs +++ b/ibc-testkit/tests/core/ics02_client/upgrade_client.rs @@ -34,7 +34,7 @@ fn msg_upgrade_client_fixture(ctx_variant: Ctx, msg_variant: Msg) -> Fixture = vec![ Test { name: "Processing fails because no channel exists in the context".to_string(), - ctx: context.clone(), + ctx: MockContext::default(), packet: packet.clone(), want_pass: false, }, Test { name: "Good parameters".to_string(), - ctx: context - .clone() + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -109,8 +106,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height same as destination chain height".to_string(), - ctx: context - .clone() + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -128,8 +124,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout height one more than destination chain height".to_string(), - ctx: context - .clone() + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -147,7 +142,7 @@ fn send_packet_processing() { }, Test { name: "Packet timeout due to timestamp".to_string(), - ctx: context + ctx: MockContext::default() .with_client_config( MockClientConfig::builder() .latest_height(client_height) @@ -173,7 +168,7 @@ fn send_packet_processing() { "send_packet: test passed but was supposed to fail for test: {}, \nparams {:?} {:?}", test.name, test.packet.clone(), - test.ctx.clone() + test.ctx ); let ibc_events = test.ctx.get_events(); @@ -194,7 +189,7 @@ fn send_packet_processing() { "send_packet: did not pass test: {}, \nparams {:?} {:?} error: {:?}", test.name, test.packet.clone(), - test.ctx.clone(), + test.ctx, e, ); } From aa44d48f776e8aa0ab72c7fd412f3419392ad21f Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 12:16:08 +0100 Subject: [PATCH 06/22] add with_{client,consensus}_state --- ibc-testkit/src/testapp/ibc/core/types.rs | 46 +++++++++++++++---- .../tests/core/ics02_client/update_client.rs | 9 +--- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 0c8365a0f..ff09856af 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -6,6 +6,8 @@ use core::cmp::min; use core::fmt::Debug; use core::ops::{Add, Sub}; use core::time::Duration; +use ibc::core::client::context::ClientExecutionContext; +use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath}; use ibc::clients::tendermint::client_state::ClientState as TmClientState; use ibc::clients::tendermint::types::TENDERMINT_CLIENT_TYPE; @@ -423,7 +425,31 @@ impl MockContext { ) } - pub fn with_client_config(self, client: MockClientConfig) -> Self { + pub fn with_client_state(mut self, client_id: &ClientId, client_state: AnyClientState) -> Self { + let client_state_path = ClientStatePath::new(client_id); + self.store_client_state(client_state_path, client_state) + .expect("error writing to store"); + self + } + + pub fn with_consensus_state( + mut self, + client_id: &ClientId, + height: Height, + consensus_state: AnyConsensusState, + ) -> Self { + let consensus_state_path = ClientConsensusStatePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); + self.store_consensus_state(consensus_state_path, consensus_state) + .expect("error writing to store"); + + self + } + + pub fn with_client_config(mut self, client: MockClientConfig) -> Self { let cs_heights = if client.consensus_state_heights.is_empty() { vec![client.latest_height] } else { @@ -436,7 +462,10 @@ impl MockContext { .then(|| a.revision_height() - b.revision_height()) } - let (client_state, consensus_states) = match client.client_type.as_str() { + let (client_state, consensus_states): ( + AnyClientState, + BTreeMap, + ) = match client.client_type.as_str() { MOCK_CLIENT_TYPE => { let blocks: Vec<_> = cs_heights .into_iter() @@ -508,15 +537,12 @@ impl MockContext { _ => panic!("unknown client type"), }; - let client_record = MockClientRecord { - client_state: Some(client_state), - consensus_states, - }; + self = self.with_client_state(&client.client_id, client_state); + + for (height, consensus_state) in consensus_states { + self = self.with_consensus_state(&client.client_id, height, consensus_state); + } - self.ibc_store - .lock() - .clients - .insert(client.client_id.clone(), client_record); self } diff --git a/ibc-testkit/tests/core/ics02_client/update_client.rs b/ibc-testkit/tests/core/ics02_client/update_client.rs index 22eb77471..cce2a9161 100644 --- a/ibc-testkit/tests/core/ics02_client/update_client.rs +++ b/ibc-testkit/tests/core/ics02_client/update_client.rs @@ -602,14 +602,9 @@ fn test_update_synthetic_tendermint_client_duplicate_ok() { ClientState::from(client_state).into() }; - let mut ibc_store = ctx_a.ibc_store.lock(); - let client_record = ibc_store.clients.get_mut(&client_id).unwrap(); + ctx_a = ctx_a.with_client_state(&client_id, client_state); - client_record - .consensus_states - .insert(client_height, consensus_state); - - client_record.client_state = Some(client_state); + ctx_a = ctx_a.with_consensus_state(&client_id, client_height, consensus_state); } let latest_header_height = block.height(); From 8a8c448bc2ee389fe8858135d2400b9de5331a64 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 12:32:14 +0100 Subject: [PATCH 07/22] add blocks_since in utils.rs --- ibc-testkit/src/testapp/ibc/core/types.rs | 7 +------ ibc-testkit/src/testapp/ibc/utils.rs | 6 ++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index ff09856af..28ca26f95 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -41,6 +41,7 @@ use crate::testapp::ibc::clients::mock::client_state::{ use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use crate::testapp::ibc::clients::mock::header::MockHeader; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::testapp::ibc::utils::blocks_since; pub const DEFAULT_BLOCK_TIME_SECS: u64 = 3; /// An object that stores all IBC related data. @@ -456,12 +457,6 @@ impl MockContext { client.consensus_state_heights }; - fn blocks_since(a: Height, b: Height) -> Option { - (a.revision_number() == b.revision_number() - && a.revision_height() >= b.revision_height()) - .then(|| a.revision_height() - b.revision_height()) - } - let (client_state, consensus_states): ( AnyClientState, BTreeMap, diff --git a/ibc-testkit/src/testapp/ibc/utils.rs b/ibc-testkit/src/testapp/ibc/utils.rs index de04df3ef..d4e86321c 100644 --- a/ibc-testkit/src/testapp/ibc/utils.rs +++ b/ibc-testkit/src/testapp/ibc/utils.rs @@ -1,5 +1,6 @@ use core::time::Duration; +use ibc::core::client::types::Height; use ibc::primitives::Timestamp; use ibc_proto::google::protobuf::{Duration as GDuration, Timestamp as GTimestamp}; @@ -28,3 +29,8 @@ pub fn duration_ibc_to_gbp(ibc_duration: Duration) -> GDuration { nanos: ibc_duration.subsec_nanos() as i32, } } + +pub fn blocks_since(a: Height, b: Height) -> Option { + (a.revision_number() == b.revision_number() && a.revision_height() >= b.revision_height()) + .then(|| a.revision_height() - b.revision_height()) +} From e213d2de9528ae3521d28ccd7da794377d6ff2ef Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 12:40:17 +0100 Subject: [PATCH 08/22] client takes host timestamp by default --- ibc-testkit/src/testapp/ibc/core/types.rs | 29 +++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 28ca26f95..3106f81c0 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -136,8 +136,8 @@ pub struct MockClientConfig { latest_height: Height, #[builder(default)] consensus_state_heights: Vec, - #[builder(default = Timestamp::now())] - latest_timestamp: Timestamp, + #[builder(default, setter(strip_option))] + latest_timestamp: Option, #[builder(default = Duration::from_secs(64000))] trusting_period: Duration, @@ -457,6 +457,10 @@ impl MockContext { client.consensus_state_heights }; + let client_latest_timestamp = client + .latest_timestamp + .unwrap_or_else(|| self.latest_timestamp()); + let (client_state, consensus_states): ( AnyClientState, BTreeMap, @@ -470,8 +474,7 @@ impl MockContext { ( cs_height, MockHeader::new(cs_height).with_timestamp( - client - .latest_timestamp + client_latest_timestamp .sub(self.block_time * (n_blocks as u32)) .expect("never fails"), ), @@ -480,7 +483,7 @@ impl MockContext { .collect(); let client_state = MockClientState::new( - MockHeader::new(client.latest_height).with_timestamp(client.latest_timestamp), + MockHeader::new(client.latest_height).with_timestamp(client_latest_timestamp), ); let cs_states = blocks @@ -501,8 +504,7 @@ impl MockContext { HostBlock::generate_tm_block( client.client_chain_id.clone(), cs_height.revision_height(), - client - .latest_timestamp + client_latest_timestamp .sub(self.block_time * (n_blocks as u32)) .expect("never fails"), ), @@ -765,6 +767,19 @@ impl MockContext { self.ibc_store.clone() } + pub fn latest_timestamp(&self) -> Timestamp { + self.host_block(&self.latest_height()) + .expect("Never fails") + .timestamp() + } + + pub fn timestamp_at(&self, height: Height) -> Timestamp { + let n_blocks = blocks_since(self.latest_height(), height).expect("less or equal height"); + self.latest_timestamp() + .sub(self.block_time * (n_blocks as u32)) + .expect("Never fails") + } + pub fn query_latest_header(&self) -> Option { let block_ref = self.host_block(&self.host_height().expect("Never fails")); block_ref.cloned() From 0f80861287530e9f81465dd41f1c65dfcebfedee Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 12:48:27 +0100 Subject: [PATCH 09/22] refactor relayer context test --- ibc-testkit/src/relayer/context.rs | 47 +++++++++++++++++------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index c8e8d3bcc..c374c83c2 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -126,30 +126,37 @@ mod tests { let mut ctx_a = MockContextConfig::builder() .host_id(chain_id_a.clone()) .latest_height(chain_a_start_height) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_b.clone()) - .client_id(client_on_a_for_b.clone()) - .latest_height(client_on_a_for_b_height) - .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. - .build(), - ); - // dummy; not actually used in client updates - let mut router_a = MockRouter::new_with_transfer(); + .build::(); let mut ctx_b = MockContextConfig::builder() - .host_id(chain_id_b) + .host_id(chain_id_b.clone()) .host_type(HostType::SyntheticTendermint) .latest_height(chain_b_start_height) - .build::() - .with_client_config( - MockClientConfig::builder() - .client_chain_id(chain_id_a) - .client_id(client_on_b_for_a.clone()) - .latest_height(client_on_b_for_a_height) - .build(), - ); + .latest_timestamp(ctx_a.timestamp_at(chain_a_start_height.decrement().unwrap())) // chain B is running slower than chain A + .build::(); + + ctx_a = ctx_a.with_client_config( + MockClientConfig::builder() + .client_chain_id(chain_id_b) + .client_id(client_on_a_for_b.clone()) + .latest_height(client_on_a_for_b_height) + .latest_timestamp(ctx_b.timestamp_at(client_on_a_for_b_height)) + .client_type(tm_client_type()) // The target host chain (B) is synthetic TM. + .build(), + ); + + ctx_b = ctx_b.with_client_config( + MockClientConfig::builder() + .client_chain_id(chain_id_a) + .client_id(client_on_b_for_a.clone()) + .latest_height(client_on_b_for_a_height) + .latest_timestamp(ctx_a.timestamp_at(client_on_b_for_a_height)) + .build(), + ); + + // dummy; not actually used in client updates + let mut router_a = MockRouter::new_with_transfer(); + // dummy; not actually used in client updates let mut router_b = MockRouter::new_with_transfer(); From c65c053f465c1b0150d035c33deb4b4d01d2b04f Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 12:51:23 +0100 Subject: [PATCH 10/22] fix few tests --- .../tests/core/ics04_channel/timeout.rs | 8 +++++++- ibc-testkit/tests/core/router.rs | 20 +++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/ibc-testkit/tests/core/ics04_channel/timeout.rs b/ibc-testkit/tests/core/ics04_channel/timeout.rs index ce5954abf..29675bb2e 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -1,3 +1,5 @@ +use std::ops::Sub; + use ibc::core::channel::types::channel::{ChannelEnd, Counterparty, Order, State}; use ibc::core::channel::types::commitment::{compute_packet_commitment, PacketCommitment}; use ibc::core::channel::types::msgs::{MsgTimeout, PacketMsg}; @@ -45,7 +47,11 @@ fn fixture() -> Fixture { let msg_proof_height = 2; let msg_timeout_height = 5; - let timeout_timestamp = Timestamp::now().nanoseconds(); + let timeout_timestamp = ctx + .latest_timestamp() + .sub(core::time::Duration::from_secs(3)) + .expect("no overflow") + .nanoseconds(); let msg = MsgTimeout::try_from(dummy_raw_msg_timeout( msg_proof_height, diff --git a/ibc-testkit/tests/core/router.rs b/ibc-testkit/tests/core/router.rs index 70756530f..fd6cd00e1 100644 --- a/ibc-testkit/tests/core/router.rs +++ b/ibc-testkit/tests/core/router.rs @@ -1,3 +1,5 @@ +use std::ops::Add; + use ibc::apps::transfer::handler::send_transfer; use ibc::apps::transfer::types::error::TokenTransferError; use ibc::apps::transfer::types::msgs::transfer::MsgTransfer; @@ -33,6 +35,7 @@ use ibc_testkit::fixtures::core::connection::{ dummy_msg_conn_open_ack, dummy_msg_conn_open_init, dummy_msg_conn_open_init_with_client_id, dummy_msg_conn_open_try, msg_conn_open_try_with_client_id, }; +use ibc_testkit::fixtures::core::context::MockContextConfig; use ibc_testkit::fixtures::core::signer::dummy_account_id; use ibc_testkit::testapp::ibc::applications::transfer::types::DummyTransferModule; use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; @@ -87,14 +90,23 @@ fn routing_module_and_keepers() { let upgrade_client_height_second = Height::new(1, 1).unwrap(); // We reuse this same context across all tests. Nothing in particular needs parametrizing. - let mut ctx = MockContext::default(); + let mut ctx = MockContextConfig::builder() + // a future timestamp, so that submitted packets are considered from past + // not more than 5 secs, as later dummy_raw_msg_timeout_on_close(*, 5) is used + .latest_timestamp( + Timestamp::now() + .add(core::time::Duration::from_secs(4)) + .unwrap(), + ) + .build::(); let mut router = MockRouter::new_with_transfer(); + let header = MockHeader::new(start_client_height).with_current_timestamp(); + let create_client_msg = MsgCreateClient::new( - MockClientState::new(MockHeader::new(start_client_height).with_current_timestamp()).into(), - MockConsensusState::new(MockHeader::new(start_client_height).with_current_timestamp()) - .into(), + MockClientState::new(header).into(), + MockConsensusState::new(header).into(), default_signer.clone(), ); From 68d8ab01e116f79ce85d8192ff4cf956cbb04400 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 13:03:43 +0100 Subject: [PATCH 11/22] add ibc-query and basecoin-store deps --- ibc-testkit/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ibc-testkit/Cargo.toml b/ibc-testkit/Cargo.toml index c5343396d..6bfdbcc7d 100644 --- a/ibc-testkit/Cargo.toml +++ b/ibc-testkit/Cargo.toml @@ -31,6 +31,8 @@ typed-builder = { version = "0.18.0" } # ibc dependencies ibc = { workspace = true, features = ["std"] } ibc-proto = { workspace = true } +ibc-query = { version = "0.50.0", path = "../ibc-query" } +basecoin-store = { package = "basecoin-store", path = "../../basecoin-rs/crates/store" } # cosmos dependencies tendermint = { workspace = true } From bbec93a39cbd6a439f50ae1fa8d94d397b9c9eec Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 13:04:22 +0100 Subject: [PATCH 12/22] add basecoin-store in mock ibc context --- ibc-testkit/src/testapp/ibc/clients/mod.rs | 9 +- .../src/testapp/ibc/core/client_ctx.rs | 415 ++++---- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 914 +++++++++++------- ibc-testkit/src/testapp/ibc/core/types.rs | 298 +++--- 4 files changed, 970 insertions(+), 666 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mod.rs b/ibc-testkit/src/testapp/ibc/clients/mod.rs index e5f7ad7be..d897d5338 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mod.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mod.rs @@ -1,5 +1,8 @@ pub mod mock; +use alloc::fmt::Debug; + +use basecoin_store::context::ProvableStore; use derive_more::{From, TryInto}; use ibc::clients::tendermint::client_state::ClientState as TmClientState; use ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; @@ -17,11 +20,11 @@ use crate::testapp::ibc::clients::mock::client_state::{ use crate::testapp::ibc::clients::mock::consensus_state::{ MockConsensusState, MOCK_CONSENSUS_STATE_TYPE_URL, }; -use crate::testapp::ibc::core::types::MockContext; +use crate::testapp::ibc::core::types::MockGenericContext; #[derive(Debug, Clone, From, PartialEq, ClientState)] -#[validation(MockContext)] -#[execution(MockContext)] +#[validation(MockGenericContext)] +#[execution(MockGenericContext)] pub enum AnyClientState { Tendermint(TmClientState), Mock(MockClientState), diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index 667aabdf8..3d75fbc0d 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -1,6 +1,9 @@ use alloc::collections::BTreeMap; use alloc::vec::Vec; +use core::fmt::Debug; +use basecoin_store::context::{ProvableStore, Store}; +use basecoin_store::types::Height as StoreHeight; use ibc::clients::tendermint::context::{ CommonContext as TmCommonContext, ValidationContext as TmValidationContext, }; @@ -9,13 +12,14 @@ use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; use ibc::core::handler::types::error::ContextError; use ibc::core::host::types::identifiers::{ChannelId, ClientId, PortId}; -use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath}; +use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath, Path}; use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; +use ibc::primitives::prelude::{format, *}; use crate::testapp::ibc::clients::mock::client_state::MockClientContext; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; -use crate::testapp::ibc::core::types::MockContext; +use crate::testapp::ibc::core::types::MockGenericContext; pub type PortChannelIdMap = BTreeMap>; @@ -30,7 +34,10 @@ pub struct MockClientRecord { pub consensus_states: BTreeMap, } -impl MockClientContext for MockContext { +impl MockClientContext for MockGenericContext +where + S: ProvableStore + Debug, +{ type ConversionError = &'static str; type AnyConsensusState = AnyConsensusState; @@ -49,285 +56,287 @@ impl MockClientContext for MockContext { ValidationContext::consensus_state(self, client_cons_state_path) } } - -impl TmCommonContext for MockContext { - type ConversionError = &'static str; - type AnyConsensusState = AnyConsensusState; - - fn host_timestamp(&self) -> Result { - ValidationContext::host_timestamp(self) - } - - fn host_height(&self) -> Result { - ValidationContext::host_height(self) - } - - fn consensus_state( - &self, - client_cons_state_path: &ClientConsensusStatePath, - ) -> Result { - ValidationContext::consensus_state(self, client_cons_state_path) - } - - fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { - let ibc_store = self.ibc_store.lock(); - let client_record = - ibc_store - .clients - .get(client_id) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?; - - let heights = client_record.consensus_states.keys().cloned().collect(); - - Ok(heights) - } -} - -impl TmValidationContext for MockContext { - fn next_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError> { - let ibc_store = self.ibc_store.lock(); - let client_record = - ibc_store - .clients - .get(client_id) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?; - - // Get the consensus state heights and sort them in ascending order. - let mut heights: Vec = client_record.consensus_states.keys().cloned().collect(); - heights.sort(); - - // Search for next state. - for h in heights { - if h > *height { - // unwrap should never happen, as the consensus state for h must exist - return Ok(Some( - client_record - .consensus_states - .get(&h) - .expect("Never fails") - .clone(), - )); - } - } - Ok(None) - } - - fn prev_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError> { - let ibc_store = self.ibc_store.lock(); - let client_record = - ibc_store - .clients - .get(client_id) - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - })?; - - // Get the consensus state heights and sort them in descending order. - let mut heights: Vec = client_record.consensus_states.keys().cloned().collect(); - heights.sort_by(|a, b| b.cmp(a)); - - // Search for previous state. - for h in heights { - if h < *height { - // unwrap should never happen, as the consensus state for h must exist - return Ok(Some( - client_record - .consensus_states - .get(&h) - .expect("Never fails") - .clone(), - )); - } - } - Ok(None) - } -} - -impl ClientValidationContext for MockContext { +impl ClientValidationContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Returns the time when the client state for the given [`ClientId`] was updated with a header for the given [`IbcHeight`] fn client_update_time( &self, client_id: &ClientId, height: &Height, ) -> Result { - match self + let processed_timestamp = self .ibc_store - .lock() .client_processed_times + .lock() .get(&(client_id.clone(), *height)) - { - Some(time) => Ok(*time), - None => Err(ClientError::ProcessedTimeNotFound { + .cloned() + .ok_or(ClientError::ProcessedTimeNotFound { client_id: client_id.clone(), height: *height, - })?, - } + })?; + Ok(processed_timestamp) } + /// Returns the height when the client state for the given [`ClientId`] was updated with a header for the given [`IbcHeight`] fn client_update_height( &self, client_id: &ClientId, height: &Height, ) -> Result { - match self + let processed_height = self .ibc_store - .lock() .client_processed_heights + .lock() .get(&(client_id.clone(), *height)) - { - Some(height) => Ok(*height), - None => Err(ClientError::ProcessedHeightNotFound { + .cloned() + .ok_or(ClientError::ProcessedHeightNotFound { client_id: client_id.clone(), height: *height, - })?, - } + })?; + Ok(processed_height) } } -impl ClientExecutionContext for MockContext { +impl ClientExecutionContext for MockGenericContext +where + S: ProvableStore + Debug, +{ type V = Self; + type AnyClientState = AnyClientState; + type AnyConsensusState = AnyConsensusState; + /// Called upon successful client creation and update fn store_client_state( &mut self, client_state_path: ClientStatePath, client_state: Self::AnyClientState, ) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - let client_id = client_state_path.0; - let client_record = ibc_store - .clients - .entry(client_id) - .or_insert(MockClientRecord { - consensus_states: Default::default(), - client_state: Default::default(), - }); - - client_record.client_state = Some(client_state); + self.ibc_store + .client_state_store + .set(client_state_path.clone(), client_state) + .map_err(|_| ClientError::Other { + description: "Client state store error".to_string(), + })?; Ok(()) } + /// Called upon successful client creation and update fn store_consensus_state( &mut self, consensus_state_path: ClientConsensusStatePath, consensus_state: Self::AnyConsensusState, ) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - let client_record = ibc_store - .clients - .entry(consensus_state_path.client_id) - .or_insert(MockClientRecord { - consensus_states: Default::default(), - client_state: Default::default(), - }); - - let height = Height::new( - consensus_state_path.revision_number, - consensus_state_path.revision_height, - ) - .expect("Never fails"); - client_record - .consensus_states - .insert(height, consensus_state); - + self.ibc_store + .consensus_state_store + .set(consensus_state_path, consensus_state) + .map_err(|_| ClientError::Other { + description: "Consensus state store error".to_string(), + })?; Ok(()) } - fn delete_consensus_state( + /// Called upon successful client update. + /// Implementations are expected to use this to record the specified time as the time at which + /// this update (or header) was processed. + fn store_update_time( &mut self, - consensus_state_path: ClientConsensusStatePath, + client_id: ClientId, + height: Height, + timestamp: Timestamp, ) -> Result<(), ContextError> { - let mut ibc_store = self.ibc_store.lock(); - - let client_record = ibc_store - .clients - .entry(consensus_state_path.client_id) - .or_insert(MockClientRecord { - consensus_states: Default::default(), - client_state: Default::default(), - }); - - let height = Height::new( - consensus_state_path.revision_number, - consensus_state_path.revision_height, - ) - .expect("Never fails"); - - client_record.consensus_states.remove(&height); - + self.ibc_store + .client_processed_times + .lock() + .insert((client_id, height), timestamp); Ok(()) } - fn delete_update_height( + /// Called upon successful client update. + /// Implementations are expected to use this to record the specified height as the height at + /// at which this update (or header) was processed. + fn store_update_height( &mut self, client_id: ClientId, height: Height, + host_height: Height, ) -> Result<(), ContextError> { - let _ = self - .ibc_store - .lock() + self.ibc_store .client_processed_heights - .remove(&(client_id, height)); - + .lock() + .insert((client_id, height), host_height); Ok(()) } + /// Delete the update time associated with the client at the specified height. fn delete_update_time( &mut self, client_id: ClientId, height: Height, ) -> Result<(), ContextError> { - let _ = self - .ibc_store - .lock() + self.ibc_store .client_processed_times + .lock() .remove(&(client_id, height)); - Ok(()) } - fn store_update_time( + /// Delete the update height associated with the client at the specified height. + fn delete_update_height( &mut self, client_id: ClientId, height: Height, - timestamp: Timestamp, ) -> Result<(), ContextError> { - let _ = self - .ibc_store + self.ibc_store + .client_processed_heights .lock() - .client_processed_times - .insert((client_id, height), timestamp); - + .remove(&(client_id, height)); Ok(()) } - fn store_update_height( + fn delete_consensus_state( &mut self, - client_id: ClientId, - height: Height, - host_height: Height, + consensus_state_path: ClientConsensusStatePath, ) -> Result<(), ContextError> { - let _ = self - .ibc_store - .lock() - .client_processed_heights - .insert((client_id, height), host_height); - + self.ibc_store + .consensus_state_store + .delete(consensus_state_path); Ok(()) } } + +impl TmCommonContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + type ConversionError = &'static str; + type AnyConsensusState = AnyConsensusState; + + fn host_timestamp(&self) -> Result { + ValidationContext::host_timestamp(self) + } + + fn host_height(&self) -> Result { + ValidationContext::host_height(self) + } + + fn consensus_state( + &self, + client_cons_state_path: &ClientConsensusStatePath, + ) -> Result { + ValidationContext::consensus_state(self, client_cons_state_path) + } + + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { + let path = format!("clients/{}/consensusStates", client_id) + .try_into() + .map_err(|_| ClientError::Other { + description: "Invalid consensus state path".into(), + })?; + + self.ibc_store + .consensus_state_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { + Some(consensus_path) + } else { + None + } + }) + .map(|consensus_path| { + let height = Height::new( + consensus_path.revision_number, + consensus_path.revision_height, + )?; + Ok(height) + }) + .collect() + } +} + +impl TmValidationContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + fn next_consensus_state( + &self, + client_id: &ClientId, + height: &Height, + ) -> Result, ContextError> { + let path = format!("clients/{client_id}/consensusStates") + .try_into() + .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers + + let keys = self.ibc_store.store.get_keys(&path); + let found_path = keys.into_iter().find_map(|path| { + if let Ok(Path::ClientConsensusState(path)) = path.try_into() { + if height + > &Height::new(path.revision_number, path.revision_height).expect("no error") + { + return Some(path); + } + } + None + }); + + if let Some(path) = found_path { + let consensus_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, &path) + .ok_or(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height: *height, + })?; + + Ok(Some(consensus_state)) + } else { + Ok(None) + } + } + + fn prev_consensus_state( + &self, + client_id: &ClientId, + height: &Height, + ) -> Result, ContextError> { + let path = format!("clients/{client_id}/consensusStates") + .try_into() + .unwrap(); // safety - path must be valid since ClientId and height are valid Identifiers + + let keys = self.ibc_store.store.get_keys(&path); + let pos = keys.iter().position(|path| { + if let Ok(Path::ClientConsensusState(path)) = path.clone().try_into() { + height + >= &Height::new(path.revision_number, path.revision_height).expect("no error") + } else { + false + } + }); + + if let Some(pos) = pos { + if pos > 0 { + let prev_path = match keys[pos - 1].clone().try_into() { + Ok(Path::ClientConsensusState(p)) => p, + _ => unreachable!(), // safety - path retrieved from store + }; + let consensus_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, &prev_path) + .ok_or(ClientError::ConsensusStateNotFound { + client_id: client_id.clone(), + height: *height, + })?; + return Ok(Some(consensus_state)); + } + } + Ok(None) + } +} diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 5c561d14e..c1773bbce 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -1,343 +1,263 @@ //! Implementation of a global context mock. Used in testing handlers of all IBC modules. -use core::ops::Add; +use alloc::fmt::Debug; use core::time::Duration; -use ibc::clients::tendermint::client_state::ClientState; -use ibc::core::channel::types::channel::ChannelEnd; +use basecoin_store::context::ProvableStore; +use basecoin_store::types::height::Height as StoreHeight; +use ibc::core::channel::types::channel::{ChannelEnd, IdentifiedChannelEnd}; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; use ibc::core::channel::types::error::{ChannelError, PacketError}; -use ibc::core::channel::types::packet::Receipt; +use ibc::core::channel::types::packet::{PacketState, Receipt}; +use ibc::core::client::context::consensus_state::ConsensusState; use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; use ibc::core::commitment_types::commitment::CommitmentPrefix; use ibc::core::connection::types::error::ConnectionError; -use ibc::core::connection::types::ConnectionEnd; +use ibc::core::connection::types::version::Version as ConnectionVersion; +use ibc::core::connection::types::{ConnectionEnd, IdentifiedConnectionEnd}; use ibc::core::handler::types::error::ContextError; use ibc::core::handler::types::events::IbcEvent; use ibc::core::host::types::identifiers::{ClientId, ConnectionId, Sequence}; use ibc::core::host::types::path::{ - AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, CommitmentPath, - ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, + AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, + CommitmentPath, ConnectionPath, Path, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; use ibc::core::host::{ExecutionContext, ValidationContext}; use ibc::core::primitives::prelude::*; use ibc::core::primitives::{Signer, Timestamp}; use ibc::primitives::proto::Any; +use ibc::primitives::ToVec; +use ibc_query::core::context::{ProvableContext, QueryContext}; -use super::types::MockContext; -use crate::testapp::ibc::clients::mock::client_state::MockClientState; +use super::types::MockGenericContext; use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState}; +use crate::testapp::ibc::utils::blocks_since; -impl ValidationContext for MockContext { +impl ValidationContext for MockGenericContext +where + S: ProvableStore + Debug, +{ type V = Self; type E = Self; type AnyConsensusState = AnyConsensusState; type AnyClientState = AnyClientState; fn client_state(&self, client_id: &ClientId) -> Result { - match self.ibc_store.lock().clients.get(client_id) { - Some(client_record) => { - client_record - .client_state - .clone() - .ok_or_else(|| ClientError::ClientStateNotFound { - client_id: client_id.clone(), - }) - } - None => Err(ClientError::ClientStateNotFound { + Ok(self + .ibc_store + .client_state_store + .get(StoreHeight::Pending, &ClientStatePath(client_id.clone())) + .ok_or(ClientError::ClientStateNotFound { client_id: client_id.clone(), - }), - } - .map_err(ContextError::ClientError) + })?) } fn decode_client_state(&self, client_state: Any) -> Result { - if let Ok(client_state) = ClientState::try_from(client_state.clone()) { - client_state.inner().validate().map_err(ClientError::from)?; - Ok(client_state.into()) - } else if let Ok(client_state) = MockClientState::try_from(client_state.clone()) { - Ok(client_state.into()) - } else { - Err(ClientError::UnknownClientStateType { - client_state_type: client_state.type_url, - }) - } - .map_err(ContextError::ClientError) + Ok(AnyClientState::try_from(client_state)?) } fn consensus_state( &self, client_cons_state_path: &ClientConsensusStatePath, - ) -> Result { - let client_id = &client_cons_state_path.client_id; + ) -> Result { let height = Height::new( client_cons_state_path.revision_number, client_cons_state_path.revision_height, - )?; - match self.ibc_store.lock().clients.get(client_id) { - Some(client_record) => match client_record.consensus_states.get(&height) { - Some(consensus_state) => Ok(consensus_state.clone()), - None => Err(ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), - height, - }), - }, - None => Err(ClientError::ConsensusStateNotFound { - client_id: client_id.clone(), + ) + .map_err(|_| ClientError::InvalidHeight)?; + let consensus_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, client_cons_state_path) + .ok_or(ClientError::ConsensusStateNotFound { + client_id: client_cons_state_path.client_id.clone(), height, - }), - } - .map_err(ContextError::ClientError) + })?; + + Ok(consensus_state) } fn host_height(&self) -> Result { - Ok(self.latest_height()) + // TODO(rano): height sync with block and merkle tree + Ok(self.history.last().expect("atleast one block").height()) } fn host_timestamp(&self) -> Result { - Ok(self - .history - .last() - .expect("history cannot be empty") - .timestamp() - .add(self.block_time) - .expect("Never fails")) + let host_height = self.host_height()?; + let host_cons_state = self.host_consensus_state(&host_height)?; + Ok(host_cons_state.timestamp()) } - fn host_consensus_state(&self, height: &Height) -> Result { - match self.host_block(height) { - Some(block_ref) => Ok(block_ref.clone().into()), - None => Err(ClientError::MissingLocalConsensusState { height: *height }), - } - .map_err(ConnectionError::Client) - .map_err(ContextError::ConnectionError) + fn host_consensus_state( + &self, + height: &Height, + ) -> Result { + // TODO(rano): height sync with block and merkle tree + let height_delta = blocks_since(self.host_height().expect("no error"), *height) + .expect("no error") as usize; + + let index = self + .history + .len() + .checked_sub(1 + height_delta) + .ok_or(ClientError::MissingLocalConsensusState { height: *height })?; + + let consensus_state = self.history[index].clone().into(); + Ok(consensus_state) } fn client_counter(&self) -> Result { - Ok(self.ibc_store.lock().client_ids_counter) + Ok(*self.ibc_store.client_counter.lock()) } - fn connection_end(&self, cid: &ConnectionId) -> Result { - match self.ibc_store.lock().connections.get(cid) { - Some(connection_end) => Ok(connection_end.clone()), - None => Err(ConnectionError::ConnectionNotFound { - connection_id: cid.clone(), - }), - } - .map_err(ContextError::ConnectionError) + fn connection_end(&self, conn_id: &ConnectionId) -> Result { + Ok(self + .ibc_store + .connection_end_store + .get(StoreHeight::Pending, &ConnectionPath::new(conn_id)) + .ok_or(ConnectionError::ConnectionNotFound { + connection_id: conn_id.clone(), + })?) } - fn validate_self_client( - &self, - client_state_of_host_on_counterparty: Any, - ) -> Result<(), ContextError> { - let mock_client_state = MockClientState::try_from(client_state_of_host_on_counterparty) - .map_err(|_| ConnectionError::InvalidClientState { - reason: "client must be a mock client".to_string(), - }) - .map_err(ContextError::ConnectionError)?; - - if mock_client_state.is_frozen() { - return Err(ClientError::ClientFrozen { - description: String::new(), - } - .into()); - } - - let self_chain_id = &self.host_chain_id; - let self_revision_number = self_chain_id.revision_number(); - if self_revision_number != mock_client_state.latest_height().revision_number() { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client is not in the same revision as the chain. expected: {}, got: {}", - self_revision_number, - mock_client_state.latest_height().revision_number() - ), - }, - )); - } - - let host_current_height = self.latest_height().increment(); - if mock_client_state.latest_height() >= host_current_height { - return Err(ContextError::ConnectionError( - ConnectionError::InvalidClientState { - reason: format!( - "client has latest height {} greater than or equal to chain height {}", - mock_client_state.latest_height(), - host_current_height - ), - }, - )); - } - + fn validate_self_client(&self, _counterparty_client_state: Any) -> Result<(), ContextError> { Ok(()) } fn commitment_prefix(&self) -> CommitmentPrefix { - CommitmentPrefix::try_from(b"mock".to_vec()).expect("Never fails") + // this is prefix of ibc store + // using default, as in our mock context, we don't store any other data + CommitmentPrefix::default() } fn connection_counter(&self) -> Result { - Ok(self.ibc_store.lock().connection_ids_counter) + Ok(*self.ibc_store.conn_counter.lock()) } - fn channel_end(&self, chan_end_path: &ChannelEndPath) -> Result { - let port_id = &chan_end_path.0; - let channel_id = &chan_end_path.1; + fn get_compatible_versions(&self) -> Vec { + vec![ConnectionVersion::default()] + } - match self + fn channel_end(&self, channel_end_path: &ChannelEndPath) -> Result { + let channel_end = self .ibc_store - .lock() - .channels - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(channel_end) => Ok(channel_end.clone()), - None => Err(ChannelError::ChannelNotFound { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::ChannelError) + .channel_end_store + .get( + StoreHeight::Pending, + &ChannelEndPath::new(&channel_end_path.0, &channel_end_path.1), + ) + .ok_or(ChannelError::MissingChannel)?; + Ok(channel_end) } fn get_next_sequence_send( &self, seq_send_path: &SeqSendPath, ) -> Result { - let port_id = &seq_send_path.0; - let channel_id = &seq_send_path.1; - - match self + let seq_send = self .ibc_store - .lock() - .next_sequence_send - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextSendSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) + .send_sequence_store + .get( + StoreHeight::Pending, + &SeqSendPath::new(&seq_send_path.0, &seq_send_path.1), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(seq_send) } fn get_next_sequence_recv( &self, seq_recv_path: &SeqRecvPath, ) -> Result { - let port_id = &seq_recv_path.0; - let channel_id = &seq_recv_path.1; - - match self + let seq_recv = self .ibc_store - .lock() - .next_sequence_recv - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextRecvSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) + .recv_sequence_store + .get( + StoreHeight::Pending, + &SeqRecvPath::new(&seq_recv_path.0, &seq_recv_path.1), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(seq_recv) } fn get_next_sequence_ack(&self, seq_ack_path: &SeqAckPath) -> Result { - let port_id = &seq_ack_path.0; - let channel_id = &seq_ack_path.1; - - match self + let seq_ack = self .ibc_store - .lock() - .next_sequence_ack - .get(port_id) - .and_then(|map| map.get(channel_id)) - { - Some(sequence) => Ok(*sequence), - None => Err(PacketError::MissingNextAckSeq { - port_id: port_id.clone(), - channel_id: channel_id.clone(), - }), - } - .map_err(ContextError::PacketError) + .ack_sequence_store + .get( + StoreHeight::Pending, + &SeqAckPath::new(&seq_ack_path.0, &seq_ack_path.1), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(seq_ack) } fn get_packet_commitment( &self, commitment_path: &CommitmentPath, ) -> Result { - let port_id = &commitment_path.port_id; - let channel_id = &commitment_path.channel_id; - let seq = &commitment_path.sequence; - - match self + let commitment = self .ibc_store - .lock() - .packet_commitment - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(commitment) => Ok(commitment.clone()), - None => Err(PacketError::PacketCommitmentNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) + .packet_commitment_store + .get( + StoreHeight::Pending, + &CommitmentPath::new( + &commitment_path.port_id, + &commitment_path.channel_id, + commitment_path.sequence, + ), + ) + .ok_or(PacketError::ImplementationSpecific)?; + Ok(commitment) } fn get_packet_receipt(&self, receipt_path: &ReceiptPath) -> Result { - let port_id = &receipt_path.port_id; - let channel_id = &receipt_path.channel_id; - let seq = &receipt_path.sequence; - - match self + let receipt = self .ibc_store - .lock() - .packet_receipt - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(receipt) => Ok(receipt.clone()), - None => Err(PacketError::PacketReceiptNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) + .packet_receipt_store + .is_path_set( + StoreHeight::Pending, + &ReceiptPath::new( + &receipt_path.port_id, + &receipt_path.channel_id, + receipt_path.sequence, + ), + ) + .then_some(Receipt::Ok) + .ok_or(PacketError::PacketReceiptNotFound { + sequence: receipt_path.sequence, + })?; + Ok(receipt) } fn get_packet_acknowledgement( &self, ack_path: &AckPath, ) -> Result { - let port_id = &ack_path.port_id; - let channel_id = &ack_path.channel_id; - let seq = &ack_path.sequence; - - match self + let ack = self .ibc_store - .lock() - .packet_acknowledgement - .get(port_id) - .and_then(|map| map.get(channel_id)) - .and_then(|map| map.get(seq)) - { - Some(ack) => Ok(ack.clone()), - None => Err(PacketError::PacketAcknowledgementNotFound { sequence: *seq }), - } - .map_err(ContextError::PacketError) - } - + .packet_ack_store + .get( + StoreHeight::Pending, + &AckPath::new(&ack_path.port_id, &ack_path.channel_id, ack_path.sequence), + ) + .ok_or(PacketError::PacketAcknowledgementNotFound { + sequence: ack_path.sequence, + })?; + Ok(ack) + } + + /// Returns a counter on the number of channel ids have been created thus far. + /// The value of this counter should increase only via method + /// `ChannelKeeper::increase_channel_counter`. fn channel_counter(&self) -> Result { - Ok(self.ibc_store.lock().channel_ids_counter) + Ok(*self.ibc_store.channel_counter.lock()) } + /// Returns the maximum expected time per block fn max_expected_time_per_block(&self) -> Duration { - self.block_time + Duration::from_secs(8) } fn validate_message_signer(&self, _signer: &Signer) -> Result<(), ContextError> { @@ -349,44 +269,428 @@ impl ValidationContext for MockContext { } } -impl ExecutionContext for MockContext { - fn get_client_execution_context(&mut self) -> &mut Self::E { - self +/// Trait to provide proofs in gRPC service blanket implementations. +impl ProvableContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Returns the proof for the given [`IbcHeight`] and [`Path`] + fn get_proof(&self, height: Height, path: &Path) -> Option> { + self.ibc_store + .store + .get_proof(height.revision_height().into(), &path.to_string().into()) + .map(|p| p.to_vec()) + } +} + +/// Trait to complete the gRPC service blanket implementations. +impl QueryContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Returns the list of all client states. + fn client_states(&self) -> Result, ContextError> { + let path = "clients".to_owned().into(); + + self.ibc_store + .client_state_store + .get_keys(&path) + .into_iter() + .filter_map(|path| { + if let Ok(Path::ClientState(client_path)) = path.try_into() { + Some(client_path) + } else { + None + } + }) + .map(|client_state_path| { + let client_state = self + .ibc_store + .client_state_store + .get(StoreHeight::Pending, &client_state_path) + .ok_or_else(|| ClientError::ClientStateNotFound { + client_id: client_state_path.0.clone(), + })?; + Ok((client_state_path.0, client_state)) + }) + .collect() + } + + /// Returns the list of all consensus states of the given client. + fn consensus_states( + &self, + client_id: &ClientId, + ) -> Result, ContextError> { + let path = format!("clients/{}/consensusStates", client_id) + .try_into() + .map_err(|_| ClientError::Other { + description: "Invalid consensus state path".into(), + })?; + + self.ibc_store + .consensus_state_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { + Some(consensus_path) + } else { + None + } + }) + .map(|consensus_path| { + let height = Height::new( + consensus_path.revision_number, + consensus_path.revision_height, + )?; + let client_state = self + .ibc_store + .consensus_state_store + .get(StoreHeight::Pending, &consensus_path) + .ok_or({ + ClientError::ConsensusStateNotFound { + client_id: consensus_path.client_id, + height, + } + })?; + Ok((height, client_state)) + }) + .collect() + } + + /// Returns the list of heights at which the consensus state of the given client was updated. + fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError> { + let path = format!("clients/{}/consensusStates", client_id) + .try_into() + .map_err(|_| ClientError::Other { + description: "Invalid consensus state path".into(), + })?; + + self.ibc_store + .consensus_state_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() { + Some(consensus_path) + } else { + None + } + }) + .map(|consensus_path| { + Ok(Height::new( + consensus_path.revision_number, + consensus_path.revision_height, + )?) + }) + .collect::, _>>() + } + + /// Connections queries all the IBC connections of a chain. + fn connection_ends(&self) -> Result, ContextError> { + let path = "connections".to_owned().into(); + + self.ibc_store + .connection_end_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Connection(connection_path)) = path.try_into() { + Some(connection_path) + } else { + None + } + }) + .map(|connection_path| { + let connection_end = self + .ibc_store + .connection_end_store + .get(StoreHeight::Pending, &connection_path) + .ok_or_else(|| ConnectionError::ConnectionNotFound { + connection_id: connection_path.0.clone(), + })?; + Ok(IdentifiedConnectionEnd { + connection_id: connection_path.0, + connection_end, + }) + }) + .collect() + } + + /// ClientConnections queries all the connection paths associated with a client. + fn client_connection_ends( + &self, + client_id: &ClientId, + ) -> Result, ContextError> { + let client_connection_path = ClientConnectionPath::new(client_id); + + Ok(self + .ibc_store + .connection_ids_store + .get(StoreHeight::Pending, &client_connection_path) + .unwrap_or_default()) + } + + /// Channels queries all the IBC channels of a chain. + fn channel_ends(&self) -> Result, ContextError> { + let path = "channelEnds".to_owned().into(); + + self.ibc_store + .channel_end_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::ChannelEnd(channel_path)) = path.try_into() { + Some(channel_path) + } else { + None + } + }) + .map(|channel_path| { + let channel_end = self + .ibc_store + .channel_end_store + .get(StoreHeight::Pending, &channel_path) + .ok_or_else(|| ChannelError::ChannelNotFound { + port_id: channel_path.0.clone(), + channel_id: channel_path.1.clone(), + })?; + Ok(IdentifiedChannelEnd { + port_id: channel_path.0, + channel_id: channel_path.1, + channel_end, + }) + }) + .collect() + } + + /// PacketCommitments returns all the packet commitments associated with a channel. + fn packet_commitments( + &self, + channel_end_path: &ChannelEndPath, + ) -> Result, ContextError> { + let path = format!( + "commitments/ports/{}/channels/{}/sequences", + channel_end_path.0, channel_end_path.1 + ) + .try_into() + .map_err(|_| PacketError::Other { + description: "Invalid commitment path".into(), + })?; + + self.ibc_store + .packet_commitment_store + .get_keys(&path) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Commitment(commitment_path)) = path.try_into() { + Some(commitment_path) + } else { + None + } + }) + .filter(|commitment_path| { + self.ibc_store + .packet_commitment_store + .get(StoreHeight::Pending, commitment_path) + .is_some() + }) + .map(|commitment_path| { + self.get_packet_commitment(&commitment_path) + .map(|packet| PacketState { + seq: commitment_path.sequence, + port_id: commitment_path.port_id, + chan_id: commitment_path.channel_id, + data: packet.as_ref().into(), + }) + }) + .collect::, _>>() } + /// PacketAcknowledgements returns all the packet acknowledgements associated with a channel. + /// Returns all the packet acknowledgements if sequences is empty. + fn packet_acknowledgements( + &self, + channel_end_path: &ChannelEndPath, + sequences: impl ExactSizeIterator, + ) -> Result, ContextError> { + let collected_paths: Vec<_> = if sequences.len() == 0 { + // if sequences is empty, return all the acks + let ack_path_prefix = format!( + "acks/ports/{}/channels/{}/sequences", + channel_end_path.0, channel_end_path.1 + ) + .try_into() + .map_err(|_| PacketError::Other { + description: "Invalid ack path".into(), + })?; + + self.ibc_store + .packet_ack_store + .get_keys(&ack_path_prefix) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Ack(ack_path)) = path.try_into() { + Some(ack_path) + } else { + None + } + }) + .collect() + } else { + sequences + .into_iter() + .map(|seq| AckPath::new(&channel_end_path.0, &channel_end_path.1, seq)) + .collect() + }; + + collected_paths + .into_iter() + .filter(|ack_path| { + self.ibc_store + .packet_ack_store + .get(StoreHeight::Pending, ack_path) + .is_some() + }) + .map(|ack_path| { + self.get_packet_acknowledgement(&ack_path) + .map(|packet| PacketState { + seq: ack_path.sequence, + port_id: ack_path.port_id, + chan_id: ack_path.channel_id, + data: packet.as_ref().into(), + }) + }) + .collect::, _>>() + } + + /// UnreceivedPackets returns all the unreceived IBC packets associated with + /// a channel and sequences. + fn unreceived_packets( + &self, + channel_end_path: &ChannelEndPath, + sequences: impl ExactSizeIterator, + ) -> Result, ContextError> { + // QUESTION. Currently only works for unordered channels; ordered channels + // don't use receipts. However, ibc-go does it this way. Investigate if + // this query only ever makes sense on unordered channels. + + Ok(sequences + .into_iter() + .map(|seq| ReceiptPath::new(&channel_end_path.0, &channel_end_path.1, seq)) + .filter(|receipt_path| { + self.ibc_store + .packet_receipt_store + .get(StoreHeight::Pending, receipt_path) + .is_none() + }) + .map(|receipts_path| receipts_path.sequence) + .collect()) + } + + /// UnreceivedAcks returns all the unreceived IBC acknowledgements associated with a channel and sequences. + /// Returns all the unreceived acks if sequences is empty. + fn unreceived_acks( + &self, + channel_end_path: &ChannelEndPath, + sequences: impl ExactSizeIterator, + ) -> Result, ContextError> { + let collected_paths: Vec<_> = if sequences.len() == 0 { + // if sequences is empty, return all the acks + let commitment_path_prefix = format!( + "commitments/ports/{}/channels/{}/sequences", + channel_end_path.0, channel_end_path.1 + ) + .try_into() + .map_err(|_| PacketError::Other { + description: "Invalid commitment path".into(), + })?; + + self.ibc_store + .packet_commitment_store + .get_keys(&commitment_path_prefix) + .into_iter() + .flat_map(|path| { + if let Ok(Path::Commitment(commitment_path)) = path.try_into() { + Some(commitment_path) + } else { + None + } + }) + .collect() + } else { + sequences + .into_iter() + .map(|seq| CommitmentPath::new(&channel_end_path.0, &channel_end_path.1, seq)) + .collect() + }; + + Ok(collected_paths + .into_iter() + .filter(|commitment_path: &CommitmentPath| -> bool { + self.ibc_store + .packet_commitment_store + .get(StoreHeight::Pending, commitment_path) + .is_some() + }) + .map(|commitment_path| commitment_path.sequence) + .collect()) + } +} + +impl ExecutionContext for MockGenericContext +where + S: ProvableStore + Debug, +{ + /// Called upon client creation. + /// Increases the counter which keeps track of how many clients have been created. + /// Should never fail. fn increase_client_counter(&mut self) -> Result<(), ContextError> { - self.ibc_store.lock().client_ids_counter += 1; + *self.ibc_store.client_counter.lock() += 1; Ok(()) } + /// Stores the given connection_end at path fn store_connection( &mut self, connection_path: &ConnectionPath, connection_end: ConnectionEnd, ) -> Result<(), ContextError> { - let connection_id = connection_path.0.clone(); self.ibc_store - .lock() - .connections - .insert(connection_id, connection_end); + .connection_end_store + .set(connection_path.clone(), connection_end) + .map_err(|_| ConnectionError::Other { + description: "Connection end store error".to_string(), + })?; Ok(()) } + /// Stores the given connection_id at a path associated with the client_id. fn store_connection_to_client( &mut self, client_connection_path: &ClientConnectionPath, conn_id: ConnectionId, ) -> Result<(), ContextError> { - let client_id = client_connection_path.0.clone(); + let mut conn_ids: Vec = self + .ibc_store + .connection_ids_store + .get(StoreHeight::Pending, client_connection_path) + .unwrap_or_default(); + conn_ids.push(conn_id); self.ibc_store - .lock() - .client_connections - .insert(client_id, conn_id); + .connection_ids_store + .set(client_connection_path.clone(), conn_ids) + .map_err(|_| ConnectionError::Other { + description: "Connection ids store error".to_string(), + })?; Ok(()) } + /// Called upon connection identifier creation (Init or Try process). + /// Increases the counter which keeps track of how many connections have been created. + /// Should never fail. fn increase_connection_counter(&mut self) -> Result<(), ContextError> { - self.ibc_store.lock().connection_ids_counter += 1; + *self.ibc_store.conn_counter.lock() += 1; Ok(()) } @@ -396,42 +700,26 @@ impl ExecutionContext for MockContext { commitment: PacketCommitment, ) -> Result<(), ContextError> { self.ibc_store - .lock() - .packet_commitment - .entry(commitment_path.port_id.clone()) - .or_default() - .entry(commitment_path.channel_id.clone()) - .or_default() - .insert(commitment_path.sequence, commitment); + .packet_commitment_store + .set(commitment_path.clone(), commitment) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } - fn delete_packet_commitment( - &mut self, - commitment_path: &CommitmentPath, - ) -> Result<(), ContextError> { - self.ibc_store - .lock() - .packet_commitment - .get_mut(&commitment_path.port_id) - .and_then(|map| map.get_mut(&commitment_path.channel_id)) - .and_then(|map| map.remove(&commitment_path.sequence)); + fn delete_packet_commitment(&mut self, key: &CommitmentPath) -> Result<(), ContextError> { + self.ibc_store.packet_commitment_store.delete(key.clone()); Ok(()) } fn store_packet_receipt( &mut self, - path: &ReceiptPath, - receipt: Receipt, + receipt_path: &ReceiptPath, + _receipt: Receipt, ) -> Result<(), ContextError> { self.ibc_store - .lock() - .packet_receipt - .entry(path.port_id.clone()) - .or_default() - .entry(path.channel_id.clone()) - .or_default() - .insert(path.sequence, receipt); + .packet_receipt_store + .set_path(receipt_path.clone()) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } @@ -440,49 +728,30 @@ impl ExecutionContext for MockContext { ack_path: &AckPath, ack_commitment: AcknowledgementCommitment, ) -> Result<(), ContextError> { - let port_id = ack_path.port_id.clone(); - let channel_id = ack_path.channel_id.clone(); - let seq = ack_path.sequence; - self.ibc_store - .lock() - .packet_acknowledgement - .entry(port_id) - .or_default() - .entry(channel_id) - .or_default() - .insert(seq, ack_commitment); + .packet_ack_store + .set(ack_path.clone(), ack_commitment) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } fn delete_packet_acknowledgement(&mut self, ack_path: &AckPath) -> Result<(), ContextError> { - let port_id = ack_path.port_id.clone(); - let channel_id = ack_path.channel_id.clone(); - let sequence = ack_path.sequence; - - self.ibc_store - .lock() - .packet_acknowledgement - .get_mut(&port_id) - .and_then(|map| map.get_mut(&channel_id)) - .and_then(|map| map.remove(&sequence)); + self.ibc_store.packet_ack_store.delete(ack_path.clone()); Ok(()) } + /// Stores the given channel_end at a path associated with the port_id and channel_id. fn store_channel( &mut self, channel_end_path: &ChannelEndPath, channel_end: ChannelEnd, ) -> Result<(), ContextError> { - let port_id = channel_end_path.0.clone(); - let channel_id = channel_end_path.1.clone(); - self.ibc_store - .lock() - .channels - .entry(port_id) - .or_default() - .insert(channel_id, channel_end); + .channel_end_store + .set(channel_end_path.clone(), channel_end) + .map_err(|_| ChannelError::Other { + description: "Channel end store error".to_string(), + })?; Ok(()) } @@ -491,15 +760,10 @@ impl ExecutionContext for MockContext { seq_send_path: &SeqSendPath, seq: Sequence, ) -> Result<(), ContextError> { - let port_id = seq_send_path.0.clone(); - let channel_id = seq_send_path.1.clone(); - self.ibc_store - .lock() - .next_sequence_send - .entry(port_id) - .or_default() - .insert(channel_id, seq); + .send_sequence_store + .set(seq_send_path.clone(), seq) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } @@ -508,15 +772,10 @@ impl ExecutionContext for MockContext { seq_recv_path: &SeqRecvPath, seq: Sequence, ) -> Result<(), ContextError> { - let port_id = seq_recv_path.0.clone(); - let channel_id = seq_recv_path.1.clone(); - self.ibc_store - .lock() - .next_sequence_recv - .entry(port_id) - .or_default() - .insert(channel_id, seq); + .recv_sequence_store + .set(seq_recv_path.clone(), seq) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } @@ -525,30 +784,29 @@ impl ExecutionContext for MockContext { seq_ack_path: &SeqAckPath, seq: Sequence, ) -> Result<(), ContextError> { - let port_id = seq_ack_path.0.clone(); - let channel_id = seq_ack_path.1.clone(); - self.ibc_store - .lock() - .next_sequence_ack - .entry(port_id) - .or_default() - .insert(channel_id, seq); + .ack_sequence_store + .set(seq_ack_path.clone(), seq) + .map_err(|_| PacketError::ImplementationSpecific)?; Ok(()) } fn increase_channel_counter(&mut self) -> Result<(), ContextError> { - self.ibc_store.lock().channel_ids_counter += 1; + *self.ibc_store.channel_counter.lock() += 1; Ok(()) } fn emit_ibc_event(&mut self, event: IbcEvent) -> Result<(), ContextError> { - self.ibc_store.lock().events.push(event); + self.ibc_store.events.lock().push(event); Ok(()) } fn log_message(&mut self, message: String) -> Result<(), ContextError> { - self.ibc_store.lock().logs.push(message); + self.ibc_store.logs.lock().push(message); Ok(()) } + + fn get_client_execution_context(&mut self) -> &mut Self::E { + self + } } diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 3106f81c0..4a2fef5b4 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -1,19 +1,20 @@ //! Implementation of a global context mock. Used in testing handlers of all IBC modules. -use alloc::collections::btree_map::BTreeMap; +use alloc::collections::BTreeMap; use alloc::sync::Arc; use core::cmp::min; use core::fmt::Debug; use core::ops::{Add, Sub}; use core::time::Duration; -use ibc::core::client::context::ClientExecutionContext; -use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath}; +use basecoin_store::context::ProvableStore; +use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore, SharedStore}; +use basecoin_store::types::{BinStore, JsonStore, ProtobufStore, TypedSet, TypedStore}; use ibc::clients::tendermint::client_state::ClientState as TmClientState; use ibc::clients::tendermint::types::TENDERMINT_CLIENT_TYPE; use ibc::core::channel::types::channel::ChannelEnd; use ibc::core::channel::types::commitment::{AcknowledgementCommitment, PacketCommitment}; -use ibc::core::channel::types::packet::Receipt; +use ibc::core::client::context::ClientExecutionContext; use ibc::core::client::types::Height; use ibc::core::connection::types::ConnectionEnd; use ibc::core::entrypoint::dispatch; @@ -22,15 +23,21 @@ use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::core::host::types::identifiers::{ ChainId, ChannelId, ClientId, ClientType, ConnectionId, PortId, Sequence, }; -use ibc::core::host::ValidationContext; +use ibc::core::host::types::path::{ + AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, + CommitmentPath, ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, +}; +use ibc::core::host::{ExecutionContext, ValidationContext}; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; use ibc::core::router::router::Router; +use ibc_proto::google::protobuf::Any; +use ibc_proto::ibc::core::channel::v1::Channel as RawChannelEnd; +use ibc_proto::ibc::core::connection::v1::ConnectionEnd as RawConnectionEnd; use parking_lot::Mutex; use tendermint_testgen::Validator as TestgenValidator; use typed_builder::TypedBuilder; -use super::client_ctx::{MockClientRecord, PortChannelIdMap}; use crate::fixtures::clients::tendermint::ClientStateConfig as TmClientStateConfig; use crate::fixtures::core::context::MockContextConfig; use crate::hosts::block::{HostBlock, HostType}; @@ -45,66 +52,102 @@ use crate::testapp::ibc::utils::blocks_since; pub const DEFAULT_BLOCK_TIME_SECS: u64 = 3; /// An object that stores all IBC related data. -#[derive(Debug, Default)] -pub struct MockIbcStore { - /// The set of all clients, indexed by their id. - pub clients: BTreeMap, - - /// Tracks the processed time for clients header updates - pub client_processed_times: BTreeMap<(ClientId, Height), Timestamp>, - - /// Tracks the processed height for the clients - pub client_processed_heights: BTreeMap<(ClientId, Height), Height>, - - /// Counter for the client identifiers, necessary for `increase_client_counter` and the - /// `client_counter` methods. - pub client_ids_counter: u64, - - /// Association between client ids and connection ids. - pub client_connections: BTreeMap, - - /// All the connections in the store. - pub connections: BTreeMap, - - /// Counter for connection identifiers (see `increase_connection_counter`). - pub connection_ids_counter: u64, - - /// Association between connection ids and channel ids. - pub connection_channels: BTreeMap>, - - /// Counter for channel identifiers (see `increase_channel_counter`). - pub channel_ids_counter: u64, - - /// All the channels in the store. TODO Make new key PortId X ChannelId - pub channels: PortChannelIdMap, - - /// Tracks the sequence number for the next packet to be sent. - pub next_sequence_send: PortChannelIdMap, - - /// Tracks the sequence number for the next packet to be received. - pub next_sequence_recv: PortChannelIdMap, - - /// Tracks the sequence number for the next packet to be acknowledged. - pub next_sequence_ack: PortChannelIdMap, - - pub packet_acknowledgement: PortChannelIdMap>, - - /// Constant-size commitments to packets data fields - pub packet_commitment: PortChannelIdMap>, - - /// Used by unordered channel - pub packet_receipt: PortChannelIdMap>, +#[derive(Debug)] +pub struct MockIbcStore +where + S: ProvableStore + Debug, +{ + /// Handle to store instance. + /// The module is guaranteed exclusive access to all paths in the store key-space. + pub store: SharedStore, + /// Counter for clients + pub client_counter: Arc>, + /// Counter for connections + pub conn_counter: Arc>, + /// Counter for channels + pub channel_counter: Arc>, + /// Tracks the processed time for client updates + pub client_processed_times: Arc>>, + /// Tracks the processed height for client updates + pub client_processed_heights: Arc>>, + /// Map of host consensus states + pub consensus_states: Arc>>, + /// A typed-store for AnyClientState + pub client_state_store: ProtobufStore, ClientStatePath, AnyClientState, Any>, + /// A typed-store for AnyConsensusState + pub consensus_state_store: + ProtobufStore, ClientConsensusStatePath, AnyConsensusState, Any>, + /// A typed-store for ConnectionEnd + pub connection_end_store: + ProtobufStore, ConnectionPath, ConnectionEnd, RawConnectionEnd>, + /// A typed-store for ConnectionIds + pub connection_ids_store: JsonStore, ClientConnectionPath, Vec>, + /// A typed-store for ChannelEnd + pub channel_end_store: ProtobufStore, ChannelEndPath, ChannelEnd, RawChannelEnd>, + /// A typed-store for send sequences + pub send_sequence_store: JsonStore, SeqSendPath, Sequence>, + /// A typed-store for receive sequences + pub recv_sequence_store: JsonStore, SeqRecvPath, Sequence>, + /// A typed-store for ack sequences + pub ack_sequence_store: JsonStore, SeqAckPath, Sequence>, + /// A typed-store for packet commitments + pub packet_commitment_store: BinStore, CommitmentPath, PacketCommitment>, + /// A typed-store for packet receipts + pub packet_receipt_store: TypedSet, ReceiptPath>, + /// A typed-store for packet ack + pub packet_ack_store: BinStore, AckPath, AcknowledgementCommitment>, + /// IBC Events + pub events: Arc>>, + /// message logs + pub logs: Arc>>, +} - /// Emitted IBC events in order - pub events: Vec, +impl MockIbcStore +where + S: ProvableStore + Debug, +{ + pub fn new(store: S) -> Self { + let shared_store = SharedStore::new(store); + Self { + client_counter: Arc::new(Mutex::new(0)), + conn_counter: Arc::new(Mutex::new(0)), + channel_counter: Arc::new(Mutex::new(0)), + client_processed_times: Arc::new(Mutex::new(Default::default())), + client_processed_heights: Arc::new(Mutex::new(Default::default())), + consensus_states: Arc::new(Mutex::new(Default::default())), + client_state_store: TypedStore::new(shared_store.clone()), + consensus_state_store: TypedStore::new(shared_store.clone()), + connection_end_store: TypedStore::new(shared_store.clone()), + connection_ids_store: TypedStore::new(shared_store.clone()), + channel_end_store: TypedStore::new(shared_store.clone()), + send_sequence_store: TypedStore::new(shared_store.clone()), + recv_sequence_store: TypedStore::new(shared_store.clone()), + ack_sequence_store: TypedStore::new(shared_store.clone()), + packet_commitment_store: TypedStore::new(shared_store.clone()), + packet_receipt_store: TypedStore::new(shared_store.clone()), + packet_ack_store: TypedStore::new(shared_store.clone()), + events: Arc::new(Mutex::new(Vec::new())), + logs: Arc::new(Mutex::new(Vec::new())), + store: shared_store, + } + } +} - /// Logs of the IBC module - pub logs: Vec, +impl Default for MockIbcStore +where + S: ProvableStore + Debug + Default, +{ + fn default() -> Self { + Self::new(S::default()) + } } /// A context implementing the dependencies necessary for testing any IBC module. #[derive(Debug)] -pub struct MockContext { +pub struct MockGenericContext +where + S: ProvableStore + Debug, +{ /// The type of host chain underlying this mock context. pub host_chain_type: HostType, @@ -122,9 +165,11 @@ pub struct MockContext { pub block_time: Duration, /// An object that stores all IBC related data. - pub ibc_store: Arc>, + pub ibc_store: MockIbcStore, } +pub type MockContext = MockGenericContext>>; + #[derive(Debug, TypedBuilder)] pub struct MockClientConfig { #[builder(default = ChainId::new("mockZ-1").expect("no error"))] @@ -150,7 +195,10 @@ pub struct MockClientConfig { /// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are /// present, and the chain has Height(5). This should be used sparingly, mostly for testing the /// creation of new domain objects. -impl Default for MockContext { +impl Default for MockGenericContext +where + S: ProvableStore + Debug + Default, +{ fn default() -> Self { MockContextConfig::builder().build() } @@ -158,7 +206,10 @@ impl Default for MockContext { /// Implementation of internal interface for use in testing. The methods in this interface should /// _not_ be accessible to any Ics handler. -impl MockContext { +impl MockGenericContext +where + S: ProvableStore + Debug, +{ /// Creates a mock context. Parameter `max_history_size` determines how many blocks will /// the chain maintain in its history, which also determines the pruning window. Parameter /// `latest_height` determines the current height of the chain. This context @@ -172,7 +223,10 @@ impl MockContext { host_type: HostType, max_history_size: u64, latest_height: Height, - ) -> Self { + ) -> Self + where + S: Default, + { assert_ne!( max_history_size, 0, "The chain must have a non-zero max_history_size" @@ -195,7 +249,7 @@ impl MockContext { let block_time = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS); let next_block_timestamp = Timestamp::now().add(block_time).expect("Never fails"); - MockContext { + Self { host_chain_type: host_type, host_chain_id: host_id.clone(), max_history_size, @@ -215,7 +269,7 @@ impl MockContext { }) .collect(), block_time, - ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), + ibc_store: MockIbcStore::default(), } } @@ -232,7 +286,10 @@ impl MockContext { host_type: HostType, validator_history: &[Vec], latest_height: Height, - ) -> Self { + ) -> Self + where + S: Default, + { let max_history_size = validator_history.len() as u64 - 1; assert_ne!( @@ -278,16 +335,20 @@ impl MockContext { }) .collect(); - MockContext { + Self { host_chain_type: host_type, host_chain_id: host_id.clone(), max_history_size, history, block_time, - ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), + ibc_store: MockIbcStore::default(), } } + pub fn chain_revision_number(&self) -> u64 { + self.host_chain_id.revision_number() + } + /// Associates a client record to this context. /// Given a client id and a height, registers a new client in the context and also associates /// to this client a mock client state and a mock consensus state for height `height`. The type @@ -545,75 +606,62 @@ impl MockContext { /// Associates a connection to this context. pub fn with_connection( - self, + mut self, connection_id: ConnectionId, connection_end: ConnectionEnd, ) -> Self { - self.ibc_store - .lock() - .connections - .insert(connection_id, connection_end); + let connection_path = ConnectionPath::new(&connection_id); + self.store_connection(&connection_path, connection_end) + .expect("error writing to store"); self } /// Associates a channel (in an arbitrary state) to this context. pub fn with_channel( - self, + mut self, port_id: PortId, chan_id: ChannelId, channel_end: ChannelEnd, ) -> Self { - let mut channels = self.ibc_store.lock().channels.clone(); - channels - .entry(port_id) - .or_default() - .insert(chan_id, channel_end); - self.ibc_store.lock().channels = channels; + let channel_end_path = ChannelEndPath::new(&port_id, &chan_id); + self.store_channel(&channel_end_path, channel_end) + .expect("error writing to store"); self } pub fn with_send_sequence( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq_number: Sequence, ) -> Self { - let mut next_sequence_send = self.ibc_store.lock().next_sequence_send.clone(); - next_sequence_send - .entry(port_id) - .or_default() - .insert(chan_id, seq_number); - self.ibc_store.lock().next_sequence_send = next_sequence_send; + let seq_send_path = SeqSendPath::new(&port_id, &chan_id); + self.store_next_sequence_send(&seq_send_path, seq_number) + .expect("error writing to store"); self } pub fn with_recv_sequence( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq_number: Sequence, ) -> Self { - let mut next_sequence_recv = self.ibc_store.lock().next_sequence_recv.clone(); - next_sequence_recv - .entry(port_id) - .or_default() - .insert(chan_id, seq_number); - self.ibc_store.lock().next_sequence_recv = next_sequence_recv; + let seq_recv_path = SeqRecvPath::new(&port_id, &chan_id); + self.store_next_sequence_recv(&seq_recv_path, seq_number) + .expect("error writing to store"); self } pub fn with_ack_sequence( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq_number: Sequence, ) -> Self { - let mut next_sequence_ack = self.ibc_store.lock().next_sequence_send.clone(); - next_sequence_ack - .entry(port_id) - .or_default() - .insert(chan_id, seq_number); - self.ibc_store.lock().next_sequence_ack = next_sequence_ack; + let seq_ack_path = SeqAckPath::new(&port_id, &chan_id); + self.store_next_sequence_ack(&seq_ack_path, seq_number) + .expect("error writing to store"); self } @@ -639,20 +687,15 @@ impl MockContext { } pub fn with_packet_commitment( - self, + mut self, port_id: PortId, chan_id: ChannelId, seq: Sequence, data: PacketCommitment, ) -> Self { - let mut packet_commitment = self.ibc_store.lock().packet_commitment.clone(); - packet_commitment - .entry(port_id) - .or_default() - .entry(chan_id) - .or_default() - .insert(seq, data); - self.ibc_store.lock().packet_commitment = packet_commitment; + let commitment_path = CommitmentPath::new(&port_id, &chan_id, seq); + self.store_packet_commitment(&commitment_path, data) + .expect("error writing to store"); self } @@ -737,11 +780,8 @@ impl MockContext { } pub fn latest_client_states(&self, client_id: &ClientId) -> AnyClientState { - self.ibc_store.lock().clients[client_id] - .client_state - .as_ref() - .expect("Never fails") - .clone() + self.client_state(client_id) + .expect("error reading from store") } pub fn latest_consensus_states( @@ -749,22 +789,16 @@ impl MockContext { client_id: &ClientId, height: &Height, ) -> AnyConsensusState { - self.ibc_store.lock().clients[client_id] - .consensus_states - .get(height) - .expect("Never fails") - .clone() + self.consensus_state(&ClientConsensusStatePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + )) + .expect("error reading from store") } pub fn latest_height(&self) -> Height { - self.history - .last() - .expect("history cannot be empty") - .height() - } - - pub fn ibc_store_share(&self) -> Arc> { - self.ibc_store.clone() + self.host_height().expect("Never fails") } pub fn latest_timestamp(&self) -> Timestamp { @@ -781,16 +815,16 @@ impl MockContext { } pub fn query_latest_header(&self) -> Option { - let block_ref = self.host_block(&self.host_height().expect("Never fails")); + let block_ref = self.host_block(&self.latest_height()); block_ref.cloned() } pub fn get_events(&self) -> Vec { - self.ibc_store.lock().events.clone() + self.ibc_store.events.lock().clone() } pub fn get_logs(&self) -> Vec { - self.ibc_store.lock().logs.clone() + self.ibc_store.logs.lock().clone() } } From 16f6d60bc8cd142c0c971d2c37d0a46f8196609c Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 30 Jan 2024 13:04:49 +0100 Subject: [PATCH 13/22] refactor for updated mock ibc context --- ibc-testkit/src/fixtures/core/context.rs | 15 +++++++++------ ibc-testkit/src/relayer/context.rs | 10 ++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ibc-testkit/src/fixtures/core/context.rs b/ibc-testkit/src/fixtures/core/context.rs index 6ee78f116..f6522ce2b 100644 --- a/ibc-testkit/src/fixtures/core/context.rs +++ b/ibc-testkit/src/fixtures/core/context.rs @@ -1,18 +1,18 @@ -use alloc::sync::Arc; +use alloc::fmt::Debug; use core::cmp::min; use core::ops::{Add, Sub}; use core::time::Duration; +use basecoin_store::context::ProvableStore; use ibc::core::client::types::Height; use ibc::core::host::types::identifiers::ChainId; use ibc::core::primitives::prelude::*; use ibc::core::primitives::Timestamp; -use parking_lot::Mutex; use tendermint_testgen::Validator as TestgenValidator; use typed_builder::TypedBuilder; use crate::hosts::block::{HostBlock, HostType}; -use crate::testapp::ibc::core::types::{MockContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; +use crate::testapp::ibc::core::types::{MockGenericContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS}; /// Configuration of the `MockContext` type for generating dummy contexts. #[derive(Debug, TypedBuilder)] @@ -41,7 +41,10 @@ pub struct MockContextConfig { latest_timestamp: Timestamp, } -impl From for MockContext { +impl From for MockGenericContext +where + S: ProvableStore + Debug + Default, +{ fn from(params: MockContextConfig) -> Self { assert_ne!( params.max_history_size, 0, @@ -115,13 +118,13 @@ impl From for MockContext { .collect() }; - MockContext { + MockGenericContext { host_chain_type: params.host_type, host_chain_id: params.host_id.clone(), max_history_size: params.max_history_size, history, block_time: params.block_time, - ibc_store: Arc::new(Mutex::new(MockIbcStore::default())), + ibc_store: MockIbcStore::default(), } } } diff --git a/ibc-testkit/src/relayer/context.rs b/ibc-testkit/src/relayer/context.rs index c374c83c2..c35cd080f 100644 --- a/ibc-testkit/src/relayer/context.rs +++ b/ibc-testkit/src/relayer/context.rs @@ -1,3 +1,6 @@ +use alloc::fmt::Debug; + +use basecoin_store::context::ProvableStore; use ibc::core::client::types::Height; use ibc::core::handler::types::error::ContextError; use ibc::core::host::types::identifiers::ClientId; @@ -6,7 +9,7 @@ use ibc::core::primitives::prelude::*; use ibc::core::primitives::Signer; use crate::testapp::ibc::clients::AnyClientState; -use crate::testapp::ibc::core::types::MockContext; +use crate::testapp::ibc::core::types::MockGenericContext; /// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to /// relay packets between chains. This trait comprises the dependencies towards a single chain. /// Most of the functions in this represent wrappers over the ABCI interface. @@ -24,7 +27,10 @@ pub trait RelayerContext { fn signer(&self) -> Signer; } -impl RelayerContext for MockContext { +impl RelayerContext for MockGenericContext +where + S: ProvableStore + Debug, +{ fn query_latest_height(&self) -> Result { ValidationContext::host_height(self) } From 1de553c3ea1802d7df9d29b2291e1a5123a28ae1 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Thu, 1 Feb 2024 09:34:27 +0100 Subject: [PATCH 14/22] imp timeout test --- ibc-testkit/tests/core/ics04_channel/timeout.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ibc-testkit/tests/core/ics04_channel/timeout.rs b/ibc-testkit/tests/core/ics04_channel/timeout.rs index 29675bb2e..013dc0ee2 100644 --- a/ibc-testkit/tests/core/ics04_channel/timeout.rs +++ b/ibc-testkit/tests/core/ics04_channel/timeout.rs @@ -1,5 +1,3 @@ -use std::ops::Sub; - use ibc::core::channel::types::channel::{ChannelEnd, Counterparty, Order, State}; use ibc::core::channel::types::commitment::{compute_packet_commitment, PacketCommitment}; use ibc::core::channel::types::msgs::{MsgTimeout, PacketMsg}; @@ -45,13 +43,10 @@ fn fixture() -> Fixture { let router = MockRouter::new_with_transfer(); + // in case of timeout, timeout timestamp should be less than host's timestamp + let timeout_timestamp = ctx.latest_timestamp().nanoseconds() - 1; let msg_proof_height = 2; let msg_timeout_height = 5; - let timeout_timestamp = ctx - .latest_timestamp() - .sub(core::time::Duration::from_secs(3)) - .expect("no overflow") - .nanoseconds(); let msg = MsgTimeout::try_from(dummy_raw_msg_timeout( msg_proof_height, From 6061b74feb30d78d2709c687ac8bea52416c4f41 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 2 Feb 2024 10:41:04 +0100 Subject: [PATCH 15/22] public git commit as dep source --- Cargo.toml | 2 +- ibc-testkit/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c47412a2..ac45f6c20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,4 +107,4 @@ parity-scale-codec = { version = "3.6.5", default-features = false, features = [ scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } [patch.crates-io] -ibc-proto = { path = "../ibc-proto-rs" } +ibc-proto = { git = "https://github.com/cosmos/ibc-proto-rs", rev = "7643fa3" } diff --git a/ibc-testkit/Cargo.toml b/ibc-testkit/Cargo.toml index 6bfdbcc7d..4d9619576 100644 --- a/ibc-testkit/Cargo.toml +++ b/ibc-testkit/Cargo.toml @@ -32,7 +32,7 @@ typed-builder = { version = "0.18.0" } ibc = { workspace = true, features = ["std"] } ibc-proto = { workspace = true } ibc-query = { version = "0.50.0", path = "../ibc-query" } -basecoin-store = { package = "basecoin-store", path = "../../basecoin-rs/crates/store" } +basecoin-store = { git = "https://github.com/informalsystems/basecoin-rs", rev = "dc3b43a" } # cosmos dependencies tendermint = { workspace = true } From e00615105a11a8b9f1896e0885ff635e19ab9ea7 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 2 Feb 2024 10:43:53 +0100 Subject: [PATCH 16/22] fix spelling --- ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index 84b04f2ff..fc5eca00a 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -39,7 +39,7 @@ pub fn client_type() -> ClientType { pub struct MockClientState { pub header: MockHeader, pub max_clock_drift: Option, - pub trusing_period: Option, + pub trusting_period: Option, pub frozen: bool, } @@ -48,7 +48,7 @@ impl MockClientState { Self { header, max_clock_drift: None, - trusing_period: None, + trusting_period: None, frozen: false, } } @@ -91,7 +91,7 @@ impl TryFrom for MockClientState { })? .try_into()?, max_clock_drift: raw.max_clock_drift.map(duration_gpb_to_ibc), - trusing_period: raw.trusing_period.map(duration_gpb_to_ibc), + trusting_period: raw.trusting_period.map(duration_gpb_to_ibc), frozen: raw.frozen, }) } @@ -102,7 +102,7 @@ impl From for RawMockClientState { RawMockClientState { header: Some(value.header.into()), max_clock_drift: value.max_clock_drift.map(duration_ibc_to_gbp), - trusing_period: value.trusing_period.map(duration_ibc_to_gbp), + trusting_period: value.trusting_period.map(duration_ibc_to_gbp), frozen: value.frozen, } } From 173ff7b7a73dd4dafb358b1d953361f667cfece8 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 2 Feb 2024 10:53:43 +0100 Subject: [PATCH 17/22] update MockClient types --- .../testapp/ibc/clients/mock/client_state.rs | 20 ++++++++++--------- .../ibc/clients/mock/consensus_state.rs | 1 - .../src/testapp/ibc/clients/mock/header.rs | 14 ++++++------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs index fc5eca00a..60b306cd5 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs @@ -22,7 +22,6 @@ use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState; use crate::testapp::ibc::clients::mock::header::MockHeader; use crate::testapp::ibc::clients::mock::misbehaviour::Misbehaviour; use crate::testapp::ibc::clients::mock::proto::ClientState as RawMockClientState; -use crate::testapp::ibc::utils::{duration_gpb_to_ibc, duration_ibc_to_gbp}; pub const MOCK_CLIENT_STATE_TYPE_URL: &str = "/ibc.mock.ClientState"; pub const MOCK_CLIENT_TYPE: &str = "9999-mock"; @@ -38,8 +37,7 @@ pub fn client_type() -> ClientType { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct MockClientState { pub header: MockHeader, - pub max_clock_drift: Option, - pub trusting_period: Option, + pub trusting_period: Duration, pub frozen: bool, } @@ -47,8 +45,7 @@ impl MockClientState { pub fn new(header: MockHeader) -> Self { Self { header, - max_clock_drift: None, - trusting_period: None, + trusting_period: Duration::from_nanos(0), frozen: false, } } @@ -61,6 +58,13 @@ impl MockClientState { None } + pub fn with_trusting_period(self, trusting_period: Duration) -> Self { + Self { + trusting_period, + ..self + } + } + pub fn with_frozen_height(self, _frozen_height: Height) -> Self { Self { frozen: true, @@ -90,8 +94,7 @@ impl TryFrom for MockClientState { description: "header is not present".into(), })? .try_into()?, - max_clock_drift: raw.max_clock_drift.map(duration_gpb_to_ibc), - trusting_period: raw.trusting_period.map(duration_gpb_to_ibc), + trusting_period: Duration::from_nanos(raw.trusting_period), frozen: raw.frozen, }) } @@ -101,8 +104,7 @@ impl From for RawMockClientState { fn from(value: MockClientState) -> Self { RawMockClientState { header: Some(value.header.into()), - max_clock_drift: value.max_clock_drift.map(duration_ibc_to_gbp), - trusting_period: value.trusting_period.map(duration_ibc_to_gbp), + trusting_period: value.trusting_period.as_nanos() as _, frozen: value.frozen, } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs index ca6f33e09..43db3edc4 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs @@ -48,7 +48,6 @@ impl From for RawMockConsensusState { fn from(value: MockConsensusState) -> Self { RawMockConsensusState { header: Some(value.header.into()), - bytes: value.root.into_vec(), } } } diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index ac23f5ef7..ff6da161e 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -7,7 +7,6 @@ use ibc::core::primitives::Timestamp; use ibc::primitives::proto::{Any, Protobuf}; use crate::testapp::ibc::clients::mock::proto::Header as RawMockHeader; -use crate::testapp::ibc::utils::{timestamp_gpb_to_ibc, timestamp_ibc_to_gpb}; pub const MOCK_HEADER_TYPE_URL: &str = "/ibc.mock.Header"; @@ -50,12 +49,11 @@ impl TryFrom for MockHeader { description: "missing height".into(), })? .try_into()?, - timestamp: raw - .timestamp - .map(timestamp_gpb_to_ibc) - .ok_or(ClientError::Other { - description: "missing timestamp".into(), - })?, + timestamp: Timestamp::from_nanoseconds(raw.timestamp).map_err(|err| { + ClientError::Other { + description: err.to_string(), + } + })?, }) } } @@ -64,7 +62,7 @@ impl From for RawMockHeader { fn from(value: MockHeader) -> Self { RawMockHeader { height: Some(value.height.into()), - timestamp: Some(timestamp_ibc_to_gpb(value.timestamp)), + timestamp: value.timestamp.nanoseconds(), } } } From 0c9bee1acd65abf9847013db2ca8a12b9b43a8c2 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 2 Feb 2024 10:55:54 +0100 Subject: [PATCH 18/22] fix failing tests --- ibc-testkit/src/fixtures/core/client/mod.rs | 2 +- ibc-testkit/src/testapp/ibc/clients/mock/header.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ibc-testkit/src/fixtures/core/client/mod.rs b/ibc-testkit/src/fixtures/core/client/mod.rs index f55b9eae7..a536d778c 100644 --- a/ibc-testkit/src/fixtures/core/client/mod.rs +++ b/ibc-testkit/src/fixtures/core/client/mod.rs @@ -52,7 +52,7 @@ mod tests { "07-tendermint", "0-5", "0-5,0-7", - "0a102f6962632e6d6f636b2e48656164657212060a0210051200", + "0a102f6962632e6d6f636b2e48656164657212040a021005", ]; let tests: Vec = vec![ diff --git a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs index ff6da161e..ab4785054 100644 --- a/ibc-testkit/src/testapp/ibc/clients/mock/header.rs +++ b/ibc-testkit/src/testapp/ibc/clients/mock/header.rs @@ -134,7 +134,7 @@ mod tests { &bytes, &[ 10, 16, 47, 105, 98, 99, 46, 109, 111, 99, 107, 46, 72, 101, 97, 100, 101, 114, 18, - 8, 10, 4, 8, 1, 16, 10, 18, 0 + 6, 10, 4, 8, 1, 16, 10 ] ); } From ebf30194db5a073eb96c834d7466a5818548d2b8 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 2 Feb 2024 10:54:38 +0100 Subject: [PATCH 19/22] rm unused utils and deps --- ibc-testkit/Cargo.toml | 1 - ibc-testkit/src/testapp/ibc/utils.rs | 30 ---------------------------- 2 files changed, 31 deletions(-) diff --git a/ibc-testkit/Cargo.toml b/ibc-testkit/Cargo.toml index 4d9619576..2008e2316 100644 --- a/ibc-testkit/Cargo.toml +++ b/ibc-testkit/Cargo.toml @@ -37,7 +37,6 @@ basecoin-store = { git = "https://github.com/informalsystems/basecoin-rs", rev = # cosmos dependencies tendermint = { workspace = true } tendermint-testgen = { workspace = true } -tendermint-proto = { workspace = true } [dev-dependencies] env_logger = "0.11.0" diff --git a/ibc-testkit/src/testapp/ibc/utils.rs b/ibc-testkit/src/testapp/ibc/utils.rs index d4e86321c..d5032cae1 100644 --- a/ibc-testkit/src/testapp/ibc/utils.rs +++ b/ibc-testkit/src/testapp/ibc/utils.rs @@ -1,34 +1,4 @@ -use core::time::Duration; - use ibc::core::client::types::Height; -use ibc::primitives::Timestamp; -use ibc_proto::google::protobuf::{Duration as GDuration, Timestamp as GTimestamp}; - -pub fn timestamp_gpb_to_ibc(gpb_timestamp: GTimestamp) -> Timestamp { - let GTimestamp { seconds, nanos } = gpb_timestamp; - Timestamp::from_nanoseconds(seconds as u64 * 1_000_000_000 + nanos as u64) - .expect("no hmm overflow") -} - -pub fn timestamp_ibc_to_gpb(ibc_timestamp: Timestamp) -> GTimestamp { - let tendermint_proto::google::protobuf::Timestamp { seconds, nanos } = ibc_timestamp - .into_tm_time() - .unwrap_or_else(|| tendermint::Time::from_unix_timestamp(0, 0).expect("no overflow")) - .into(); - GTimestamp { seconds, nanos } -} - -pub fn duration_gpb_to_ibc(gbp_duration: GDuration) -> Duration { - let GDuration { seconds, nanos } = gbp_duration; - Duration::from_nanos(seconds as u64 * 1_000_000_000 + nanos as u64) -} - -pub fn duration_ibc_to_gbp(ibc_duration: Duration) -> GDuration { - GDuration { - seconds: ibc_duration.as_secs() as i64, - nanos: ibc_duration.subsec_nanos() as i32, - } -} pub fn blocks_since(a: Height, b: Height) -> Option { (a.revision_number() == b.revision_number() && a.revision_height() >= b.revision_height()) From 4bb11d64dbe57733ebfa20b3a33a3b966bb46ee3 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Fri, 2 Feb 2024 11:01:44 +0100 Subject: [PATCH 20/22] fix cargo doc lint --- ibc-testkit/src/testapp/ibc/core/client_ctx.rs | 4 ++-- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index 3d75fbc0d..e48fac748 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -60,7 +60,7 @@ impl ClientValidationContext for MockGenericContext where S: ProvableStore + Debug, { - /// Returns the time when the client state for the given [`ClientId`] was updated with a header for the given [`IbcHeight`] + /// Returns the time when the client state for the given [`ClientId`] was updated with a header for the given [`Height`] fn client_update_time( &self, client_id: &ClientId, @@ -79,7 +79,7 @@ where Ok(processed_timestamp) } - /// Returns the height when the client state for the given [`ClientId`] was updated with a header for the given [`IbcHeight`] + /// Returns the height when the client state for the given [`ClientId`] was updated with a header for the given [`Height`] fn client_update_height( &self, client_id: &ClientId, diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index c1773bbce..2b484349e 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -274,7 +274,7 @@ impl ProvableContext for MockGenericContext where S: ProvableStore + Debug, { - /// Returns the proof for the given [`IbcHeight`] and [`Path`] + /// Returns the proof for the given [`Height`] and [`Path`] fn get_proof(&self, height: Height, path: &Path) -> Option> { self.ibc_store .store From 78cb644654129b33ebd4bceeedcbaec2ce63060a Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 6 Feb 2024 20:16:56 +0100 Subject: [PATCH 21/22] use ibc host paths in mock context --- .../src/testapp/ibc/core/client_ctx.rs | 60 +++++++++++---- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 75 +++++++++++++++++-- ibc-testkit/src/testapp/ibc/core/types.rs | 56 +++++++++----- 3 files changed, 152 insertions(+), 39 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs index e48fac748..7658ba9d4 100644 --- a/ibc-testkit/src/testapp/ibc/core/client_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/client_ctx.rs @@ -12,7 +12,9 @@ use ibc::core::client::types::error::ClientError; use ibc::core::client::types::Height; use ibc::core::handler::types::error::ContextError; use ibc::core::host::types::identifiers::{ChannelId, ClientId, PortId}; -use ibc::core::host::types::path::{ClientConsensusStatePath, ClientStatePath, Path}; +use ibc::core::host::types::path::{ + ClientConsensusStatePath, ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, Path, +}; use ibc::core::host::ValidationContext; use ibc::core::primitives::Timestamp; use ibc::primitives::prelude::{format, *}; @@ -66,12 +68,15 @@ where client_id: &ClientId, height: &Height, ) -> Result { + let client_update_time_path = ClientUpdateTimePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); let processed_timestamp = self .ibc_store .client_processed_times - .lock() - .get(&(client_id.clone(), *height)) - .cloned() + .get(StoreHeight::Pending, &client_update_time_path) .ok_or(ClientError::ProcessedTimeNotFound { client_id: client_id.clone(), height: *height, @@ -85,12 +90,15 @@ where client_id: &ClientId, height: &Height, ) -> Result { + let client_update_height_path = ClientUpdateHeightPath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); let processed_height = self .ibc_store .client_processed_heights - .lock() - .get(&(client_id.clone(), *height)) - .cloned() + .get(StoreHeight::Pending, &client_update_height_path) .ok_or(ClientError::ProcessedHeightNotFound { client_id: client_id.clone(), height: *height, @@ -149,10 +157,17 @@ where height: Height, timestamp: Timestamp, ) -> Result<(), ContextError> { + let client_update_time_path = ClientUpdateTimePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); self.ibc_store .client_processed_times - .lock() - .insert((client_id, height), timestamp); + .set(client_update_time_path, timestamp) + .map_err(|_| ClientError::Other { + description: "store update error".into(), + })?; Ok(()) } @@ -165,10 +180,17 @@ where height: Height, host_height: Height, ) -> Result<(), ContextError> { + let client_update_height_path = ClientUpdateHeightPath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); self.ibc_store .client_processed_heights - .lock() - .insert((client_id, height), host_height); + .set(client_update_height_path, host_height) + .map_err(|_| ClientError::Other { + description: "store update error".into(), + })?; Ok(()) } @@ -178,10 +200,14 @@ where client_id: ClientId, height: Height, ) -> Result<(), ContextError> { + let client_update_time_path = ClientUpdateTimePath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); self.ibc_store .client_processed_times - .lock() - .remove(&(client_id, height)); + .delete(client_update_time_path); Ok(()) } @@ -191,10 +217,14 @@ where client_id: ClientId, height: Height, ) -> Result<(), ContextError> { + let client_update_height_path = ClientUpdateHeightPath::new( + client_id.clone(), + height.revision_number(), + height.revision_height(), + ); self.ibc_store .client_processed_heights - .lock() - .remove(&(client_id, height)); + .delete(client_update_height_path); Ok(()) } diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index 2b484349e..bd450ff18 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -21,7 +21,8 @@ use ibc::core::handler::types::events::IbcEvent; use ibc::core::host::types::identifiers::{ClientId, ConnectionId, Sequence}; use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, - CommitmentPath, ConnectionPath, Path, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, + CommitmentPath, ConnectionPath, NextChannelSequencePath, NextClientSequencePath, + NextConnectionSequencePath, Path, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; use ibc::core::host::{ExecutionContext, ValidationContext}; use ibc::core::primitives::prelude::*; @@ -108,7 +109,13 @@ where } fn client_counter(&self) -> Result { - Ok(*self.ibc_store.client_counter.lock()) + Ok(self + .ibc_store + .client_counter + .get(StoreHeight::Pending, &NextClientSequencePath {}) + .ok_or(ClientError::Other { + description: "client counter not found".into(), + })?) } fn connection_end(&self, conn_id: &ConnectionId) -> Result { @@ -132,7 +139,13 @@ where } fn connection_counter(&self) -> Result { - Ok(*self.ibc_store.conn_counter.lock()) + Ok(self + .ibc_store + .conn_counter + .get(StoreHeight::Pending, &NextConnectionSequencePath {}) + .ok_or(ConnectionError::Other { + description: "connection counter not found".into(), + })?) } fn get_compatible_versions(&self) -> Vec { @@ -252,7 +265,13 @@ where /// The value of this counter should increase only via method /// `ChannelKeeper::increase_channel_counter`. fn channel_counter(&self) -> Result { - Ok(*self.ibc_store.channel_counter.lock()) + Ok(self + .ibc_store + .channel_counter + .get(StoreHeight::Pending, &NextChannelSequencePath {}) + .ok_or(ChannelError::Other { + description: "channel counter not found".into(), + })?) } /// Returns the maximum expected time per block @@ -646,7 +665,21 @@ where /// Increases the counter which keeps track of how many clients have been created. /// Should never fail. fn increase_client_counter(&mut self) -> Result<(), ContextError> { - *self.ibc_store.client_counter.lock() += 1; + let current_sequence = self + .ibc_store + .client_counter + .get(StoreHeight::Pending, &NextClientSequencePath {}) + .ok_or(ClientError::Other { + description: "client counter not found".into(), + })?; + + self.ibc_store + .client_counter + .set(NextClientSequencePath {}, current_sequence + 1) + .map_err(|e| ClientError::Other { + description: format!("client counter update failed: {e:?}"), + })?; + Ok(()) } @@ -690,7 +723,21 @@ where /// Increases the counter which keeps track of how many connections have been created. /// Should never fail. fn increase_connection_counter(&mut self) -> Result<(), ContextError> { - *self.ibc_store.conn_counter.lock() += 1; + let current_sequence = self + .ibc_store + .conn_counter + .get(StoreHeight::Pending, &NextConnectionSequencePath {}) + .ok_or(ConnectionError::Other { + description: "connection counter not found".into(), + })?; + + self.ibc_store + .conn_counter + .set(NextConnectionSequencePath {}, current_sequence + 1) + .map_err(|e| ConnectionError::Other { + description: format!("connection counter update failed: {e:?}"), + })?; + Ok(()) } @@ -792,7 +839,21 @@ where } fn increase_channel_counter(&mut self) -> Result<(), ContextError> { - *self.ibc_store.channel_counter.lock() += 1; + let current_sequence = self + .ibc_store + .channel_counter + .get(StoreHeight::Pending, &NextChannelSequencePath {}) + .ok_or(ChannelError::Other { + description: "channel counter not found".into(), + })?; + + self.ibc_store + .channel_counter + .set(NextChannelSequencePath {}, current_sequence + 1) + .map_err(|e| ChannelError::Other { + description: format!("channel counter update failed: {e:?}"), + })?; + Ok(()) } diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 4a2fef5b4..63b091a63 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -25,7 +25,9 @@ use ibc::core::host::types::identifiers::{ }; use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, - CommitmentPath, ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, + ClientUpdateHeightPath, ClientUpdateTimePath, CommitmentPath, ConnectionPath, + NextChannelSequencePath, NextClientSequencePath, NextConnectionSequencePath, ReceiptPath, + SeqAckPath, SeqRecvPath, SeqSendPath, }; use ibc::core::host::{ExecutionContext, ValidationContext}; use ibc::core::primitives::prelude::*; @@ -33,6 +35,7 @@ use ibc::core::primitives::Timestamp; use ibc::core::router::router::Router; use ibc_proto::google::protobuf::Any; use ibc_proto::ibc::core::channel::v1::Channel as RawChannelEnd; +use ibc_proto::ibc::core::client::v1::Height as RawHeight; use ibc_proto::ibc::core::connection::v1::ConnectionEnd as RawConnectionEnd; use parking_lot::Mutex; use tendermint_testgen::Validator as TestgenValidator; @@ -60,18 +63,18 @@ where /// Handle to store instance. /// The module is guaranteed exclusive access to all paths in the store key-space. pub store: SharedStore, - /// Counter for clients - pub client_counter: Arc>, - /// Counter for connections - pub conn_counter: Arc>, - /// Counter for channels - pub channel_counter: Arc>, + /// A typed-store for next client counter sequence + pub client_counter: JsonStore, NextClientSequencePath, u64>, + /// A typed-store for next connection counter sequence + pub conn_counter: JsonStore, NextConnectionSequencePath, u64>, + /// A typed-store for next channel counter sequence + pub channel_counter: JsonStore, NextChannelSequencePath, u64>, /// Tracks the processed time for client updates - pub client_processed_times: Arc>>, - /// Tracks the processed height for client updates - pub client_processed_heights: Arc>>, - /// Map of host consensus states - pub consensus_states: Arc>>, + // pub client_processed_times: Arc>>, + pub client_processed_times: JsonStore, ClientUpdateTimePath, Timestamp>, + /// A typed-store to track the processed height for client updates + pub client_processed_heights: + ProtobufStore, ClientUpdateHeightPath, Height, RawHeight>, /// A typed-store for AnyClientState pub client_state_store: ProtobufStore, ClientStatePath, AnyClientState, Any>, /// A typed-store for AnyConsensusState @@ -96,6 +99,8 @@ where pub packet_receipt_store: TypedSet, ReceiptPath>, /// A typed-store for packet ack pub packet_ack_store: BinStore, AckPath, AcknowledgementCommitment>, + /// Map of host consensus states + pub consensus_states: Arc>>, /// IBC Events pub events: Arc>>, /// message logs @@ -108,12 +113,29 @@ where { pub fn new(store: S) -> Self { let shared_store = SharedStore::new(store); + + let mut client_counter = TypedStore::new(shared_store.clone()); + let mut conn_counter = TypedStore::new(shared_store.clone()); + let mut channel_counter = TypedStore::new(shared_store.clone()); + + client_counter + .set(NextClientSequencePath {}, 0) + .expect("no error"); + + conn_counter + .set(NextConnectionSequencePath {}, 0) + .expect("no error"); + + channel_counter + .set(NextChannelSequencePath {}, 0) + .expect("no error"); + Self { - client_counter: Arc::new(Mutex::new(0)), - conn_counter: Arc::new(Mutex::new(0)), - channel_counter: Arc::new(Mutex::new(0)), - client_processed_times: Arc::new(Mutex::new(Default::default())), - client_processed_heights: Arc::new(Mutex::new(Default::default())), + client_counter, + conn_counter, + channel_counter, + client_processed_times: TypedStore::new(shared_store.clone()), + client_processed_heights: TypedStore::new(shared_store.clone()), consensus_states: Arc::new(Mutex::new(Default::default())), client_state_store: TypedStore::new(shared_store.clone()), consensus_state_store: TypedStore::new(shared_store.clone()), From 283a9de6f63c783b7350db17be4ace6fc6059538 Mon Sep 17 00:00:00 2001 From: Ranadeep Biswas Date: Tue, 6 Feb 2024 20:35:11 +0100 Subject: [PATCH 22/22] rm redundant curly brackets --- ibc-testkit/src/testapp/ibc/core/core_ctx.rs | 18 +++++++++--------- ibc-testkit/src/testapp/ibc/core/types.rs | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs index bd450ff18..aaf2df979 100644 --- a/ibc-testkit/src/testapp/ibc/core/core_ctx.rs +++ b/ibc-testkit/src/testapp/ibc/core/core_ctx.rs @@ -112,7 +112,7 @@ where Ok(self .ibc_store .client_counter - .get(StoreHeight::Pending, &NextClientSequencePath {}) + .get(StoreHeight::Pending, &NextClientSequencePath) .ok_or(ClientError::Other { description: "client counter not found".into(), })?) @@ -142,7 +142,7 @@ where Ok(self .ibc_store .conn_counter - .get(StoreHeight::Pending, &NextConnectionSequencePath {}) + .get(StoreHeight::Pending, &NextConnectionSequencePath) .ok_or(ConnectionError::Other { description: "connection counter not found".into(), })?) @@ -268,7 +268,7 @@ where Ok(self .ibc_store .channel_counter - .get(StoreHeight::Pending, &NextChannelSequencePath {}) + .get(StoreHeight::Pending, &NextChannelSequencePath) .ok_or(ChannelError::Other { description: "channel counter not found".into(), })?) @@ -668,14 +668,14 @@ where let current_sequence = self .ibc_store .client_counter - .get(StoreHeight::Pending, &NextClientSequencePath {}) + .get(StoreHeight::Pending, &NextClientSequencePath) .ok_or(ClientError::Other { description: "client counter not found".into(), })?; self.ibc_store .client_counter - .set(NextClientSequencePath {}, current_sequence + 1) + .set(NextClientSequencePath, current_sequence + 1) .map_err(|e| ClientError::Other { description: format!("client counter update failed: {e:?}"), })?; @@ -726,14 +726,14 @@ where let current_sequence = self .ibc_store .conn_counter - .get(StoreHeight::Pending, &NextConnectionSequencePath {}) + .get(StoreHeight::Pending, &NextConnectionSequencePath) .ok_or(ConnectionError::Other { description: "connection counter not found".into(), })?; self.ibc_store .conn_counter - .set(NextConnectionSequencePath {}, current_sequence + 1) + .set(NextConnectionSequencePath, current_sequence + 1) .map_err(|e| ConnectionError::Other { description: format!("connection counter update failed: {e:?}"), })?; @@ -842,14 +842,14 @@ where let current_sequence = self .ibc_store .channel_counter - .get(StoreHeight::Pending, &NextChannelSequencePath {}) + .get(StoreHeight::Pending, &NextChannelSequencePath) .ok_or(ChannelError::Other { description: "channel counter not found".into(), })?; self.ibc_store .channel_counter - .set(NextChannelSequencePath {}, current_sequence + 1) + .set(NextChannelSequencePath, current_sequence + 1) .map_err(|e| ChannelError::Other { description: format!("channel counter update failed: {e:?}"), })?; diff --git a/ibc-testkit/src/testapp/ibc/core/types.rs b/ibc-testkit/src/testapp/ibc/core/types.rs index 63b091a63..f38a8321d 100644 --- a/ibc-testkit/src/testapp/ibc/core/types.rs +++ b/ibc-testkit/src/testapp/ibc/core/types.rs @@ -119,15 +119,15 @@ where let mut channel_counter = TypedStore::new(shared_store.clone()); client_counter - .set(NextClientSequencePath {}, 0) + .set(NextClientSequencePath, 0) .expect("no error"); conn_counter - .set(NextConnectionSequencePath {}, 0) + .set(NextConnectionSequencePath, 0) .expect("no error"); channel_counter - .set(NextChannelSequencePath {}, 0) + .set(NextChannelSequencePath, 0) .expect("no error"); Self {