1use crate::error::ContractError;
2use cosmwasm_std::{
3 attr, entry_point, DepsMut, Env, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg,
4 IbcChannelOpenMsg, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
5 IbcReceiveResponse, StdError, StdResult,
6};
7
8use crate::relay::{ack_fail, enforce_order_and_version, on_recv_packet};
9use crate::state::{ChannelData, CHANNELS_INFO};
10
11#[entry_point]
12pub fn ibc_channel_open(
13 _deps: DepsMut,
14 _env: Env,
15 msg: IbcChannelOpenMsg,
16) -> Result<(), ContractError> {
17 enforce_order_and_version(msg.channel(), msg.counterparty_version())?;
18
19 Ok(())
20}
21
22#[entry_point]
23pub fn ibc_channel_connect(
24 deps: DepsMut,
25 env: Env,
26 msg: IbcChannelConnectMsg,
27) -> Result<IbcBasicResponse, ContractError> {
28 let channel = msg.channel();
29 enforce_order_and_version(channel, msg.counterparty_version())?;
30
31 let channel_id = &channel.endpoint.channel_id;
32 let data = ChannelData {
33 creation_time: env.block.time,
34 };
35 CHANNELS_INFO.save(deps.storage, channel_id, &data)?;
36
37 Ok(IbcBasicResponse::new()
38 .add_attribute("action", "ibc_connect")
39 .add_attribute("channel_id", channel_id))
40}
41
42#[entry_point]
43pub fn ibc_channel_close(
44 deps: DepsMut,
45 _env: Env,
46 msg: IbcChannelCloseMsg,
47) -> StdResult<IbcBasicResponse> {
48 let channel = msg.channel();
49
50 let channel_id = &channel.endpoint.channel_id;
52 CHANNELS_INFO.remove(deps.storage, channel_id);
53
54 Ok(IbcBasicResponse::new()
55 .add_attribute("action", "ibc_close")
56 .add_attribute("channel_id", channel_id))
57}
58
59#[entry_point]
60pub fn ibc_packet_receive(
61 deps: DepsMut,
62 _env: Env,
63 msg: IbcPacketReceiveMsg,
64) -> StdResult<IbcReceiveResponse> {
65 on_recv_packet(deps, &msg.packet).or_else(|err| {
66 Ok(IbcReceiveResponse::new()
67 .set_ack(ack_fail(err.to_string()))
68 .add_attributes(vec![
69 attr("action", "receive"),
70 attr("error", err.to_string()),
71 ]))
72 })
73}
74
75#[entry_point]
76pub fn ibc_packet_ack(
77 _deps: DepsMut,
78 _env: Env,
79 _msg: IbcPacketAckMsg,
80) -> StdResult<IbcBasicResponse> {
81 Err(StdError::generic_err("cannot receive acknowledgement"))
82}
83
84#[entry_point]
85pub fn ibc_packet_timeout(
86 _deps: DepsMut,
87 _env: Env,
88 _msg: IbcPacketTimeoutMsg,
89) -> StdResult<IbcBasicResponse> {
90 Err(StdError::generic_err("cannot cause a packet timeout"))
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::contract::{instantiate, query};
97 use crate::msg::{ChannelResponse, InstantiateMsg, QueryMsg};
98
99 use crate::ibc_msg::PacketMsg;
100 use crate::relay::QUERY_VERSION;
101 use cosmwasm_std::testing::{
102 mock_dependencies, mock_env, mock_ibc_channel_connect_ack, mock_ibc_channel_open_init,
103 mock_ibc_channel_open_try, mock_ibc_packet_ack, mock_ibc_packet_recv,
104 mock_ibc_packet_timeout, mock_info, MockApi, MockQuerier, MockStorage,
105 };
106 use cosmwasm_std::{from_slice, Binary, IbcAcknowledgement, IbcOrder, OwnedDeps};
107
108 const CREATOR: &str = "creator";
109
110 fn setup() -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
111 let mut deps = mock_dependencies();
112 let msg = InstantiateMsg {};
113 let info = mock_info(CREATOR, &[]);
114 let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
115 assert_eq!(0, res.messages.len());
116 deps
117 }
118
119 fn connect(mut deps: DepsMut, channel_id: &str) {
122 let handshake_open =
123 mock_ibc_channel_open_init(channel_id, IbcOrder::Unordered, QUERY_VERSION);
124 ibc_channel_open(deps.branch(), mock_env(), handshake_open).unwrap();
126
127 let handshake_connect =
129 mock_ibc_channel_connect_ack(channel_id, IbcOrder::Unordered, QUERY_VERSION);
130 let res = ibc_channel_connect(deps.branch(), mock_env(), handshake_connect).unwrap();
131
132 assert_eq!(0, res.messages.len());
133 }
134
135 #[test]
136 fn enforce_version_in_handshake() {
137 let mut deps = setup();
138
139 let wrong_order = mock_ibc_channel_open_try("channel-12", IbcOrder::Ordered, QUERY_VERSION);
140 ibc_channel_open(deps.as_mut(), mock_env(), wrong_order).unwrap_err();
141
142 let wrong_version = mock_ibc_channel_open_try("channel-12", IbcOrder::Unordered, "reflect");
143 ibc_channel_open(deps.as_mut(), mock_env(), wrong_version).unwrap_err();
144
145 let valid_handshake =
146 mock_ibc_channel_open_try("channel-12", IbcOrder::Unordered, QUERY_VERSION);
147 ibc_channel_open(deps.as_mut(), mock_env(), valid_handshake).unwrap();
148 }
149
150 #[test]
151 fn proper_handshake_flow() {
152 let mut deps = setup();
154 let channel_id = "channel-1234";
155 connect(deps.as_mut(), channel_id);
156
157 let q = QueryMsg::Channel {
159 id: channel_id.into(),
160 };
161 let r = query(deps.as_ref(), mock_env(), q).unwrap();
162 let acct: ChannelResponse = from_slice(&r).unwrap();
163 assert_eq!(true, acct.creation_time.nanos() > 0);
164
165 let q = QueryMsg::Channel {
167 id: channel_id.into(),
168 };
169 let r = query(deps.as_ref(), mock_env(), q).unwrap();
170 let acct: ChannelResponse = from_slice(&r).unwrap();
171 assert_eq!(true, acct.creation_time.nanos() > 0);
172 }
173
174 #[test]
175 fn no_ack_packet_allowed() {
176 let mut deps = setup();
177 let channel_id = "channel-1234";
178 connect(deps.as_mut(), channel_id);
179
180 let ack_msg =
181 mock_ibc_packet_ack(channel_id, b"{}", IbcAcknowledgement::new(&[1])).unwrap();
182 ibc_packet_ack(deps.as_mut(), mock_env(), ack_msg).unwrap_err();
183
184 let timeout_msg = mock_ibc_packet_timeout(channel_id, b"{}").unwrap();
185 ibc_packet_timeout(deps.as_mut(), mock_env(), timeout_msg).unwrap_err();
186 }
187
188 #[test]
189 fn rcv_query_packet() {
190 let mut deps = setup();
191 let channel_id = "channel-1234";
192 connect(deps.as_mut(), channel_id);
193
194 let packet = PacketMsg {
195 client_id: None,
196 path: "/osmosis.gamm.v1beta1.Query/SpotPrice".to_string(),
197 data: Binary::from(&[1]),
198 };
199 let rcv_msg = mock_ibc_packet_recv(channel_id, &packet).unwrap();
200 let res = ibc_packet_receive(deps.as_mut(), mock_env(), rcv_msg).unwrap();
201
202 let error = res.attributes.iter().find(|r| r.key == "error".to_string());
203 assert_eq!("error", error.unwrap().key);
205 assert_eq!(0, res.messages.len());
206 }
207}