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 4b046b8124..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 (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_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 (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_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/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index 8f901937b6..775a8a452f 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, + 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, }; use crate::msg::{ @@ -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,24 +140,54 @@ 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)], }) } #[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_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)?; + 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![] + }; + let steal_funds = !messages.is_empty(); + + Ok(IbcBasicResponse { + messages, + attributes: vec![ + attr("action", "ibc_close"), + attr("channel_id", channel_id), + attr("steal_funds", steal_funds), + ], + }) } #[entry_point] @@ -205,7 +242,7 @@ fn receive_dispatch( Ok(IbcReceiveResponse { acknowledgement, messages: vec![wasm_msg.into()], - attributes: vec![], + attributes: vec![attr("action", "receive_dispatch")], }) } @@ -218,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)] @@ -261,9 +304,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 @@ -408,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 { @@ -477,4 +520,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 = "channel-123"; + let account = "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()); + } } 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 { diff --git a/contracts/reflect/schema/handle_msg.json b/contracts/reflect/schema/handle_msg.json index 7694ac46d8..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 (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_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 (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_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 40c8ae534d..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 (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_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 (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_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 diff --git a/packages/std/src/ibc.rs b/packages/std/src/ibc.rs index e536f9d0e0..7a0c6cc67b 100644 --- a/packages/std/src/ibc.rs +++ b/packages/std/src/ibc.rs @@ -31,11 +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 timestamp (in nanoseconds) after which the packet times out. - /// at least one of timeout_height, timeout_timestamp is required + /// 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_block, timeout_timestamp is required timeout_timestamp: Option, }, /// Sends an IBC packet with given data over the existing channel. @@ -45,10 +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, - /// block timestamp (in nanoseconds) after which the packet times out. - /// at least one of timeout_height, timeout_timestamp is required + /// 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_block, timeout_timestamp is required timeout_timestamp: Option, }, /// This will close an existing channel that is owned by this contract. @@ -133,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, } } @@ -174,10 +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, - /// block timestamp (in nanoseconds) after which the packet times out. - /// at least one of timeout_height, timeout_timestamp is required + /// 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_block, timeout_timestamp is required pub timeout_timestamp: Option, } @@ -259,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, }) 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/src/ibc_calls.rs b/packages/vm/src/ibc_calls.rs index fe07a20fd4..4be0588c1d 100644 --- a/packages/vm/src/ibc_calls.rs +++ b/packages/vm/src/ibc_calls.rs @@ -231,3 +231,110 @@ 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, mock_ibc_packet_ack}; + use cosmwasm_std::{Empty, IbcOrder}; + + static CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm"); + const IBC_VERSION: &str = "ibc-reflect"; + + 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(); + } + + const CHANNEL_ID: &str = "channel-123"; + const ACCOUNT: &str = "account-456"; + + #[test] + fn call_ibc_channel_open_and_connect_works() { + let mut instance = mock_instance(&CONTRACT, &[]); + + setup(&mut instance, CHANNEL_ID, ACCOUNT); + } + + #[test] + 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); + call_ibc_channel_close::<_, _, _, Empty>(&mut instance, &mock_env(), &handshake_close) + .unwrap() + .unwrap(); + } + + #[test] + 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(); + let ack = IbcAcknowledgement { + 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 call_ibc_packet_receive_works() { + 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(); + } +} 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 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 0000000000..7813f7330d Binary files /dev/null and b/packages/vm/testdata/ibc_reflect_0.14.wasm differ