From d6250014e8d8fcddde651f9e3ceb6be5c93cbeae Mon Sep 17 00:00:00 2001 From: cezarad <9439384+cezarad@users.noreply.github.com> Date: Fri, 5 Mar 2021 14:39:42 +0100 Subject: [PATCH 1/4] Ics04 packets setup and send packet handler (#706) * minor app setup * on going * partial send packet * update * added timestamp to the Mock * Update send_packet.rs * test send_packet * send packet. * ics26 * Update Cargo.toml * update mock time * sha * send packet * minor updates * update clippy * clippy * fmt * Update send_packet.rs * Update send_packet.rs * Update send_packet.rs * Update send_packet.rs * update to u64 * Update client_def.rs * update * time u64 (#703) * update * local * test * modif for ocmpile * Update handler.rs * update test * write to context * Update packet_handler.rs * remove unused methods * Update send_packet.rs * update reviews * Update send_packet.rs * update timestamp overflow error * error * update * PP w/ Cezara * Update error.rs * Update mod.rs * moved send_paccket to handlers * update * Replaced rust-crypto with sha2 * Refactored tests in send_packet module * Removed handlers from ICS20 * Introduced Ics20Context trait. Removed proto-compiler/Cargo.lock * changelog * remove dispatch in relay_application_logic * update transfer * updates review Anca * Update send_packet.rs * Optimized imports * Update send_packet.rs * Update error.rs Co-authored-by: Adi Seredinschi --- CHANGELOG.md | 3 +- Cargo.lock | 1 + modules/Cargo.toml | 4 +- .../ics20_fungible_token_transfer/context.rs | 5 + .../ics20_fungible_token_transfer/error.rs | 23 +- .../ics20_fungible_token_transfer/mod.rs | 2 + .../msgs/transfer.rs | 3 +- .../relay_application_logic.rs | 3 + .../relay_application_logic/send_transfer.rs | 55 ++++ modules/src/ics02_client/client_def.rs | 15 ++ modules/src/ics02_client/error.rs | 3 + .../src/ics02_client/handler/create_client.rs | 17 +- .../src/ics02_client/handler/update_client.rs | 11 +- modules/src/ics03_connection/context.rs | 8 +- modules/src/ics03_connection/error.rs | 2 +- modules/src/ics04_channel/context.rs | 67 ++++- modules/src/ics04_channel/error.rs | 38 ++- modules/src/ics04_channel/handler.rs | 5 +- .../handler/chan_close_confirm.rs | 4 +- .../src/ics04_channel/handler/send_packet.rs | 237 ++++++++++++++++++ modules/src/ics04_channel/msgs.rs | 12 + .../src/ics04_channel/msgs/acknowledgement.rs | 2 +- modules/src/ics04_channel/msgs/recv_packet.rs | 2 +- modules/src/ics04_channel/msgs/timeout.rs | 2 +- modules/src/ics04_channel/packet.rs | 63 +++-- modules/src/ics26_routing/context.rs | 3 + modules/src/ics26_routing/handler.rs | 111 +++++++- modules/src/ics26_routing/msgs.rs | 5 +- modules/src/mock/client_def.rs | 1 - modules/src/mock/client_state.rs | 16 +- modules/src/mock/context.rs | 157 ++++++++---- modules/src/mock/header.rs | 27 +- modules/src/mock/host.rs | 5 +- modules/tests/executor/mod.rs | 4 +- proto/definitions/mock/ibc.mock.proto | 1 + proto/src/prost/ibc.mock.rs | 8 +- 36 files changed, 772 insertions(+), 153 deletions(-) create mode 100644 modules/src/application/ics20_fungible_token_transfer/context.rs create mode 100644 modules/src/application/ics20_fungible_token_transfer/relay_application_logic.rs create mode 100644 modules/src/application/ics20_fungible_token_transfer/relay_application_logic/send_transfer.rs create mode 100644 modules/src/ics04_channel/handler/send_packet.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e1aec6587..bf03418ea1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ ### FEATURES - [ibc] - - [nothing yet] + - Added handler(s) for sending packets ([#695]) - [ibc-relayer] - Support for relayer restart ([#561]) @@ -62,6 +62,7 @@ [#672]: https://github.com/informalsystems/ibc-rs/issues/672 [#685]: https://github.com/informalsystems/ibc-rs/issues/685 [#689]: https://github.com/informalsystems/ibc-rs/issues/689 +[#695]: https://github.com/informalsystems/ibc-rs/issues/695 [#699]: https://github.com/informalsystems/ibc-rs/issues/699 [#700]: https://github.com/informalsystems/ibc-rs/pull/700 diff --git a/Cargo.lock b/Cargo.lock index f185d4eef6..40d5bc4721 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,6 +999,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "sha2", "subtle-encoding", "tendermint", "tendermint-proto", diff --git a/modules/Cargo.toml b/modules/Cargo.toml index 864dcb2837..716119ab01 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -18,7 +18,7 @@ description = """ [features] # 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", "sha2" ] [dependencies] # Proto definitions for all IBC-related interfaces, e.g., connections or channels. @@ -37,6 +37,7 @@ dyn-clonable = "0.9.0" regex = "1" bech32 = "0.8.0" subtle-encoding = "0.5" +sha2 = { version = "0.9.3", optional = true } [dependencies.tendermint] version = "=0.18.1" @@ -52,6 +53,7 @@ optional = true tokio = { version = "1.0", features = ["macros"] } tendermint-rpc = { version = "=0.18.1", features = ["http-client", "websocket-client"] } tendermint-testgen = { version = "=0.18.1" } # Needed for generating (synthetic) light blocks. +sha2 = { version = "0.9.3" } [[test]] name = "mbt" diff --git a/modules/src/application/ics20_fungible_token_transfer/context.rs b/modules/src/application/ics20_fungible_token_transfer/context.rs new file mode 100644 index 0000000000..a39773d0bc --- /dev/null +++ b/modules/src/application/ics20_fungible_token_transfer/context.rs @@ -0,0 +1,5 @@ +use crate::ics04_channel::context::{ChannelKeeper, ChannelReader}; + +/// Captures all the dependencies which the ICS20 module requires to be able to dispatch and +/// process IBC messages. +pub trait Ics20Context: ChannelReader + ChannelKeeper + Clone {} diff --git a/modules/src/application/ics20_fungible_token_transfer/error.rs b/modules/src/application/ics20_fungible_token_transfer/error.rs index e46dba492a..bcf4d4a262 100644 --- a/modules/src/application/ics20_fungible_token_transfer/error.rs +++ b/modules/src/application/ics20_fungible_token_transfer/error.rs @@ -1,10 +1,29 @@ use anomaly::{BoxError, Context}; use thiserror::Error; +use crate::ics24_host::identifier::{ChannelId, PortId}; + pub type Error = anomaly::Error; -#[derive(Clone, Debug, Error)] -pub enum Kind {} +#[derive(Clone, Debug, Error, PartialEq, Eq)] +pub enum Kind { + #[error("unrecognized ICS-20 transfer message type URL {0}")] + UnknownMessageTypeUrl(String), + + #[error("error raised by message handler")] + HandlerRaisedError, + + #[error("Sending sequence number not found for port {0} and channel {1}")] + SequenceSendNotFound(PortId, ChannelId), + + #[error("Missing channel for port_id {0} and channel_id {1} ")] + ChannelNotFound(PortId, ChannelId), + + #[error( + "Destination channel not found in the counterparty of port_id {0} and channel_id {1} " + )] + DestinationChannelNotFound(PortId, ChannelId), +} impl Kind { pub fn context(self, source: impl Into) -> Context { diff --git a/modules/src/application/ics20_fungible_token_transfer/mod.rs b/modules/src/application/ics20_fungible_token_transfer/mod.rs index f1a12ada40..9987bb891b 100644 --- a/modules/src/application/ics20_fungible_token_transfer/mod.rs +++ b/modules/src/application/ics20_fungible_token_transfer/mod.rs @@ -1,3 +1,5 @@ //! ICS 20: IBC Transfer implementation +pub mod context; pub mod error; pub mod msgs; +pub mod relay_application_logic; diff --git a/modules/src/application/ics20_fungible_token_transfer/msgs/transfer.rs b/modules/src/application/ics20_fungible_token_transfer/msgs/transfer.rs index 7ae0f6825f..a20dc48a90 100644 --- a/modules/src/application/ics20_fungible_token_transfer/msgs/transfer.rs +++ b/modules/src/application/ics20_fungible_token_transfer/msgs/transfer.rs @@ -1,5 +1,6 @@ -use std::convert::{TryFrom, TryInto}; +//! This is the definition of a transfer messages that an application submits to a chain. +use std::convert::{TryFrom, TryInto}; use tendermint::account::Id as AccountId; use tendermint_proto::Protobuf; diff --git a/modules/src/application/ics20_fungible_token_transfer/relay_application_logic.rs b/modules/src/application/ics20_fungible_token_transfer/relay_application_logic.rs new file mode 100644 index 0000000000..eb612947c2 --- /dev/null +++ b/modules/src/application/ics20_fungible_token_transfer/relay_application_logic.rs @@ -0,0 +1,3 @@ +//! This module implements the processing logic for ICS20 (token transfer) message. + +pub mod send_transfer; diff --git a/modules/src/application/ics20_fungible_token_transfer/relay_application_logic/send_transfer.rs b/modules/src/application/ics20_fungible_token_transfer/relay_application_logic/send_transfer.rs new file mode 100644 index 0000000000..13b1fc4ca2 --- /dev/null +++ b/modules/src/application/ics20_fungible_token_transfer/relay_application_logic/send_transfer.rs @@ -0,0 +1,55 @@ +use crate::application::ics20_fungible_token_transfer::context::Ics20Context; +use crate::application::ics20_fungible_token_transfer::error::{Error, Kind}; +use crate::application::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; +use crate::handler::HandlerOutput; +use crate::ics04_channel::handler::send_packet::send_packet; +use crate::ics04_channel::packet::Packet; +use crate::ics04_channel::packet::PacketResult; + +pub(crate) fn send_transfer( + ctx: &Ctx, + msg: MsgTransfer, +) -> Result, Error> +where + Ctx: Ics20Context, +{ + let source_channel_end = ctx + .channel_end(&(msg.source_port.clone(), msg.source_channel.clone())) + .ok_or_else(|| { + Kind::ChannelNotFound(msg.source_port.clone(), msg.source_channel.clone()) + })?; + + let destination_port = source_channel_end.counterparty().port_id().clone(); + let destination_channel = source_channel_end + .counterparty() + .channel_id() + .ok_or_else(|| { + Kind::DestinationChannelNotFound(msg.source_port.clone(), msg.source_channel.clone()) + })?; + + // get the next sequence + let sequence = ctx + .get_next_sequence_send(&(msg.source_port.clone(), msg.source_channel.clone())) + .ok_or_else(|| { + Kind::SequenceSendNotFound(msg.source_port.clone(), msg.source_channel.clone()) + })?; + + //TODO: Application LOGIC. + + let packet = Packet { + sequence, + source_port: msg.source_port, + source_channel: msg.source_channel, + destination_port, + destination_channel: destination_channel.clone(), + data: vec![], + timeout_height: msg.timeout_height, + timeout_timestamp: msg.timeout_timestamp, + }; + + let handler_output = + send_packet(ctx, packet).map_err(|e| Kind::HandlerRaisedError.context(e))?; + + //TODO: add event/atributes and writes to the store issued by the application logic for packet sending. + Ok(handler_output) +} diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index 64f8f47fe8..86c62d2751 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -1,5 +1,6 @@ use std::convert::TryFrom; +use chrono::{DateTime, Utc}; use prost_types::Any; use serde::Serialize; use tendermint_proto::Protobuf; @@ -287,6 +288,20 @@ pub enum AnyConsensusState { } impl AnyConsensusState { + pub fn timestamp(&self) -> Result { + match self { + Self::Tendermint(cs_state) => { + let date: DateTime = cs_state.timestamp.into(); + let value = date.timestamp(); + u64::try_from(value) + .map_err(|_| Kind::NegativeConsensusStateTimestamp(value.to_string())) + } + + #[cfg(any(test, feature = "mocks"))] + Self::Mock(mock_state) => Ok(mock_state.timestamp()), + } + } + pub fn client_type(&self) -> ClientType { match self { AnyConsensusState::Tendermint(_cs) => ClientType::Tendermint, diff --git a/modules/src/ics02_client/error.rs b/modules/src/ics02_client/error.rs index 2a735a9382..850520fe11 100644 --- a/modules/src/ics02_client/error.rs +++ b/modules/src/ics02_client/error.rs @@ -27,6 +27,9 @@ pub enum Kind { #[error("implementation specific")] ImplementationSpecific, + #[error("Negative timestamp in consensus state {0}; timestamp must be a positive value")] + NegativeConsensusStateTimestamp(String), + #[error("header verification failed")] HeaderVerificationFailure, diff --git a/modules/src/ics02_client/handler/create_client.rs b/modules/src/ics02_client/handler/create_client.rs index 77960d3330..b19446ee97 100644 --- a/modules/src/ics02_client/handler/create_client.rs +++ b/modules/src/ics02_client/handler/create_client.rs @@ -58,7 +58,6 @@ pub fn process( mod tests { use std::convert::TryInto; use std::time::Duration; - use tendermint::trust_threshold::TrustThresholdFraction as TrustThreshold; use crate::events::IbcEvent; @@ -84,8 +83,8 @@ mod tests { let height = Height::new(0, 42); let msg = MsgCreateAnyClient::new( - MockClientState(MockHeader(height)).into(), - MockConsensusState(MockHeader(height)).into(), + MockClientState(MockHeader::new(height)).into(), + MockConsensusState(MockHeader::new(height)).into(), signer, ) .unwrap(); @@ -130,12 +129,12 @@ mod tests { let create_client_msgs: Vec = vec![ MsgCreateAnyClient::new( - MockClientState(MockHeader(Height { + MockClientState(MockHeader::new(Height { revision_height: 42, ..height })) .into(), - MockConsensusState(MockHeader(Height { + MockConsensusState(MockHeader::new(Height { revision_height: 42, ..height })) @@ -144,12 +143,12 @@ mod tests { ) .unwrap(), MsgCreateAnyClient::new( - MockClientState(MockHeader(Height { + MockClientState(MockHeader::new(Height { revision_height: 42, ..height })) .into(), - MockConsensusState(MockHeader(Height { + MockConsensusState(MockHeader::new(Height { revision_height: 42, ..height })) @@ -158,12 +157,12 @@ mod tests { ) .unwrap(), MsgCreateAnyClient::new( - MockClientState(MockHeader(Height { + MockClientState(MockHeader::new(Height { revision_height: 50, ..height })) .into(), - MockConsensusState(MockHeader(Height { + MockConsensusState(MockHeader::new(Height { revision_height: 50, ..height })) diff --git a/modules/src/ics02_client/handler/update_client.rs b/modules/src/ics02_client/handler/update_client.rs index 625d400c59..7c3cc4c1aa 100644 --- a/modules/src/ics02_client/handler/update_client.rs +++ b/modules/src/ics02_client/handler/update_client.rs @@ -95,10 +95,9 @@ mod tests { let signer = get_dummy_account_id(); let ctx = MockContext::default().with_client(&client_id, Height::new(0, 42)); - let msg = MsgUpdateAnyClient { client_id: client_id.clone(), - header: MockHeader(Height::new(0, 46)).into(), + header: MockHeader::new(Height::new(0, 46)).into(), signer, }; @@ -122,7 +121,9 @@ mod tests { assert_eq!(upd_res.client_id, client_id); assert_eq!( upd_res.client_state, - AnyClientState::Mock(MockClientState(MockHeader(msg.header.height()))) + AnyClientState::Mock(MockClientState(MockHeader::new( + msg.header.height() + ))) ) } Create(_) => panic!("update handler result has type CreateResult"), @@ -143,7 +144,7 @@ mod tests { let msg = MsgUpdateAnyClient { client_id: ClientId::from_str("nonexistingclient").unwrap(), - header: MockHeader(Height::new(0, 46)).into(), + header: MockHeader::new(Height::new(0, 46)).into(), signer, }; @@ -179,7 +180,7 @@ mod tests { for cid in &client_ids { let msg = MsgUpdateAnyClient { client_id: cid.clone(), - header: MockHeader(update_height).into(), + header: MockHeader::new(update_height).into(), signer, }; diff --git a/modules/src/ics03_connection/context.rs b/modules/src/ics03_connection/context.rs index ba35c6ab5b..9abeb31aef 100644 --- a/modules/src/ics03_connection/context.rs +++ b/modules/src/ics03_connection/context.rs @@ -64,7 +64,7 @@ pub trait ConnectionReader { /// for processing any `ConnectionMsg`. pub trait ConnectionKeeper { fn store_connection_result(&mut self, result: ConnectionResult) -> Result<(), Error> { - self.store_connection(&result.connection_id, &result.connection_end)?; + self.store_connection(result.connection_id.clone(), &result.connection_end)?; // If we generated an identifier, increase the counter & associate this new identifier // with the client id. @@ -73,7 +73,7 @@ pub trait ConnectionKeeper { // Also associate the connection end to its client identifier. self.store_connection_to_client( - &result.connection_id, + result.connection_id.clone(), &result.connection_end.client_id(), )?; } @@ -84,14 +84,14 @@ pub trait ConnectionKeeper { /// Stores the given connection_end at a path associated with the connection_id. fn store_connection( &mut self, - connection_id: &ConnectionId, + connection_id: ConnectionId, connection_end: &ConnectionEnd, ) -> Result<(), Error>; /// Stores the given connection_id at a path associated with the client_id. fn store_connection_to_client( &mut self, - connection_id: &ConnectionId, + connection_id: ConnectionId, client_id: &ClientId, ) -> Result<(), Error>; diff --git a/modules/src/ics03_connection/error.rs b/modules/src/ics03_connection/error.rs index b9c4e7e773..754873a2d5 100644 --- a/modules/src/ics03_connection/error.rs +++ b/modules/src/ics03_connection/error.rs @@ -74,7 +74,7 @@ pub enum Kind { #[error("client proof must be present")] NullClientProof, - #[error("the client running locally is frozen")] + #[error("the client {0} running locally is frozen")] FrozenClient(ClientId), #[error("the connection proof verification failed")] diff --git a/modules/src/ics04_channel/context.rs b/modules/src/ics04_channel/context.rs index 4f6906e668..7d456443a6 100644 --- a/modules/src/ics04_channel/context.rs +++ b/modules/src/ics04_channel/context.rs @@ -1,6 +1,7 @@ //! ICS4 (channel) context. The two traits `ChannelReader ` and `ChannelKeeper` define //! the interface that any host chain must implement to be able to process any `ChannelMsg`. //! + use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics03_connection::connection::ConnectionEnd; use crate::ics04_channel::channel::ChannelEnd; @@ -10,6 +11,8 @@ use crate::ics05_port::capabilities::Capability; use crate::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; use crate::Height; +use super::packet::{PacketResult, Sequence}; + /// A context supplying all the necessary read-only dependencies for processing any `ChannelMsg`. pub trait ChannelReader { /// Returns the ChannelEnd for the given `port_id` and `chan_id`. @@ -32,6 +35,11 @@ pub trait ChannelReader { fn authenticated_capability(&self, port_id: &PortId) -> Result; + fn get_next_sequence_send(&self, port_channel_id: &(PortId, ChannelId)) -> Option; + + /// A hashing function for packet commitments + fn hash(&self, value: String) -> String; + /// Returns a counter on the number of channel ids have been created thus far. /// The value of this counter should increase only via method /// `ChannelKeeper::increase_channel_counter`. @@ -44,7 +52,7 @@ pub trait ChannelKeeper { fn store_channel_result(&mut self, result: ChannelResult) -> Result<(), Error> { // The handler processed this channel & some modifications occurred, store the new end. self.store_channel( - &(result.port_id.clone(), result.channel_id.clone()), + (result.port_id.clone(), result.channel_id.clone()), &result.channel_end, )?; @@ -55,48 +63,81 @@ pub trait ChannelKeeper { // Associate also the channel end to its connection. self.store_connection_channels( - &result.channel_end.connection_hops()[0].clone(), + result.channel_end.connection_hops()[0].clone(), &(result.port_id.clone(), result.channel_id.clone()), )?; // Initialize send, recv, and ack sequence numbers. - self.store_next_sequence_send(&(result.port_id.clone(), result.channel_id.clone()), 1)?; - self.store_next_sequence_recv(&(result.port_id.clone(), result.channel_id.clone()), 1)?; - self.store_next_sequence_ack(&(result.port_id, result.channel_id), 1)?; + self.store_next_sequence_send( + (result.port_id.clone(), result.channel_id.clone()), + 1.into(), + )?; + self.store_next_sequence_recv( + (result.port_id.clone(), result.channel_id.clone()), + 1.into(), + )?; + self.store_next_sequence_ack((result.port_id, result.channel_id), 1.into())?; } Ok(()) } + fn store_packet_result(&mut self, general_result: PacketResult) -> Result<(), Error> { + match general_result { + PacketResult::Send(res) => { + self.store_next_sequence_send( + (res.port_id.clone(), res.channel_id.clone()), + res.seq_number, + )?; + + self.store_packet_commitment( + (res.port_id.clone(), res.channel_id.clone(), res.seq), + res.timeout_timestamp, + res.timeout_height, + res.data, + )?; + } + } + Ok(()) + } + + fn store_packet_commitment( + &mut self, + key: (PortId, ChannelId, Sequence), + timestamp: u64, + heigh: Height, + data: Vec, + ) -> Result<(), Error>; + fn store_connection_channels( &mut self, - conn_id: &ConnectionId, + conn_id: ConnectionId, port_channel_id: &(PortId, ChannelId), ) -> Result<(), Error>; /// Stores the given channel_end at a path associated with the port_id and channel_id. fn store_channel( &mut self, - port_channel_id: &(PortId, ChannelId), + port_channel_id: (PortId, ChannelId), channel_end: &ChannelEnd, ) -> Result<(), Error>; fn store_next_sequence_send( &mut self, - port_channel_id: &(PortId, ChannelId), - seq: u64, + port_channel_id: (PortId, ChannelId), + seq: Sequence, ) -> Result<(), Error>; fn store_next_sequence_recv( &mut self, - port_channel_id: &(PortId, ChannelId), - seq: u64, + port_channel_id: (PortId, ChannelId), + seq: Sequence, ) -> Result<(), Error>; fn store_next_sequence_ack( &mut self, - port_channel_id: &(PortId, ChannelId), - seq: u64, + port_channel_id: (PortId, ChannelId), + seq: Sequence, ) -> Result<(), Error>; /// Called upon channel identifier creation (Init or Try message processing). diff --git a/modules/src/ics04_channel/error.rs b/modules/src/ics04_channel/error.rs index 43d6a655dd..6f1edcf833 100644 --- a/modules/src/ics04_channel/error.rs +++ b/modules/src/ics04_channel/error.rs @@ -3,9 +3,10 @@ use thiserror::Error; pub type Error = anomaly::Error; +use super::packet::Sequence; use crate::ics04_channel::channel::State; use crate::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; -use crate::Height; +use crate::{ics02_client, Height}; #[derive(Clone, Debug, Error, Eq, PartialEq)] pub enum Kind { @@ -21,6 +22,9 @@ pub enum Kind { #[error("invalid connection hops length: expected {0}; actual {1}")] InvalidConnectionHopsLength(usize, usize), + #[error("packet destination port/channel doesn't match the counterparty's port/channel")] + InvalidPacketCounterparty(PortId, ChannelId), + #[error("invalid version")] InvalidVersion, @@ -33,6 +37,15 @@ pub enum Kind { #[error("invalid proof: missing height")] MissingHeight, + #[error("packet sequence cannot be 0")] + ZeroPacketSequence, + + #[error("packet data bytes cannot be empty")] + ZeroPacketData, + + #[error("packet timeout height and packet timeout timestamp cannot both be 0")] + ZeroPacketTimeout, + #[error("invalid timeout height for the packet")] InvalidTimeoutHeight, @@ -42,12 +55,8 @@ pub enum Kind { #[error("there is no packet in this message")] MissingPacket, - #[error("acknowledgement too long")] - AcknowledgementTooLong, - #[error("missing counterparty")] MissingCounterparty, - #[error("no commong version")] NoCommonVersion, @@ -89,6 +98,21 @@ pub enum Kind { #[error("No client state associated with client id {0}")] MissingClientState(ClientId), + #[error("Missing sequence number for send packets")] + MissingNextSendSeq, + + #[error("Invalid packet sequence {0} ≠ next send sequence {1}")] + InvalidPacketSequence(Sequence, Sequence), + + #[error("Receiving chain block height {0} >= packet timeout height {1}")] + LowPacketHeight(Height, Height), + + #[error("Receiving chain block timestamp >= packet timeout timestamp")] + LowPacketTimestamp, + + #[error("Invalid timestamp in consensus state; timestamp must be a positive value")] + ErrorInvalidConsensusState(ics02_client::error::Kind), + #[error("Client with id {0} is frozen")] FrozenClient(ClientId), @@ -104,8 +128,8 @@ pub enum Kind { #[error("Channel {0} should not be state {1}")] InvalidChannelState(ChannelId, State), - #[error("Channel is in state {0}")] - ChannelAlreadyClosed(ChannelId), + #[error("Channel {0} is Closed")] + ChannelClosed(ChannelId), #[error("Handshake proof verification fails at ChannelOpenAck")] ChanOpenAckProofVerification, diff --git a/modules/src/ics04_channel/handler.rs b/modules/src/ics04_channel/handler.rs index 1e5ab4da40..561b704f80 100644 --- a/modules/src/ics04_channel/handler.rs +++ b/modules/src/ics04_channel/handler.rs @@ -14,6 +14,7 @@ pub mod chan_open_ack; pub mod chan_open_confirm; pub mod chan_open_init; pub mod chan_open_try; +pub mod send_packet; mod verify; @@ -37,8 +38,8 @@ pub struct ChannelResult { pub channel_end: ChannelEnd, } -/// General entry point for processing any type of message related to the ICS4 channel open -/// handshake protocol. +/// General entry point for processing any type of message related to the ICS4 channel open and +/// channel close handshake protocols. pub fn dispatch(ctx: &Ctx, msg: ChannelMsg) -> Result, Error> where Ctx: ChannelReader, diff --git a/modules/src/ics04_channel/handler/chan_close_confirm.rs b/modules/src/ics04_channel/handler/chan_close_confirm.rs index bc638c3b2d..1c1c00add3 100644 --- a/modules/src/ics04_channel/handler/chan_close_confirm.rs +++ b/modules/src/ics04_channel/handler/chan_close_confirm.rs @@ -1,4 +1,4 @@ -//! Protocol logic specific to ICS4 messages of type `MsgChannelCloseInit`. +//! Protocol logic specific to ICS4 messages of type `MsgChannelCloseConfirm`. use crate::events::IbcEvent; use crate::handler::{HandlerOutput, HandlerResult}; use crate::ics03_connection::connection::State as ConnectionState; @@ -23,7 +23,7 @@ pub(crate) fn process( // Validate that the channel end is in a state where it can be closed. if channel_end.state_matches(&State::Closed) { - return Err(Kind::ChannelAlreadyClosed(msg.channel_id().clone()).into()); + return Err(Kind::ChannelClosed(msg.channel_id().clone()).into()); } // Channel capabilities diff --git a/modules/src/ics04_channel/handler/send_packet.rs b/modules/src/ics04_channel/handler/send_packet.rs new file mode 100644 index 0000000000..5615e75484 --- /dev/null +++ b/modules/src/ics04_channel/handler/send_packet.rs @@ -0,0 +1,237 @@ +use crate::events::IbcEvent; +use crate::handler::{HandlerOutput, HandlerResult}; +use crate::ics02_client::state::ClientState; +use crate::ics04_channel::channel::Counterparty; +use crate::ics04_channel::channel::State; +use crate::ics04_channel::events::SendPacket; +use crate::ics04_channel::packet::{PacketResult, Sequence}; +use crate::ics04_channel::{context::ChannelReader, error::Error, error::Kind, packet::Packet}; +use crate::ics24_host::identifier::{ChannelId, PortId}; +use crate::Height; + +#[derive(Clone, Debug)] +pub struct SendPacketResult { + pub port_id: PortId, + pub channel_id: ChannelId, + pub seq: Sequence, + pub seq_number: Sequence, + pub timeout_height: Height, + pub timeout_timestamp: u64, + pub data: Vec, +} + +pub fn send_packet(ctx: &dyn ChannelReader, packet: Packet) -> HandlerResult { + let mut output = HandlerOutput::builder(); + + let source_channel_end = ctx + .channel_end(&(packet.source_port.clone(), packet.source_channel.clone())) + .ok_or_else(|| { + Kind::ChannelNotFound(packet.source_port.clone(), packet.source_channel.clone()) + .context(packet.source_channel.clone().to_string()) + })?; + + if source_channel_end.state_matches(&State::Closed) { + return Err(Kind::ChannelClosed(packet.source_channel).into()); + } + + let _channel_cap = ctx.authenticated_capability(&packet.source_port)?; + + let counterparty = Counterparty::new( + packet.destination_port.clone(), + Some(packet.destination_channel.clone()), + ); + + if !source_channel_end.counterparty_matches(&counterparty) { + return Err(Kind::InvalidPacketCounterparty( + packet.destination_port.clone(), + packet.destination_channel, + ) + .into()); + } + + let connection_end = ctx + .connection_end(&source_channel_end.connection_hops()[0]) + .ok_or_else(|| Kind::MissingConnection(source_channel_end.connection_hops()[0].clone()))?; + + let client_id = connection_end.client_id().clone(); + + let client_state = ctx + .client_state(&client_id) + .ok_or_else(|| Kind::MissingClientState(client_id.clone()))?; + + // prevent accidental sends with clients that cannot be updated + if client_state.is_frozen() { + return Err(Kind::FrozenClient(connection_end.client_id().clone()).into()); + } + + // check if packet height is newer than the height of the latest client state on the receiving chain + let latest_height = client_state.latest_height(); + let packet_height = packet.timeout_height; + + if !packet.timeout_height.is_zero() && packet_height <= latest_height { + return Err(Kind::LowPacketHeight(latest_height, packet.timeout_height).into()); + } + + //check if packet timestamp is newer than the timestamp of the latest consensus state of the receiving chain + let consensus_state = ctx + .client_consensus_state(&client_id, latest_height) + .ok_or_else(|| Kind::MissingClientConsensusState(client_id.clone(), latest_height))?; + + let latest_timestamp = consensus_state + .timestamp() + .map_err(Kind::ErrorInvalidConsensusState)?; + + let packet_timestamp = packet.timeout_timestamp; + if !packet.timeout_timestamp == 0 && packet_timestamp <= latest_timestamp { + return Err(Kind::LowPacketTimestamp.into()); + } + + // check sequence number + let next_seq_send = ctx + .get_next_sequence_send(&(packet.source_port.clone(), packet.source_channel.clone())) + .ok_or(Kind::MissingNextSendSeq)?; + + if packet.sequence != next_seq_send { + return Err(Kind::InvalidPacketSequence(packet.sequence, next_seq_send).into()); + } + + output.log("success: packet send "); + + let result = PacketResult::Send(SendPacketResult { + port_id: packet.source_port.clone(), + channel_id: packet.source_channel.clone(), + seq: packet.sequence, + seq_number: next_seq_send.increment(), + data: packet.clone().data, + timeout_height: packet.timeout_height, + timeout_timestamp: packet.timeout_timestamp, + }); + + output.emit(IbcEvent::SendPacket(SendPacket { + height: packet_height, + packet, + })); + + Ok(output.with_result(result)) +} + +#[cfg(test)] +mod tests { + use std::convert::TryInto; + + use crate::events::IbcEvent; + use crate::ics02_client::height::Height; + use crate::ics03_connection::connection::ConnectionEnd; + use crate::ics03_connection::connection::Counterparty as ConnectionCounterparty; + use crate::ics03_connection::connection::State as ConnectionState; + use crate::ics03_connection::version::get_compatible_versions; + use crate::ics04_channel::channel::{ChannelEnd, Counterparty, Order, State}; + use crate::ics04_channel::handler::send_packet::send_packet; + use crate::ics04_channel::packet::test_utils::get_dummy_raw_packet; + use crate::ics04_channel::packet::{Packet, Sequence}; + use crate::ics24_host::identifier::{ChannelId, ClientId, ConnectionId, PortId}; + use crate::mock::context::MockContext; + + #[test] + fn send_packet_processing() { + struct Test { + name: String, + ctx: MockContext, + packet: Packet, + want_pass: bool, + } + + let context = MockContext::default(); + + let mut packet: Packet = get_dummy_raw_packet(1, 6).try_into().unwrap(); + packet.sequence = Sequence::from(1); + packet.data = vec![0]; + + let channel_end = ChannelEnd::new( + State::TryOpen, + Order::default(), + Counterparty::new(PortId::default(), Some(ChannelId::default())), + vec![ConnectionId::default()], + "ics20".to_string(), + ); + + let connection_end = ConnectionEnd::new( + ConnectionState::Open, + ClientId::default(), + ConnectionCounterparty::new( + ClientId::default(), + Some(ConnectionId::default()), + Default::default(), + ), + get_compatible_versions(), + 0, + ); + + let tests: Vec = vec![ + Test { + name: "Processing fails because no channel exists in the context".to_string(), + ctx: context.clone(), + packet: packet.clone(), + want_pass: false, + }, + Test { + name: "Processing fails because the port does not have a capability associated" + .to_string(), + ctx: context.clone().with_channel( + PortId::default(), + ChannelId::default(), + channel_end.clone(), + ), + packet: packet.clone(), + want_pass: false, + }, + Test { + name: "Good parameters".to_string(), + ctx: context + .with_client(&ClientId::default(), Height::default()) + .with_connection(ConnectionId::default(), connection_end) + .with_port_capability(PortId::default()) + .with_channel(PortId::default(), ChannelId::default(), channel_end) + .with_send_sequence(PortId::default(), ChannelId::default(), 1.into()), + packet, + want_pass: true, + }, + ] + .into_iter() + .collect(); + + for test in tests { + let res = send_packet(&test.ctx, test.packet.clone()); + // Additionally check the events and the output objects in the result. + match res { + Ok(proto_output) => { + assert_eq!( + test.want_pass, + true, + "send_packet: test passed but was supposed to fail for test: {}, \nparams {:?} {:?}", + test.name, + test.packet.clone(), + test.ctx.clone() + ); + assert_ne!(proto_output.events.is_empty(), true); // Some events must exist. + + // TODO: The object in the output is a PacketResult what can we check on it? + for e in proto_output.events.iter() { + assert!(matches!(e, &IbcEvent::SendPacket(_))); + } + } + Err(e) => { + assert_eq!( + test.want_pass, + false, + "send_packet: did not pass test: {}, \nparams {:?} {:?} error: {:?}", + test.name, + test.packet.clone(), + test.ctx.clone(), + e, + ); + } + } + } + } +} diff --git a/modules/src/ics04_channel/msgs.rs b/modules/src/ics04_channel/msgs.rs index e871dd8abb..74f30b9edc 100644 --- a/modules/src/ics04_channel/msgs.rs +++ b/modules/src/ics04_channel/msgs.rs @@ -1,6 +1,8 @@ //! Message definitions for all ICS4 domain types: channel open & close handshake datagrams, as well //! as packets. +use acknowledgement::MsgAcknowledgement; + use crate::ics04_channel::msgs::chan_close_confirm::MsgChannelCloseConfirm; use crate::ics04_channel::msgs::chan_close_init::MsgChannelCloseInit; use crate::ics04_channel::msgs::chan_open_ack::MsgChannelOpenAck; @@ -8,6 +10,8 @@ use crate::ics04_channel::msgs::chan_open_confirm::MsgChannelOpenConfirm; use crate::ics04_channel::msgs::chan_open_init::MsgChannelOpenInit; use crate::ics04_channel::msgs::chan_open_try::MsgChannelOpenTry; +use self::{recv_packet::MsgRecvPacket, timeout::MsgTimeout, timeout_on_close::MsgTimeoutOnClose}; + // Opening handshake messages. pub mod chan_open_ack; pub mod chan_open_confirm; @@ -34,3 +38,11 @@ pub enum ChannelMsg { ChannelCloseInit(MsgChannelCloseInit), ChannelCloseConfirm(MsgChannelCloseConfirm), } + +#[derive(Clone, Debug, PartialEq)] +pub enum PacketMsg { + RecvPacket(MsgRecvPacket), + AckPacket(MsgAcknowledgement), + ToPacket(MsgTimeout), + ToClosePacket(MsgTimeoutOnClose), +} diff --git a/modules/src/ics04_channel/msgs/acknowledgement.rs b/modules/src/ics04_channel/msgs/acknowledgement.rs index 3816b6f057..7ff55ca996 100644 --- a/modules/src/ics04_channel/msgs/acknowledgement.rs +++ b/modules/src/ics04_channel/msgs/acknowledgement.rs @@ -114,7 +114,7 @@ mod test_util { /// The `height` parametrizes both the proof height as well as the timeout height. pub fn get_dummy_raw_msg_acknowledgement(height: u64) -> RawMsgAcknowledgement { RawMsgAcknowledgement { - packet: Some(get_dummy_raw_packet(height)), + packet: Some(get_dummy_raw_packet(height, 1)), acknowledgement: get_dummy_proof(), proof_acked: get_dummy_proof(), proof_height: Some(RawHeight { diff --git a/modules/src/ics04_channel/msgs/recv_packet.rs b/modules/src/ics04_channel/msgs/recv_packet.rs index 9260ab5f92..982726466c 100644 --- a/modules/src/ics04_channel/msgs/recv_packet.rs +++ b/modules/src/ics04_channel/msgs/recv_packet.rs @@ -105,7 +105,7 @@ mod test_util { /// proof height as well as the timeout height. pub fn get_dummy_raw_msg_recv_packet(height: u64) -> RawMsgRecvPacket { RawMsgRecvPacket { - packet: Some(get_dummy_raw_packet(height)), + packet: Some(get_dummy_raw_packet(height, 1)), proof_commitment: get_dummy_proof(), proof_height: Some(RawHeight { revision_number: 0, diff --git a/modules/src/ics04_channel/msgs/timeout.rs b/modules/src/ics04_channel/msgs/timeout.rs index b71f79e709..1e7112f5b7 100644 --- a/modules/src/ics04_channel/msgs/timeout.rs +++ b/modules/src/ics04_channel/msgs/timeout.rs @@ -116,7 +116,7 @@ mod test_util { /// The `height` parametrizes both the proof height as well as the timeout height. pub fn get_dummy_raw_msg_timeout(height: u64) -> RawMsgTimeout { RawMsgTimeout { - packet: Some(get_dummy_raw_packet(height)), + packet: Some(get_dummy_raw_packet(height, 0)), proof_unreceived: get_dummy_proof(), proof_height: Some(RawHeight { revision_number: 0, diff --git a/modules/src/ics04_channel/packet.rs b/modules/src/ics04_channel/packet.rs index 88a10236ce..d777608961 100644 --- a/modules/src/ics04_channel/packet.rs +++ b/modules/src/ics04_channel/packet.rs @@ -8,7 +8,9 @@ use crate::ics04_channel::error::Kind; use crate::ics24_host::identifier::{ChannelId, PortId}; use crate::Height; -/// Enumeration of proof carrying ICS3 message, helper for relayer. +use super::handler::send_packet::SendPacketResult; + +/// Enumeration of proof carrying ICS4 message, helper for relayer. #[derive(Clone, Debug, PartialEq, Eq)] pub enum PacketMsgType { Recv, @@ -18,6 +20,11 @@ pub enum PacketMsgType { TimeoutOnClose, } +#[derive(Clone, Debug)] +pub enum PacketResult { + Send(SendPacketResult), +} + impl std::fmt::Display for PacketMsgType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -34,6 +41,15 @@ impl std::fmt::Display for PacketMsgType { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Sequence(u64); +impl Sequence { + pub fn is_zero(&self) -> bool { + self.0 == 0 + } + + pub fn increment(&self) -> Sequence { + Sequence(self.0 + 1) + } +} impl From for Sequence { fn from(seq: u64) -> Self { Sequence(seq) @@ -52,7 +68,7 @@ impl std::fmt::Display for Sequence { } } -#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)] +#[derive(Debug, PartialEq, Deserialize, Serialize, Hash, Clone)] pub struct Packet { pub sequence: Sequence, pub source_port: PortId, @@ -94,6 +110,22 @@ impl TryFrom for Packet { type Error = anomaly::Error; fn try_from(raw_pkt: RawPacket) -> Result { + if Sequence::from(raw_pkt.sequence).is_zero() { + return Err(Kind::ZeroPacketSequence.into()); + } + let packet_timeout_height: Height = raw_pkt + .timeout_height + .ok_or(Kind::MissingHeight)? + .try_into() + .map_err(|e| Kind::InvalidTimeoutHeight.context(e))?; + + if packet_timeout_height.is_zero() && raw_pkt.timeout_timestamp == 0 { + return Err(Kind::ZeroPacketTimeout.into()); + } + if raw_pkt.data.is_empty() { + return Err(Kind::ZeroPacketData.into()); + } + Ok(Packet { sequence: Sequence::from(raw_pkt.sequence), source_port: raw_pkt @@ -113,11 +145,7 @@ impl TryFrom for Packet { .parse() .map_err(|e| Kind::IdentifierError.context(e))?, data: raw_pkt.data, - timeout_height: raw_pkt - .timeout_height - .ok_or(Kind::MissingHeight)? - .try_into() - .map_err(|e| Kind::InvalidTimeoutHeight.context(e))?, + timeout_height: packet_timeout_height, timeout_timestamp: raw_pkt.timeout_timestamp, }) } @@ -140,23 +168,24 @@ impl From for RawPacket { #[cfg(test)] pub mod test_utils { + use crate::ics24_host::identifier::{ChannelId, PortId}; use ibc_proto::ibc::core::channel::v1::Packet as RawPacket; use ibc_proto::ibc::core::client::v1::Height as RawHeight; /// Returns a dummy `RawPacket`, for testing only! - pub fn get_dummy_raw_packet(timeout_height: u64) -> RawPacket { + pub fn get_dummy_raw_packet(timeout_height: u64, timeout_timestamp: u64) -> RawPacket { RawPacket { - sequence: 0, - source_port: "sourceportid".to_string(), - source_channel: "srchannelid".to_string(), - destination_port: "destinationport".to_string(), - destination_channel: "dstchannelid".to_string(), - data: vec![], + sequence: 1, + source_port: PortId::default().to_string(), + source_channel: ChannelId::default().to_string(), + destination_port: PortId::default().to_string(), + destination_channel: ChannelId::default().to_string(), + data: vec![0], timeout_height: Some(RawHeight { revision_number: 0, revision_height: timeout_height, }), - timeout_timestamp: 0, + timeout_timestamp, } } } @@ -179,7 +208,7 @@ mod tests { } let proof_height = 10; - let default_raw_msg = get_dummy_raw_packet(proof_height); + let default_raw_msg = get_dummy_raw_packet(proof_height, 0); let tests: Vec = vec![ Test { @@ -309,7 +338,7 @@ mod tests { #[test] fn to_and_from() { - let raw = get_dummy_raw_packet(15); + let raw = get_dummy_raw_packet(15, 0); let msg = Packet::try_from(raw.clone()).unwrap(); let raw_back = RawPacket::from(msg.clone()); let msg_back = Packet::try_from(raw_back.clone()).unwrap(); diff --git a/modules/src/ics26_routing/context.rs b/modules/src/ics26_routing/context.rs index 7c01773615..e94b11085a 100644 --- a/modules/src/ics26_routing/context.rs +++ b/modules/src/ics26_routing/context.rs @@ -1,7 +1,9 @@ +use crate::application::ics20_fungible_token_transfer::context::Ics20Context; use crate::ics02_client::context::{ClientKeeper, ClientReader}; use crate::ics03_connection::context::{ConnectionKeeper, ConnectionReader}; use crate::ics04_channel::context::{ChannelKeeper, ChannelReader}; use crate::ics05_port::context::PortReader; + /// This trait captures all the functional dependencies (i.e., context) which the ICS26 module /// requires to be able to dispatch and process IBC messages. In other words, this is the /// representation of a chain from the perspective of the IBC module of that chain. @@ -13,6 +15,7 @@ pub trait Ics26Context: + ChannelKeeper + ChannelReader + PortReader + + Ics20Context + Clone { } diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index 9dda75fc32..a39e5f901c 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -1,18 +1,24 @@ use prost_types::Any; use tendermint_proto::Protobuf; +use crate::application::ics20_fungible_token_transfer::msgs::transfer; +use crate::application::ics20_fungible_token_transfer::relay_application_logic::send_transfer::send_transfer as ics20_msg_dispatcher; use crate::events::IbcEvent; use crate::handler::HandlerOutput; use crate::ics02_client::handler::dispatch as ics2_msg_dispatcher; -use crate::ics02_client::msgs::create_client; -use crate::ics02_client::msgs::update_client; -use crate::ics02_client::msgs::ClientMsg; +use crate::ics02_client::msgs::{create_client, update_client, ClientMsg}; use crate::ics03_connection::handler::dispatch as ics3_msg_dispatcher; +use crate::ics03_connection::msgs::{ + conn_open_ack, conn_open_confirm, conn_open_init, conn_open_try, ConnectionMsg, +}; use crate::ics04_channel::handler::dispatch as ics4_msg_dispatcher; +use crate::ics04_channel::msgs::{ + chan_close_confirm, chan_close_init, chan_open_ack, chan_open_confirm, chan_open_init, + chan_open_try, ChannelMsg, +}; use crate::ics26_routing::context::Ics26Context; use crate::ics26_routing::error::{Error, Kind}; -use crate::ics26_routing::msgs::Ics26Envelope; -use crate::ics26_routing::msgs::Ics26Envelope::{Ics2Msg, Ics3Msg, Ics4Msg}; +use crate::ics26_routing::msgs::Ics26Envelope::{self, Ics20Msg, Ics2Msg, Ics3Msg, Ics4Msg}; /// Mimics the DeliverTx ABCI interface, but a slightly lower level. No need for authentication /// info or signature checks here. @@ -43,7 +49,75 @@ where .map_err(|e| Kind::MalformedMessageBytes.context(e))?; Ok(Ics2Msg(ClientMsg::UpdateClient(domain_msg))) } - // TODO: ICS3 messages + + //ICS03 + conn_open_init::TYPE_URL => { + let domain_msg = conn_open_init::MsgConnectionOpenInit::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics3Msg(ConnectionMsg::ConnectionOpenInit(domain_msg))) + } + conn_open_try::TYPE_URL => { + let domain_msg = conn_open_try::MsgConnectionOpenTry::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics3Msg(ConnectionMsg::ConnectionOpenTry(Box::new( + domain_msg, + )))) + } + conn_open_ack::TYPE_URL => { + let domain_msg = conn_open_ack::MsgConnectionOpenAck::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics3Msg(ConnectionMsg::ConnectionOpenAck(Box::new( + domain_msg, + )))) + } + conn_open_confirm::TYPE_URL => { + let domain_msg = + conn_open_confirm::MsgConnectionOpenConfirm::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics3Msg(ConnectionMsg::ConnectionOpenConfirm(domain_msg))) + } + + //ICS04 + chan_open_init::TYPE_URL => { + let domain_msg = chan_open_init::MsgChannelOpenInit::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics4Msg(ChannelMsg::ChannelOpenInit(domain_msg))) + } + chan_open_try::TYPE_URL => { + let domain_msg = chan_open_try::MsgChannelOpenTry::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics4Msg(ChannelMsg::ChannelOpenTry(domain_msg))) + } + chan_open_ack::TYPE_URL => { + let domain_msg = chan_open_ack::MsgChannelOpenAck::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics4Msg(ChannelMsg::ChannelOpenAck(domain_msg))) + } + chan_open_confirm::TYPE_URL => { + let domain_msg = + chan_open_confirm::MsgChannelOpenConfirm::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics4Msg(ChannelMsg::ChannelOpenConfirm(domain_msg))) + } + chan_close_init::TYPE_URL => { + let domain_msg = chan_close_init::MsgChannelCloseInit::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics4Msg(ChannelMsg::ChannelCloseInit(domain_msg))) + } + chan_close_confirm::TYPE_URL => { + let domain_msg = + chan_close_confirm::MsgChannelCloseConfirm::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics4Msg(ChannelMsg::ChannelCloseConfirm(domain_msg))) + } + //ICS20 + transfer::TYPE_URL => { + // Pop out the message and then wrap it in the corresponding type. + let domain_msg = transfer::MsgTransfer::decode_vec(&any_msg.value) + .map_err(|e| Kind::MalformedMessageBytes.context(e))?; + Ok(Ics20Msg(domain_msg)) + } + _ => Err(Kind::UnknownMessageTypeUrl(any_msg.type_url)), }?; @@ -106,7 +180,22 @@ where .with_log(handler_output.log) .with_events(handler_output.events) .with_result(()) - } // TODO: add dispatchers for others. + } + + Ics20Msg(msg) => { + let handler_output = + ics20_msg_dispatcher(ctx, msg).map_err(|e| Kind::HandlerRaisedError.context(e))?; + + // Apply any results to the host chain store. + ctx.store_packet_result(handler_output.result) + .map_err(|e| Kind::KeeperRaisedError.context(e))?; + + HandlerOutput::builder() + .with_log(handler_output.log) + .with_events(handler_output.events) + .with_result(()) + } + _ => todo!(), }; Ok(output) @@ -167,8 +256,8 @@ mod tests { let mut ctx = MockContext::default(); let create_client_msg = MsgCreateAnyClient::new( - AnyClientState::from(MockClientState(MockHeader(start_client_height))), - AnyConsensusState::from(MockConsensusState(MockHeader(start_client_height))), + AnyClientState::from(MockClientState(MockHeader::new(start_client_height))), + AnyConsensusState::from(MockConsensusState(MockHeader::new(start_client_height))), default_signer, ) .unwrap(); @@ -254,7 +343,7 @@ mod tests { name: "Client update successful".to_string(), msg: Ics26Envelope::Ics2Msg(ClientMsg::UpdateClient(MsgUpdateAnyClient { client_id: client_id.clone(), - header: MockHeader(update_client_height).into(), + header: MockHeader::new(update_client_height).into(), signer: default_signer, })), want_pass: true, @@ -263,7 +352,7 @@ mod tests { name: "Client update fails due to stale header".to_string(), msg: Ics26Envelope::Ics2Msg(ClientMsg::UpdateClient(MsgUpdateAnyClient { client_id: client_id.clone(), - header: MockHeader(update_client_height).into(), + header: MockHeader::new(update_client_height).into(), signer: default_signer, })), want_pass: false, diff --git a/modules/src/ics26_routing/msgs.rs b/modules/src/ics26_routing/msgs.rs index 5be41d6b02..65d411d578 100644 --- a/modules/src/ics26_routing/msgs.rs +++ b/modules/src/ics26_routing/msgs.rs @@ -1,6 +1,7 @@ +use crate::application::ics20_fungible_token_transfer::msgs::transfer::MsgTransfer; use crate::ics02_client::msgs::ClientMsg; -use crate::ics03_connection::msgs::ConnectionMsg; use crate::ics04_channel::msgs::ChannelMsg; +use crate::{ics03_connection::msgs::ConnectionMsg, ics04_channel::msgs::PacketMsg}; /// Enumeration of all messages that the local ICS26 module is capable of routing. #[derive(Clone, Debug)] @@ -8,4 +9,6 @@ pub enum Ics26Envelope { Ics2Msg(ClientMsg), Ics3Msg(ConnectionMsg), Ics4Msg(ChannelMsg), + Ics4PacketMsg(PacketMsg), + Ics20Msg(MsgTransfer), } diff --git a/modules/src/mock/client_def.rs b/modules/src/mock/client_def.rs index ec31ff9caa..102e837257 100644 --- a/modules/src/mock/client_def.rs +++ b/modules/src/mock/client_def.rs @@ -27,7 +27,6 @@ impl ClientDef for MockClient { "received header height is lower than (or equal to) client latest height".into(), ); } - Ok((MockClientState(header), MockConsensusState(header))) } diff --git a/modules/src/mock/client_state.rs b/modules/src/mock/client_state.rs index 9c45ebe287..fc401d8115 100644 --- a/modules/src/mock/client_state.rs +++ b/modules/src/mock/client_state.rs @@ -10,7 +10,7 @@ use ibc_proto::ibc::mock::ConsensusState as RawMockConsensusState; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::client_type::ClientType; use crate::ics02_client::error::Error; -use crate::ics02_client::error::Kind; +use crate::ics02_client::error::Kind as ClientKind; use crate::ics02_client::state::{ClientState, ConsensusState}; use crate::ics23_commitment::commitment::CommitmentRoot; use crate::ics24_host::identifier::ChainId; @@ -34,14 +34,14 @@ pub struct MockClientRecord { /// A mock of a client state. For an example of a real structure that this mocks, you can see /// `ClientState` of ics07_tendermint/client_state.rs. // TODO: `MockClientState` should evolve, at the very least needs a `is_frozen` boolean field. -#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Serialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] pub struct MockClientState(pub MockHeader); impl Protobuf for MockClientState {} impl MockClientState { pub fn latest_height(&self) -> Height { - (self.0).0 + (self.0).height } } @@ -64,6 +64,7 @@ impl From for RawMockClientState { RawMockClientState { header: Some(ibc_proto::ibc::mock::Header { height: Some(value.0.height().into()), + timestamp: (value.0).timestamp, }), } } @@ -101,6 +102,12 @@ impl From for MockClientState { #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] pub struct MockConsensusState(pub MockHeader); +impl MockConsensusState { + pub fn timestamp(&self) -> u64 { + (self.0).timestamp + } +} + impl Protobuf for MockConsensusState {} impl TryFrom for MockConsensusState { @@ -109,7 +116,7 @@ impl TryFrom for MockConsensusState { fn try_from(raw: RawMockConsensusState) -> Result { let raw_header = raw .header - .ok_or_else(|| Kind::InvalidRawConsensusState.context("missing header"))?; + .ok_or_else(|| ClientKind::InvalidRawConsensusState.context("missing header"))?; Ok(Self(MockHeader::try_from(raw_header)?)) } @@ -120,6 +127,7 @@ impl From for RawMockConsensusState { RawMockConsensusState { header: Some(ibc_proto::ibc::mock::Header { height: Some(value.0.height().into()), + timestamp: (value.0).timestamp, }), } } diff --git a/modules/src/mock/context.rs b/modules/src/mock/context.rs index b685e31d71..24eb4f05e3 100644 --- a/modules/src/mock/context.rs +++ b/modules/src/mock/context.rs @@ -6,30 +6,36 @@ use std::error::Error; use std::str::FromStr; use prost_types::Any; +use sha2::Digest; use tendermint::account::Id; -use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState, AnyHeader}; -use crate::ics02_client::client_type::ClientType; use crate::ics02_client::context::{ClientKeeper, ClientReader}; -use crate::ics02_client::error::Error as ICS2Error; +use crate::ics02_client::error::Error as Ics2Error; +use crate::{ + ics02_client::client_def::{AnyClientState, AnyConsensusState, AnyHeader}, + ics04_channel::packet::Sequence, +}; +use crate::{ + ics02_client::client_type::ClientType, ics23_commitment::commitment::CommitmentPrefix, +}; use crate::ics05_port::capabilities::Capability; use crate::ics05_port::context::PortReader; use crate::ics03_connection::connection::ConnectionEnd; use crate::ics03_connection::context::{ConnectionKeeper, ConnectionReader}; -use crate::ics03_connection::error::Error as ICS3Error; +use crate::ics03_connection::error::Error as Ics3Error; use crate::events::IbcEvent; use crate::ics04_channel::channel::ChannelEnd; use crate::ics04_channel::context::{ChannelKeeper, ChannelReader}; -use crate::ics04_channel::error::Error as ICS4Error; -use crate::ics04_channel::error::Kind as ICS4Kind; +use crate::ics04_channel::error::Error as Ics4Error; +use crate::ics04_channel::error::Kind as Ics4Kind; +use crate::application::ics20_fungible_token_transfer::context::Ics20Context; use crate::ics07_tendermint::client_state::test_util::get_dummy_tendermint_client_state; use crate::ics18_relayer::context::Ics18Context; -use crate::ics18_relayer::error::{Error as ICS18Error, Kind as ICS18ErrorKind}; -use crate::ics23_commitment::commitment::CommitmentPrefix; +use crate::ics18_relayer::error::{Error as Ics18Error, Kind as Ics18ErrorKind}; use crate::ics24_host::identifier::{ChainId, ChannelId, ClientId, ConnectionId, PortId}; use crate::ics26_routing::context::Ics26Context; use crate::ics26_routing::handler::{deliver, dispatch}; @@ -84,16 +90,19 @@ pub struct MockContext { channels: HashMap<(PortId, ChannelId), ChannelEnd>, /// Tracks the sequence number for the next packet to be sent. - next_sequence_send: HashMap<(PortId, ChannelId), u64>, + next_sequence_send: HashMap<(PortId, ChannelId), Sequence>, /// Tracks the sequence number for the next packet to be received. - next_sequence_recv: HashMap<(PortId, ChannelId), u64>, + next_sequence_recv: HashMap<(PortId, ChannelId), Sequence>, /// Tracks the sequence number for the next packet to be acknowledged. - next_sequence_ack: HashMap<(PortId, ChannelId), u64>, + next_sequence_ack: HashMap<(PortId, ChannelId), Sequence>, /// Maps ports to their capabilities port_capabilities: HashMap, + + /// Constant-size commitments to packets data fields + packet_commitment: HashMap<(PortId, ChannelId, Sequence), String>, } /// Returns a MockContext with bare minimum initialization: no clients, no connections and no channels are @@ -111,7 +120,7 @@ impl Default for MockContext { } /// Implementation of internal interface for use in testing. The methods in this interface should -/// _not_ be accessible to any ICS handler. +/// _not_ be accessible to any Ics handler. impl MockContext { /// Creates a mock context. Parameter `max_history_size` determines how many blocks will /// the chain maintain in its history, which also determines the pruning window. Parameter @@ -162,6 +171,7 @@ impl MockContext { next_sequence_recv: Default::default(), next_sequence_ack: Default::default(), port_capabilities: Default::default(), + packet_commitment: Default::default(), connection_ids_counter: 0, channel_ids_counter: 0, } @@ -193,8 +203,8 @@ impl MockContext { let (client_state, consensus_state) = match client_type { // If it's a mock client, create the corresponding mock states. ClientType::Mock => ( - Some(MockClientState(MockHeader(client_state_height)).into()), - MockConsensusState(MockHeader(cs_height)).into(), + Some(MockClientState(MockHeader::new(client_state_height)).into()), + MockConsensusState(MockHeader::new(cs_height)).into(), ), // If it's a Tendermint client, we need TM states. ClientType::Tendermint => { @@ -248,6 +258,20 @@ impl MockContext { Self { channels, ..self } } + pub fn with_send_sequence( + self, + port_id: PortId, + chan_id: ChannelId, + seq_number: Sequence, + ) -> Self { + let mut next_sequence_send = self.next_sequence_send.clone(); + next_sequence_send.insert((port_id, chan_id), seq_number); + Self { + next_sequence_send, + ..self + } + } + /// Accessor for a block of the local (host) chain from this context. /// Returns `None` if the block at the requested height does not exist. fn host_block(&self, target_height: Height) -> Option<&HostBlock> { @@ -283,10 +307,10 @@ impl MockContext { } /// A datagram passes from the relayer to the IBC module (on host chain). - /// 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(|e| ICS18ErrorKind::TransactionFailed.context(e))?; + /// 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(|e| Ics18ErrorKind::TransactionFailed.context(e))?; // Create a new block. self.advance_host_chain_height(); Ok(()) @@ -327,6 +351,8 @@ impl MockContext { impl Ics26Context for MockContext {} +impl Ics20Context for MockContext {} + impl PortReader for MockContext { fn lookup_module_by_port(&self, port_id: &PortId) -> Option { self.port_capabilities.get(port_id).cloned() @@ -362,20 +388,29 @@ impl ChannelReader for MockContext { ClientReader::consensus_state(self, client_id, height) } - fn authenticated_capability(&self, port_id: &PortId) -> Result { + fn authenticated_capability(&self, port_id: &PortId) -> Result { let cap = PortReader::lookup_module_by_port(self, port_id); match cap { Some(key) => { if !PortReader::authenticate(self, &key, port_id) { - Err(ICS4Error::from(ICS4Kind::InvalidPortCapability)) + Err(Ics4Kind::InvalidPortCapability.into()) } else { Ok(key) } } - None => Err(ICS4Error::from(ICS4Kind::NoPortCapability(port_id.clone()))), + None => Err(Ics4Kind::NoPortCapability(port_id.clone()).into()), } } + fn get_next_sequence_send(&self, port_channel_id: &(PortId, ChannelId)) -> Option { + self.next_sequence_send.get(port_channel_id).cloned() + } + + fn hash(&self, input: String) -> String { + let r = sha2::Sha256::digest(input.as_bytes()); + format!("{:x}", r) + } + fn channel_counter(&self) -> u64 { self.channel_ids_counter } @@ -384,11 +419,11 @@ impl ChannelReader for MockContext { impl ChannelKeeper for MockContext { fn store_connection_channels( &mut self, - cid: &ConnectionId, + cid: ConnectionId, port_channel_id: &(PortId, ChannelId), - ) -> Result<(), ICS4Error> { + ) -> Result<(), Ics4Error> { self.connection_channels - .entry(cid.clone()) + .entry(cid) .or_insert_with(Vec::new) .push(port_channel_id.clone()); Ok(()) @@ -396,44 +431,56 @@ impl ChannelKeeper for MockContext { fn store_channel( &mut self, - port_channel_id: &(PortId, ChannelId), + port_channel_id: (PortId, ChannelId), channel_end: &ChannelEnd, - ) -> Result<(), ICS4Error> { - self.channels - .insert(port_channel_id.clone(), channel_end.clone()); + ) -> Result<(), Ics4Error> { + self.channels.insert(port_channel_id, channel_end.clone()); Ok(()) } fn store_next_sequence_send( &mut self, - port_channel_id: &(PortId, ChannelId), - seq: u64, - ) -> Result<(), ICS4Error> { - self.next_sequence_send.insert(port_channel_id.clone(), seq); + port_channel_id: (PortId, ChannelId), + seq: Sequence, + ) -> Result<(), Ics4Error> { + self.next_sequence_send.insert(port_channel_id, seq); Ok(()) } fn store_next_sequence_recv( &mut self, - port_channel_id: &(PortId, ChannelId), - seq: u64, - ) -> Result<(), ICS4Error> { - self.next_sequence_recv.insert(port_channel_id.clone(), seq); + port_channel_id: (PortId, ChannelId), + seq: Sequence, + ) -> Result<(), Ics4Error> { + self.next_sequence_recv.insert(port_channel_id, seq); Ok(()) } fn store_next_sequence_ack( &mut self, - port_channel_id: &(PortId, ChannelId), - seq: u64, - ) -> Result<(), ICS4Error> { - self.next_sequence_ack.insert(port_channel_id.clone(), seq); + port_channel_id: (PortId, ChannelId), + seq: Sequence, + ) -> Result<(), Ics4Error> { + self.next_sequence_ack.insert(port_channel_id, seq); Ok(()) } fn increase_channel_counter(&mut self) { self.channel_ids_counter += 1; } + + fn store_packet_commitment( + &mut self, + key: (PortId, ChannelId, Sequence), + timeout_timestamp: u64, + timeout_height: Height, + data: Vec, + ) -> Result<(), Ics4Error> { + let input = format!("{:?},{:?},{:?}", timeout_timestamp, timeout_height, data); + self.packet_commitment + .insert(key, ChannelReader::hash(self, input)); + Ok(()) + } } impl ConnectionReader for MockContext { @@ -442,7 +489,7 @@ impl ConnectionReader for MockContext { } fn client_state(&self, client_id: &ClientId) -> Option { - // Forward method call to the ICS2 Client-specific method. + // Forward method call to the Ics2 Client-specific method. ClientReader::client_state(self, client_id) } @@ -464,7 +511,7 @@ impl ConnectionReader for MockContext { client_id: &ClientId, height: Height, ) -> Option { - // Forward method call to the ICS2Client-specific method. + // Forward method call to the Ics2Client-specific method. self.consensus_state(client_id, height) } @@ -481,21 +528,21 @@ impl ConnectionReader for MockContext { impl ConnectionKeeper for MockContext { fn store_connection( &mut self, - connection_id: &ConnectionId, + connection_id: ConnectionId, connection_end: &ConnectionEnd, - ) -> Result<(), ICS3Error> { + ) -> Result<(), Ics3Error> { self.connections - .insert(connection_id.clone(), connection_end.clone()); + .insert(connection_id, connection_end.clone()); Ok(()) } fn store_connection_to_client( &mut self, - connection_id: &ConnectionId, + connection_id: ConnectionId, client_id: &ClientId, - ) -> Result<(), ICS3Error> { + ) -> Result<(), Ics3Error> { self.client_connections - .insert(client_id.clone(), connection_id.clone()); + .insert(client_id.clone(), connection_id); Ok(()) } @@ -539,7 +586,7 @@ impl ClientKeeper for MockContext { &mut self, client_id: ClientId, client_type: ClientType, - ) -> Result<(), ICS2Error> { + ) -> Result<(), Ics2Error> { let mut client_record = self.clients.entry(client_id).or_insert(MockClientRecord { client_type, consensus_states: Default::default(), @@ -554,7 +601,7 @@ impl ClientKeeper for MockContext { &mut self, client_id: ClientId, client_state: AnyClientState, - ) -> Result<(), ICS2Error> { + ) -> Result<(), Ics2Error> { let mut client_record = self.clients.entry(client_id).or_insert(MockClientRecord { client_type: client_state.client_type(), consensus_states: Default::default(), @@ -570,7 +617,7 @@ impl ClientKeeper for MockContext { client_id: ClientId, height: Height, consensus_state: AnyConsensusState, - ) -> Result<(), ICS2Error> { + ) -> Result<(), Ics2Error> { let client_record = self.clients.entry(client_id).or_insert(MockClientRecord { client_type: ClientType::Mock, consensus_states: Default::default(), @@ -594,7 +641,7 @@ impl Ics18Context for MockContext { } fn query_client_full_state(&self, client_id: &ClientId) -> Option { - // Forward call to ICS2. + // Forward call to Ics2. ClientReader::client_state(self, client_id) } @@ -603,10 +650,10 @@ impl Ics18Context for MockContext { block_ref.cloned().map(Into::into) } - fn send(&mut self, msgs: Vec) -> Result, ICS18Error> { - // Forward call to ICS26 delivery method. + fn send(&mut self, msgs: Vec) -> Result, Ics18Error> { + // Forward call to Ics26 delivery method. let events = - deliver(self, msgs).map_err(|e| ICS18ErrorKind::TransactionFailed.context(e))?; + deliver(self, msgs).map_err(|e| Ics18ErrorKind::TransactionFailed.context(e))?; self.advance_host_chain_height(); // Advance chain height Ok(events) diff --git a/modules/src/mock/header.rs b/modules/src/mock/header.rs index f2cd8babc7..0a7c7f7fd0 100644 --- a/modules/src/mock/header.rs +++ b/modules/src/mock/header.rs @@ -5,15 +5,20 @@ use tendermint_proto::Protobuf; use ibc_proto::ibc::mock::Header as RawMockHeader; -use crate::ics02_client::client_def::{AnyConsensusState, AnyHeader}; +use crate::ics02_client::client_def::AnyConsensusState; +use crate::ics02_client::client_def::AnyHeader; + use crate::ics02_client::client_type::ClientType; use crate::ics02_client::error::{self, Error}; use crate::ics02_client::header::Header; use crate::mock::client_state::MockConsensusState; use crate::Height; -#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Serialize)] -pub struct MockHeader(pub Height); +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize)] +pub struct MockHeader { + pub height: Height, + pub timestamp: u64, +} impl Protobuf for MockHeader {} @@ -21,12 +26,14 @@ impl TryFrom for MockHeader { type Error = Error; fn try_from(raw: RawMockHeader) -> Result { - Ok(MockHeader( - raw.height + Ok(MockHeader { + height: raw + .height .ok_or_else(|| error::Kind::InvalidRawHeader.context("missing height in header"))? .try_into() .map_err(|e| error::Kind::InvalidRawHeader.context(e))?, - )) + timestamp: raw.timestamp, + }) } } @@ -38,7 +45,13 @@ impl From for RawMockHeader { impl MockHeader { pub fn height(&self) -> Height { - self.0 + self.height + } + pub fn new(height: Height) -> Self { + Self { + height, + timestamp: Default::default(), + } } } diff --git a/modules/src/mock/host.rs b/modules/src/mock/host.rs index 33b625b9c2..07aec14be8 100644 --- a/modules/src/mock/host.rs +++ b/modules/src/mock/host.rs @@ -47,7 +47,10 @@ impl HostBlock { /// Generates a new block at `height` for the given chain identifier and chain type. pub fn generate_block(chain_id: ChainId, chain_type: HostType, height: u64) -> HostBlock { match chain_type { - HostType::Mock => HostBlock::Mock(MockHeader(Height::new(chain_id.version(), height))), + HostType::Mock => HostBlock::Mock(MockHeader { + height: Height::new(chain_id.version(), height), + timestamp: 1, + }), HostType::SyntheticTendermint => { HostBlock::SyntheticTendermint(Box::new(Self::generate_tm_block(chain_id, height))) } diff --git a/modules/tests/executor/mod.rs b/modules/tests/executor/mod.rs index 3f37619e98..c78b15aa02 100644 --- a/modules/tests/executor/mod.rs +++ b/modules/tests/executor/mod.rs @@ -133,8 +133,8 @@ impl IBCTestExecutor { Height::new(Self::revision(), height) } - pub fn mock_header(height: u64) -> MockHeader { - MockHeader(Self::height(height)) + fn mock_header(height: u64) -> MockHeader { + MockHeader::new(Self::height(height)) } pub fn header(height: u64) -> AnyHeader { diff --git a/proto/definitions/mock/ibc.mock.proto b/proto/definitions/mock/ibc.mock.proto index e3df854a8b..70f50ca15e 100644 --- a/proto/definitions/mock/ibc.mock.proto +++ b/proto/definitions/mock/ibc.mock.proto @@ -5,6 +5,7 @@ import "ibc/core/client/v1/client.proto"; message Header { ibc.core.client.v1.Height height = 1; + uint64 timestamp = 2; } message ClientState { diff --git a/proto/src/prost/ibc.mock.rs b/proto/src/prost/ibc.mock.rs index 706fc6b5bd..b3ae08f3d9 100644 --- a/proto/src/prost/ibc.mock.rs +++ b/proto/src/prost/ibc.mock.rs @@ -1,15 +1,17 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct Header { #[prost(message, optional, tag="1")] - pub height: ::std::option::Option, + pub height: ::core::option::Option, + #[prost(uint64, tag="2")] + pub timestamp: u64, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ClientState { #[prost(message, optional, tag="1")] - pub header: ::std::option::Option
, + pub header: ::core::option::Option
, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ConsensusState { #[prost(message, optional, tag="1")] - pub header: ::std::option::Option
, + pub header: ::core::option::Option
, } From 511e9348042e6b79098af62562a081c6a9b368a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Mar 2021 14:12:58 +0100 Subject: [PATCH 2/4] Bump once_cell from 1.7.1 to 1.7.2 (#729) Bumps [once_cell](https://github.com/matklad/once_cell) from 1.7.1 to 1.7.2. - [Release notes](https://github.com/matklad/once_cell/releases) - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.7.1...v1.7.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- relayer-cli/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40d5bc4721..f07beb16bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1341,9 +1341,9 @@ checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea78b9742c52ac729753c1590e9adc5248ea9bdaf974597efd46c74cfaa5fb54" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" dependencies = [ "parking_lot", ] diff --git a/relayer-cli/Cargo.toml b/relayer-cli/Cargo.toml index 76723f724a..5a52627dbb 100644 --- a/relayer-cli/Cargo.toml +++ b/relayer-cli/Cargo.toml @@ -66,4 +66,4 @@ version = "0.5.2" [dev-dependencies] abscissa_core = { version = "0.5.2", features = ["testing"] } -once_cell = "1.2" +once_cell = "1.7" From 600a59ef8b5422d0b6f2091610ce20586ca6acd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Mar 2021 14:13:38 +0100 Subject: [PATCH 3/4] Bump serde from 1.0.123 to 1.0.124 (#728) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.123 to 1.0.124. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.123...v1.0.124) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- modules/Cargo.toml | 2 +- relayer/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f07beb16bc..99d1347d1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1755,9 +1755,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.123" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" dependencies = [ "serde_derive", ] @@ -1783,9 +1783,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.123" +version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" dependencies = [ "proc-macro2", "quote", diff --git a/modules/Cargo.toml b/modules/Cargo.toml index 716119ab01..fd08b5306a 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -27,7 +27,7 @@ anomaly = "0.2.0" chrono = "0.4" thiserror = "1.0.24" serde_derive = "1.0.104" -serde = "1.0.104" +serde = "1.0.124" serde_json = "1" tracing = "0.1.13" prost = "0.7" diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index 107b398c53..6b2b1ff01a 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -25,7 +25,7 @@ subtle-encoding = "0.5" anomaly = "0.2.0" async-trait = "0.1.24" humantime-serde = "1.0.0" -serde = "1.0.97" +serde = "1.0.124" serde_cbor = "0.11.1" serde_derive = "1.0" sled = { version = "0.34.4", features = ["no_metrics", "no_logs"] } From bfd3d8e3010189bfc9de83c520d464d8bdc8acd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Mar 2021 14:14:01 +0100 Subject: [PATCH 4/4] Bump hex from 0.4.2 to 0.4.3 (#727) Bumps [hex](https://github.com/KokaKiwi/rust-hex) from 0.4.2 to 0.4.3. - [Release notes](https://github.com/KokaKiwi/rust-hex/releases) - [Commits](https://github.com/KokaKiwi/rust-hex/compare/v0.4.2...v0.4.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99d1347d1f..71794f70df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -886,9 +886,9 @@ dependencies = [ [[package]] name = "hex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac"