From a80f200fa2b599206d9e76e7ad5e8cd83c416178 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 18 Jan 2021 14:25:46 +0100 Subject: [PATCH 01/11] Add some comments on timeout_timestamp format --- contracts/ibc-reflect/schema/packet_msg.json | 4 ++-- contracts/reflect/schema/handle_msg.json | 4 ++-- .../reflect/schema/handle_response_for__custom_msg.json | 4 ++-- packages/std/src/ibc.rs | 9 ++++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/contracts/ibc-reflect/schema/packet_msg.json b/contracts/ibc-reflect/schema/packet_msg.json index 4b046b8124..8693b61b98 100644 --- a/contracts/ibc-reflect/schema/packet_msg.json +++ b/contracts/ibc-reflect/schema/packet_msg.json @@ -231,7 +231,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (in nanoseconds) after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", "type": [ "integer", "null" @@ -283,7 +283,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (in nanoseconds) after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", "type": [ "integer", "null" diff --git a/contracts/reflect/schema/handle_msg.json b/contracts/reflect/schema/handle_msg.json index 7694ac46d8..91421cd755 100644 --- a/contracts/reflect/schema/handle_msg.json +++ b/contracts/reflect/schema/handle_msg.json @@ -251,7 +251,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (in nanoseconds) after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", "type": [ "integer", "null" @@ -303,7 +303,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (in nanoseconds) after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", "type": [ "integer", "null" diff --git a/contracts/reflect/schema/handle_response_for__custom_msg.json b/contracts/reflect/schema/handle_response_for__custom_msg.json index 40c8ae534d..9c80a3dad2 100644 --- a/contracts/reflect/schema/handle_response_for__custom_msg.json +++ b/contracts/reflect/schema/handle_response_for__custom_msg.json @@ -254,7 +254,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (in nanoseconds) after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", "type": [ "integer", "null" @@ -306,7 +306,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (in nanoseconds) after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", "type": [ "integer", "null" diff --git a/packages/std/src/ibc.rs b/packages/std/src/ibc.rs index e536f9d0e0..afb5add101 100644 --- a/packages/std/src/ibc.rs +++ b/packages/std/src/ibc.rs @@ -34,7 +34,8 @@ pub enum IbcMsg { /// block height after which the packet times out. /// at least one of timeout_height, timeout_timestamp is required timeout_height: Option, - /// block timestamp (in nanoseconds) after which the packet times out. + /// block timestamp (nanoseconds since UNIX epoch) after which the packet times out. + /// See https://golang.org/pkg/time/#Time.UnixNano /// at least one of timeout_height, timeout_timestamp is required timeout_timestamp: Option, }, @@ -47,7 +48,8 @@ pub enum IbcMsg { /// block height after which the packet times out. /// at least one of timeout_height, timeout_timestamp is required timeout_height: Option, - /// block timestamp (in nanoseconds) after which the packet times out. + /// block timestamp (nanoseconds since UNIX epoch) after which the packet times out. + /// See https://golang.org/pkg/time/#Time.UnixNano /// at least one of timeout_height, timeout_timestamp is required timeout_timestamp: Option, }, @@ -176,7 +178,8 @@ pub struct IbcPacket { /// block height after which the packet times out. /// at least one of timeout_height, timeout_timestamp is required pub timeout_height: Option, - /// block timestamp (in nanoseconds) after which the packet times out. + /// block timestamp (nanoseconds since UNIX epoch) after which the packet times out. + /// See https://golang.org/pkg/time/#Time.UnixNano /// at least one of timeout_height, timeout_timestamp is required pub timeout_timestamp: Option, } From 448671c8cb31f0852f27cc5775461ba995f288a1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 20 Jan 2021 13:07:23 +0100 Subject: [PATCH 02/11] Rename timeout_height.timeout_height -> timeout_block.height --- packages/std/src/ibc.rs | 68 ++++++++++++++++++++-------------------- packages/std/src/lib.rs | 2 +- packages/std/src/mock.rs | 14 ++++----- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/packages/std/src/ibc.rs b/packages/std/src/ibc.rs index afb5add101..7a0c6cc67b 100644 --- a/packages/std/src/ibc.rs +++ b/packages/std/src/ibc.rs @@ -31,12 +31,12 @@ pub enum IbcMsg { /// packet data only supports one coin /// https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20 amount: Coin, - /// block height after which the packet times out. - /// at least one of timeout_height, timeout_timestamp is required - timeout_height: Option, + /// block after which the packet times out. + /// at least one of timeout_block, timeout_timestamp is required + timeout_block: Option, /// block timestamp (nanoseconds since UNIX epoch) after which the packet times out. /// See https://golang.org/pkg/time/#Time.UnixNano - /// at least one of timeout_height, timeout_timestamp is required + /// at least one of timeout_block, timeout_timestamp is required timeout_timestamp: Option, }, /// Sends an IBC packet with given data over the existing channel. @@ -46,11 +46,11 @@ pub enum IbcMsg { channel_id: String, data: Binary, /// block height after which the packet times out. - /// at least one of timeout_height, timeout_timestamp is required - timeout_height: Option, + /// at least one of timeout_block, timeout_timestamp is required + timeout_block: Option, /// block timestamp (nanoseconds since UNIX epoch) after which the packet times out. /// See https://golang.org/pkg/time/#Time.UnixNano - /// at least one of timeout_height, timeout_timestamp is required + /// at least one of timeout_block, timeout_timestamp is required timeout_timestamp: Option, }, /// This will close an existing channel that is owned by this contract. @@ -135,31 +135,31 @@ pub enum IbcOrder { /// freezing clients. /// Ordering is (revision_number, timeout_height) #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct IbcTimeoutHeight { +pub struct IbcTimeoutBlock { /// the version that the client is currently on /// (eg. after reseting the chain this could increment 1 as height drops to 0) - pub revision_number: u64, + pub revision: u64, /// block height after which the packet times out. /// the height within the given revision - pub timeout_height: u64, + pub height: u64, } -impl IbcTimeoutHeight { +impl IbcTimeoutBlock { pub fn is_zero(&self) -> bool { - self.revision_number == 0 && self.timeout_height == 0 + self.revision == 0 && self.height == 0 } } -impl PartialOrd for IbcTimeoutHeight { +impl PartialOrd for IbcTimeoutBlock { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for IbcTimeoutHeight { +impl Ord for IbcTimeoutBlock { fn cmp(&self, other: &Self) -> Ordering { - match self.revision_number.cmp(&other.revision_number) { - Ordering::Equal => self.timeout_height.cmp(&other.timeout_height), + match self.revision.cmp(&other.revision) { + Ordering::Equal => self.height.cmp(&other.height), other => other, } } @@ -176,11 +176,11 @@ pub struct IbcPacket { /// The sequence number of the packet on the given channel pub sequence: u64, /// block height after which the packet times out. - /// at least one of timeout_height, timeout_timestamp is required - pub timeout_height: Option, + /// at least one of timeout_block, timeout_timestamp is required + pub timeout_block: Option, /// block timestamp (nanoseconds since UNIX epoch) after which the packet times out. /// See https://golang.org/pkg/time/#Time.UnixNano - /// at least one of timeout_height, timeout_timestamp is required + /// at least one of timeout_block, timeout_timestamp is required pub timeout_timestamp: Option, } @@ -262,31 +262,31 @@ mod tests { channel_id: "channel-123".to_string(), to_address: "my-special-addr".into(), amount: Coin::new(12345678, "uatom"), - timeout_height: None, + timeout_block: None, timeout_timestamp: Some(1234567890), }; let encoded = to_string(&msg).unwrap(); - let expected = r#"{"transfer":{"channel_id":"channel-123","to_address":"my-special-addr","amount":{"denom":"uatom","amount":"12345678"},"timeout_height":null,"timeout_timestamp":1234567890}}"#; + let expected = r#"{"transfer":{"channel_id":"channel-123","to_address":"my-special-addr","amount":{"denom":"uatom","amount":"12345678"},"timeout_block":null,"timeout_timestamp":1234567890}}"#; assert_eq!(encoded.as_str(), expected); } #[test] - fn ibc_timeout_height_ord() { - let epoch1a = IbcTimeoutHeight { - revision_number: 1, - timeout_height: 1000, + fn ibc_timeout_block_ord() { + let epoch1a = IbcTimeoutBlock { + revision: 1, + height: 1000, }; - let epoch1b = IbcTimeoutHeight { - revision_number: 1, - timeout_height: 3000, + let epoch1b = IbcTimeoutBlock { + revision: 1, + height: 3000, }; - let epoch2a = IbcTimeoutHeight { - revision_number: 2, - timeout_height: 500, + let epoch2a = IbcTimeoutBlock { + revision: 2, + height: 500, }; - let epoch2b = IbcTimeoutHeight { - revision_number: 2, - timeout_height: 2500, + let epoch2b = IbcTimeoutBlock { + revision: 2, + height: 2500, }; // basic checks diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 3a7a729d0a..84c2094e8a 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -28,7 +28,7 @@ pub use crate::errors::{StdError, StdResult, SystemError}; #[cfg(feature = "stargate")] pub use crate::ibc::{ ChannelResponse, IbcAcknowledgement, IbcBasicResponse, IbcChannel, IbcEndpoint, IbcMsg, - IbcOrder, IbcPacket, IbcQuery, IbcReceiveResponse, IbcTimeoutHeight, ListChannelsResponse, + IbcOrder, IbcPacket, IbcQuery, IbcReceiveResponse, IbcTimeoutBlock, ListChannelsResponse, PortIdResponse, }; #[cfg(feature = "iterator")] diff --git a/packages/std/src/mock.rs b/packages/std/src/mock.rs index 54844b692e..927847bba6 100644 --- a/packages/std/src/mock.rs +++ b/packages/std/src/mock.rs @@ -9,7 +9,7 @@ use crate::coins::Coin; use crate::deps::OwnedDeps; use crate::errors::{StdError, StdResult, SystemError}; #[cfg(feature = "stargate")] -use crate::ibc::{IbcChannel, IbcEndpoint, IbcOrder, IbcPacket, IbcTimeoutHeight}; +use crate::ibc::{IbcChannel, IbcEndpoint, IbcOrder, IbcPacket, IbcTimeoutBlock}; use crate::query::{ AllBalanceResponse, AllDelegationsResponse, BalanceResponse, BankQuery, BondedDenomResponse, CustomQuery, DelegationResponse, FullDelegation, QueryRequest, StakingQuery, Validator, @@ -188,9 +188,9 @@ pub fn mock_ibc_packet_recv(my_channel_id: &str, data: &T) -> StdR channel_id: my_channel_id.into(), }, sequence: 27, - timeout_height: Some(IbcTimeoutHeight { - revision_number: 1, - timeout_height: 12345678, + timeout_block: Some(IbcTimeoutBlock { + revision: 1, + height: 12345678, }), timeout_timestamp: None, }) @@ -212,9 +212,9 @@ pub fn mock_ibc_packet_ack(my_channel_id: &str, data: &T) -> StdRe channel_id: "channel-1234".to_string(), }, sequence: 29, - timeout_height: Some(IbcTimeoutHeight { - revision_number: 1, - timeout_height: 432332552, + timeout_block: Some(IbcTimeoutBlock { + revision: 1, + height: 432332552, }), timeout_timestamp: None, }) From 809be44e274f641c40228206b12b9a148c025217 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 20 Jan 2021 13:32:58 +0100 Subject: [PATCH 03/11] Update CHANGELOG for ibc/stargate features --- CHANGELOG.md | 27 +++++++++++++++++ contracts/ibc-reflect/schema/packet_msg.json | 30 +++++++++---------- contracts/reflect/schema/handle_msg.json | 30 +++++++++---------- .../handle_response_for__custom_msg.json | 30 +++++++++---------- 4 files changed, 72 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc828b3f69..3f2b2a4eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,31 @@ and this project adheres to - cosmwasm-vm: The new `Cache::analyze` provides a static analyzis of the Wasm bytecode. This is used to tell the caller if the contract exposes IBC entry points. ([#736]) +- cosmwasm-vm: Added new `stargate` feature flag to enable new stargate and ibc + features ([#692], [#716]) +- cosmwasm-vm: (requires `stargate`) call into 6 new ibc entry points if exposed + by contract ([#692], [#716]) +- cosmwasm-std: Added new `stargate` feature flag to enable new stargate and ibc + features ([#692], [#706]) +- cosmwasm-std: (requires `stargate`) Added new `CosmosMsg::Stargate` message + type to dispatch protobuf-encoded message (contract must know proto schema) + ([#706]) +- cosmwasm-std: (requires `stargate`) Added new `QueryRequest::Stargate` message + type to dispatch protobuf-encoded queries (contract must know proto schema for + request and response) ([#706]) +- cosmwasm-std: (requires `stargate`) Added new `CosmosMsg::Ibc(IbcMsg)` message + type to use ibctransfer app or send raw ics packets (if contract has ibc entry + points) ([#692], [#710]) +- contracts: added new `ibc-reflect` contract that receives channels and assigns + each an account to redispatch. Similar idea to ICS27/Interchain Accounts (but + different implementation) ([#692], [#711], [#714]) + +[#692]: https://github.com/CosmWasm/cosmwasm/issues/692 +[#706]: https://github.com/CosmWasm/cosmwasm/pull/706 +[#710]: https://github.com/CosmWasm/cosmwasm/pull/710 +[#711]: https://github.com/CosmWasm/cosmwasm/pull/711 +[#714]: https://github.com/CosmWasm/cosmwasm/pull/714 +[#716]: https://github.com/CosmWasm/cosmwasm/pull/716 ### Changed @@ -29,6 +54,8 @@ and this project adheres to ([#697]) - cosmwasm-vm: Bump required marker export `cosmwasm_vm_version_4` to `interface_version_5`. +- contracts: `reflect` contract requires `stargate` feature and supports + redispatching `Stargate` and `IbcMsg::Transfer` messages ([#692]) [#696]: https://github.com/CosmWasm/cosmwasm/issues/696 [#697]: https://github.com/CosmWasm/cosmwasm/issues/697 diff --git a/contracts/ibc-reflect/schema/packet_msg.json b/contracts/ibc-reflect/schema/packet_msg.json index 8693b61b98..de041db355 100644 --- a/contracts/ibc-reflect/schema/packet_msg.json +++ b/contracts/ibc-reflect/schema/packet_msg.json @@ -219,11 +219,11 @@ "description": "exisiting channel to send the tokens over", "type": "string" }, - "timeout_height": { - "description": "block height after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "timeout_block": { + "description": "block after which the packet times out. at least one of timeout_block, timeout_timestamp is required", "anyOf": [ { - "$ref": "#/definitions/IbcTimeoutHeight" + "$ref": "#/definitions/IbcTimeoutBlock" }, { "type": "null" @@ -231,7 +231,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_block, timeout_timestamp is required", "type": [ "integer", "null" @@ -271,11 +271,11 @@ "data": { "$ref": "#/definitions/Binary" }, - "timeout_height": { - "description": "block height after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "timeout_block": { + "description": "block height after which the packet times out. at least one of timeout_block, timeout_timestamp is required", "anyOf": [ { - "$ref": "#/definitions/IbcTimeoutHeight" + "$ref": "#/definitions/IbcTimeoutBlock" }, { "type": "null" @@ -283,7 +283,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_block, timeout_timestamp is required", "type": [ "integer", "null" @@ -317,22 +317,22 @@ } ] }, - "IbcTimeoutHeight": { + "IbcTimeoutBlock": { "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", "type": "object", "required": [ - "revision_number", - "timeout_height" + "height", + "revision" ], "properties": { - "revision_number": { - "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "height": { + "description": "block height after which the packet times out. the height within the given revision", "type": "integer", "format": "uint64", "minimum": 0.0 }, - "timeout_height": { - "description": "block height after which the packet times out. the height within the given revision", + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", "type": "integer", "format": "uint64", "minimum": 0.0 diff --git a/contracts/reflect/schema/handle_msg.json b/contracts/reflect/schema/handle_msg.json index 91421cd755..9aff7ee39d 100644 --- a/contracts/reflect/schema/handle_msg.json +++ b/contracts/reflect/schema/handle_msg.json @@ -239,11 +239,11 @@ "description": "exisiting channel to send the tokens over", "type": "string" }, - "timeout_height": { - "description": "block height after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "timeout_block": { + "description": "block after which the packet times out. at least one of timeout_block, timeout_timestamp is required", "anyOf": [ { - "$ref": "#/definitions/IbcTimeoutHeight" + "$ref": "#/definitions/IbcTimeoutBlock" }, { "type": "null" @@ -251,7 +251,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_block, timeout_timestamp is required", "type": [ "integer", "null" @@ -291,11 +291,11 @@ "data": { "$ref": "#/definitions/Binary" }, - "timeout_height": { - "description": "block height after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "timeout_block": { + "description": "block height after which the packet times out. at least one of timeout_block, timeout_timestamp is required", "anyOf": [ { - "$ref": "#/definitions/IbcTimeoutHeight" + "$ref": "#/definitions/IbcTimeoutBlock" }, { "type": "null" @@ -303,7 +303,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_block, timeout_timestamp is required", "type": [ "integer", "null" @@ -337,22 +337,22 @@ } ] }, - "IbcTimeoutHeight": { + "IbcTimeoutBlock": { "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", "type": "object", "required": [ - "revision_number", - "timeout_height" + "height", + "revision" ], "properties": { - "revision_number": { - "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "height": { + "description": "block height after which the packet times out. the height within the given revision", "type": "integer", "format": "uint64", "minimum": 0.0 }, - "timeout_height": { - "description": "block height after which the packet times out. the height within the given revision", + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", "type": "integer", "format": "uint64", "minimum": 0.0 diff --git a/contracts/reflect/schema/handle_response_for__custom_msg.json b/contracts/reflect/schema/handle_response_for__custom_msg.json index 9c80a3dad2..9e81de49e9 100644 --- a/contracts/reflect/schema/handle_response_for__custom_msg.json +++ b/contracts/reflect/schema/handle_response_for__custom_msg.json @@ -242,11 +242,11 @@ "description": "exisiting channel to send the tokens over", "type": "string" }, - "timeout_height": { - "description": "block height after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "timeout_block": { + "description": "block after which the packet times out. at least one of timeout_block, timeout_timestamp is required", "anyOf": [ { - "$ref": "#/definitions/IbcTimeoutHeight" + "$ref": "#/definitions/IbcTimeoutBlock" }, { "type": "null" @@ -254,7 +254,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_block, timeout_timestamp is required", "type": [ "integer", "null" @@ -294,11 +294,11 @@ "data": { "$ref": "#/definitions/Binary" }, - "timeout_height": { - "description": "block height after which the packet times out. at least one of timeout_height, timeout_timestamp is required", + "timeout_block": { + "description": "block height after which the packet times out. at least one of timeout_block, timeout_timestamp is required", "anyOf": [ { - "$ref": "#/definitions/IbcTimeoutHeight" + "$ref": "#/definitions/IbcTimeoutBlock" }, { "type": "null" @@ -306,7 +306,7 @@ ] }, "timeout_timestamp": { - "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_height, timeout_timestamp is required", + "description": "block timestamp (nanoseconds since UNIX epoch) after which the packet times out. See https://golang.org/pkg/time/#Time.UnixNano at least one of timeout_block, timeout_timestamp is required", "type": [ "integer", "null" @@ -340,22 +340,22 @@ } ] }, - "IbcTimeoutHeight": { + "IbcTimeoutBlock": { "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", "type": "object", "required": [ - "revision_number", - "timeout_height" + "height", + "revision" ], "properties": { - "revision_number": { - "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "height": { + "description": "block height after which the packet times out. the height within the given revision", "type": "integer", "format": "uint64", "minimum": 0.0 }, - "timeout_height": { - "description": "block height after which the packet times out. the height within the given revision", + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", "type": "integer", "format": "uint64", "minimum": 0.0 From bf0a06d1b9152c32304b988350fb1327737b483a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 20 Jan 2021 14:00:53 +0100 Subject: [PATCH 04/11] Add ibc-close-channel handler and test it --- contracts/ibc-reflect/src/contract.rs | 104 +++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 12 deletions(-) diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index 8f901937b6..800c15e4cc 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -1,8 +1,8 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, wasm_execute, wasm_instantiate, CosmosMsg, Deps, DepsMut, - Env, HandleResponse, HumanAddr, IbcAcknowledgement, IbcBasicResponse, IbcChannel, IbcOrder, - IbcPacket, IbcReceiveResponse, InitResponse, MessageInfo, Order, QueryResponse, StdError, - StdResult, + entry_point, from_slice, to_binary, wasm_execute, wasm_instantiate, BankMsg, CosmosMsg, Deps, + DepsMut, Empty, Env, HandleResponse, HumanAddr, IbcAcknowledgement, IbcBasicResponse, + IbcChannel, IbcOrder, IbcPacket, IbcReceiveResponse, InitResponse, MessageInfo, Order, + QueryResponse, StdError, StdResult, }; use crate::msg::{ @@ -143,14 +143,39 @@ pub fn ibc_channel_connect( } #[entry_point] -/// we do nothing -/// TODO: remove the account from the lookup? +/// On closed channel, we take all tokens from reflect contract to this contract. +/// We also delete the channel entry from accounts. pub fn ibc_channel_close( - _deps: DepsMut, - _env: Env, - _channel: IbcChannel, + deps: DepsMut, + env: Env, + channel: IbcChannel, ) -> StdResult { - Ok(IbcBasicResponse::default()) + // get contract address and remove lookup + let channel_id = channel.endpoint.channel_id.as_bytes(); + let reflect_addr = accounts(deps.storage).load(channel_id)?; + accounts(deps.storage).remove(channel_id); + + // transfer current balance if any (steal the money) + let amount = deps.querier.query_all_balances(&reflect_addr)?; + let messages: Vec> = if !amount.is_empty() { + let bank_msg: CosmosMsg = BankMsg::Send { + to_address: env.contract.address.clone(), + amount, + } + .into(); + let reflect_msg = ReflectHandleMsg::ReflectMsg { + msgs: vec![bank_msg.into()], + }; + let wasm_msg = wasm_execute(reflect_addr, &reflect_msg, vec![])?; + vec![wasm_msg.into()] + } else { + vec![] + }; + + Ok(IbcBasicResponse { + messages, + attributes: vec![], + }) } #[entry_point] @@ -261,9 +286,9 @@ mod tests { use super::*; use cosmwasm_std::testing::{ mock_dependencies, mock_env, mock_ibc_channel, mock_ibc_packet_recv, mock_info, MockApi, - MockQuerier, MockStorage, + MockQuerier, MockStorage, MOCK_CONTRACT_ADDR, }; - use cosmwasm_std::{coins, from_slice, BankMsg, OwnedDeps, WasmMsg}; + use cosmwasm_std::{coin, coins, from_slice, BankMsg, OwnedDeps, WasmMsg}; const CREATOR: &str = "creator"; // code id of the reflect contract @@ -477,4 +502,59 @@ mod tests { let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); assert_eq!(ack.unwrap_err(), "invalid packet: Error parsing into type ibc_reflect::msg::PacketMsg: unknown variant `reflect_code_id`, expected one of `dispatch`, `who_am_i`, `balances`"); } + + #[test] + fn check_close_channel() { + let mut deps = setup(); + + let channel_id: &str = "channel-123"; + let account: &str = "acct-123"; + + // register the channel + connect(deps.as_mut(), channel_id, account); + // assign it some funds + let funds = vec![coin(123456, "uatom"), coin(7654321, "tgrd")]; + deps.querier.update_balance(account, funds.clone()); + + // channel should be listed and have balance + let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); + let res: ListAccountsResponse = from_slice(&raw).unwrap(); + assert_eq!(1, res.accounts.len()); + let balance = deps.as_ref().querier.query_all_balances(account).unwrap(); + assert_eq!(funds, balance); + + // close the channel + let channel = mock_ibc_channel(channel_id, IbcOrder::Ordered, IBC_VERSION); + let res = ibc_channel_close(deps.as_mut(), mock_env(), channel).unwrap(); + + // it pulls out all money from the reflect contract + assert_eq!(1, res.messages.len()); + if let CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, msg, .. + }) = &res.messages[0] + { + assert_eq!(contract_addr.as_str(), account); + let reflect: ReflectHandleMsg = from_slice(msg).unwrap(); + match reflect { + ReflectHandleMsg::ReflectMsg { msgs } => { + assert_eq!(1, msgs.len()); + assert_eq!( + &msgs[0], + &BankMsg::Send { + to_address: MOCK_CONTRACT_ADDR.into(), + amount: funds + } + .into() + ) + } + } + } else { + panic!("Unexpected message: {:?}", &res.messages[0]); + } + + // and removes the account lookup + let raw = query(deps.as_ref(), mock_env(), QueryMsg::ListAccounts {}).unwrap(); + let res: ListAccountsResponse = from_slice(&raw).unwrap(); + assert_eq!(0, res.accounts.len()); + } } From 3d39c1d295652242ec547ccaafb82156559be41e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 20 Jan 2021 14:09:13 +0100 Subject: [PATCH 05/11] Add attributes to all ibc-reflect methods --- contracts/ibc-reflect/src/contract.rs | 52 ++++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index 800c15e4cc..de93dfd10c 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - entry_point, from_slice, to_binary, wasm_execute, wasm_instantiate, BankMsg, CosmosMsg, Deps, - DepsMut, Empty, Env, HandleResponse, HumanAddr, IbcAcknowledgement, IbcBasicResponse, + attr, entry_point, from_slice, to_binary, wasm_execute, wasm_instantiate, BankMsg, CosmosMsg, + Deps, DepsMut, Empty, Env, HandleResponse, HumanAddr, IbcAcknowledgement, IbcBasicResponse, IbcChannel, IbcOrder, IbcPacket, IbcReceiveResponse, InitResponse, MessageInfo, Order, QueryResponse, StdError, StdResult, }; @@ -22,7 +22,10 @@ pub fn init(deps: DepsMut, _env: Env, _info: MessageInfo, msg: InitMsg) -> StdRe }; config(deps.storage).save(&cfg)?; - Ok(InitResponse::default()) + Ok(InitResponse { + messages: vec![], + attributes: vec![attr("action", "init")], + }) } #[entry_point] @@ -61,7 +64,11 @@ pub fn handle_init_callback( } })?; - Ok(HandleResponse::default()) + Ok(HandleResponse { + messages: vec![], + attributes: vec![attr("action", "handle_init_callback")], + data: None, + }) } #[entry_point] @@ -133,12 +140,12 @@ pub fn ibc_channel_connect( let label = format!("ibc-reflect-{}", &chan_id); let payload = ReflectInitMsg { - callback_id: Some(chan_id), + callback_id: Some(chan_id.clone()), }; let msg = wasm_instantiate(cfg.reflect_code_id, &payload, vec![], Some(label))?; Ok(IbcBasicResponse { messages: vec![msg.into()], - attributes: vec![], + attributes: vec![attr("action", "ibc_connect"), attr("channel_id", chan_id)], }) } @@ -151,9 +158,9 @@ pub fn ibc_channel_close( channel: IbcChannel, ) -> StdResult { // get contract address and remove lookup - let channel_id = channel.endpoint.channel_id.as_bytes(); - let reflect_addr = accounts(deps.storage).load(channel_id)?; - accounts(deps.storage).remove(channel_id); + let channel_id = channel.endpoint.channel_id.as_str(); + let reflect_addr = accounts(deps.storage).load(channel_id.as_bytes())?; + accounts(deps.storage).remove(channel_id.as_bytes()); // transfer current balance if any (steal the money) let amount = deps.querier.query_all_balances(&reflect_addr)?; @@ -171,10 +178,15 @@ pub fn ibc_channel_close( } else { vec![] }; + let steal_funds = !messages.is_empty(); Ok(IbcBasicResponse { messages, - attributes: vec![], + attributes: vec![ + attr("action", "ibc_close"), + attr("channel_id", channel_id), + attr("steal_funds", steal_funds), + ], }) } @@ -230,7 +242,7 @@ fn receive_dispatch( Ok(IbcReceiveResponse { acknowledgement, messages: vec![wasm_msg.into()], - attributes: vec![], + attributes: vec![attr("action", "receive_dispatch")], }) } @@ -243,7 +255,7 @@ fn receive_who_am_i(deps: DepsMut, caller: String) -> StdResult StdResult StdResult { - Ok(IbcBasicResponse::default()) + Ok(IbcBasicResponse { + messages: vec![], + attributes: vec![attr("action", "ibc_packet_ack")], + }) } #[entry_point] -/// we do nothing +/// never should be called as we do not send packets pub fn ibc_packet_timeout( _deps: DepsMut, _env: Env, _packet: IbcPacket, ) -> StdResult { - Ok(IbcBasicResponse::default()) + Ok(IbcBasicResponse { + messages: vec![], + attributes: vec![attr("action", "ibc_packet_timeout")], + }) } #[cfg(test)] From 933152bd66b07f97c79412934ae0d1cb272cb880 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 20 Jan 2021 14:13:18 +0100 Subject: [PATCH 06/11] Add ibc-reflect contract to vm testdata --- packages/vm/README.md | 6 ++++++ packages/vm/testdata/ibc_reflect_0.14.wasm | Bin 0 -> 241535 bytes 2 files changed, 6 insertions(+) create mode 100644 packages/vm/testdata/ibc_reflect_0.14.wasm diff --git a/packages/vm/README.md b/packages/vm/README.md index d8b60c8398..d42325b191 100644 --- a/packages/vm/README.md +++ b/packages/vm/README.md @@ -41,6 +41,12 @@ docker run --rm -v "$(pwd)":/code \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/rust-optimizer:0.10.7 ./contracts/hackatom \ && cp artifacts/hackatom.wasm packages/vm/testdata/hackatom_0.14.wasm + +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="devcontract_cache_ibc_reflect",target=/code/contracts/ibc-reflect/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.10.7 ./contracts/ibc-reflect \ + && cp artifacts/ibc_reflect.wasm packages/vm/testdata/ibc_reflect_0.14.wasm ``` ## Testing diff --git a/packages/vm/testdata/ibc_reflect_0.14.wasm b/packages/vm/testdata/ibc_reflect_0.14.wasm new file mode 100644 index 0000000000000000000000000000000000000000..7813f7330d07a7701e44ea87178d7d25fbd99ed2 GIT binary patch literal 241535 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 Date: Wed, 20 Jan 2021 14:28:52 +0100 Subject: [PATCH 07/11] First vm tests for ibc reflect --- packages/vm/src/ibc_calls.rs | 65 +++++++++++++++++++++++++++ packages/vm/testdata/ibc_reflect.wasm | 1 + 2 files changed, 66 insertions(+) create mode 120000 packages/vm/testdata/ibc_reflect.wasm diff --git a/packages/vm/src/ibc_calls.rs b/packages/vm/src/ibc_calls.rs index fe07a20fd4..6afa3ecb0f 100644 --- a/packages/vm/src/ibc_calls.rs +++ b/packages/vm/src/ibc_calls.rs @@ -231,3 +231,68 @@ where MAX_LENGTH_IBC, ) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::calls::{call_handle, call_init}; + use crate::testing::{mock_env, mock_info, mock_instance, MockApi, MockQuerier, MockStorage}; + use cosmwasm_std::testing::mock_ibc_channel; + use cosmwasm_std::{Empty, IbcOrder}; + + static CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm"); + const IBC_VERSION: &str = "ibc-reflect"; + + #[test] + fn call_init_works() { + let mut instance = mock_instance(&CONTRACT, &[]); + + // init + let info = mock_info("creator", &[]); + let msg = br#"{"reflect_code_id":77}"#; + call_init::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg) + .unwrap() + .unwrap(); + } + + fn setup( + instance: &mut Instance, + channel_id: &str, + account: &str, + ) { + // init + let info = mock_info("creator", &[]); + let msg = br#"{"reflect_code_id":77}"#; + call_init::<_, _, _, Empty>(instance, &mock_env(), &info, msg) + .unwrap() + .unwrap(); + + // first we try to open with a valid handshake + let mut handshake_open = mock_ibc_channel(channel_id, IbcOrder::Ordered, IBC_VERSION); + handshake_open.counterparty_version = None; + call_ibc_channel_open(instance, &mock_env(), &handshake_open) + .unwrap() + .unwrap(); + + // then we connect (with counter-party version set) + let handshake_connect = mock_ibc_channel(channel_id, IbcOrder::Ordered, IBC_VERSION); + call_ibc_channel_connect::<_, _, _, Empty>(instance, &mock_env(), &handshake_connect) + .unwrap() + .unwrap(); + + // which creates a reflect account. here we get the callback + let handle_msg = format!( + r#"{{"init_callback":{{"id":"{}","contract_addr":"{}"}}}}"#, + channel_id, account + ); + let info = mock_info(account, &[]); + call_handle::<_, _, _, Empty>(instance, &mock_env(), &info, handle_msg.as_bytes()).unwrap(); + } + + #[test] + fn handshake_works() { + let mut instance = mock_instance(&CONTRACT, &[]); + + setup(&mut instance, "channel-123", "account-456"); + } +} diff --git a/packages/vm/testdata/ibc_reflect.wasm b/packages/vm/testdata/ibc_reflect.wasm new file mode 120000 index 0000000000..60cef25e85 --- /dev/null +++ b/packages/vm/testdata/ibc_reflect.wasm @@ -0,0 +1 @@ +ibc_reflect_0.14.wasm \ No newline at end of file From 46079014166899339bdaa9c5c30b20b985a6f736 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 20 Jan 2021 14:35:13 +0100 Subject: [PATCH 08/11] Cover remaining vm ibc_calls with tests --- packages/vm/src/ibc_calls.rs | 54 ++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/ibc_calls.rs b/packages/vm/src/ibc_calls.rs index 6afa3ecb0f..fa2fa617a8 100644 --- a/packages/vm/src/ibc_calls.rs +++ b/packages/vm/src/ibc_calls.rs @@ -237,7 +237,7 @@ mod tests { use super::*; use crate::calls::{call_handle, call_init}; use crate::testing::{mock_env, mock_info, mock_instance, MockApi, MockQuerier, MockStorage}; - use cosmwasm_std::testing::mock_ibc_channel; + use cosmwasm_std::testing::{mock_ibc_channel, mock_ibc_packet_ack}; use cosmwasm_std::{Empty, IbcOrder}; static CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm"); @@ -289,10 +289,60 @@ mod tests { call_handle::<_, _, _, Empty>(instance, &mock_env(), &info, handle_msg.as_bytes()).unwrap(); } + const CHANNEL_ID: &str = "channel-123"; + const ACCOUNT: &str = "account-456"; + #[test] fn handshake_works() { let mut instance = mock_instance(&CONTRACT, &[]); - setup(&mut instance, "channel-123", "account-456"); + setup(&mut instance, CHANNEL_ID, ACCOUNT); + } + + #[test] + fn close_channel_works() { + let mut instance = mock_instance(&CONTRACT, &[]); + + setup(&mut instance, CHANNEL_ID, ACCOUNT); + + let handshake_close = mock_ibc_channel(CHANNEL_ID, IbcOrder::Ordered, IBC_VERSION); + call_ibc_channel_close::<_, _, _, Empty>(&mut instance, &mock_env(), &handshake_close) + .unwrap() + .unwrap(); + } + + #[test] + fn packet_ack_timeout_work() { + let mut instance = mock_instance(&CONTRACT, &[]); + + setup(&mut instance, CHANNEL_ID, ACCOUNT); + + let packet = mock_ibc_packet_ack(CHANNEL_ID, br#"{}"#).unwrap(); + let ack = IbcAcknowledgement { + acknowledgement: br#"{}"#.into(), + original_packet: packet.clone(), + }; + + call_ibc_packet_ack::<_, _, _, Empty>(&mut instance, &mock_env(), &ack) + .unwrap() + .unwrap(); + + call_ibc_packet_timeout::<_, _, _, Empty>(&mut instance, &mock_env(), &packet) + .unwrap() + .unwrap(); + } + + #[test] + fn packet_receive_timeout_work() { + let mut instance = mock_instance(&CONTRACT, &[]); + + setup(&mut instance, CHANNEL_ID, ACCOUNT); + + let who_am_i = br#"{"who_am_i":{}}"#; + let packet = mock_ibc_packet_ack(CHANNEL_ID, who_am_i).unwrap(); + + call_ibc_packet_receive::<_, _, _, Empty>(&mut instance, &mock_env(), &packet) + .unwrap() + .unwrap(); } } From bbea0a750d279ad33965358e20a6c71998954ac2 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 20 Jan 2021 16:10:45 +0100 Subject: [PATCH 09/11] Use type inference for let with &str --- contracts/ibc-reflect/src/contract.rs | 8 ++++---- contracts/ibc-reflect/tests/integration.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index de93dfd10c..775a8a452f 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -451,8 +451,8 @@ mod tests { fn handle_dispatch_packet() { let mut deps = setup(); - let channel_id: &str = "channel-123"; - let account: &str = "acct-123"; + let channel_id = "channel-123"; + let account = "acct-123"; // receive a packet for an unregistered channel returns app-level error (not Result::Err) let msgs_to_dispatch = vec![BankMsg::Send { @@ -525,8 +525,8 @@ mod tests { fn check_close_channel() { let mut deps = setup(); - let channel_id: &str = "channel-123"; - let account: &str = "acct-123"; + let channel_id = "channel-123"; + let account = "acct-123"; // register the channel connect(deps.as_mut(), channel_id, account); diff --git a/contracts/ibc-reflect/tests/integration.rs b/contracts/ibc-reflect/tests/integration.rs index f12778576a..a2f535774f 100644 --- a/contracts/ibc-reflect/tests/integration.rs +++ b/contracts/ibc-reflect/tests/integration.rs @@ -183,8 +183,8 @@ fn proper_handshake_flow() { fn handle_dispatch_packet() { let mut deps = setup(); - let channel_id: &str = "channel-123"; - let account: &str = "acct-123"; + let channel_id = "channel-123"; + let account = "acct-123"; // receive a packet for an unregistered channel returns app-level error (not Result::Err) let msgs_to_dispatch = vec![BankMsg::Send { From 1699dd6e3d7e4110c6e523abbb48c9148e18766a Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 20 Jan 2021 16:12:19 +0100 Subject: [PATCH 10/11] Remove obsolete call_init_works --- packages/vm/src/ibc_calls.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/vm/src/ibc_calls.rs b/packages/vm/src/ibc_calls.rs index fa2fa617a8..b6ded9eb56 100644 --- a/packages/vm/src/ibc_calls.rs +++ b/packages/vm/src/ibc_calls.rs @@ -243,18 +243,6 @@ mod tests { static CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm"); const IBC_VERSION: &str = "ibc-reflect"; - #[test] - fn call_init_works() { - let mut instance = mock_instance(&CONTRACT, &[]); - - // init - let info = mock_info("creator", &[]); - let msg = br#"{"reflect_code_id":77}"#; - call_init::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg) - .unwrap() - .unwrap(); - } - fn setup( instance: &mut Instance, channel_id: &str, From bd47be5747ad57f25938b7cbce12bfa29cb6e30a Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 20 Jan 2021 16:19:02 +0100 Subject: [PATCH 11/11] Make tests in ibc_calls specific to the function we test --- packages/vm/src/ibc_calls.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/vm/src/ibc_calls.rs b/packages/vm/src/ibc_calls.rs index b6ded9eb56..4be0588c1d 100644 --- a/packages/vm/src/ibc_calls.rs +++ b/packages/vm/src/ibc_calls.rs @@ -281,16 +281,15 @@ mod tests { const ACCOUNT: &str = "account-456"; #[test] - fn handshake_works() { + fn call_ibc_channel_open_and_connect_works() { let mut instance = mock_instance(&CONTRACT, &[]); setup(&mut instance, CHANNEL_ID, ACCOUNT); } #[test] - fn close_channel_works() { + fn call_ibc_channel_close_works() { let mut instance = mock_instance(&CONTRACT, &[]); - setup(&mut instance, CHANNEL_ID, ACCOUNT); let handshake_close = mock_ibc_channel(CHANNEL_ID, IbcOrder::Ordered, IBC_VERSION); @@ -300,9 +299,8 @@ mod tests { } #[test] - fn packet_ack_timeout_work() { + fn call_ibc_packet_ack_works() { let mut instance = mock_instance(&CONTRACT, &[]); - setup(&mut instance, CHANNEL_ID, ACCOUNT); let packet = mock_ibc_packet_ack(CHANNEL_ID, br#"{}"#).unwrap(); @@ -310,18 +308,24 @@ mod tests { acknowledgement: br#"{}"#.into(), original_packet: packet.clone(), }; - call_ibc_packet_ack::<_, _, _, Empty>(&mut instance, &mock_env(), &ack) .unwrap() .unwrap(); + } + #[test] + fn call_ibc_packet_timeout_works() { + let mut instance = mock_instance(&CONTRACT, &[]); + setup(&mut instance, CHANNEL_ID, ACCOUNT); + + let packet = mock_ibc_packet_ack(CHANNEL_ID, br#"{}"#).unwrap(); call_ibc_packet_timeout::<_, _, _, Empty>(&mut instance, &mock_env(), &packet) .unwrap() .unwrap(); } #[test] - fn packet_receive_timeout_work() { + fn call_ibc_packet_receive_works() { let mut instance = mock_instance(&CONTRACT, &[]); setup(&mut instance, CHANNEL_ID, ACCOUNT);