Skip to content

Commit 2acb2fa

Browse files
imp(ibc-testkit): Tendermint proof verifications via integration test (#1146)
* test tmclient proof verification * use ClientStateConfig directly * rm MockClientConfig * use basecoin store proofspec as default * update tests * use merkle storage in MockContext * fix ProofSpecs size * refactor MockIbcStore to perform begin_block and end_block * simpler proof verification test * use ValidationContext::commitment_prefix() * nits * refactor host related trait method * tendermint host client integration test with proof verification * rm raw test * use typed relayer * add todo for channel integration test * core over std * be semantically correct * add comment for TypedRelayer * integration test for all pairs * fix semantic bug * renames * add channel management * channel creation in RelayerContext * add channel creation in integration test * add test for channel close * query client_id from connection and channel * ibc_store_mut * utils functions for packet relay * add packet relay integration test * add comments * optimize integration utils functions * serde feature for integration tests * rm redundant chain_id * sync clock only on a * add comment * imp: place router under MockGenericContext * nit: add docstring for router * nits * rm redundant lint filters * imp: ditch RelayerContext * nit: simplify build_client_update_datagram * refactor integration tests * add doc strings for TypedRelayerOps * doc strings for RelayerContext * mv client_update_ping_pong to tests dir * rename main_store to multi_store * update TestHost trait * update mock and tendermint hosts * update relayer functions * nits * renames and comments * add comments for return values in relayer ops * imp: simplify into_header --------- Co-authored-by: Farhad Shabani <[email protected]>
1 parent 05ea5fa commit 2acb2fa

File tree

16 files changed

+1984
-342
lines changed

16 files changed

+1984
-342
lines changed

ibc-testkit/src/context.rs

Lines changed: 87 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
use core::fmt::Debug;
22
use core::time::Duration;
33

4-
use basecoin_store::avl::get_proof_spec as basecoin_proof_spec;
54
use basecoin_store::context::ProvableStore;
65
use basecoin_store::impls::{GrowingStore, InMemoryStore, RevertibleStore};
76
use ibc::core::channel::types::channel::ChannelEnd;
87
use ibc::core::channel::types::commitment::PacketCommitment;
98
use ibc::core::client::context::client_state::ClientStateValidation;
10-
use ibc::core::client::context::ClientExecutionContext;
9+
use ibc::core::client::context::{ClientExecutionContext, ClientValidationContext};
1110
use ibc::core::client::types::Height;
12-
use ibc::core::commitment_types::specs::ProofSpecs;
1311
use ibc::core::connection::types::ConnectionEnd;
1412
use ibc::core::entrypoint::dispatch;
1513
use ibc::core::handler::types::events::IbcEvent;
@@ -20,16 +18,15 @@ use ibc::core::host::types::path::{
2018
SeqAckPath, SeqRecvPath, SeqSendPath,
2119
};
2220
use ibc::core::host::{ExecutionContext, ValidationContext};
23-
use ibc::core::router::router::Router;
2421
use ibc::primitives::prelude::*;
2522
use ibc::primitives::Timestamp;
26-
use typed_builder::TypedBuilder;
2723

2824
use super::testapp::ibc::core::types::{LightClientState, MockIbcStore};
2925
use crate::fixtures::core::context::MockContextConfig;
3026
use crate::hosts::{HostClientState, TestBlock, TestHeader, TestHost};
3127
use crate::relayer::error::RelayerError;
3228
use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState};
29+
use crate::testapp::ibc::core::router::MockRouter;
3330
use crate::testapp::ibc::core::types::DEFAULT_BLOCK_TIME_SECS;
3431

3532
/// A context implementing the dependencies necessary for testing any IBC module.
@@ -40,34 +37,23 @@ where
4037
H: TestHost,
4138
HostClientState<H>: ClientStateValidation<MockIbcStore<S>>,
4239
{
40+
/// The multi store of the context.
41+
/// This is where the IBC store root is stored at IBC commitment prefix.
42+
pub multi_store: S,
43+
4344
/// The type of host chain underlying this mock context.
4445
pub host: H,
4546

4647
/// An object that stores all IBC related data.
4748
pub ibc_store: MockIbcStore<S>,
49+
50+
/// A router that can route messages to the appropriate IBC application.
51+
pub ibc_router: MockRouter,
4852
}
4953

5054
pub type MockStore = RevertibleStore<GrowingStore<InMemoryStore>>;
5155
pub type MockContext<H> = MockGenericContext<MockStore, H>;
5256

53-
#[derive(Debug, TypedBuilder)]
54-
pub struct MockClientConfig {
55-
#[builder(default = Duration::from_secs(64000))]
56-
pub trusting_period: Duration,
57-
#[builder(default = Duration::from_millis(3000))]
58-
pub max_clock_drift: Duration,
59-
#[builder(default = Duration::from_secs(128_000))]
60-
pub unbonding_period: Duration,
61-
#[builder(default = vec![basecoin_proof_spec()].into())]
62-
pub proof_specs: ProofSpecs,
63-
}
64-
65-
impl Default for MockClientConfig {
66-
fn default() -> Self {
67-
Self::builder().build()
68-
}
69-
}
70-
7157
/// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are
7258
/// present, and the chain has Height(5). This should be used sparingly, mostly for testing the
7359
/// creation of new domain objects.
@@ -94,6 +80,10 @@ where
9480
&self.ibc_store
9581
}
9682

83+
pub fn ibc_store_mut(&mut self) -> &mut MockIbcStore<S> {
84+
&mut self.ibc_store
85+
}
86+
9787
pub fn host_block(&self, target_height: &Height) -> Option<H::Block> {
9888
self.host.get_block(target_height)
9989
}
@@ -102,6 +92,13 @@ where
10292
self.host.get_block(&self.latest_height())
10393
}
10494

95+
pub fn light_client_latest_height(&self, client_id: &ClientId) -> Height {
96+
self.ibc_store
97+
.client_state(client_id)
98+
.expect("client state exists")
99+
.latest_height()
100+
}
101+
105102
pub fn advance_block_up_to(mut self, target_height: Height) -> Self {
106103
let latest_height = self.host.latest_height();
107104
if target_height.revision_number() != latest_height.revision_number() {
@@ -118,38 +115,79 @@ where
118115
}
119116

120117
pub fn generate_genesis_block(&mut self, genesis_time: Timestamp, params: &H::BlockParams) {
121-
// commit store
122-
let app_hash = self.ibc_store.commit().expect("no error");
118+
self.end_block();
123119

124-
// generate and push genesis block
125-
let genesis_block = self.host.generate_block(app_hash, 1, genesis_time, params);
126-
self.host.push_block(genesis_block);
120+
// commit multi store
121+
let multi_store_commitment = self.multi_store.commit().expect("no error");
127122

128-
// store it in ibc context as host consensus state
129-
self.ibc_store.store_host_consensus_state(
123+
// generate a genesis block
124+
let genesis_block =
130125
self.host
131-
.latest_block()
132-
.into_header()
133-
.into_consensus_state()
134-
.into(),
126+
.generate_block(multi_store_commitment, 1, genesis_time, params);
127+
128+
// push the genesis block to the host
129+
self.host.push_block(genesis_block);
130+
131+
self.begin_block();
132+
}
133+
134+
pub fn begin_block(&mut self) {
135+
let consensus_state = self
136+
.host
137+
.latest_block()
138+
.into_header()
139+
.into_consensus_state()
140+
.into();
141+
142+
let ibc_commitment_proof = self
143+
.multi_store
144+
.get_proof(
145+
self.host.latest_height().revision_height().into(),
146+
&self
147+
.ibc_store
148+
.commitment_prefix()
149+
.as_bytes()
150+
.try_into()
151+
.expect("valid utf8 prefix"),
152+
)
153+
.expect("no error");
154+
155+
self.ibc_store.begin_block(
156+
self.host.latest_height().revision_height(),
157+
consensus_state,
158+
ibc_commitment_proof,
135159
);
136160
}
137161

138-
pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) {
139-
// commit store
140-
let app_hash = self.ibc_store.commit().expect("no error");
162+
pub fn end_block(&mut self) {
163+
// commit ibc store
164+
let ibc_store_commitment = self.ibc_store.end_block().expect("no error");
165+
166+
// commit ibc store commitment in multi store
167+
self.multi_store
168+
.set(
169+
self.ibc_store
170+
.commitment_prefix()
171+
.as_bytes()
172+
.try_into()
173+
.expect("valid utf8 prefix"),
174+
ibc_store_commitment,
175+
)
176+
.expect("no error");
177+
}
141178

179+
pub fn produce_block(&mut self, block_time: Duration, params: &H::BlockParams) {
180+
// commit the multi store
181+
let multi_store_commitment = self.multi_store.commit().expect("no error");
142182
// generate a new block
143-
self.host.advance_block(app_hash, block_time, params);
183+
self.host
184+
.advance_block(multi_store_commitment, block_time, params);
185+
}
144186

145-
// store it in ibc context as host consensus state
146-
self.ibc_store.store_host_consensus_state(
147-
self.host
148-
.latest_block()
149-
.into_header()
150-
.into_consensus_state()
151-
.into(),
152-
);
187+
pub fn advance_with_block_params(&mut self, block_time: Duration, params: &H::BlockParams) {
188+
self.end_block();
189+
self.produce_block(block_time, params);
190+
self.begin_block();
153191
}
154192

155193
pub fn advance_block(&mut self) {
@@ -355,12 +393,9 @@ where
355393
/// A datagram passes from the relayer to the IBC module (on host chain).
356394
/// Alternative method to `Ics18Context::send` that does not exercise any serialization.
357395
/// Used in testing the Ics18 algorithms, hence this may return a Ics18Error.
358-
pub fn deliver(
359-
&mut self,
360-
router: &mut impl Router,
361-
msg: MsgEnvelope,
362-
) -> Result<(), RelayerError> {
363-
dispatch(&mut self.ibc_store, router, msg).map_err(RelayerError::TransactionFailed)?;
396+
pub fn deliver(&mut self, msg: MsgEnvelope) -> Result<(), RelayerError> {
397+
dispatch(&mut self.ibc_store, &mut self.ibc_router, msg)
398+
.map_err(RelayerError::TransactionFailed)?;
364399
// Create a new block.
365400
self.advance_block();
366401
Ok(())

ibc-testkit/src/fixtures/clients/tendermint.rs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use core::str::FromStr;
22
use core::time::Duration;
33

4+
use basecoin_store::avl::get_proof_spec as basecoin_proof_spec;
45
use ibc::clients::tendermint::client_state::ClientState as TmClientState;
56
use ibc::clients::tendermint::types::error::{Error as ClientError, Error};
67
use ibc::clients::tendermint::types::proto::v1::{ClientState as RawTmClientState, Fraction};
@@ -15,6 +16,7 @@ use ibc::core::commitment_types::specs::ProofSpecs;
1516
use ibc::core::host::types::identifiers::ChainId;
1617
use ibc::core::primitives::prelude::*;
1718
use tendermint::block::Header as TmHeader;
19+
use typed_builder::TypedBuilder;
1820

1921
/// Returns a dummy tendermint `ClientState` by given `frozen_height`, for testing purposes only!
2022
pub fn dummy_tm_client_state_from_raw(frozen_height: RawHeight) -> Result<TmClientState, Error> {
@@ -64,43 +66,48 @@ pub fn dummy_raw_tm_client_state(frozen_height: RawHeight) -> RawTmClientState {
6466
}
6567
}
6668

67-
#[derive(typed_builder::TypedBuilder, Debug)]
69+
#[derive(TypedBuilder, Debug)]
6870
pub struct ClientStateConfig {
69-
pub chain_id: ChainId,
7071
#[builder(default = TrustThreshold::ONE_THIRD)]
7172
pub trust_level: TrustThreshold,
7273
#[builder(default = Duration::from_secs(64000))]
7374
pub trusting_period: Duration,
7475
#[builder(default = Duration::from_secs(128_000))]
7576
pub unbonding_period: Duration,
7677
#[builder(default = Duration::from_millis(3000))]
77-
max_clock_drift: Duration,
78-
pub latest_height: Height,
79-
#[builder(default = ProofSpecs::cosmos())]
78+
pub max_clock_drift: Duration,
79+
#[builder(default = vec![basecoin_proof_spec(); 2].into())]
8080
pub proof_specs: ProofSpecs,
8181
#[builder(default)]
8282
pub upgrade_path: Vec<String>,
8383
#[builder(default = AllowUpdate { after_expiry: false, after_misbehaviour: false })]
8484
allow_update: AllowUpdate,
8585
}
8686

87-
impl TryFrom<ClientStateConfig> for TmClientState {
88-
type Error = ClientError;
89-
90-
fn try_from(config: ClientStateConfig) -> Result<Self, Self::Error> {
91-
let client_state = ClientStateType::new(
92-
config.chain_id,
93-
config.trust_level,
94-
config.trusting_period,
95-
config.unbonding_period,
96-
config.max_clock_drift,
97-
config.latest_height,
98-
config.proof_specs,
99-
config.upgrade_path,
100-
config.allow_update,
101-
)?;
102-
103-
Ok(client_state.into())
87+
impl Default for ClientStateConfig {
88+
fn default() -> Self {
89+
Self::builder().build()
90+
}
91+
}
92+
93+
impl ClientStateConfig {
94+
pub fn into_client_state(
95+
self,
96+
chain_id: ChainId,
97+
latest_height: Height,
98+
) -> Result<TmClientState, ClientError> {
99+
Ok(ClientStateType::new(
100+
chain_id,
101+
self.trust_level,
102+
self.trusting_period,
103+
self.unbonding_period,
104+
self.max_clock_drift,
105+
latest_height,
106+
self.proof_specs,
107+
self.upgrade_path,
108+
self.allow_update,
109+
)?
110+
.into())
104111
}
105112
}
106113

ibc-testkit/src/fixtures/core/context.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use typed_builder::TypedBuilder;
1010

1111
use crate::context::MockGenericContext;
1212
use crate::hosts::{HostClientState, TestBlock, TestHost};
13+
use crate::testapp::ibc::core::router::MockRouter;
1314
use crate::testapp::ibc::core::types::{MockIbcStore, DEFAULT_BLOCK_TIME_SECS};
1415
use crate::utils::year_2023;
1516

@@ -21,7 +22,7 @@ where
2122
H: TestHost,
2223
{
2324
#[builder(default)]
24-
pub host: H,
25+
host: H,
2526

2627
#[builder(default = Duration::from_secs(DEFAULT_BLOCK_TIME_SECS))]
2728
block_time: Duration,
@@ -56,11 +57,13 @@ where
5657
.expect("no underflow");
5758

5859
let mut context = Self {
60+
multi_store: Default::default(),
61+
host: params.host,
5962
ibc_store: MockIbcStore::new(
6063
params.latest_height.revision_number(),
6164
Default::default(),
6265
),
63-
host: params.host,
66+
ibc_router: MockRouter::new_with_transfer(),
6467
};
6568

6669
// store is a height 0; no block

ibc-testkit/src/hosts/mock.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ impl TestBlock for MockHeader {
8383
fn timestamp(&self) -> Timestamp {
8484
self.timestamp
8585
}
86+
87+
fn into_header_with_trusted(self, _: &Self) -> Self::Header {
88+
self
89+
}
8690
}
8791

8892
impl From<MockHeader> for MockConsensusState {

ibc-testkit/src/hosts/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,21 @@ pub trait TestHost: Default + Debug + Sized {
124124
/// TestBlock is a trait that defines the interface for a block produced by a host blockchain.
125125
pub trait TestBlock: Clone + Debug {
126126
/// The type of header can be extracted from the block.
127-
type Header: TestHeader + From<Self>;
127+
type Header: TestHeader;
128128

129129
/// The height of the block.
130130
fn height(&self) -> Height;
131131

132132
/// The timestamp of the block.
133133
fn timestamp(&self) -> Timestamp;
134134

135-
/// Extract the header from the block.
135+
/// Extract the IBC header using the target and trusted blocks.
136+
fn into_header_with_trusted(self, trusted_block: &Self) -> Self::Header;
137+
138+
/// Extract the IBC header only using the target block (sets the trusted
139+
/// block to itself).
136140
fn into_header(self) -> Self::Header {
137-
self.into()
141+
self.clone().into_header_with_trusted(&self)
138142
}
139143
}
140144

0 commit comments

Comments
 (0)