Skip to content

Commit

Permalink
imp(testkit): Mock IBC context with basecoin-store types (#1068)
Browse files Browse the repository at this point in the history
* non empty default CommitmentPrefix

* update ibc mock client types

* fix tests for updated mock types

* generalize MockContextConfig::into

* rm clone from MockContext

* add with_{client,consensus}_state

* add blocks_since in utils.rs

* client takes host timestamp by default

* refactor relayer context test

* fix few tests

* add ibc-query and basecoin-store deps

* add basecoin-store in mock ibc context

* refactor for updated mock ibc context

* imp timeout test

* public git commit as dep source

* fix spelling

* update MockClient types

* fix failing tests

* rm unused utils and deps

* fix cargo doc lint

* use ibc host paths in mock context

* rm redundant curly brackets

---------

Co-authored-by: Farhad Shabani <[email protected]>
  • Loading branch information
rnbguy and Farhad-Shabani authored Feb 7, 2024
1 parent 4428fa9 commit 1c3ba50
Show file tree
Hide file tree
Showing 23 changed files with 1,296 additions and 791 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,6 @@ tendermint-testgen = { version = "0.34.0", default-features = fals
# parity dependencies
parity-scale-codec = { version = "3.6.5", default-features = false, features = ["full"] }
scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }

[patch.crates-io]
ibc-proto = { git = "https://github.com/cosmos/ibc-proto-rs", rev = "7643fa3" }
8 changes: 7 additions & 1 deletion ibc-core/ics23-commitment/types/src/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>,
}

impl Default for CommitmentPrefix {
fn default() -> Self {
Self { bytes: vec![0x00] }
}
}

impl CommitmentPrefix {
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
Expand Down
2 changes: 2 additions & 0 deletions ibc-testkit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ typed-builder = { version = "0.18.0" }
# ibc dependencies
ibc = { workspace = true, features = ["std"] }
ibc-proto = { workspace = true }
ibc-query = { version = "0.50.0", path = "../ibc-query" }
basecoin-store = { git = "https://github.com/informalsystems/basecoin-rs", rev = "dc3b43a" }

# cosmos dependencies
tendermint = { workspace = true }
Expand Down
17 changes: 10 additions & 7 deletions ibc-testkit/src/fixtures/core/context.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
use alloc::sync::Arc;
use alloc::fmt::Debug;
use core::cmp::min;
use core::ops::{Add, Sub};
use core::time::Duration;

use basecoin_store::context::ProvableStore;
use ibc::core::client::types::Height;
use ibc::core::host::types::identifiers::ChainId;
use ibc::core::primitives::prelude::*;
use ibc::core::primitives::Timestamp;
use parking_lot::Mutex;
use tendermint_testgen::Validator as TestgenValidator;
use typed_builder::TypedBuilder;

use crate::hosts::block::{HostBlock, HostType};
use crate::testapp::ibc::core::types::{MockContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS};
use crate::testapp::ibc::core::types::{MockGenericContext, MockIbcStore, DEFAULT_BLOCK_TIME_SECS};

/// Configuration of the `MockContext` type for generating dummy contexts.
#[derive(Debug, TypedBuilder)]
#[builder(build_method(into = MockContext))]
#[builder(build_method(into))]
pub struct MockContextConfig {
#[builder(default = HostType::Mock)]
host_type: HostType,
Expand All @@ -41,7 +41,10 @@ pub struct MockContextConfig {
latest_timestamp: Timestamp,
}

impl From<MockContextConfig> for MockContext {
impl<S> From<MockContextConfig> for MockGenericContext<S>
where
S: ProvableStore + Debug + Default,
{
fn from(params: MockContextConfig) -> Self {
assert_ne!(
params.max_history_size, 0,
Expand Down Expand Up @@ -115,13 +118,13 @@ impl From<MockContextConfig> 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(),
}
}
}
2 changes: 1 addition & 1 deletion ibc-testkit/src/fixtures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub enum Expect {
Failure(Option<ContextError>),
}

#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Fixture<M: Debug> {
pub ctx: MockContext,
pub msg: M,
Expand Down
59 changes: 36 additions & 23 deletions ibc-testkit/src/relayer/context.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
Expand All @@ -24,7 +27,10 @@ pub trait RelayerContext {
fn signer(&self) -> Signer;
}

impl RelayerContext for MockContext {
impl<S> RelayerContext for MockGenericContext<S>
where
S: ProvableStore + Debug,
{
fn query_latest_height(&self) -> Result<Height, ContextError> {
ValidationContext::host_height(self)
}
Expand Down Expand Up @@ -59,7 +65,7 @@ mod tests {
use crate::relayer::error::RelayerError;
use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client_type;
use crate::testapp::ibc::core::router::MockRouter;
use crate::testapp::ibc::core::types::MockClientConfig;
use crate::testapp::ibc::core::types::{MockClientConfig, MockContext};

/// Builds a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest`
/// context, assuming that the latest header on the source context is `src_header`.
Expand Down Expand Up @@ -126,30 +132,37 @@ mod tests {
let mut ctx_a = MockContextConfig::builder()
.host_id(chain_id_a.clone())
.latest_height(chain_a_start_height)
.build()
.with_client_config(
MockClientConfig::builder()
.client_chain_id(chain_id_b.clone())
.client_id(client_on_a_for_b.clone())
.latest_height(client_on_a_for_b_height)
.client_type(tm_client_type()) // The target host chain (B) is synthetic TM.
.build(),
);
// dummy; not actually used in client updates
let mut router_a = MockRouter::new_with_transfer();
.build::<MockContext>();

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::<MockContext>();

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();

Expand Down
41 changes: 28 additions & 13 deletions ibc-testkit/src/testapp/ibc/clients/mock/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ use crate::testapp::ibc::clients::mock::client_state::client_type as mock_client
use crate::testapp::ibc::clients::mock::consensus_state::MockConsensusState;
use crate::testapp::ibc::clients::mock::header::{MockHeader, MOCK_HEADER_TYPE_URL};
use crate::testapp::ibc::clients::mock::misbehaviour::{Misbehaviour, MOCK_MISBEHAVIOUR_TYPE_URL};
use crate::testapp::ibc::clients::mock::proto::{
ClientState as RawMockClientState, Header as RawMockHeader,
};
use crate::testapp::ibc::clients::mock::proto::ClientState as RawMockClientState;

pub const MOCK_CLIENT_STATE_TYPE_URL: &str = "/ibc.mock.ClientState";
pub const MOCK_CLIENT_TYPE: &str = "9999-mock";
Expand All @@ -39,14 +37,16 @@ pub fn client_type() -> ClientType {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MockClientState {
pub header: MockHeader,
pub frozen_height: Option<Height>,
pub trusting_period: Duration,
pub frozen: bool,
}

impl MockClientState {
pub fn new(header: MockHeader) -> Self {
Self {
header,
frozen_height: None,
trusting_period: Duration::from_nanos(0),
frozen: false,
}
}

Expand All @@ -58,15 +58,22 @@ impl MockClientState {
None
}

pub fn with_frozen_height(self, frozen_height: Height) -> Self {
pub fn with_trusting_period(self, trusting_period: Duration) -> Self {
Self {
frozen_height: Some(frozen_height),
trusting_period,
..self
}
}

pub fn with_frozen_height(self, _frozen_height: Height) -> Self {
Self {
frozen: true,
..self
}
}

pub fn is_frozen(&self) -> bool {
self.frozen_height.is_some()
self.frozen
}

fn expired(&self, _elapsed: Duration) -> bool {
Expand All @@ -80,17 +87,25 @@ impl TryFrom<RawMockClientState> for MockClientState {
type Error = ClientError;

fn try_from(raw: RawMockClientState) -> Result<Self, Self::Error> {
Ok(Self::new(raw.header.expect("Never fails").try_into()?))
Ok(Self {
header: raw
.header
.ok_or(ClientError::Other {
description: "header is not present".into(),
})?
.try_into()?,
trusting_period: Duration::from_nanos(raw.trusting_period),
frozen: raw.frozen,
})
}
}

impl From<MockClientState> for RawMockClientState {
fn from(value: MockClientState) -> Self {
RawMockClientState {
header: Some(RawMockHeader {
height: Some(value.header.height().into()),
timestamp: value.header.timestamp.nanoseconds(),
}),
header: Some(value.header.into()),
trusting_period: value.trusting_period.as_nanos() as _,
frozen: value.frozen,
}
}
}
Expand Down
11 changes: 3 additions & 8 deletions ibc-testkit/src/testapp/ibc/clients/mock/consensus_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -40,7 +38,7 @@ impl TryFrom<RawMockConsensusState> 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]),
})
}
Expand All @@ -49,10 +47,7 @@ impl TryFrom<RawMockConsensusState> for MockConsensusState {
impl From<MockConsensusState> 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()),
}
}
}
Expand Down
14 changes: 9 additions & 5 deletions ibc-testkit/src/testapp/ibc/clients/mock/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ impl TryFrom<RawMockHeader> for MockHeader {
Ok(MockHeader {
height: raw
.height
.and_then(|raw_height| raw_height.try_into().ok())
.ok_or(ClientError::MissingClientMessage)?,

timestamp: Timestamp::from_nanoseconds(raw.timestamp)
.map_err(ClientError::InvalidPacketTimestamp)?,
.ok_or(ClientError::Other {
description: "missing height".into(),
})?
.try_into()?,
timestamp: Timestamp::from_nanoseconds(raw.timestamp).map_err(|err| {
ClientError::Other {
description: err.to_string(),
}
})?,
})
}
}
Expand Down
9 changes: 6 additions & 3 deletions ibc-testkit/src/testapp/ibc/clients/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<S: ProvableStore + Debug>)]
#[execution(MockGenericContext<S: ProvableStore + Debug>)]
pub enum AnyClientState {
Tendermint(TmClientState),
Mock(MockClientState),
Expand Down
Loading

0 comments on commit 1c3ba50

Please sign in to comment.