cosmwasm_vm/
calls.rs

1use serde::de::DeserializeOwned;
2use wasmer::Value;
3
4use cosmwasm_std::{
5    ContractResult, CustomMsg, Env, IbcBasicResponse, IbcDestinationCallbackMsg,
6    IbcSourceCallbackMsg, MessageInfo, MigrateInfo, QueryResponse, Reply, Response,
7};
8
9#[cfg(any(feature = "stargate", feature = "ibc2"))]
10use cosmwasm_std::IbcReceiveResponse;
11
12#[cfg(feature = "stargate")]
13use cosmwasm_std::{
14    Ibc3ChannelOpenResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg,
15    IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
16};
17
18use crate::backend::{BackendApi, Querier, Storage};
19use crate::conversion::ref_to_u32;
20use crate::errors::{VmError, VmResult};
21use crate::instance::Instance;
22use crate::serde::{from_slice, to_vec};
23#[cfg(feature = "ibc2")]
24use cosmwasm_std::{
25    Ibc2PacketAckMsg, Ibc2PacketReceiveMsg, Ibc2PacketSendMsg, Ibc2PacketTimeoutMsg,
26};
27
28/// The limits in here protect the host from allocating an unreasonable amount of memory
29/// and copying an unreasonable amount of data.
30///
31/// A JSON deserializer would want to set the limit to a much smaller value because
32/// deserializing JSON is more expensive. As a consequence, any sane contract should hit
33/// the deserializer limit before the read limit.
34mod read_limits {
35    /// A mebi (mega binary)
36    const MI: usize = 1024 * 1024;
37    /// Max length (in bytes) of the result data from an instantiate call.
38    pub const RESULT_INSTANTIATE: usize = 64 * MI;
39    /// Max length (in bytes) of the result data from an execute call.
40    pub const RESULT_EXECUTE: usize = 64 * MI;
41    /// Max length (in bytes) of the result data from a migrate call.
42    pub const RESULT_MIGRATE: usize = 64 * MI;
43    /// Max length (in bytes) of the result data from a sudo call.
44    pub const RESULT_SUDO: usize = 64 * MI;
45    /// Max length (in bytes) of the result data from a reply call.
46    pub const RESULT_REPLY: usize = 64 * MI;
47    /// Max length (in bytes) of the result data from a query call.
48    pub const RESULT_QUERY: usize = 64 * MI;
49    /// Max length (in bytes) of the result data from a ibc_channel_open call.
50    #[cfg(feature = "stargate")]
51    pub const RESULT_IBC_CHANNEL_OPEN: usize = 64 * MI;
52    /// Max length (in bytes) of the result data from a ibc_channel_connect call.
53    #[cfg(feature = "stargate")]
54    pub const RESULT_IBC_CHANNEL_CONNECT: usize = 64 * MI;
55    /// Max length (in bytes) of the result data from a ibc_channel_close call.
56    #[cfg(feature = "stargate")]
57    pub const RESULT_IBC_CHANNEL_CLOSE: usize = 64 * MI;
58    /// Max length (in bytes) of the result data from a ibc_packet_receive call.
59    #[cfg(any(feature = "stargate", feature = "ibc2"))]
60    pub const RESULT_IBC_PACKET_RECEIVE: usize = 64 * MI;
61    /// Max length (in bytes) of the result data from a ibc_packet_ack call.
62    #[cfg(any(feature = "stargate", feature = "ibc2"))]
63    pub const RESULT_IBC_PACKET_ACK: usize = 64 * MI;
64    /// Max length (in bytes) of the result data from a ibc_packet_timeout call.
65    #[cfg(any(feature = "stargate", feature = "ibc2"))]
66    pub const RESULT_IBC_PACKET_TIMEOUT: usize = 64 * MI;
67    /// Max length (in bytes) of the result data from a ibc_source_callback call.
68    pub const RESULT_IBC_SOURCE_CALLBACK: usize = 64 * MI;
69    /// Max length (in bytes) of the result data from a ibc_destination_callback call.
70    pub const RESULT_IBC_DESTINATION_CALLBACK: usize = 64 * MI;
71    #[cfg(feature = "ibc2")]
72    pub const RESULT_IBC2_PACKET_SEND: usize = 64 * MI;
73}
74
75/// The limits for the JSON deserialization.
76///
77/// Those limits are not used when the Rust JSON deserializer is bypassed by using the
78/// public `call_*_raw` functions directly.
79mod deserialization_limits {
80    /// A kibi (kilo binary)
81    const KI: usize = 1024;
82    /// Max length (in bytes) of the result data from an instantiate call.
83    pub const RESULT_INSTANTIATE: usize = 256 * KI;
84    /// Max length (in bytes) of the result data from an execute call.
85    pub const RESULT_EXECUTE: usize = 256 * KI;
86    /// Max length (in bytes) of the result data from a migrate call.
87    pub const RESULT_MIGRATE: usize = 256 * KI;
88    /// Max length (in bytes) of the result data from a sudo call.
89    pub const RESULT_SUDO: usize = 256 * KI;
90    /// Max length (in bytes) of the result data from a reply call.
91    pub const RESULT_REPLY: usize = 256 * KI;
92    /// Max length (in bytes) of the result data from a query call.
93    pub const RESULT_QUERY: usize = 256 * KI;
94    /// Max length (in bytes) of the result data from a ibc_channel_open call.
95    #[cfg(feature = "stargate")]
96    pub const RESULT_IBC_CHANNEL_OPEN: usize = 256 * KI;
97    /// Max length (in bytes) of the result data from a ibc_channel_connect call.
98    #[cfg(feature = "stargate")]
99    pub const RESULT_IBC_CHANNEL_CONNECT: usize = 256 * KI;
100    /// Max length (in bytes) of the result data from a ibc_channel_close call.
101    #[cfg(feature = "stargate")]
102    pub const RESULT_IBC_CHANNEL_CLOSE: usize = 256 * KI;
103    /// Max length (in bytes) of the result data from a ibc_packet_receive call.
104    #[cfg(any(feature = "stargate", feature = "ibc2"))]
105    pub const RESULT_IBC_PACKET_RECEIVE: usize = 256 * KI;
106    /// Max length (in bytes) of the result data from a ibc_packet_ack call.
107    #[cfg(feature = "stargate")]
108    pub const RESULT_IBC_PACKET_ACK: usize = 256 * KI;
109    /// Max length (in bytes) of the result data from a ibc_packet_timeout call.
110    #[cfg(any(feature = "stargate", feature = "ibc2"))]
111    pub const RESULT_IBC_PACKET_TIMEOUT: usize = 256 * KI;
112    /// Max length (in bytes) of the result data from a ibc_source_callback call.
113    pub const RESULT_IBC_SOURCE_CALLBACK: usize = 256 * KI;
114    /// Max length (in bytes) of the result data from a ibc_destination_callback call.
115    pub const RESULT_IBC_DESTINATION_CALLBACK: usize = 256 * KI;
116    /// Max length (in bytes) of the result data from a ibc_packet_receive call.
117    #[cfg(feature = "ibc2")]
118    pub const RESULT_IBC2_PACKET_SEND: usize = 256 * KI;
119}
120
121pub fn call_instantiate<A, S, Q, U>(
122    instance: &mut Instance<A, S, Q>,
123    env: &Env,
124    info: &MessageInfo,
125    msg: &[u8],
126) -> VmResult<ContractResult<Response<U>>>
127where
128    A: BackendApi + 'static,
129    S: Storage + 'static,
130    Q: Querier + 'static,
131    U: DeserializeOwned + CustomMsg,
132{
133    let env = to_vec(env)?;
134    let info = to_vec(info)?;
135    let data = call_instantiate_raw(instance, &env, &info, msg)?;
136    let result: ContractResult<Response<U>> =
137        from_slice(&data, deserialization_limits::RESULT_INSTANTIATE)?;
138    Ok(result)
139}
140
141pub fn call_execute<A, S, Q, U>(
142    instance: &mut Instance<A, S, Q>,
143    env: &Env,
144    info: &MessageInfo,
145    msg: &[u8],
146) -> VmResult<ContractResult<Response<U>>>
147where
148    A: BackendApi + 'static,
149    S: Storage + 'static,
150    Q: Querier + 'static,
151    U: DeserializeOwned + CustomMsg,
152{
153    let env = to_vec(env)?;
154    let info = to_vec(info)?;
155    let data = call_execute_raw(instance, &env, &info, msg)?;
156    let result: ContractResult<Response<U>> =
157        from_slice(&data, deserialization_limits::RESULT_EXECUTE)?;
158    Ok(result)
159}
160
161pub fn call_migrate<A, S, Q, U>(
162    instance: &mut Instance<A, S, Q>,
163    env: &Env,
164    msg: &[u8],
165) -> VmResult<ContractResult<Response<U>>>
166where
167    A: BackendApi + 'static,
168    S: Storage + 'static,
169    Q: Querier + 'static,
170    U: DeserializeOwned + CustomMsg,
171{
172    let env = to_vec(env)?;
173    let data = call_migrate_raw(instance, &env, msg)?;
174    let result: ContractResult<Response<U>> =
175        from_slice(&data, deserialization_limits::RESULT_MIGRATE)?;
176    Ok(result)
177}
178
179pub fn call_migrate_with_info<A, S, Q, U>(
180    instance: &mut Instance<A, S, Q>,
181    env: &Env,
182    msg: &[u8],
183    migrate_info: &MigrateInfo,
184) -> VmResult<ContractResult<Response<U>>>
185where
186    A: BackendApi + 'static,
187    S: Storage + 'static,
188    Q: Querier + 'static,
189    U: DeserializeOwned + CustomMsg,
190{
191    let env = to_vec(env)?;
192    let migrate_info = to_vec(migrate_info)?;
193    let data = call_migrate_with_info_raw(instance, &env, msg, &migrate_info)?;
194    let result: ContractResult<Response<U>> =
195        from_slice(&data, deserialization_limits::RESULT_MIGRATE)?;
196    Ok(result)
197}
198
199pub fn call_sudo<A, S, Q, U>(
200    instance: &mut Instance<A, S, Q>,
201    env: &Env,
202    msg: &[u8],
203) -> VmResult<ContractResult<Response<U>>>
204where
205    A: BackendApi + 'static,
206    S: Storage + 'static,
207    Q: Querier + 'static,
208    U: DeserializeOwned + CustomMsg,
209{
210    let env = to_vec(env)?;
211    let data = call_sudo_raw(instance, &env, msg)?;
212    let result: ContractResult<Response<U>> =
213        from_slice(&data, deserialization_limits::RESULT_SUDO)?;
214    Ok(result)
215}
216
217pub fn call_reply<A, S, Q, U>(
218    instance: &mut Instance<A, S, Q>,
219    env: &Env,
220    msg: &Reply,
221) -> VmResult<ContractResult<Response<U>>>
222where
223    A: BackendApi + 'static,
224    S: Storage + 'static,
225    Q: Querier + 'static,
226    U: DeserializeOwned + CustomMsg,
227{
228    let env = to_vec(env)?;
229    let msg = to_vec(msg)?;
230    let data = call_reply_raw(instance, &env, &msg)?;
231    let result: ContractResult<Response<U>> =
232        from_slice(&data, deserialization_limits::RESULT_REPLY)?;
233    Ok(result)
234}
235
236pub fn call_query<A, S, Q>(
237    instance: &mut Instance<A, S, Q>,
238    env: &Env,
239    msg: &[u8],
240) -> VmResult<ContractResult<QueryResponse>>
241where
242    A: BackendApi + 'static,
243    S: Storage + 'static,
244    Q: Querier + 'static,
245{
246    let env = to_vec(env)?;
247    let data = call_query_raw(instance, &env, msg)?;
248    let result: ContractResult<QueryResponse> =
249        from_slice(&data, deserialization_limits::RESULT_QUERY)?;
250    // Ensure query response is valid JSON
251    if let ContractResult::Ok(binary_response) = &result {
252        serde_json::from_slice::<serde_json::Value>(binary_response.as_slice())
253            .map_err(|e| VmError::generic_err(format!("Query response must be valid JSON. {e}")))?;
254    }
255
256    Ok(result)
257}
258
259#[cfg(feature = "stargate")]
260pub fn call_ibc_channel_open<A, S, Q>(
261    instance: &mut Instance<A, S, Q>,
262    env: &Env,
263    msg: &IbcChannelOpenMsg,
264) -> VmResult<ContractResult<Option<Ibc3ChannelOpenResponse>>>
265where
266    A: BackendApi + 'static,
267    S: Storage + 'static,
268    Q: Querier + 'static,
269{
270    let env = to_vec(env)?;
271    let msg = to_vec(msg)?;
272    let data = call_ibc_channel_open_raw(instance, &env, &msg)?;
273    let result: ContractResult<Option<Ibc3ChannelOpenResponse>> =
274        from_slice(&data, deserialization_limits::RESULT_IBC_CHANNEL_OPEN)?;
275    Ok(result)
276}
277
278#[cfg(feature = "stargate")]
279pub fn call_ibc_channel_connect<A, S, Q, U>(
280    instance: &mut Instance<A, S, Q>,
281    env: &Env,
282    msg: &IbcChannelConnectMsg,
283) -> VmResult<ContractResult<IbcBasicResponse<U>>>
284where
285    A: BackendApi + 'static,
286    S: Storage + 'static,
287    Q: Querier + 'static,
288    U: DeserializeOwned + CustomMsg,
289{
290    let env = to_vec(env)?;
291    let msg = to_vec(msg)?;
292    let data = call_ibc_channel_connect_raw(instance, &env, &msg)?;
293    let result = from_slice(&data, deserialization_limits::RESULT_IBC_CHANNEL_CONNECT)?;
294    Ok(result)
295}
296
297#[cfg(feature = "stargate")]
298pub fn call_ibc_channel_close<A, S, Q, U>(
299    instance: &mut Instance<A, S, Q>,
300    env: &Env,
301    msg: &IbcChannelCloseMsg,
302) -> VmResult<ContractResult<IbcBasicResponse<U>>>
303where
304    A: BackendApi + 'static,
305    S: Storage + 'static,
306    Q: Querier + 'static,
307    U: DeserializeOwned + CustomMsg,
308{
309    let env = to_vec(env)?;
310    let msg = to_vec(msg)?;
311    let data = call_ibc_channel_close_raw(instance, &env, &msg)?;
312    let result = from_slice(&data, deserialization_limits::RESULT_IBC_CHANNEL_CLOSE)?;
313    Ok(result)
314}
315
316#[cfg(feature = "stargate")]
317pub fn call_ibc_packet_receive<A, S, Q, U>(
318    instance: &mut Instance<A, S, Q>,
319    env: &Env,
320    msg: &IbcPacketReceiveMsg,
321) -> VmResult<ContractResult<IbcReceiveResponse<U>>>
322where
323    A: BackendApi + 'static,
324    S: Storage + 'static,
325    Q: Querier + 'static,
326    U: DeserializeOwned + CustomMsg,
327{
328    let env = to_vec(env)?;
329    let msg = to_vec(msg)?;
330    let data = call_ibc_packet_receive_raw(instance, &env, &msg)?;
331    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_RECEIVE)?;
332    Ok(result)
333}
334
335#[cfg(feature = "stargate")]
336pub fn call_ibc_packet_ack<A, S, Q, U>(
337    instance: &mut Instance<A, S, Q>,
338    env: &Env,
339    msg: &IbcPacketAckMsg,
340) -> VmResult<ContractResult<IbcBasicResponse<U>>>
341where
342    A: BackendApi + 'static,
343    S: Storage + 'static,
344    Q: Querier + 'static,
345    U: DeserializeOwned + CustomMsg,
346{
347    let env = to_vec(env)?;
348    let msg = to_vec(msg)?;
349    let data = call_ibc_packet_ack_raw(instance, &env, &msg)?;
350    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_ACK)?;
351    Ok(result)
352}
353
354#[cfg(feature = "stargate")]
355pub fn call_ibc_packet_timeout<A, S, Q, U>(
356    instance: &mut Instance<A, S, Q>,
357    env: &Env,
358    msg: &IbcPacketTimeoutMsg,
359) -> VmResult<ContractResult<IbcBasicResponse<U>>>
360where
361    A: BackendApi + 'static,
362    S: Storage + 'static,
363    Q: Querier + 'static,
364    U: DeserializeOwned + CustomMsg,
365{
366    let env = to_vec(env)?;
367    let msg = to_vec(msg)?;
368    let data = call_ibc_packet_timeout_raw(instance, &env, &msg)?;
369    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_TIMEOUT)?;
370    Ok(result)
371}
372
373pub fn call_ibc_source_callback<A, S, Q, U>(
374    instance: &mut Instance<A, S, Q>,
375    env: &Env,
376    msg: &IbcSourceCallbackMsg,
377) -> VmResult<ContractResult<IbcBasicResponse<U>>>
378where
379    A: BackendApi + 'static,
380    S: Storage + 'static,
381    Q: Querier + 'static,
382    U: DeserializeOwned + CustomMsg,
383{
384    let env = to_vec(env)?;
385    let msg = to_vec(msg)?;
386    let data = call_ibc_source_callback_raw(instance, &env, &msg)?;
387    let result = from_slice(&data, deserialization_limits::RESULT_IBC_SOURCE_CALLBACK)?;
388    Ok(result)
389}
390
391pub fn call_ibc_destination_callback<A, S, Q, U>(
392    instance: &mut Instance<A, S, Q>,
393    env: &Env,
394    msg: &IbcDestinationCallbackMsg,
395) -> VmResult<ContractResult<IbcBasicResponse<U>>>
396where
397    A: BackendApi + 'static,
398    S: Storage + 'static,
399    Q: Querier + 'static,
400    U: DeserializeOwned + CustomMsg,
401{
402    let env = to_vec(env)?;
403    let msg = to_vec(msg)?;
404    let data = call_ibc_destination_callback_raw(instance, &env, &msg)?;
405    let result = from_slice(
406        &data,
407        deserialization_limits::RESULT_IBC_DESTINATION_CALLBACK,
408    )?;
409    Ok(result)
410}
411
412/// Calls Wasm export "instantiate" and returns raw data from the contract.
413/// The result is length limited to prevent abuse but otherwise unchecked.
414pub fn call_instantiate_raw<A, S, Q>(
415    instance: &mut Instance<A, S, Q>,
416    env: &[u8],
417    info: &[u8],
418    msg: &[u8],
419) -> VmResult<Vec<u8>>
420where
421    A: BackendApi + 'static,
422    S: Storage + 'static,
423    Q: Querier + 'static,
424{
425    instance.set_storage_readonly(false);
426    call_raw(
427        instance,
428        "instantiate",
429        &[env, info, msg],
430        read_limits::RESULT_INSTANTIATE,
431    )
432}
433
434/// Calls Wasm export "execute" and returns raw data from the contract.
435/// The result is length limited to prevent abuse but otherwise unchecked.
436pub fn call_execute_raw<A, S, Q>(
437    instance: &mut Instance<A, S, Q>,
438    env: &[u8],
439    info: &[u8],
440    msg: &[u8],
441) -> VmResult<Vec<u8>>
442where
443    A: BackendApi + 'static,
444    S: Storage + 'static,
445    Q: Querier + 'static,
446{
447    instance.set_storage_readonly(false);
448    call_raw(
449        instance,
450        "execute",
451        &[env, info, msg],
452        read_limits::RESULT_EXECUTE,
453    )
454}
455
456/// Calls Wasm export "migrate" and returns raw data from the contract.
457/// The result is length limited to prevent abuse but otherwise unchecked.
458pub fn call_migrate_raw<A, S, Q>(
459    instance: &mut Instance<A, S, Q>,
460    env: &[u8],
461    msg: &[u8],
462) -> VmResult<Vec<u8>>
463where
464    A: BackendApi + 'static,
465    S: Storage + 'static,
466    Q: Querier + 'static,
467{
468    instance.set_storage_readonly(false);
469    call_raw(
470        instance,
471        "migrate",
472        &[env, msg],
473        read_limits::RESULT_MIGRATE,
474    )
475}
476
477/// Calls Wasm export "migrate" and returns raw data from the contract.
478/// The result is length limited to prevent abuse but otherwise unchecked.
479/// The difference between this function and [call_migrate_raw] is the
480/// additional argument - `migrate_info`. It contains additional data
481/// related to the contract's migration procedure.
482///
483/// It is safe to call this method instead of [call_migrate_raw] even
484/// if a contract contains the migrate entrypoint without `migrate_info`.
485/// In such case this structure is omitted.
486pub fn call_migrate_with_info_raw<A, S, Q>(
487    instance: &mut Instance<A, S, Q>,
488    env: &[u8],
489    msg: &[u8],
490    migrate_info: &[u8],
491) -> VmResult<Vec<u8>>
492where
493    A: BackendApi + 'static,
494    S: Storage + 'static,
495    Q: Querier + 'static,
496{
497    instance.set_storage_readonly(false);
498    call_raw(
499        instance,
500        "migrate",
501        &[env, msg, migrate_info],
502        read_limits::RESULT_MIGRATE,
503    )
504    .or_else(|err| {
505        if matches!(err, VmError::FunctionArityMismatch { .. }) {
506            call_raw(
507                instance,
508                "migrate",
509                &[env, msg],
510                read_limits::RESULT_MIGRATE,
511            )
512        } else {
513            Err(err)
514        }
515    })
516}
517
518/// Calls Wasm export "sudo" and returns raw data from the contract.
519/// The result is length limited to prevent abuse but otherwise unchecked.
520pub fn call_sudo_raw<A, S, Q>(
521    instance: &mut Instance<A, S, Q>,
522    env: &[u8],
523    msg: &[u8],
524) -> VmResult<Vec<u8>>
525where
526    A: BackendApi + 'static,
527    S: Storage + 'static,
528    Q: Querier + 'static,
529{
530    instance.set_storage_readonly(false);
531    call_raw(instance, "sudo", &[env, msg], read_limits::RESULT_SUDO)
532}
533
534/// Calls Wasm export "reply" and returns raw data from the contract.
535/// The result is length limited to prevent abuse but otherwise unchecked.
536pub fn call_reply_raw<A, S, Q>(
537    instance: &mut Instance<A, S, Q>,
538    env: &[u8],
539    msg: &[u8],
540) -> VmResult<Vec<u8>>
541where
542    A: BackendApi + 'static,
543    S: Storage + 'static,
544    Q: Querier + 'static,
545{
546    instance.set_storage_readonly(false);
547    call_raw(instance, "reply", &[env, msg], read_limits::RESULT_REPLY)
548}
549
550/// Calls Wasm export "query" and returns raw data from the contract.
551/// The result is length limited to prevent abuse but otherwise unchecked.
552pub fn call_query_raw<A, S, Q>(
553    instance: &mut Instance<A, S, Q>,
554    env: &[u8],
555    msg: &[u8],
556) -> VmResult<Vec<u8>>
557where
558    A: BackendApi + 'static,
559    S: Storage + 'static,
560    Q: Querier + 'static,
561{
562    instance.set_storage_readonly(true);
563    call_raw(instance, "query", &[env, msg], read_limits::RESULT_QUERY)
564}
565
566#[cfg(feature = "stargate")]
567pub fn call_ibc_channel_open_raw<A, S, Q>(
568    instance: &mut Instance<A, S, Q>,
569    env: &[u8],
570    msg: &[u8],
571) -> VmResult<Vec<u8>>
572where
573    A: BackendApi + 'static,
574    S: Storage + 'static,
575    Q: Querier + 'static,
576{
577    instance.set_storage_readonly(false);
578    call_raw(
579        instance,
580        "ibc_channel_open",
581        &[env, msg],
582        read_limits::RESULT_IBC_CHANNEL_OPEN,
583    )
584}
585
586#[cfg(feature = "stargate")]
587pub fn call_ibc_channel_connect_raw<A, S, Q>(
588    instance: &mut Instance<A, S, Q>,
589    env: &[u8],
590    msg: &[u8],
591) -> VmResult<Vec<u8>>
592where
593    A: BackendApi + 'static,
594    S: Storage + 'static,
595    Q: Querier + 'static,
596{
597    instance.set_storage_readonly(false);
598    call_raw(
599        instance,
600        "ibc_channel_connect",
601        &[env, msg],
602        read_limits::RESULT_IBC_CHANNEL_CONNECT,
603    )
604}
605
606#[cfg(feature = "stargate")]
607pub fn call_ibc_channel_close_raw<A, S, Q>(
608    instance: &mut Instance<A, S, Q>,
609    env: &[u8],
610    msg: &[u8],
611) -> VmResult<Vec<u8>>
612where
613    A: BackendApi + 'static,
614    S: Storage + 'static,
615    Q: Querier + 'static,
616{
617    instance.set_storage_readonly(false);
618    call_raw(
619        instance,
620        "ibc_channel_close",
621        &[env, msg],
622        read_limits::RESULT_IBC_CHANNEL_CLOSE,
623    )
624}
625
626#[cfg(feature = "stargate")]
627pub fn call_ibc_packet_receive_raw<A, S, Q>(
628    instance: &mut Instance<A, S, Q>,
629    env: &[u8],
630    msg: &[u8],
631) -> VmResult<Vec<u8>>
632where
633    A: BackendApi + 'static,
634    S: Storage + 'static,
635    Q: Querier + 'static,
636{
637    instance.set_storage_readonly(false);
638    call_raw(
639        instance,
640        "ibc_packet_receive",
641        &[env, msg],
642        read_limits::RESULT_IBC_PACKET_RECEIVE,
643    )
644}
645
646#[cfg(feature = "stargate")]
647pub fn call_ibc_packet_ack_raw<A, S, Q>(
648    instance: &mut Instance<A, S, Q>,
649    env: &[u8],
650    msg: &[u8],
651) -> VmResult<Vec<u8>>
652where
653    A: BackendApi + 'static,
654    S: Storage + 'static,
655    Q: Querier + 'static,
656{
657    instance.set_storage_readonly(false);
658    call_raw(
659        instance,
660        "ibc_packet_ack",
661        &[env, msg],
662        read_limits::RESULT_IBC_PACKET_ACK,
663    )
664}
665
666#[cfg(feature = "stargate")]
667pub fn call_ibc_packet_timeout_raw<A, S, Q>(
668    instance: &mut Instance<A, S, Q>,
669    env: &[u8],
670    msg: &[u8],
671) -> VmResult<Vec<u8>>
672where
673    A: BackendApi + 'static,
674    S: Storage + 'static,
675    Q: Querier + 'static,
676{
677    instance.set_storage_readonly(false);
678    call_raw(
679        instance,
680        "ibc_packet_timeout",
681        &[env, msg],
682        read_limits::RESULT_IBC_PACKET_TIMEOUT,
683    )
684}
685
686#[cfg(feature = "ibc2")]
687pub fn call_ibc2_packet_ack<A, S, Q, U>(
688    instance: &mut Instance<A, S, Q>,
689    env: &Env,
690    msg: &Ibc2PacketAckMsg,
691) -> VmResult<ContractResult<IbcBasicResponse<U>>>
692where
693    A: BackendApi + 'static,
694    S: Storage + 'static,
695    Q: Querier + 'static,
696    U: DeserializeOwned + CustomMsg,
697{
698    let env = to_vec(env)?;
699    let msg = to_vec(msg)?;
700    let data = call_ibc2_packet_ack_raw(instance, &env, &msg)?;
701    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_ACK)?;
702    Ok(result)
703}
704
705#[cfg(feature = "ibc2")]
706pub fn call_ibc2_packet_ack_raw<A, S, Q>(
707    instance: &mut Instance<A, S, Q>,
708    env: &[u8],
709    msg: &[u8],
710) -> VmResult<Vec<u8>>
711where
712    A: BackendApi + 'static,
713    S: Storage + 'static,
714    Q: Querier + 'static,
715{
716    instance.set_storage_readonly(false);
717    call_raw(
718        instance,
719        "ibc2_packet_ack",
720        &[env, msg],
721        read_limits::RESULT_IBC_PACKET_ACK,
722    )
723}
724
725#[cfg(feature = "ibc2")]
726pub fn call_ibc2_packet_receive_raw<A, S, Q>(
727    instance: &mut Instance<A, S, Q>,
728    env: &[u8],
729    msg: &[u8],
730) -> VmResult<Vec<u8>>
731where
732    A: BackendApi + 'static,
733    S: Storage + 'static,
734    Q: Querier + 'static,
735{
736    instance.set_storage_readonly(false);
737    call_raw(
738        instance,
739        "ibc2_packet_receive",
740        &[env, msg],
741        read_limits::RESULT_IBC_PACKET_RECEIVE,
742    )
743}
744
745#[cfg(feature = "ibc2")]
746pub fn call_ibc2_packet_timeout<A, S, Q, U>(
747    instance: &mut Instance<A, S, Q>,
748    env: &Env,
749    msg: &Ibc2PacketTimeoutMsg,
750) -> VmResult<ContractResult<IbcBasicResponse<U>>>
751where
752    A: BackendApi + 'static,
753    S: Storage + 'static,
754    Q: Querier + 'static,
755    U: DeserializeOwned + CustomMsg,
756{
757    let env = to_vec(env)?;
758    let msg = to_vec(msg)?;
759    let data = call_ibc2_packet_timeout_raw(instance, &env, &msg)?;
760    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_TIMEOUT)?;
761    Ok(result)
762}
763
764#[cfg(feature = "ibc2")]
765pub fn call_ibc2_packet_timeout_raw<A, S, Q>(
766    instance: &mut Instance<A, S, Q>,
767    env: &[u8],
768    msg: &[u8],
769) -> VmResult<Vec<u8>>
770where
771    A: BackendApi + 'static,
772    S: Storage + 'static,
773    Q: Querier + 'static,
774{
775    instance.set_storage_readonly(false);
776    call_raw(
777        instance,
778        "ibc2_packet_timeout",
779        &[env, msg],
780        read_limits::RESULT_IBC_PACKET_TIMEOUT,
781    )
782}
783
784#[cfg(feature = "ibc2")]
785pub fn call_ibc2_packet_send_raw<A, S, Q>(
786    instance: &mut Instance<A, S, Q>,
787    env: &[u8],
788    msg: &[u8],
789) -> VmResult<Vec<u8>>
790where
791    A: BackendApi + 'static,
792    S: Storage + 'static,
793    Q: Querier + 'static,
794{
795    instance.set_storage_readonly(false);
796    call_raw(
797        instance,
798        "ibc2_packet_send",
799        &[env, msg],
800        read_limits::RESULT_IBC2_PACKET_SEND,
801    )
802}
803
804#[cfg(feature = "ibc2")]
805pub fn call_ibc2_packet_send<A, S, Q, U>(
806    instance: &mut Instance<A, S, Q>,
807    env: &Env,
808    msg: &Ibc2PacketSendMsg,
809) -> VmResult<ContractResult<IbcBasicResponse<U>>>
810where
811    A: BackendApi + 'static,
812    S: Storage + 'static,
813    Q: Querier + 'static,
814    U: DeserializeOwned + CustomMsg,
815{
816    let env = to_vec(env)?;
817    let msg = to_vec(msg)?;
818    let data = call_ibc2_packet_send_raw(instance, &env, &msg)?;
819    let result = from_slice(&data, deserialization_limits::RESULT_IBC2_PACKET_SEND)?;
820    Ok(result)
821}
822
823pub fn call_ibc_source_callback_raw<A, S, Q>(
824    instance: &mut Instance<A, S, Q>,
825    env: &[u8],
826    msg: &[u8],
827) -> VmResult<Vec<u8>>
828where
829    A: BackendApi + 'static,
830    S: Storage + 'static,
831    Q: Querier + 'static,
832{
833    instance.set_storage_readonly(false);
834    call_raw(
835        instance,
836        "ibc_source_callback",
837        &[env, msg],
838        read_limits::RESULT_IBC_SOURCE_CALLBACK,
839    )
840}
841
842pub fn call_ibc_destination_callback_raw<A, S, Q>(
843    instance: &mut Instance<A, S, Q>,
844    env: &[u8],
845    msg: &[u8],
846) -> VmResult<Vec<u8>>
847where
848    A: BackendApi + 'static,
849    S: Storage + 'static,
850    Q: Querier + 'static,
851{
852    instance.set_storage_readonly(false);
853    call_raw(
854        instance,
855        "ibc_destination_callback",
856        &[env, msg],
857        read_limits::RESULT_IBC_DESTINATION_CALLBACK,
858    )
859}
860
861/// Calls a function with the given arguments.
862/// The exported function must return exactly one result (an offset to the result Region).
863pub(crate) fn call_raw<A, S, Q>(
864    instance: &mut Instance<A, S, Q>,
865    name: &str,
866    args: &[&[u8]],
867    result_max_length: usize,
868) -> VmResult<Vec<u8>>
869where
870    A: BackendApi + 'static,
871    S: Storage + 'static,
872    Q: Querier + 'static,
873{
874    let mut arg_region_ptrs = Vec::<Value>::with_capacity(args.len());
875    for arg in args {
876        let region_ptr = instance.allocate(arg.len())?;
877        instance.write_memory(region_ptr, arg)?;
878        arg_region_ptrs.push(region_ptr.into());
879    }
880    let result = instance.call_function1(name, &arg_region_ptrs)?;
881    let res_region_ptr = ref_to_u32(&result)?;
882    let data = instance.read_memory(res_region_ptr, result_max_length)?;
883    // free return value in wasm (arguments were freed in wasm code)
884    instance.deallocate(res_region_ptr)?;
885    Ok(data)
886}
887
888#[cfg(feature = "ibc2")]
889pub fn call_ibc2_packet_receive<A, S, Q, U>(
890    instance: &mut Instance<A, S, Q>,
891    env: &Env,
892    msg: &Ibc2PacketReceiveMsg,
893) -> VmResult<ContractResult<IbcReceiveResponse<U>>>
894where
895    A: BackendApi + 'static,
896    S: Storage + 'static,
897    Q: Querier + 'static,
898    U: DeserializeOwned + CustomMsg,
899{
900    let env = to_vec(env)?;
901    let msg = to_vec(msg)?;
902    let data = call_ibc2_packet_receive_raw(instance, &env, &msg)?;
903    let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_RECEIVE)?;
904    Ok(result)
905}
906
907#[cfg(test)]
908mod tests {
909    use super::*;
910    use crate::testing::{
911        mock_env, mock_info, mock_instance, mock_instance_with_options, MockInstanceOptions,
912    };
913    use cosmwasm_std::{coins, from_json, to_json_string, Addr, Empty};
914    use sha2::{Digest, Sha256};
915
916    static HACKATOM: &[u8] = include_bytes!("../testdata/hackatom.wasm");
917    static HACKATOM_1_3: &[u8] = include_bytes!("../testdata/hackatom_1.3.wasm");
918    static CYBERPUNK: &[u8] = include_bytes!("../testdata/cyberpunk.wasm");
919    static FLOATY2: &[u8] = include_bytes!("../testdata/floaty_2.0.wasm");
920    static EMPTY: &[u8] = include_bytes!("../testdata/empty.wasm");
921
922    #[test]
923    fn call_instantiate_works() {
924        let mut instance = mock_instance(HACKATOM, &[]);
925
926        // init
927        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
928        let verifier = instance.api().addr_make("verifies");
929        let beneficiary = instance.api().addr_make("benefits");
930        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
931        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
932            .unwrap()
933            .unwrap();
934    }
935
936    #[test]
937    fn call_instantiate_handles_missing_export() {
938        let mut deps = mock_instance(EMPTY, &[]);
939
940        let msg = Empty {};
941        let info = mock_info("creator", &coins(1000, "earth"));
942
943        let serialized_msg = to_vec(&msg).unwrap();
944        let err =
945            call_instantiate::<_, _, _, Empty>(&mut deps, &mock_env(), &info, &serialized_msg)
946                .unwrap_err();
947
948        assert!(matches!(
949            err,
950            VmError::ResolveErr {
951                msg,
952                ..
953            }
954            if msg == "Could not get export: Missing export instantiate"
955        ));
956    }
957
958    #[test]
959    fn call_execute_works() {
960        let mut instance = mock_instance(HACKATOM, &[]);
961
962        // init
963        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
964        let verifier = instance.api().addr_make("verifies");
965        let beneficiary = instance.api().addr_make("benefits");
966        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
967        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
968            .unwrap()
969            .unwrap();
970
971        // execute
972        let info = mock_info(&verifier, &coins(15, "earth"));
973        let msg = br#"{"release":{"denom":"earth"}}"#;
974        call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg)
975            .unwrap()
976            .unwrap();
977    }
978
979    #[test]
980    fn call_execute_runs_out_of_gas() {
981        let mut instance = mock_instance(CYBERPUNK, &[]);
982
983        // init
984        let info = mock_info("creator", &[]);
985        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
986            .unwrap()
987            .unwrap();
988
989        // execute
990        let info = mock_info("looper", &[]);
991        let msg = br#"{"cpu_loop":{}}"#;
992        let err =
993            call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg).unwrap_err();
994        assert!(matches!(err, VmError::GasDepletion { .. }));
995    }
996
997    #[test]
998    fn call_execute_handles_panic() {
999        let mut instance = mock_instance(CYBERPUNK, &[]);
1000
1001        let info = mock_info("creator", &[]);
1002        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
1003            .unwrap()
1004            .unwrap();
1005
1006        // execute
1007        let info = mock_info("troll", &[]);
1008        let msg = br#"{"panic":{}}"#;
1009        let err =
1010            call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg).unwrap_err();
1011        match err {
1012            VmError::RuntimeErr { msg, .. } => {
1013                assert!(
1014                    msg.contains("RuntimeError: Aborted: panicked at src/contract.rs:"),
1015                    "Unexpected error msg: {msg}"
1016                )
1017            }
1018            err => panic!("Unexpected error: {err:?}"),
1019        }
1020    }
1021
1022    #[test]
1023    fn call_execute_handles_unreachable() {
1024        let mut instance = mock_instance(CYBERPUNK, &[]);
1025
1026        let info = mock_info("creator", &[]);
1027        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
1028            .unwrap()
1029            .unwrap();
1030
1031        // execute
1032        let info = mock_info("troll", &[]);
1033        let msg = br#"{"unreachable":{}}"#;
1034        let err =
1035            call_execute::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg).unwrap_err();
1036        match err {
1037            VmError::RuntimeErr { msg, .. } => {
1038                assert!(msg.contains("RuntimeError: unreachable"))
1039            }
1040            err => panic!("Unexpected error: {err:?}"),
1041        }
1042    }
1043
1044    #[test]
1045    fn call_migrate_works() {
1046        let mut instance = mock_instance(
1047            HACKATOM_1_3, // only old version of hackatom supports classic migrate signature
1048            &[],
1049        );
1050
1051        // init
1052        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1053        let verifier = instance.api().addr_make("verifies");
1054        let beneficiary = instance.api().addr_make("benefits");
1055        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1056        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1057            .unwrap()
1058            .unwrap();
1059
1060        // change the verifier via migrate
1061        let someone_else = instance.api().addr_make("someone else");
1062        let msg = format!(r#"{{"verifier": "{someone_else}"}}"#);
1063        let _res = call_migrate::<_, _, _, Empty>(&mut instance, &mock_env(), msg.as_bytes())
1064            .unwrap()
1065            .unwrap();
1066
1067        // query the new_verifier with verifier
1068        let msg = br#"{"verifier":{}}"#;
1069        let contract_result = call_query(&mut instance, &mock_env(), msg).unwrap();
1070        let query_response = contract_result.unwrap();
1071        assert_eq!(
1072            query_response,
1073            format!(r#"{{"verifier":"{}"}}"#, someone_else).as_bytes(),
1074        );
1075    }
1076
1077    #[test]
1078    fn call_migrate_with_info_works_for_classic_migrate_signature() {
1079        let mut instance = mock_instance(
1080            HACKATOM_1_3, // we test again the old version of hackatom which did not implement the migrate info argument
1081            &[],
1082        );
1083
1084        // init
1085        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1086        let verifier = instance.api().addr_make("verifies");
1087        let beneficiary = instance.api().addr_make("benefits");
1088        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1089        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1090            .unwrap()
1091            .unwrap();
1092
1093        // change the verifier via migrate
1094        let someone_else = instance.api().addr_make("someone else");
1095        let msg = format!(r#"{{"verifier": "{someone_else}"}}"#);
1096        let migrate_info = MigrateInfo {
1097            sender: Addr::unchecked(someone_else.clone()),
1098            old_migrate_version: Some(33),
1099        };
1100        let _res = call_migrate_with_info::<_, _, _, Empty>(
1101            &mut instance,
1102            &mock_env(),
1103            msg.as_bytes(),
1104            &migrate_info,
1105        )
1106        .unwrap()
1107        .unwrap();
1108
1109        // query the new_verifier with verifier
1110        let msg = br#"{"verifier":{}}"#;
1111        let contract_result = call_query(&mut instance, &mock_env(), msg).unwrap();
1112        let query_response = contract_result.unwrap();
1113        assert_eq!(
1114            query_response,
1115            format!(r#"{{"verifier":"{}"}}"#, someone_else).as_bytes(),
1116        );
1117    }
1118
1119    #[test]
1120    fn call_query_works() {
1121        let mut instance = mock_instance(HACKATOM, &[]);
1122
1123        // init
1124        let info = mock_info(&instance.api().addr_make("creator"), &coins(1000, "earth"));
1125        let verifier = instance.api().addr_make("verifies");
1126        let beneficiary = instance.api().addr_make("benefits");
1127        let msg = format!(r#"{{"verifier": "{verifier}", "beneficiary": "{beneficiary}"}}"#);
1128        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, msg.as_bytes())
1129            .unwrap()
1130            .unwrap();
1131
1132        // query
1133        let msg = br#"{"verifier":{}}"#;
1134        let contract_result = call_query(&mut instance, &mock_env(), msg).unwrap();
1135        let query_response = contract_result.unwrap();
1136        assert_eq!(
1137            query_response,
1138            format!("{{\"verifier\":\"{verifier}\"}}").as_bytes()
1139        );
1140    }
1141
1142    #[test]
1143    fn float_instrs_are_deterministic() {
1144        #[derive(Debug, serde::Serialize, serde::Deserialize)]
1145        #[serde(rename_all = "snake_case")]
1146        pub enum Value {
1147            U32(u32),
1148            U64(u64),
1149            F32(u32),
1150            F64(u64),
1151        }
1152
1153        let mut instance = mock_instance_with_options(
1154            FLOATY2,
1155            MockInstanceOptions {
1156                gas_limit: u64::MAX,
1157                memory_limit: None,
1158                ..Default::default()
1159            },
1160        );
1161
1162        // init
1163        let info = mock_info("creator", &[]);
1164        call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
1165            .unwrap()
1166            .unwrap();
1167
1168        // query instructions
1169        let msg = br#"{"instructions":{}}"#;
1170        let contract_result = call_query(&mut instance, &mock_env(), msg)
1171            .unwrap()
1172            .unwrap();
1173        let instructions: Vec<String> = from_json(contract_result).unwrap();
1174        // little sanity check
1175        assert_eq!(instructions.len(), 70);
1176
1177        const RUNS_PER_INSTRUCTION: u64 = 150;
1178        let mut hasher = Sha256::new();
1179        for instr in &instructions {
1180            for seed in 0..RUNS_PER_INSTRUCTION {
1181                // query some input values for the instruction
1182                let args: Vec<Value> = from_json(
1183                    call_query(
1184                        &mut instance,
1185                        &mock_env(),
1186                        format!(
1187                            r#"{{"random_args_for":{{ "instruction": "{instr}", "seed": {seed}}}}}"#
1188                        )
1189                        .as_bytes(),
1190                    )
1191                    .unwrap()
1192                    .unwrap(),
1193                )
1194                .unwrap();
1195
1196                // build the run message
1197                let args = to_json_string(&args).unwrap();
1198                let msg: String = format!(
1199                    r#"{{"run":{{
1200                        "instruction": "{instr}",
1201                        "args": {args}
1202                    }}}}"#
1203                );
1204                // run the instruction
1205                // this might throw a runtime error (e.g. if the instruction traps)
1206                let result = match call_query(&mut instance, &mock_env(), msg.as_bytes()) {
1207                    Ok(ContractResult::Ok(r)) => format!("{:?}", from_json::<Value>(&r).unwrap()),
1208                    Err(VmError::RuntimeErr { msg, .. }) => msg,
1209                    e => panic!("unexpected error: {e:?}"),
1210                };
1211                // add the result to the hash
1212                hasher.update(format!("{instr}{seed}{result}").as_bytes());
1213            }
1214        }
1215        let hash = Digest::finalize(hasher);
1216        assert_eq!(
1217            hex::encode(hash.as_slice()),
1218            "95f70fa6451176ab04a9594417a047a1e4d8e2ff809609b8f81099496bee2393"
1219        );
1220    }
1221
1222    #[cfg(feature = "stargate")]
1223    mod ibc {
1224        use super::*;
1225        use crate::testing::{MockApi, MockQuerier, MockStorage};
1226        use cosmwasm_std::testing::mock_ibc_packet_timeout;
1227        use cosmwasm_std::testing::{
1228            mock_ibc_channel_close_init, mock_ibc_channel_connect_ack, mock_ibc_channel_open_init,
1229            mock_ibc_packet_ack, mock_ibc_packet_recv, mock_wasmd_attr,
1230        };
1231        use cosmwasm_std::{
1232            Event, IbcAckCallbackMsg, IbcAcknowledgement, IbcOrder, IbcTimeoutCallbackMsg, ReplyOn,
1233            SubMsgResponse, SubMsgResult,
1234        };
1235        static IBC_REFLECT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm");
1236        static IBC_CALLBACKS: &[u8] = include_bytes!("../testdata/ibc_callbacks.wasm");
1237        const IBC_VERSION: &str = "ibc-reflect-v1";
1238
1239        fn setup(
1240            instance: &mut Instance<MockApi, MockStorage, MockQuerier>,
1241            channel_id: &str,
1242            account: &str,
1243        ) {
1244            // init
1245            let info = mock_info("creator", &[]);
1246            let msg = br#"{"reflect_code_id":77}"#;
1247            call_instantiate::<_, _, _, Empty>(instance, &mock_env(), &info, msg)
1248                .unwrap()
1249                .unwrap();
1250            // first we try to open with a valid handshake
1251            let handshake_open =
1252                mock_ibc_channel_open_init(channel_id, IbcOrder::Ordered, IBC_VERSION);
1253            call_ibc_channel_open(instance, &mock_env(), &handshake_open)
1254                .unwrap()
1255                .unwrap();
1256            // then we connect (with counter-party version set)
1257            let handshake_connect =
1258                mock_ibc_channel_connect_ack(channel_id, IbcOrder::Ordered, IBC_VERSION);
1259            let res: IbcBasicResponse = call_ibc_channel_connect::<_, _, _, Empty>(
1260                instance,
1261                &mock_env(),
1262                &handshake_connect,
1263            )
1264            .unwrap()
1265            .unwrap();
1266            assert_eq!(1, res.messages.len());
1267            assert_eq!(
1268                res.events,
1269                [Event::new("ibc").add_attribute("channel", "connect")]
1270            );
1271            assert_eq!(ReplyOn::Success, res.messages[0].reply_on);
1272            let id = res.messages[0].id;
1273            let payload = res.messages[0].payload.clone();
1274            let event = Event::new("instantiate").add_attributes(vec![
1275                // We have to force this one to avoid the debug assertion against _
1276                mock_wasmd_attr("_contract_address", account),
1277            ]);
1278            // which creates a reflect account. here we get the callback
1279            #[allow(deprecated)]
1280            let response = Reply {
1281                id,
1282                payload,
1283                gas_used: 1234567,
1284                result: SubMsgResult::Ok(SubMsgResponse {
1285                    events: vec![event],
1286                    msg_responses: vec![],
1287                    data: None,
1288                }),
1289            };
1290            call_reply::<_, _, _, Empty>(instance, &mock_env(), &response).unwrap();
1291        }
1292
1293        const CHANNEL_ID: &str = "channel-123";
1294        const ACCOUNT: &str = "account-456";
1295
1296        #[test]
1297        fn call_ibc_channel_open_and_connect_works() {
1298            let mut instance = mock_instance(IBC_REFLECT, &[]);
1299            setup(&mut instance, CHANNEL_ID, ACCOUNT);
1300        }
1301
1302        #[test]
1303        fn call_ibc_channel_close_works() {
1304            let mut instance = mock_instance(IBC_REFLECT, &[]);
1305            let account = instance.api().addr_make(ACCOUNT);
1306            setup(&mut instance, CHANNEL_ID, &account);
1307            let handshake_close =
1308                mock_ibc_channel_close_init(CHANNEL_ID, IbcOrder::Ordered, IBC_VERSION);
1309            call_ibc_channel_close::<_, _, _, Empty>(&mut instance, &mock_env(), &handshake_close)
1310                .unwrap()
1311                .unwrap();
1312        }
1313
1314        #[test]
1315        fn call_ibc_packet_ack_works() {
1316            let mut instance = mock_instance(IBC_REFLECT, &[]);
1317            setup(&mut instance, CHANNEL_ID, ACCOUNT);
1318            let ack = IbcAcknowledgement::new(br#"{}"#);
1319            let msg = mock_ibc_packet_ack(CHANNEL_ID, br#"{}"#, ack).unwrap();
1320            call_ibc_packet_ack::<_, _, _, Empty>(&mut instance, &mock_env(), &msg)
1321                .unwrap()
1322                .unwrap();
1323        }
1324
1325        #[test]
1326        fn call_ibc_packet_timeout_works() {
1327            let mut instance = mock_instance(IBC_REFLECT, &[]);
1328            setup(&mut instance, CHANNEL_ID, ACCOUNT);
1329            let msg = mock_ibc_packet_timeout(CHANNEL_ID, br#"{}"#).unwrap();
1330            call_ibc_packet_timeout::<_, _, _, Empty>(&mut instance, &mock_env(), &msg)
1331                .unwrap()
1332                .unwrap();
1333        }
1334
1335        #[test]
1336        fn call_ibc_packet_receive_works() {
1337            let mut instance = mock_instance(IBC_REFLECT, &[]);
1338            setup(&mut instance, CHANNEL_ID, ACCOUNT);
1339            let who_am_i = br#"{"who_am_i":{}}"#;
1340            let msg = mock_ibc_packet_recv(CHANNEL_ID, who_am_i).unwrap();
1341            call_ibc_packet_receive::<_, _, _, Empty>(&mut instance, &mock_env(), &msg)
1342                .unwrap()
1343                .unwrap();
1344        }
1345
1346        #[test]
1347        fn call_ibc_source_callback_works() {
1348            let mut instance = mock_instance(IBC_CALLBACKS, &[]);
1349
1350            // init
1351            let creator = instance.api().addr_make("creator");
1352            let info = mock_info(&creator, &[]);
1353            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, br#"{}"#)
1354                .unwrap()
1355                .unwrap();
1356
1357            /// Response type for the `callback_stats` query
1358            #[derive(serde::Serialize, serde::Deserialize)]
1359            struct CallbackStats {
1360                pub ibc_ack_callbacks: Vec<IbcPacketAckMsg>,
1361                pub ibc_timeout_callbacks: Vec<IbcPacketTimeoutMsg>,
1362            }
1363
1364            // send ack callback
1365            let ack = mock_ibc_packet_ack(CHANNEL_ID, br#"{}"#, IbcAcknowledgement::new(br#"{}"#))
1366                .unwrap();
1367            let msg = IbcSourceCallbackMsg::Acknowledgement(IbcAckCallbackMsg::new(
1368                ack.acknowledgement,
1369                ack.original_packet,
1370                ack.relayer,
1371            ));
1372            call_ibc_source_callback::<_, _, _, Empty>(&mut instance, &mock_env(), &msg)
1373                .unwrap()
1374                .unwrap();
1375            // query the CallbackStats
1376            let stats: CallbackStats = serde_json::from_slice(
1377                &call_query::<_, _, _>(&mut instance, &mock_env(), br#"{"callback_stats":{}}"#)
1378                    .unwrap()
1379                    .unwrap(),
1380            )
1381            .unwrap();
1382            assert_eq!(1, stats.ibc_ack_callbacks.len());
1383            assert_eq!(0, stats.ibc_timeout_callbacks.len());
1384
1385            // send timeout callback
1386            let timeout = mock_ibc_packet_timeout(CHANNEL_ID, br#"{}"#).unwrap();
1387            let msg = IbcSourceCallbackMsg::Timeout(IbcTimeoutCallbackMsg::new(
1388                timeout.packet,
1389                timeout.relayer,
1390            ));
1391            call_ibc_source_callback::<_, _, _, Empty>(&mut instance, &mock_env(), &msg)
1392                .unwrap()
1393                .unwrap();
1394            // query the CallbackStats
1395            let stats: CallbackStats = serde_json::from_slice(
1396                &call_query::<_, _, _>(&mut instance, &mock_env(), br#"{"callback_stats":{}}"#)
1397                    .unwrap()
1398                    .unwrap(),
1399            )
1400            .unwrap();
1401            assert_eq!(1, stats.ibc_ack_callbacks.len());
1402            assert_eq!(1, stats.ibc_timeout_callbacks.len());
1403        }
1404    }
1405
1406    #[cfg(feature = "ibc2")]
1407    mod ibc2 {
1408        use super::*;
1409        use cosmwasm_std::testing::{
1410            mock_ibc2_packet_ack, mock_ibc2_packet_recv, mock_ibc2_packet_send,
1411            mock_ibc2_packet_timeout,
1412        };
1413        static IBC2: &[u8] = include_bytes!("../testdata/ibc2.wasm");
1414
1415        #[derive(serde::Serialize)]
1416        pub struct IbcPayload {
1417            pub response_without_ack: bool,
1418            pub send_async_ack_for_prev_msg: bool,
1419        }
1420
1421        #[test]
1422        fn call_ibc2_packet_ack_works() {
1423            // init
1424            let mut instance = mock_instance(IBC2, &[]);
1425            let info = mock_info("creator", &[]);
1426            let instantiate_msg = br#"{}"#;
1427            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, instantiate_msg)
1428                .unwrap()
1429                .unwrap();
1430
1431            let ibc2_msg = IbcPayload {
1432                response_without_ack: false,
1433                send_async_ack_for_prev_msg: false,
1434            };
1435            let ibc2_ack = mock_ibc2_packet_ack(&ibc2_msg).unwrap();
1436            call_ibc2_packet_ack::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_ack)
1437                .unwrap()
1438                .unwrap();
1439        }
1440
1441        #[test]
1442        fn call_ibc2_packet_receive_works() {
1443            // init
1444            let mut instance = mock_instance(IBC2, &[]);
1445            let info = mock_info("creator", &[]);
1446            let instantiate_msg = br#"{}"#;
1447            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, instantiate_msg)
1448                .unwrap()
1449                .unwrap();
1450
1451            let ibc2_msg = IbcPayload {
1452                response_without_ack: false,
1453                send_async_ack_for_prev_msg: false,
1454            };
1455            let ibc2_timeout = mock_ibc2_packet_recv(&ibc2_msg).unwrap();
1456            call_ibc2_packet_receive::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_timeout)
1457                .unwrap()
1458                .unwrap();
1459        }
1460
1461        #[test]
1462        fn call_ibc2_packet_timeout_works() {
1463            // init
1464            let mut instance = mock_instance(IBC2, &[]);
1465            let info = mock_info("creator", &[]);
1466            let instantiate_msg = br#"{}"#;
1467            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, instantiate_msg)
1468                .unwrap()
1469                .unwrap();
1470
1471            let ibc2_msg = br#"SomeRandomMsg"#;
1472            let ibc2_msg = mock_ibc2_packet_timeout(ibc2_msg).unwrap();
1473            call_ibc2_packet_timeout::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_msg)
1474                .unwrap()
1475                .unwrap();
1476        }
1477
1478        #[test]
1479        fn call_ibc2_packet_send_works() {
1480            // init
1481            let mut instance = mock_instance(IBC2, &[]);
1482            let info = mock_info("creator", &[]);
1483            let instantiate_msg = br#"{}"#;
1484            call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, instantiate_msg)
1485                .unwrap()
1486                .unwrap();
1487
1488            let ibc2_msg = IbcPayload {
1489                response_without_ack: false,
1490                send_async_ack_for_prev_msg: false,
1491            };
1492            let ibc2_sent = mock_ibc2_packet_send(&ibc2_msg).unwrap();
1493            call_ibc2_packet_send::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_sent)
1494                .unwrap()
1495                .unwrap();
1496        }
1497    }
1498}