Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: tendermint client update when client expires or validator set has changed #921

Merged
merged 26 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1d78333
add client expiry test
rnbguy Oct 13, 2023
90b006d
tm hostblock supports trusted next validator set
rnbguy Oct 13, 2023
6039366
fix validator change update test
rnbguy Oct 13, 2023
a2163f1
fix incorrect validator updates at each block
rnbguy Oct 13, 2023
7dfc285
add sad client update for validator change
rnbguy Oct 13, 2023
78f5eda
cargo fmt
rnbguy Oct 13, 2023
89ffdbf
catch up with main branch changes
rnbguy Oct 16, 2023
2d90996
update MockContextConfig with validator set history
rnbguy Oct 16, 2023
ef774cc
refactor tests with updated MockContextConfig
rnbguy Oct 16, 2023
2750e90
rm duplicate def of `on`
rnbguy Oct 16, 2023
25b9b4d
add todo for max_history_size and validator_set_history
rnbguy Oct 16, 2023
f05537a
consistent variable naming in tests
rnbguy Oct 17, 2023
e827067
bump typed-builder version
rnbguy Oct 17, 2023
bf55a97
rm redundant builder arguments
rnbguy Oct 17, 2023
87196d3
replace todo with panic
rnbguy Oct 17, 2023
122a570
mv Tendermint ClientStateConfig under ics07
rnbguy Oct 17, 2023
7f7f79f
use ctx_a with ctx_b instead of only ctx
rnbguy Oct 17, 2023
eb8798c
use client_id consistently
rnbguy Oct 17, 2023
6b47dc7
use mocks feature directly in dev-deps
rnbguy Oct 17, 2023
f7cc801
include trusting_period and max_clock_drift in mock light client config
rnbguy Oct 18, 2023
0d2d642
revert advance chain height with timestamp
rnbguy Oct 18, 2023
3020a41
update client expiry test
rnbguy Oct 18, 2023
1d3bba8
add test to check max_clock_drift
rnbguy Oct 18, 2023
554ac48
rm TODO comments in favor of gh issue
rnbguy Oct 18, 2023
0da962b
revert ctx_a renaming
rnbguy Oct 18, 2023
57180f9
add changelog entry
rnbguy Oct 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/cw-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ on:
paths:
- .github/workflows/cw-check.yml
- ci/cw-check/**

on:
push:
tags:
- v[0-9]+.*
Expand Down
4 changes: 3 additions & 1 deletion crates/ibc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ schema = ["dep:schemars", "serde", "std"]

# This feature grants access to development-time mocking libraries, such as `MockContext` or `MockHeader`.
# Depends on the `testgen` suite for generating Tendermint light blocks.
mocks = ["tendermint-testgen", "tendermint/clock", "parking_lot"]
mocks = ["tendermint-testgen", "tendermint/clock", "parking_lot", "typed-builder"]

[dependencies]
# Proto definitions for all IBC-related interfaces, e.g., connections or channels.
Expand All @@ -73,6 +73,7 @@ scale-info = { version = "2.1.2", default-features = false, features = ["derive"
## for borsh encode or decode
borsh = {version = "0.10", default-features = false, optional = true }
parking_lot = { version = "0.12.1", default-features = false, optional = true }
typed-builder = { version = "0.16.2", optional = true }
Farhad-Shabani marked this conversation as resolved.
Show resolved Hide resolved

ibc-derive = { version ="0.3.0", path = "../ibc-derive" }

Expand Down Expand Up @@ -104,3 +105,4 @@ test-log = { version = "0.2.10", features = ["trace"] }
tendermint-rpc = { version = "0.34", features = ["http-client", "websocket-client"] }
tendermint-testgen = { version = "0.34" } # Needed for generating (synthetic) light blocks.
parking_lot = { version = "0.12.1" }
typed-builder = { version = "0.16.2" }
3 changes: 2 additions & 1 deletion crates/ibc/src/clients/ics07_tendermint/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,14 @@ pub mod test_util {
fn from(light_block: SyntheticTmBlock) -> Self {
let SyntheticTmBlock {
trusted_height,
trusted_next_validators,
light_block,
} = light_block;
Self {
signed_header: light_block.signed_header,
validator_set: light_block.validators,
trusted_height,
trusted_next_validator_set: light_block.next_validators,
trusted_next_validator_set: trusted_next_validators,
}
}
}
Expand Down
239 changes: 207 additions & 32 deletions crates/ibc/src/core/ics02_client/handler/update_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ where

#[cfg(test)]
mod tests {
use core::ops::Add;
use core::str::FromStr;
use core::time::Duration;

Expand All @@ -143,7 +144,9 @@ mod tests {
use crate::core::ics24_host::identifier::{ChainId, ClientId};
use crate::core::timestamp::Timestamp;
use crate::mock::client_state::{client_type as mock_client_type, MockClientState};
use crate::mock::context::{AnyConsensusState, MockContext};
use crate::mock::context::{
AnyClientState, AnyConsensusState, MockClientConfig, MockContext, MockContextConfig,
};
use crate::mock::header::MockHeader;
use crate::mock::host::{HostBlock, HostType};
use crate::mock::misbehaviour::Misbehaviour as MockMisbehaviour;
Expand Down Expand Up @@ -249,47 +252,74 @@ mod tests {
fn test_update_synthetic_tendermint_client_validator_change_ok() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_height = Height::new(1, 20).unwrap();
let update_height = Height::new(1, 21).unwrap();
let chain_id_b = ChainId::new("mockgaiaB", 1).unwrap();

let mut ctx = MockContext::new(
ChainId::new("mockgaiaA", 1).unwrap(),
HostType::Mock,
5,
Height::new(1, 1).unwrap(),
)
.with_client_parametrized_with_chain_id(
chain_id_b.clone(),
&client_id,
client_height,
Some(tm_client_type()), // The target host chain (B) is synthetic TM.
Some(client_height),
);
let mut ctx = MockContextConfig::builder()
.host_id(ChainId::new("mockgaiaA", 1).unwrap())
Farhad-Shabani marked this conversation as resolved.
Show resolved Hide resolved
.host_type(HostType::Mock)
.latest_height(Height::new(1, 1).unwrap())
.max_history_size(5)
.build()
.with_client_config(
// client state initialized with client_height, and
// [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set.
MockClientConfig::builder()
.client_chain_id(chain_id_b.clone())
.client_id(client_id.clone())
.client_state_height(client_height)
.client_type(tm_client_type())
.build(),
);

let ctx_b = MockContext::new_with_validator_history(
chain_id_b,
HostType::SyntheticTendermint,
&[
// TODO(rano): the validator set params during setups.
// Here I picked the default validator set which is
// used at host side client creation.
vec![
TestgenValidator::new("1").voting_power(50),
TestgenValidator::new("2").voting_power(50),
],
vec![
TestgenValidator::new("1").voting_power(60),
TestgenValidator::new("2").voting_power(40),
],
let ctx_b_val_history = vec![
// TODO(rano): the validator set params during setups.
// Here I picked the default validator set which is
// used at host side client creation.
//
// validator set of height-20
Farhad-Shabani marked this conversation as resolved.
Show resolved Hide resolved
vec![
TestgenValidator::new("1").voting_power(50),
TestgenValidator::new("2").voting_power(50),
],
update_height,
);
// validator set of height-21
vec![
TestgenValidator::new("1").voting_power(50),
TestgenValidator::new("2").voting_power(50),
],
// validator set of height-22
vec![
TestgenValidator::new("1").voting_power(30),
TestgenValidator::new("2").voting_power(70),
],
// validator set of height-23
vec![
TestgenValidator::new("1").voting_power(20),
TestgenValidator::new("2").voting_power(80),
],
];

let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2);

let ctx_b = MockContextConfig::builder()
.host_id(chain_id_b.clone())
.host_type(HostType::SyntheticTendermint)
.latest_height(update_height)
.max_history_size(ctx_b_val_history.len() as u64 - 1)
.validator_set_history(ctx_b_val_history)
.build();

let signer = get_dummy_account_id();

let mut block = ctx_b.host_block(&update_height).unwrap().clone();
block.set_trusted_height(client_height);

let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") {
HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(),
_ => panic!("unexpected host block type"),
};

block.set_trusted_next_validators_set(trusted_next_validator_set);

let latest_header_height = block.height();
let msg = MsgUpdateClient {
client_id,
Expand All @@ -311,6 +341,89 @@ mod tests {
assert_eq!(client_state.latest_height(), latest_header_height);
}

#[test]
fn test_update_synthetic_tendermint_client_validator_change_fail() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
let client_height = Height::new(1, 20).unwrap();
let chain_id_b = ChainId::new("mockgaiaB", 1).unwrap();

let ctx = MockContextConfig::builder()
.host_id(ChainId::new("mockgaiaA", 1).unwrap())
.host_type(HostType::Mock)
.latest_height(Height::new(1, 1).unwrap())
.max_history_size(5)
Farhad-Shabani marked this conversation as resolved.
Show resolved Hide resolved
.build()
.with_client_config(
// client state initialized with client_height, and
// [{id: 1, power: 50}, {id: 2, power: 50}] for validator set and next validator set.
MockClientConfig::builder()
.client_chain_id(chain_id_b.clone())
.client_id(client_id.clone())
.client_state_height(client_height)
.client_type(tm_client_type())
.build(),
);

let ctx_b_val_history = vec![
// TODO(rano): the validator set params during setups.
// Here I picked the default validator set which is
// used at host side client creation.
//
// validator set of height-20
vec![
TestgenValidator::new("1").voting_power(50),
TestgenValidator::new("2").voting_power(50),
],
// incorrect next validator set for height-20
// validator set of height-21
vec![
TestgenValidator::new("1").voting_power(45),
TestgenValidator::new("2").voting_power(55),
],
// validator set of height-22
vec![
TestgenValidator::new("1").voting_power(30),
TestgenValidator::new("2").voting_power(70),
],
// validator set of height-23
vec![
TestgenValidator::new("1").voting_power(20),
TestgenValidator::new("2").voting_power(80),
],
];

let update_height = client_height.add(ctx_b_val_history.len() as u64 - 2);

let ctx_b = MockContextConfig::builder()
.host_id(chain_id_b.clone())
.host_type(HostType::SyntheticTendermint)
.latest_height(update_height)
.max_history_size(ctx_b_val_history.len() as u64 - 1)
.validator_set_history(ctx_b_val_history)
.build();

let signer = get_dummy_account_id();

let mut block = ctx_b.host_block(&update_height).unwrap().clone();
block.set_trusted_height(client_height);

let trusted_next_validator_set = match ctx_b.host_block(&client_height).expect("no error") {
HostBlock::SyntheticTendermint(header) => header.light_block.next_validators.clone(),
_ => panic!("unexpected host block type"),
};

block.set_trusted_next_validators_set(trusted_next_validator_set);

let msg = MsgUpdateClient {
client_id,
client_message: block.into(),
signer,
};

let res = validate(&ctx, MsgUpdateOrMisbehaviour::UpdateClient(msg.clone()));
assert!(res.is_err());
}

#[test]
fn test_update_synthetic_tendermint_client_non_adjacent_ok() {
let client_id = ClientId::new(tm_client_type(), 0).unwrap();
Expand Down Expand Up @@ -753,4 +866,66 @@ mod tests {
assert!(res.is_ok());
ensure_misbehaviour(&ctx_a, &client_id, &tm_client_type());
}

#[test]
fn test_expired_client() {
let remote_chain_id = ChainId::new("mockgaiaB", 1).unwrap();
let host_chain_id = ChainId::new("mockgaiaA", 1).unwrap();
Farhad-Shabani marked this conversation as resolved.
Show resolved Hide resolved

let remote_height = Height::new(1, 21).unwrap();
let remote_client_height_on_host = remote_height.sub(3).unwrap();

let remote_client_id_on_host = ClientId::new(tm_client_type(), 0).unwrap();

let timestamp = Timestamp::now();

let mut host_ctx = MockContextConfig::builder()
.host_id(host_chain_id.clone())
.latest_height(Height::new(1, 1).unwrap())
.latest_timestamp(timestamp)
.build()
.with_client_config(
MockClientConfig::builder()
.client_chain_id(remote_chain_id.clone())
.client_id(remote_client_id_on_host.clone())
.client_state_height(remote_client_height_on_host)
.client_type(tm_client_type())
.latest_timestamp(timestamp)
.build(),
);

let mut remote_ctx = MockContextConfig::builder()
.host_id(remote_chain_id.clone())
.host_type(HostType::SyntheticTendermint)
.latest_height(remote_height)
.latest_timestamp(timestamp)
.build();

{
let remote_raw_client_state = host_ctx.client_state(&remote_client_id_on_host).unwrap();

let remote_client_state = match remote_raw_client_state {
AnyClientState::Tendermint(tm_client_state) => tm_client_state,
_ => panic!("never fails. not mock client"),
};

let client_trusting_period = remote_client_state.trusting_period;

let future_timestamp = host_ctx
.host_timestamp()
.expect("never fails")
.add(client_trusting_period)
.expect("overflow");

host_ctx.advance_host_chain_height_with_timestamp(future_timestamp);
remote_ctx.advance_host_chain_height_with_timestamp(future_timestamp);
}

let remote_client_state = host_ctx.client_state(&remote_client_id_on_host).unwrap();

assert!(remote_client_state
.status(&host_ctx, &remote_client_id_on_host)
.unwrap()
.is_expired());
}
}
Loading
Loading