diff --git a/.changelog/unreleased/breaking-changes/1612-ibc-clock.md b/.changelog/unreleased/breaking-changes/1612-ibc-clock.md new file mode 100644 index 0000000000..64a72d6e45 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1612-ibc-clock.md @@ -0,0 +1 @@ +- Hide `ibc::Timestamp::now()` behind `clock` feature flag (#1612)[https://github.com/informalsystems/ibc-rs/pull/1612] diff --git a/modules/Cargo.toml b/modules/Cargo.toml index 53c8028274..1cbea37a23 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -17,11 +17,13 @@ description = """ all-features = true [features] -default = ["flex-error/std", "flex-error/eyre_tracer"] +default = ["std"] +std = ["flex-error/std", "flex-error/eyre_tracer", "clock"] +clock = ["tendermint/clock", "time/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"] +mocks = ["tendermint-testgen", "clock"] [dependencies] # Proto definitions for all IBC-related interfaces, e.g., connections or channels. @@ -44,9 +46,11 @@ derive_more = { version = "0.99.0", default-features = false, features = ["from" [dependencies.tendermint] version = "=0.23.5" +default-features = false [dependencies.tendermint-proto] version = "=0.23.5" +default-features = false [dependencies.tendermint-light-client-verifier] version = "=0.23.5" @@ -55,6 +59,7 @@ default-features = false [dependencies.tendermint-testgen] version = "=0.23.5" optional = true +default-features = false [dev-dependencies] env_logger = "0.9.0" diff --git a/modules/src/clients/ics07_tendermint/client_def.rs b/modules/src/clients/ics07_tendermint/client_def.rs index b0bff063cb..d78a575259 100644 --- a/modules/src/clients/ics07_tendermint/client_def.rs +++ b/modules/src/clients/ics07_tendermint/client_def.rs @@ -2,10 +2,10 @@ use core::convert::TryInto; use ibc_proto::ibc::core::commitment::v1::MerkleProof as RawMerkleProof; use prost::Message; +use tendermint::Time; use tendermint_light_client_verifier::types::{TrustedBlockState, UntrustedBlockState}; use tendermint_light_client_verifier::{ProdVerifier, Verdict, Verifier}; use tendermint_proto::Protobuf; -use time::OffsetDateTime; use crate::clients::ics07_tendermint::client_state::ClientState; use crate::clients::ics07_tendermint::consensus_state::ConsensusState; @@ -50,6 +50,7 @@ impl ClientDef for TendermintClient { fn check_header_and_update_state( &self, + now: Time, ctx: &dyn ClientReader, client_id: ClientId, client_state: Self::ClientState, @@ -112,12 +113,9 @@ impl ClientDef for TendermintClient { let options = client_state.as_light_client_options()?; - let verdict = self.verifier.verify( - untrusted_state, - trusted_state, - &options, - OffsetDateTime::now_utc().try_into().unwrap(), - ); + let verdict = self + .verifier + .verify(untrusted_state, trusted_state, &options, now); match verdict { Verdict::Success => {} diff --git a/modules/src/core/ics02_client/client_def.rs b/modules/src/core/ics02_client/client_def.rs index d1a4311408..fdfb01781a 100644 --- a/modules/src/core/ics02_client/client_def.rs +++ b/modules/src/core/ics02_client/client_def.rs @@ -1,4 +1,5 @@ use ibc_proto::ibc::core::commitment::v1::MerkleProof; +use tendermint::Time; use crate::clients::ics07_tendermint::client_def::TendermintClient; use crate::core::ics02_client::client_consensus::{AnyConsensusState, ConsensusState}; @@ -29,6 +30,7 @@ pub trait ClientDef: Clone { fn check_header_and_update_state( &self, + now: Time, ctx: &dyn ClientReader, client_id: ClientId, client_state: Self::ClientState, @@ -195,6 +197,7 @@ impl ClientDef for AnyClient { /// Validates an incoming `header` against the latest consensus state of this client. fn check_header_and_update_state( &self, + now: Time, ctx: &dyn ClientReader, client_id: ClientId, client_state: AnyClientState, @@ -208,8 +211,13 @@ impl ClientDef for AnyClient { ) .ok_or_else(|| Error::client_args_type_mismatch(ClientType::Tendermint))?; - let (new_state, new_consensus) = - client.check_header_and_update_state(ctx, client_id, client_state, header)?; + let (new_state, new_consensus) = client.check_header_and_update_state( + now, + ctx, + client_id, + client_state, + header, + )?; Ok(( AnyClientState::Tendermint(new_state), @@ -225,8 +233,13 @@ impl ClientDef for AnyClient { ) .ok_or_else(|| Error::client_args_type_mismatch(ClientType::Mock))?; - let (new_state, new_consensus) = - client.check_header_and_update_state(ctx, client_id, client_state, header)?; + let (new_state, new_consensus) = client.check_header_and_update_state( + now, + ctx, + client_id, + client_state, + header, + )?; Ok(( AnyClientState::Mock(new_state), diff --git a/modules/src/core/ics02_client/handler.rs b/modules/src/core/ics02_client/handler.rs index 9b2980e917..fdc487bbb7 100644 --- a/modules/src/core/ics02_client/handler.rs +++ b/modules/src/core/ics02_client/handler.rs @@ -1,5 +1,7 @@ //! This module implements the processing logic for ICS2 (client abstractions and functions) msgs. +use tendermint::Time; + use crate::core::ics02_client::context::ClientReader; use crate::core::ics02_client::error::Error; use crate::core::ics02_client::msgs::ClientMsg; @@ -17,13 +19,17 @@ pub enum ClientResult { } /// General entry point for processing any message related to ICS2 (client functions) protocols. -pub fn dispatch(ctx: &Ctx, msg: ClientMsg) -> Result, Error> +pub fn dispatch( + now: Time, + ctx: &Ctx, + msg: ClientMsg, +) -> Result, Error> where Ctx: ClientReader, { match msg { - ClientMsg::CreateClient(msg) => create_client::process(ctx, msg), - ClientMsg::UpdateClient(msg) => update_client::process(ctx, msg), + ClientMsg::CreateClient(msg) => create_client::process(now, ctx, msg), + ClientMsg::UpdateClient(msg) => update_client::process(now, ctx, msg), ClientMsg::UpgradeClient(msg) => upgrade_client::process(ctx, msg), _ => { unimplemented!() diff --git a/modules/src/core/ics02_client/handler/create_client.rs b/modules/src/core/ics02_client/handler/create_client.rs index 811bbf9e3c..c8bb0c9f70 100644 --- a/modules/src/core/ics02_client/handler/create_client.rs +++ b/modules/src/core/ics02_client/handler/create_client.rs @@ -1,4 +1,7 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgCreateAnyClient`. + +use tendermint::Time; + use crate::prelude::*; use crate::core::ics02_client::client_consensus::AnyConsensusState; @@ -27,6 +30,7 @@ pub struct Result { } pub fn process( + now: Time, ctx: &dyn ClientReader, msg: MsgCreateAnyClient, ) -> HandlerResult { @@ -48,7 +52,7 @@ pub fn process( client_type: msg.client_state().client_type(), client_state: msg.client_state(), consensus_state: msg.consensus_state(), - processed_time: Timestamp::now(), + processed_time: now.into(), processed_height: ctx.host_height(), }); @@ -66,6 +70,7 @@ mod tests { use crate::prelude::*; use core::time::Duration; + use tendermint::Time; use test_log::test; use crate::clients::ics07_tendermint::client_state::{ @@ -102,7 +107,7 @@ mod tests { ) .unwrap(); - let output = dispatch(&ctx, ClientMsg::CreateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::CreateClient(msg.clone())); match output { Ok(HandlerOutput { @@ -193,7 +198,7 @@ mod tests { let expected_client_id = ClientId::new(ClientType::Mock, 0).unwrap(); for msg in create_client_msgs { - let output = dispatch(&ctx, ClientMsg::CreateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::CreateClient(msg.clone())); match output { Ok(HandlerOutput { @@ -255,7 +260,7 @@ mod tests { ) .unwrap(); - let output = dispatch(&ctx, ClientMsg::CreateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::CreateClient(msg.clone())); match output { Ok(HandlerOutput { diff --git a/modules/src/core/ics02_client/handler/update_client.rs b/modules/src/core/ics02_client/handler/update_client.rs index a15e917d69..e357357d92 100644 --- a/modules/src/core/ics02_client/handler/update_client.rs +++ b/modules/src/core/ics02_client/handler/update_client.rs @@ -1,5 +1,7 @@ //! Protocol logic specific to processing ICS2 messages of type `MsgUpdateAnyClient`. +use core::convert::From; +use tendermint::Time; use tracing::debug; use crate::core::ics02_client::client_consensus::AnyConsensusState; @@ -27,6 +29,7 @@ pub struct Result { } pub fn process( + now: Time, ctx: &dyn ClientReader, msg: MsgUpdateAnyClient, ) -> HandlerResult { @@ -59,12 +62,12 @@ pub fn process( debug!("latest consensus state: {:?}", latest_consensus_state); - let duration = Timestamp::now() + let duration = Timestamp::from(now) .duration_since(&latest_consensus_state.timestamp()) .ok_or_else(|| { Error::invalid_consensus_state_timestamp( latest_consensus_state.timestamp(), - header.timestamp(), + Timestamp::from(now), ) })?; @@ -79,7 +82,7 @@ pub fn process( // This function will return the new client_state (its latest_height changed) and a // consensus_state obtained from header. These will be later persisted by the keeper. let (new_client_state, new_consensus_state) = client_def - .check_header_and_update_state(ctx, client_id.clone(), client_state, header) + .check_header_and_update_state(now, ctx, client_id.clone(), client_state, header) .map_err(|e| Error::header_verification_failure(e.to_string()))?; let result = ClientResult::Update(Result { @@ -100,6 +103,7 @@ pub fn process( #[cfg(test)] mod tests { use core::str::FromStr; + use tendermint::Time; use test_log::test; use crate::core::ics02_client::client_consensus::AnyConsensusState; @@ -139,7 +143,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpdateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpdateClient(msg.clone())); match output { Ok(HandlerOutput { @@ -186,7 +190,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpdateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpdateClient(msg.clone())); match output { Err(Error(ErrorDetail::ClientNotFound(e), _)) => { @@ -222,7 +226,7 @@ mod tests { signer: signer.clone(), }; - let output = dispatch(&ctx, ClientMsg::UpdateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpdateClient(msg.clone())); match output { Ok(HandlerOutput { @@ -289,7 +293,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpdateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpdateClient(msg.clone())); match output { Ok(HandlerOutput { @@ -366,7 +370,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpdateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpdateClient(msg.clone())); match output { Ok(HandlerOutput { @@ -446,7 +450,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpdateClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpdateClient(msg.clone())); match output { Ok(HandlerOutput { @@ -520,7 +524,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpdateClient(msg)); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpdateClient(msg)); match output { Ok(_) => { diff --git a/modules/src/core/ics02_client/handler/upgrade_client.rs b/modules/src/core/ics02_client/handler/upgrade_client.rs index c1689e61b4..f2dbaed6ce 100644 --- a/modules/src/core/ics02_client/handler/upgrade_client.rs +++ b/modules/src/core/ics02_client/handler/upgrade_client.rs @@ -78,6 +78,7 @@ mod tests { use crate::prelude::*; use core::str::FromStr; + use tendermint::Time; use crate::core::ics02_client::error::{Error, ErrorDetail}; use crate::core::ics02_client::handler::dispatch; @@ -117,7 +118,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpgradeClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpgradeClient(msg.clone())); match output { Ok(HandlerOutput { @@ -168,7 +169,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpgradeClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpgradeClient(msg.clone())); match output { Err(Error(ErrorDetail::ClientNotFound(e), _)) => { @@ -202,7 +203,7 @@ mod tests { signer, }; - let output = dispatch(&ctx, ClientMsg::UpgradeClient(msg.clone())); + let output = dispatch(Time::now(), &ctx, ClientMsg::UpgradeClient(msg.clone())); match output { Err(Error(ErrorDetail::LowUpgradeHeight(e), _)) => { diff --git a/modules/src/core/ics26_routing/handler.rs b/modules/src/core/ics26_routing/handler.rs index 78c66b251e..2d1fc504db 100644 --- a/modules/src/core/ics26_routing/handler.rs +++ b/modules/src/core/ics26_routing/handler.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use prost_types::Any; +use tendermint::Time; use crate::applications::ics20_fungible_token_transfer::relay_application_logic::send_transfer::send_transfer as ics20_msg_dispatcher; use crate::core::ics02_client::handler::dispatch as ics2_msg_dispatcher; @@ -17,7 +18,7 @@ use crate::{events::IbcEvent, handler::HandlerOutput}; /// Mimics the DeliverTx ABCI interface, but a slightly lower level. No need for authentication /// info or signature checks here. /// Returns a vector of all events that got generated as a byproduct of processing `messages`. -pub fn deliver(ctx: &mut Ctx, messages: Vec) -> Result, Error> +pub fn deliver(now: Time, ctx: &mut Ctx, messages: Vec) -> Result, Error> where Ctx: Ics26Context, { @@ -32,7 +33,7 @@ where let envelope = decode(any_msg)?; // Process the envelope, and accumulate any events that were generated. - let mut output = dispatch(&mut ctx_interim, envelope)?; + let mut output = dispatch(now, &mut ctx_interim, envelope)?; // TODO: output.log and output.result are discarded res.append(&mut output.events); } @@ -50,13 +51,17 @@ pub fn decode(message: Any) -> Result { /// Top-level ICS dispatch function. Routes incoming IBC messages to their corresponding module. /// Returns a handler output with empty result of type `HandlerOutput<()>` which contains the log /// and events produced after processing the input `msg`. -pub fn dispatch(ctx: &mut Ctx, msg: Ics26Envelope) -> Result, Error> +pub fn dispatch( + now: Time, + ctx: &mut Ctx, + msg: Ics26Envelope, +) -> Result, Error> where Ctx: Ics26Context, { let output = match msg { Ics2Msg(msg) => { - let handler_output = ics2_msg_dispatcher(ctx, msg).map_err(Error::ics02_client)?; + let handler_output = ics2_msg_dispatcher(now, ctx, msg).map_err(Error::ics02_client)?; // Apply the result to the context (host chain store). ctx.store_client_result(handler_output.result) .map_err(Error::ics02_client)?; @@ -129,6 +134,7 @@ where mod tests { use crate::prelude::*; + use tendermint::Time; use test_log::test; use crate::core::ics02_client::client_consensus::AnyConsensusState; @@ -268,6 +274,7 @@ mod tests { // First, create a client.. let res = dispatch( + Time::now(), &mut ctx, Ics26Envelope::Ics2Msg(ClientMsg::CreateClient(create_client_msg.clone())), ); @@ -468,7 +475,7 @@ mod tests { .collect(); for test in tests { - let res = dispatch(&mut ctx, test.msg.clone()); + let res = dispatch(Time::now(), &mut ctx, test.msg.clone()); assert_eq!( test.want_pass, diff --git a/modules/src/mock/client_def.rs b/modules/src/mock/client_def.rs index 5da5e284c0..06978ae754 100644 --- a/modules/src/mock/client_def.rs +++ b/modules/src/mock/client_def.rs @@ -1,4 +1,5 @@ use ibc_proto::ibc::core::commitment::v1::MerkleProof; +use tendermint::Time; use crate::core::ics02_client::client_consensus::AnyConsensusState; use crate::core::ics02_client::client_def::ClientDef; @@ -31,6 +32,7 @@ impl ClientDef for MockClient { fn check_header_and_update_state( &self, + _now: Time, _ctx: &dyn ClientReader, _client_id: ClientId, client_state: Self::ClientState, diff --git a/modules/src/mock/context.rs b/modules/src/mock/context.rs index 3123b40f44..f7f0cbad28 100644 --- a/modules/src/mock/context.rs +++ b/modules/src/mock/context.rs @@ -9,6 +9,7 @@ use tracing::debug; use prost_types::Any; use sha2::Digest; +use tendermint::Time; use crate::applications::ics20_fungible_token_transfer::context::Ics20Context; use crate::clients::ics07_tendermint::client_state::test_util::get_dummy_tendermint_client_state; @@ -464,7 +465,7 @@ impl MockContext { /// Alternative method to `Ics18Context::send` that does not exercise any serialization. /// Used in testing the Ics18 algorithms, hence this may return a Ics18Error. pub fn deliver(&mut self, msg: Ics26Envelope) -> Result<(), Ics18Error> { - dispatch(self, msg).map_err(Ics18Error::transaction_failed)?; + dispatch(Time::now(), self, msg).map_err(Ics18Error::transaction_failed)?; // Create a new block. self.advance_host_chain_height(); Ok(()) @@ -1084,7 +1085,7 @@ impl Ics18Context for MockContext { fn send(&mut self, msgs: Vec) -> Result, Ics18Error> { // Forward call to Ics26 delivery method. - let events = deliver(self, msgs).map_err(Ics18Error::transaction_failed)?; + let events = deliver(Time::now(), self, msgs).map_err(Ics18Error::transaction_failed)?; self.advance_host_chain_height(); // Advance chain height Ok(events) diff --git a/modules/src/timestamp.rs b/modules/src/timestamp.rs index 142fc489d0..eee1272d84 100644 --- a/modules/src/timestamp.rs +++ b/modules/src/timestamp.rs @@ -74,9 +74,9 @@ impl Timestamp { } /// Returns a `Timestamp` representation of the current time. + #[cfg(feature = "clock")] pub fn now() -> Timestamp { - let ts = OffsetDateTime::now_utc().try_into().unwrap(); - Timestamp { time: Some(ts) } + Time::now().into() } /// Returns a `Timestamp` representation of a timestamp not being set. diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index 95de574e69..7263a44c06 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -21,7 +21,7 @@ profiling = [] telemetry = ["ibc-telemetry"] [dependencies] -ibc = { version = "0.10.0", path = "../modules" } +ibc = { version = "0.10.0", path = "../modules", features = ["clock"] } ibc-proto = { version = "0.14.0", path = "../proto" } ibc-telemetry = { version = "0.10.0", path = "../telemetry", optional = true }