From b87385ff408ee0c0a3419710dc3d2ef6c9e977ad Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 14 Jan 2021 20:22:42 +0100 Subject: [PATCH] Integration tests for receiving packets --- contracts/ibc-reflect/src/contract.rs | 6 +- contracts/ibc-reflect/tests/integration.rs | 142 ++++++++++++++++++++- 2 files changed, 136 insertions(+), 12 deletions(-) diff --git a/contracts/ibc-reflect/src/contract.rs b/contracts/ibc-reflect/src/contract.rs index b40fa9ad2a..accda03d35 100644 --- a/contracts/ibc-reflect/src/contract.rs +++ b/contracts/ibc-reflect/src/contract.rs @@ -448,10 +448,6 @@ mod tests { // receive a packet for an unregistered channel returns app-level error (not Result::Err) let packet = mock_ibc_packet(channel_id, &ibc_msg); let res = ibc_packet_receive(deps.as_mut(), mock_env(), packet.clone()).unwrap(); - println!( - "{}", - String::from_utf8(res.acknowledgement.0.clone()).unwrap() - ); // TODO: blocked on serde-json-wasm fix // see: https://github.com/CosmWasm/serde-json-wasm/issues/27 @@ -471,7 +467,7 @@ mod tests { assert_eq!(account, contract_addr.as_str()); assert_eq!(0, send.len()); // parse the message - should callback with proper channel_id - let rmsg: ReflectHandleMsg = from_binary(&msg).unwrap(); + let rmsg: ReflectHandleMsg = from_slice(&msg).unwrap(); assert_eq!( rmsg, ReflectHandleMsg::ReflectMsg { diff --git a/contracts/ibc-reflect/tests/integration.rs b/contracts/ibc-reflect/tests/integration.rs index a83745c11f..e7ed65848e 100644 --- a/contracts/ibc-reflect/tests/integration.rs +++ b/contracts/ibc-reflect/tests/integration.rs @@ -18,20 +18,22 @@ //! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...) use cosmwasm_std::{ - ContractResult, CosmosMsg, HandleResponse, HumanAddr, IbcBasicResponse, IbcChannel, - IbcEndpoint, IbcOrder, InitResponse, WasmMsg, + coins, BankMsg, ContractResult, CosmosMsg, HandleResponse, HumanAddr, IbcBasicResponse, + IbcChannel, IbcEndpoint, IbcOrder, IbcPacket, IbcReceiveResponse, IbcTimeoutHeight, + InitResponse, WasmMsg, }; use cosmwasm_vm::testing::{ - handle, ibc_channel_connect, ibc_channel_open, init, mock_env, mock_info, mock_instance, query, - MockApi, MockQuerier, MockStorage, + handle, ibc_channel_connect, ibc_channel_open, ibc_packet_receive, init, mock_env, mock_info, + mock_instance, query, MockApi, MockQuerier, MockStorage, }; -use cosmwasm_vm::{from_slice, Instance}; +use cosmwasm_vm::{from_slice, to_vec, Instance}; use ibc_reflect::contract::IBC_VERSION; use ibc_reflect::msg::{ - AccountInfo, AccountResponse, HandleMsg, InitMsg, ListAccountsResponse, QueryMsg, - ReflectInitMsg, + AccountInfo, AccountResponse, AcknowledgementMsg, HandleMsg, InitMsg, ListAccountsResponse, + PacketMsg, QueryMsg, ReflectHandleMsg, ReflectInitMsg, }; +use serde::Serialize; // This line will test the output of cargo wasm static WASM: &[u8] = include_bytes!("../target/wasm32-unknown-unknown/release/ibc_reflect.wasm"); @@ -75,6 +77,55 @@ fn mock_channel(order: IbcOrder, version: &str) -> IbcChannel { } } +// this provides a minimal packet around custom data, which can be modified to set certain fields +// TODO: something similar should be a helper in ibc.rs +fn mock_ibc_packet(channel_id: &str, data: &T) -> IbcPacket { + IbcPacket { + data: to_vec(data).unwrap().into(), + src: IbcEndpoint { + port_id: "their-port".to_string(), + channel_id: "channel-1234".to_string(), + }, + dest: IbcEndpoint { + port_id: "our-port".to_string(), + channel_id: channel_id.into(), + }, + sequence: 27, + timeout_height: IbcTimeoutHeight { + revision_number: 1, + timeout_height: 12345678, + }, + timeout_timestamp: 0, + version: 1, + } +} + +// connect will run through the entire handshake to set up a proper connect and +// save the account (tested in detail in `proper_handshake_flow`) +fn connect>( + deps: &mut Instance, + channel_id: &str, + account: T, +) { + let account = account.into(); + // first we try to open with a valid handshake + let mut valid_handshake = mock_channel(IbcOrder::Ordered, IBC_VERSION); + valid_handshake.endpoint.channel_id = channel_id.into(); + ibc_channel_open(deps, mock_env(), valid_handshake.clone()).unwrap(); + + // then we connect (with counter-party version set) + valid_handshake.counterparty_version = Some(IBC_VERSION.to_string()); + let _: IbcBasicResponse = ibc_channel_connect(deps, mock_env(), valid_handshake).unwrap(); + + // which creates a reflect account. here we get the callback + let handle_msg = HandleMsg::InitCallback { + id: channel_id.into(), + contract_addr: account.clone(), + }; + let info = mock_info(account, &[]); + let _: HandleResponse = handle(deps, mock_env(), info, handle_msg).unwrap(); +} + #[test] fn init_works() { let mut deps = mock_instance(WASM, &[]); @@ -172,3 +223,80 @@ fn proper_handshake_flow() { let res: AccountResponse = from_slice(&raw).unwrap(); assert_eq!(res.account.unwrap(), HumanAddr::from(REFLECT_ADDR)); } + +#[test] +fn handle_packet() { + let mut deps = setup(); + + let channel_id: &str = "channel-123"; + let account: &str = "acct-123"; + + // receive a packet for an unregistered channel returns app-level error (not Result::Err) + let ibc_msg = PacketMsg { + msgs: vec![BankMsg::Send { + to_address: "my-friend".into(), + amount: coins(123456789, "uatom"), + } + .into()], + }; + let packet = mock_ibc_packet(channel_id, &ibc_msg); + let res: IbcReceiveResponse = + ibc_packet_receive(&mut deps, mock_env(), packet.clone()).unwrap(); + // we didn't dispatch anything + assert_eq!(0, res.messages.len()); + // acknowledgement is an error + let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); + assert_eq!(ack.unwrap_err(), "invalid packet"); + + // register the channel + connect(&mut deps, channel_id, account); + + // receive a packet for an unregistered channel returns app-level error (not Result::Err) + let packet = mock_ibc_packet(channel_id, &ibc_msg); + let res: IbcReceiveResponse = + ibc_packet_receive(&mut deps, mock_env(), packet.clone()).unwrap(); + println!( + "{}", + String::from_utf8(res.acknowledgement.0.clone()).unwrap() + ); + + // assert app-level success + let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); + ack.unwrap(); + + // and we dispatch the BankMsg + assert_eq!(1, res.messages.len()); + // parse the output, ensuring it matches + if let CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, + msg, + send, + }) = &res.messages[0] + { + assert_eq!(account, contract_addr.as_str()); + assert_eq!(0, send.len()); + // parse the message - should callback with proper channel_id + let rmsg: ReflectHandleMsg = from_slice(&msg).unwrap(); + assert_eq!( + rmsg, + ReflectHandleMsg::ReflectMsg { + msgs: ibc_msg.msgs.clone() + } + ); + } else { + panic!("invalid return message: {:?}", res.messages[0]); + } + + // invalid packet format on registered channel also returns app-level error + let bad_data = InitMsg { + reflect_code_id: 12345, + }; + let packet = mock_ibc_packet(channel_id, &bad_data); + let res: IbcReceiveResponse = + ibc_packet_receive(&mut deps, mock_env(), packet.clone()).unwrap(); + // we didn't dispatch anything + assert_eq!(0, res.messages.len()); + // acknowledgement is an error + let ack: AcknowledgementMsg = from_slice(&res.acknowledgement).unwrap(); + assert_eq!(ack.unwrap_err(), "invalid packet"); +}