Skip to content

Commit 707d032

Browse files
Disallow creation of new Tendermint client state instance with a frozen height (#602)
1 parent 907ed69 commit 707d032

File tree

6 files changed

+115
-62
lines changed

6 files changed

+115
-62
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Disallow creation of new Tendermint client state instance with a frozen height
2+
([#178](https://github.com/cosmos/ibc-rs/issues/178))

crates/ibc/src/clients/ics07_tendermint/client_state.rs

Lines changed: 102 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ impl ClientState {
8282
proof_specs: ProofSpecs,
8383
upgrade_path: Vec<String>,
8484
allow_update: AllowUpdate,
85-
frozen_height: Option<Height>,
8685
) -> Result<ClientState, Error> {
8786
if chain_id.as_str().len() > MaxChainIdLen {
8887
return Err(Error::ChainIdTooLong {
@@ -172,7 +171,7 @@ impl ClientState {
172171
proof_specs,
173172
upgrade_path,
174173
allow_update,
175-
frozen_height,
174+
frozen_height: None,
176175
verifier: ProdVerifier::default(),
177176
})
178177
}
@@ -704,9 +703,6 @@ impl Ics2ClientState for ClientState {
704703
let upgraded_tm_client_state = TmClientState::try_from(upgraded_client_state)?;
705704
let upgraded_tm_cons_state = TmConsensusState::try_from(upgraded_consensus_state)?;
706705

707-
// Frozen height is set to None fo the new client state
708-
let new_frozen_height = None;
709-
710706
// Construct new client state and consensus state relayer chosen client
711707
// parameters are ignored. All chain-chosen parameters come from
712708
// committed client, all client-chosen parameters come from current
@@ -721,7 +717,6 @@ impl Ics2ClientState for ClientState {
721717
upgraded_tm_client_state.proof_specs,
722718
upgraded_tm_client_state.upgrade_path,
723719
self.allow_update,
724-
new_frozen_height,
725720
)?;
726721

727722
// The new consensus state is merely used as a trusted kernel against
@@ -860,9 +855,13 @@ impl TryFrom<RawTmClientState> for ClientState {
860855
// In `RawClientState`, a `frozen_height` of `0` means "not frozen".
861856
// See:
862857
// https://github.com/cosmos/ibc-go/blob/8422d0c4c35ef970539466c5bdec1cd27369bab3/modules/light-clients/07-tendermint/types/client_state.go#L74
863-
let frozen_height = raw
858+
if raw
864859
.frozen_height
865-
.and_then(|raw_height| raw_height.try_into().ok());
860+
.and_then(|h| Height::try_from(h).ok())
861+
.is_some()
862+
{
863+
return Err(Error::FrozenHeightNotAllowed);
864+
}
866865

867866
// We use set this deprecated field just so that we can properly convert
868867
// it back in its raw form
@@ -882,7 +881,6 @@ impl TryFrom<RawTmClientState> for ClientState {
882881
raw.proof_specs.into(),
883882
raw.upgrade_path,
884883
allow_update,
885-
frozen_height,
886884
)?;
887885

888886
Ok(client_state)
@@ -951,16 +949,20 @@ impl From<ClientState> for Any {
951949

952950
#[cfg(test)]
953951
mod tests {
952+
use crate::clients::ics07_tendermint::header::test_util::get_dummy_tendermint_header;
954953
use crate::prelude::*;
955954
use crate::Height;
956955
use core::time::Duration;
957956
use test_log::test;
958957

958+
use ibc_proto::google::protobuf::Any;
959+
use ibc_proto::ibc::core::client::v1::Height as RawHeight;
959960
use ibc_proto::ics23::ProofSpec as Ics23ProofSpec;
960961

961962
use crate::clients::ics07_tendermint::client_state::{
962963
AllowUpdate, ClientState as TmClientState,
963964
};
965+
use crate::clients::ics07_tendermint::error::Error;
964966
use crate::core::ics02_client::client_state::ClientState;
965967
use crate::core::ics02_client::trust_threshold::TrustThreshold;
966968
use crate::core::ics23_commitment::specs::ProofSpecs;
@@ -1133,7 +1135,6 @@ mod tests {
11331135
p.proof_specs,
11341136
p.upgrade_path,
11351137
p.allow_update,
1136-
None,
11371138
);
11381139

11391140
assert_eq!(
@@ -1199,7 +1200,6 @@ mod tests {
11991200
p.proof_specs,
12001201
p.upgrade_path,
12011202
p.allow_update,
1202-
None,
12031203
)
12041204
.unwrap();
12051205
let client_state = match test.setup {
@@ -1218,6 +1218,48 @@ mod tests {
12181218
);
12191219
}
12201220
}
1221+
1222+
#[test]
1223+
fn tm_client_state_conversions_healthy() {
1224+
// check client state creation path from a proto type
1225+
let tm_client_state_from_raw = TmClientState::new_dummy_from_raw(RawHeight {
1226+
revision_number: 0,
1227+
revision_height: 0,
1228+
});
1229+
assert!(tm_client_state_from_raw.is_ok());
1230+
1231+
let any_from_tm_client_state =
1232+
Any::from(tm_client_state_from_raw.as_ref().unwrap().clone());
1233+
let tm_client_state_from_any = TmClientState::try_from(any_from_tm_client_state);
1234+
assert!(tm_client_state_from_any.is_ok());
1235+
assert_eq!(
1236+
tm_client_state_from_raw.unwrap(),
1237+
tm_client_state_from_any.unwrap()
1238+
);
1239+
1240+
// check client state creation path from a tendermint header
1241+
let tm_header = get_dummy_tendermint_header();
1242+
let tm_client_state_from_header = TmClientState::new_dummy_from_header(tm_header);
1243+
let any_from_header = Any::from(tm_client_state_from_header.clone());
1244+
let tm_client_state_from_any = TmClientState::try_from(any_from_header);
1245+
assert!(tm_client_state_from_any.is_ok());
1246+
assert_eq!(
1247+
tm_client_state_from_header,
1248+
tm_client_state_from_any.unwrap()
1249+
);
1250+
}
1251+
1252+
#[test]
1253+
fn tm_client_state_malformed_with_frozen_height() {
1254+
let tm_client_state_from_raw = TmClientState::new_dummy_from_raw(RawHeight {
1255+
revision_number: 0,
1256+
revision_height: 10,
1257+
});
1258+
match tm_client_state_from_raw {
1259+
Err(Error::FrozenHeightNotAllowed) => {}
1260+
_ => panic!("Expected to fail with FrozenHeightNotAllowed error"),
1261+
}
1262+
}
12211263
}
12221264

12231265
#[cfg(all(test, feature = "serde"))]
@@ -1249,29 +1291,58 @@ pub mod test_util {
12491291
use tendermint::block::Header;
12501292

12511293
use crate::clients::ics07_tendermint::client_state::{AllowUpdate, ClientState};
1294+
use crate::clients::ics07_tendermint::error::Error;
12521295
use crate::core::ics02_client::height::Height;
1296+
use crate::core::ics23_commitment::specs::ProofSpecs;
12531297
use crate::core::ics24_host::identifier::ChainId;
1298+
use ibc_proto::ibc::core::client::v1::Height as RawHeight;
1299+
use ibc_proto::ibc::lightclients::tendermint::v1::{ClientState as RawTmClientState, Fraction};
12541300

1255-
pub fn get_dummy_tendermint_client_state(tm_header: Header) -> ClientState {
1256-
ClientState::new(
1257-
ChainId::from(tm_header.chain_id.clone()),
1258-
Default::default(),
1259-
Duration::from_secs(64000),
1260-
Duration::from_secs(128000),
1261-
Duration::from_millis(3000),
1262-
Height::new(
1263-
ChainId::chain_version(tm_header.chain_id.as_str()),
1264-
u64::from(tm_header.height),
1301+
impl ClientState {
1302+
pub fn new_dummy_from_raw(frozen_height: RawHeight) -> Result<Self, Error> {
1303+
ClientState::try_from(get_dummy_raw_tm_client_state(frozen_height))
1304+
}
1305+
1306+
pub fn new_dummy_from_header(tm_header: Header) -> ClientState {
1307+
ClientState::new(
1308+
tm_header.chain_id.clone().into(),
1309+
Default::default(),
1310+
Duration::from_secs(64000),
1311+
Duration::from_secs(128000),
1312+
Duration::from_millis(3000),
1313+
Height::new(
1314+
ChainId::chain_version(tm_header.chain_id.as_str()),
1315+
u64::from(tm_header.height),
1316+
)
1317+
.unwrap(),
1318+
Default::default(),
1319+
Default::default(),
1320+
AllowUpdate {
1321+
after_expiry: false,
1322+
after_misbehaviour: false,
1323+
},
12651324
)
1266-
.unwrap(),
1267-
Default::default(),
1268-
Default::default(),
1269-
AllowUpdate {
1270-
after_expiry: false,
1271-
after_misbehaviour: false,
1272-
},
1273-
None,
1274-
)
1275-
.unwrap()
1325+
.unwrap()
1326+
}
1327+
}
1328+
1329+
pub fn get_dummy_raw_tm_client_state(frozen_height: RawHeight) -> RawTmClientState {
1330+
#[allow(deprecated)]
1331+
RawTmClientState {
1332+
chain_id: ChainId::new("ibc".to_string(), 0).to_string(),
1333+
trust_level: Some(Fraction {
1334+
numerator: 1,
1335+
denominator: 3,
1336+
}),
1337+
trusting_period: Some(Duration::from_secs(64000).into()),
1338+
unbonding_period: Some(Duration::from_secs(128000).into()),
1339+
max_clock_drift: Some(Duration::from_millis(3000).into()),
1340+
latest_height: Some(Height::new(0, 10).unwrap().into()),
1341+
proof_specs: ProofSpecs::default().into(),
1342+
upgrade_path: Default::default(),
1343+
frozen_height: Some(frozen_height),
1344+
allow_update_after_expiry: false,
1345+
allow_update_after_misbehaviour: false,
1346+
}
12761347
}
12771348
}

crates/ibc/src/clients/ics07_tendermint/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ pub enum Error {
6767
HeaderTimestampTooLow { actual: String, min: String },
6868
/// header revision height = `{height}` is invalid
6969
InvalidHeaderHeight { height: u64 },
70+
/// Disallowed to create a new client with a frozen height
71+
FrozenHeightNotAllowed,
7072
/// the header's current/trusted revision number (`{current_revision}`) and the update's revision number (`{update_revision}`) should be the same
7173
MismatchedRevisions {
7274
current_revision: u64,

crates/ibc/src/core/ics02_client/handler/create_client.rs

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -113,23 +113,18 @@ where
113113

114114
#[cfg(test)]
115115
mod tests {
116-
use crate::clients::ics07_tendermint::client_type as tm_client_type;
117-
use crate::core::ics02_client::handler::create_client::{execute, validate};
118-
use crate::core::ValidationContext;
119116
use crate::prelude::*;
120117

121-
use core::time::Duration;
122118
use test_log::test;
123119

124-
use crate::clients::ics07_tendermint::client_state::{
125-
AllowUpdate, ClientState as TmClientState,
126-
};
120+
use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState;
121+
use crate::clients::ics07_tendermint::client_type as tm_client_type;
127122
use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState;
128123
use crate::clients::ics07_tendermint::header::test_util::get_dummy_tendermint_header;
124+
use crate::core::ics02_client::handler::create_client::{execute, validate};
129125
use crate::core::ics02_client::msgs::create_client::MsgCreateClient;
130-
use crate::core::ics02_client::trust_threshold::TrustThreshold;
131-
use crate::core::ics23_commitment::specs::ProofSpecs;
132126
use crate::core::ics24_host::identifier::ClientId;
127+
use crate::core::ValidationContext;
133128
use crate::mock::client_state::{client_type as mock_client_type, MockClientState};
134129
use crate::mock::consensus_state::MockConsensusState;
135130
use crate::mock::context::MockContext;
@@ -177,23 +172,7 @@ mod tests {
177172

178173
let tm_header = get_dummy_tendermint_header();
179174

180-
let tm_client_state = TmClientState::new(
181-
tm_header.chain_id.clone().into(),
182-
TrustThreshold::ONE_THIRD,
183-
Duration::from_secs(64000),
184-
Duration::from_secs(128000),
185-
Duration::from_millis(3000),
186-
Height::new(0, u64::from(tm_header.height)).unwrap(),
187-
ProofSpecs::default(),
188-
vec![],
189-
AllowUpdate {
190-
after_expiry: false,
191-
after_misbehaviour: false,
192-
},
193-
None,
194-
)
195-
.unwrap()
196-
.into();
175+
let tm_client_state = TmClientState::new_dummy_from_header(tm_header.clone()).into();
197176

198177
let client_type = tm_client_type();
199178

crates/ibc/src/core/ics02_client/msgs/create_client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ mod tests {
7575

7676
use ibc_proto::ibc::core::client::v1::MsgCreateClient as RawMsgCreateClient;
7777

78-
use crate::clients::ics07_tendermint::client_state::test_util::get_dummy_tendermint_client_state;
78+
use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState;
7979
use crate::clients::ics07_tendermint::consensus_state::ConsensusState as TmConsensusState;
8080
use crate::clients::ics07_tendermint::header::test_util::get_dummy_tendermint_header;
8181
use crate::core::ics02_client::msgs::create_client::MsgCreateClient;
@@ -86,7 +86,7 @@ mod tests {
8686
let signer = get_dummy_account_id();
8787

8888
let tm_header = get_dummy_tendermint_header();
89-
let tm_client_state = get_dummy_tendermint_client_state(tm_header.clone()).into();
89+
let tm_client_state = TmClientState::new_dummy_from_header(tm_header.clone()).into();
9090

9191
let msg = MsgCreateClient::new(
9292
tm_client_state,

crates/ibc/src/mock/context.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use subtle_encoding::bech32;
2525
use ibc_proto::google::protobuf::Any;
2626
use tracing::debug;
2727

28-
use crate::clients::ics07_tendermint::client_state::test_util::get_dummy_tendermint_client_state;
2928
use crate::clients::ics07_tendermint::client_state::ClientState as TmClientState;
3029
use crate::core::context::ContextError;
3130
use crate::core::context::Router;
@@ -245,7 +244,7 @@ impl MockContext {
245244
);
246245

247246
let client_state =
248-
get_dummy_tendermint_client_state(light_block.header().clone()).into_box();
247+
TmClientState::new_dummy_from_header(light_block.header().clone()).into_box();
249248

250249
// Return the tuple.
251250
(Some(client_state), light_block.into())
@@ -312,7 +311,7 @@ impl MockContext {
312311
HostBlock::generate_tm_block(client_chain_id, cs_height.revision_height(), now);
313312

314313
let client_state =
315-
get_dummy_tendermint_client_state(light_block.header().clone()).into_box();
314+
TmClientState::new_dummy_from_header(light_block.header().clone()).into_box();
316315

317316
// Return the tuple.
318317
(Some(client_state), light_block.into())

0 commit comments

Comments
 (0)