Skip to content

Commit 2ca6b99

Browse files
committed
feat: Add IBCv2 timeout entrypoint
1 parent a5deb27 commit 2ca6b99

File tree

11 files changed

+204
-19
lines changed

11 files changed

+204
-19
lines changed

contracts/ibc2/src/contract.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use cosmwasm_std::{
22
entry_point, from_json, to_json_vec, Binary, Deps, DepsMut, Empty, Env, Ibc2Msg,
3-
Ibc2PacketReceiveMsg, Ibc2Payload, IbcAcknowledgement, IbcReceiveResponse, MessageInfo,
4-
QueryResponse, Response, StdAck, StdError, StdResult,
3+
Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg, Ibc2Payload, IbcAcknowledgement, IbcBasicResponse,
4+
IbcReceiveResponse, MessageInfo, QueryResponse, Response, StdAck, StdError, StdResult,
55
};
66

77
use crate::msg::{IbcPayload, QueryMsg};
@@ -95,3 +95,12 @@ pub fn ibc2_packet_receive(
9595
Ok(resp)
9696
}
9797
}
98+
99+
#[entry_point]
100+
pub fn ibc2_packet_timeout(
101+
_deps: DepsMut,
102+
_env: Env,
103+
_msg: Ibc2PacketTimeoutMsg,
104+
) -> StdResult<IbcBasicResponse> {
105+
Ok(IbcBasicResponse::default())
106+
}

packages/std/src/exports.rs

+54-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::ibc::{
2323
};
2424
use crate::ibc::{IbcChannelOpenMsg, IbcChannelOpenResponse};
2525
#[cfg(feature = "ibc2")]
26-
use crate::ibc2::Ibc2PacketReceiveMsg;
26+
use crate::ibc2::{Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg};
2727
use crate::imports::{ExternalApi, ExternalQuerier, ExternalStorage};
2828
use crate::memory::{Owned, Region};
2929
use crate::panic::install_panic_handler;
@@ -555,6 +555,36 @@ where
555555
Region::from_vec(v).to_heap_ptr() as u32
556556
}
557557

558+
/// do_ibc_packet_timeout is designed for use with #[entry_point] to make a "C" extern
559+
///
560+
/// contract_fn is called when a packet that this contract previously sent has provably
561+
/// timedout and will never be relayed to the calling chain. This generally behaves
562+
/// like ick_ack_fn upon an acknowledgement containing an error.
563+
///
564+
/// - `Q`: custom query type (see QueryRequest)
565+
/// - `C`: custom response message type (see CosmosMsg)
566+
/// - `E`: error type for responses
567+
#[cfg(feature = "ibc2")]
568+
pub fn do_ibc2_packet_timeout<Q, C, E>(
569+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketTimeoutMsg) -> Result<IbcBasicResponse<C>, E>,
570+
env_ptr: u32,
571+
msg_ptr: u32,
572+
) -> u32
573+
where
574+
Q: CustomQuery,
575+
C: CustomMsg,
576+
E: ToString,
577+
{
578+
install_panic_handler();
579+
let res = _do_ibc2_packet_timeout(
580+
contract_fn,
581+
env_ptr as *mut Region<Owned>,
582+
msg_ptr as *mut Region<Owned>,
583+
);
584+
let v = to_json_vec(&res).unwrap();
585+
Region::from_vec(v).to_heap_ptr() as u32
586+
}
587+
558588
fn _do_instantiate<Q, M, C, E>(
559589
instantiate_fn: &dyn Fn(DepsMut<Q>, Env, MessageInfo, M) -> Result<Response<C>, E>,
560590
env_ptr: *mut Region<Owned>,
@@ -945,3 +975,26 @@ where
945975
let mut deps = make_dependencies();
946976
contract_fn(deps.as_mut(), env, msg).into()
947977
}
978+
979+
#[cfg(feature = "ibc2")]
980+
fn _do_ibc2_packet_timeout<Q, C, E>(
981+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketTimeoutMsg) -> Result<IbcBasicResponse<C>, E>,
982+
env_ptr: *mut Region<Owned>,
983+
msg_ptr: *mut Region<Owned>,
984+
) -> ContractResult<IbcBasicResponse<C>>
985+
where
986+
Q: CustomQuery,
987+
C: CustomMsg,
988+
E: ToString,
989+
{
990+
let env: Vec<u8> =
991+
unsafe { Region::from_heap_ptr(ptr::NonNull::new(env_ptr).unwrap()).into_vec() };
992+
let msg: Vec<u8> =
993+
unsafe { Region::from_heap_ptr(ptr::NonNull::new(msg_ptr).unwrap()).into_vec() };
994+
995+
let env: Env = try_into_contract_result!(from_json(env));
996+
let msg: Ibc2PacketTimeoutMsg = try_into_contract_result!(from_json(msg));
997+
998+
let mut deps = make_dependencies();
999+
contract_fn(deps.as_mut(), env, msg).into()
1000+
}

packages/std/src/ibc2.rs

+29
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,35 @@ impl Ibc2PacketReceiveMsg {
8989
}
9090
}
9191

92+
/// The message that is passed into `ibc2_packet_timeout`
93+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
94+
#[non_exhaustive]
95+
pub struct Ibc2PacketTimeoutMsg {
96+
pub payload: Ibc2Payload,
97+
pub source_client: String,
98+
pub destination_client: String,
99+
pub packet_sequence: u64,
100+
pub relayer: Addr,
101+
}
102+
103+
impl Ibc2PacketTimeoutMsg {
104+
pub fn new(
105+
payload: Ibc2Payload,
106+
source_client: String,
107+
destination_client: String,
108+
packet_sequence: u64,
109+
relayer: Addr,
110+
) -> Self {
111+
Self {
112+
payload,
113+
source_client,
114+
destination_client,
115+
packet_sequence,
116+
relayer,
117+
}
118+
}
119+
}
120+
92121
#[cfg(test)]
93122
mod tests {
94123
use serde_json::to_string;

packages/std/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub use crate::ibc::{
7777
IbcSourceCallbackMsg, IbcSrcCallback, IbcTimeout, IbcTimeoutBlock, IbcTimeoutCallbackMsg,
7878
TransferMsgBuilder,
7979
};
80-
pub use crate::ibc2::{Ibc2Msg, Ibc2PacketReceiveMsg, Ibc2Payload};
80+
pub use crate::ibc2::{Ibc2Msg, Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg, Ibc2Payload};
8181
#[cfg(feature = "iterator")]
8282
pub use crate::iterator::{Order, Record};
8383
pub use crate::math::{
@@ -124,15 +124,15 @@ mod imports;
124124
#[cfg(target_arch = "wasm32")]
125125
mod memory; // Used by exports and imports only. This assumes pointers are 32 bit long, which makes it untestable on dev machines.
126126

127-
#[cfg(all(feature = "ibc2", target_arch = "wasm32"))]
128-
pub use crate::exports::do_ibc2_packet_receive;
129127
#[cfg(all(feature = "cosmwasm_2_2", target_arch = "wasm32"))]
130128
pub use crate::exports::do_migrate_with_info;
131129
#[cfg(target_arch = "wasm32")]
132130
pub use crate::exports::{
133131
do_execute, do_ibc_destination_callback, do_ibc_source_callback, do_instantiate, do_migrate,
134132
do_query, do_reply, do_sudo,
135133
};
134+
#[cfg(all(feature = "ibc2", target_arch = "wasm32"))]
135+
pub use crate::exports::{do_ibc2_packet_receive, do_ibc2_packet_timeout};
136136
#[cfg(all(feature = "stargate", target_arch = "wasm32"))]
137137
pub use crate::exports::{
138138
do_ibc_channel_close, do_ibc_channel_connect, do_ibc_channel_open, do_ibc_packet_ack,

packages/std/src/testing/mock.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::ibc::{
2424
IbcTimeoutBlock,
2525
};
2626
#[cfg(feature = "ibc2")]
27-
use crate::ibc2::{Ibc2PacketReceiveMsg, Ibc2Payload};
27+
use crate::ibc2::{Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg, Ibc2Payload};
2828
#[cfg(feature = "cosmwasm_1_1")]
2929
use crate::query::SupplyResponse;
3030
use crate::query::{
@@ -532,6 +532,27 @@ pub fn mock_ibc2_packet_recv(data: &impl Serialize) -> StdResult<Ibc2PacketRecei
532532
))
533533
}
534534

535+
/// Creates a Ibc2PacketTimeoutMsg for testing ibc2_packet_timeout. You set a few key parameters that are
536+
/// often parsed. If you want to set more, use this as a default and mutate other fields.
537+
/// The difference from mock_ibc_packet_recv is if `my_channel_id` is src or dest.
538+
#[cfg(feature = "ibc2")]
539+
pub fn mock_ibc2_packet_timeout(data: &impl Serialize) -> StdResult<Ibc2PacketTimeoutMsg> {
540+
let payload = Ibc2Payload {
541+
source_port: "wasm2srcport".to_string(),
542+
destination_port: "wasm2destport".to_string(),
543+
version: "v2".to_string(),
544+
encoding: "json".to_string(),
545+
value: to_json_binary(data)?,
546+
};
547+
Ok(Ibc2PacketTimeoutMsg::new(
548+
payload,
549+
"source_client".to_string(),
550+
"destination_client".to_string(),
551+
1,
552+
Addr::unchecked("relayer"),
553+
))
554+
}
555+
535556
/// Creates a IbcPacket for testing ibc_packet_{ack,timeout}. You set a few key parameters that are
536557
/// often parsed. If you want to set more, use this as a default and mutate other fields.
537558
/// The difference from mock_ibc_packet_recv is if `my_channel_id` is src or dest.

packages/std/src/testing/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ pub use assertions::assert_approx_eq_impl;
1313
pub use assertions::assert_hash_works_impl;
1414

1515
pub use message_info::message_info;
16-
#[cfg(feature = "ibc2")]
17-
pub use mock::mock_ibc2_packet_recv;
1816
#[cfg(feature = "cosmwasm_1_3")]
1917
pub use mock::DistributionQuerier;
2018
#[cfg(feature = "staking")]
@@ -24,6 +22,8 @@ pub use mock::{
2422
mock_wasmd_attr, BankQuerier, MockApi, MockQuerier, MockQuerierCustomHandlerResult,
2523
MOCK_CONTRACT_ADDR,
2624
};
25+
#[cfg(feature = "ibc2")]
26+
pub use mock::{mock_ibc2_packet_recv, mock_ibc2_packet_timeout};
2727
#[cfg(feature = "stargate")]
2828
pub use mock::{
2929
mock_ibc_channel, mock_ibc_channel_close_confirm, mock_ibc_channel_close_init,

packages/vm/src/cache.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,9 @@ where
340340
has_ibc_entry_points: REQUIRED_IBC_EXPORTS
341341
.iter()
342342
.all(|required| exports.contains(required.as_ref())),
343-
has_ibc2_entry_points: exports.contains(REQUIRED_IBC2_EXPORT.as_ref()),
343+
has_ibc2_entry_points: REQUIRED_IBC2_EXPORT
344+
.iter()
345+
.all(|required| exports.contains(required.as_ref())),
344346
entrypoints,
345347
required_capabilities: required_capabilities_from_module(&module)
346348
.into_iter()
@@ -1530,7 +1532,7 @@ mod tests {
15301532
let checksum5 = cache.store_code(IBC2, true, true).unwrap();
15311533
let report5 = cache.analyze(&checksum5).unwrap();
15321534
let mut ibc2_contract_entrypoints = BTreeSet::from([E::Instantiate, E::Query]);
1533-
ibc2_contract_entrypoints.extend([REQUIRED_IBC2_EXPORT]);
1535+
ibc2_contract_entrypoints.extend(REQUIRED_IBC2_EXPORT);
15341536
assert_eq!(
15351537
report5,
15361538
AnalysisReport {

packages/vm/src/calls.rs

+71-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use cosmwasm_std::{
1616
};
1717

1818
#[cfg(feature = "ibc2")]
19-
use cosmwasm_std::Ibc2PacketReceiveMsg;
19+
use cosmwasm_std::{Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg};
2020

2121
use crate::backend::{BackendApi, Querier, Storage};
2222
use crate::conversion::ref_to_u32;
@@ -61,7 +61,7 @@ mod read_limits {
6161
#[cfg(feature = "stargate")]
6262
pub const RESULT_IBC_PACKET_ACK: usize = 64 * MI;
6363
/// Max length (in bytes) of the result data from a ibc_packet_timeout call.
64-
#[cfg(feature = "stargate")]
64+
#[cfg(any(feature = "stargate", feature = "ibc2"))]
6565
pub const RESULT_IBC_PACKET_TIMEOUT: usize = 64 * MI;
6666
/// Max length (in bytes) of the result data from a ibc_source_callback call.
6767
pub const RESULT_IBC_SOURCE_CALLBACK: usize = 64 * MI;
@@ -104,7 +104,7 @@ mod deserialization_limits {
104104
#[cfg(feature = "stargate")]
105105
pub const RESULT_IBC_PACKET_ACK: usize = 256 * KI;
106106
/// Max length (in bytes) of the result data from a ibc_packet_timeout call.
107-
#[cfg(feature = "stargate")]
107+
#[cfg(any(feature = "stargate", feature = "ibc2"))]
108108
pub const RESULT_IBC_PACKET_TIMEOUT: usize = 256 * KI;
109109
/// Max length (in bytes) of the result data from a ibc_source_callback call.
110110
pub const RESULT_IBC_SOURCE_CALLBACK: usize = 256 * KI;
@@ -697,6 +697,45 @@ where
697697
)
698698
}
699699

700+
#[cfg(feature = "ibc2")]
701+
pub fn call_ibc2_packet_timeout<A, S, Q, U>(
702+
instance: &mut Instance<A, S, Q>,
703+
env: &Env,
704+
msg: &Ibc2PacketTimeoutMsg,
705+
) -> VmResult<ContractResult<IbcBasicResponse<U>>>
706+
where
707+
A: BackendApi + 'static,
708+
S: Storage + 'static,
709+
Q: Querier + 'static,
710+
U: DeserializeOwned + CustomMsg,
711+
{
712+
let env = to_vec(env)?;
713+
let msg = to_vec(msg)?;
714+
let data = call_ibc2_packet_timeout_raw(instance, &env, &msg)?;
715+
let result = from_slice(&data, deserialization_limits::RESULT_IBC_PACKET_TIMEOUT)?;
716+
Ok(result)
717+
}
718+
719+
#[cfg(feature = "ibc2")]
720+
pub fn call_ibc2_packet_timeout_raw<A, S, Q>(
721+
instance: &mut Instance<A, S, Q>,
722+
env: &[u8],
723+
msg: &[u8],
724+
) -> VmResult<Vec<u8>>
725+
where
726+
A: BackendApi + 'static,
727+
S: Storage + 'static,
728+
Q: Querier + 'static,
729+
{
730+
instance.set_storage_readonly(false);
731+
call_raw(
732+
instance,
733+
"ibc2_packet_timeout",
734+
&[env, msg],
735+
read_limits::RESULT_IBC_PACKET_TIMEOUT,
736+
)
737+
}
738+
700739
pub fn call_ibc_source_callback_raw<A, S, Q>(
701740
instance: &mut Instance<A, S, Q>,
702741
env: &[u8],
@@ -1283,9 +1322,15 @@ mod tests {
12831322
#[cfg(feature = "ibc2")]
12841323
mod ibc2 {
12851324
use super::*;
1286-
use cosmwasm_std::testing::mock_ibc2_packet_recv;
1325+
use cosmwasm_std::testing::{mock_ibc2_packet_recv, mock_ibc2_packet_timeout};
12871326
static IBC2: &[u8] = include_bytes!("../testdata/ibc2.wasm");
12881327

1328+
#[derive(serde::Serialize)]
1329+
pub struct IbcPayload {
1330+
pub response_without_ack: bool,
1331+
pub send_async_ack_for_prev_msg: bool,
1332+
}
1333+
12891334
#[test]
12901335
fn call_ibc2_packet_receive_works() {
12911336
// init
@@ -1296,9 +1341,29 @@ mod tests {
12961341
.unwrap()
12971342
.unwrap();
12981343

1344+
let ibc2_msg = IbcPayload {
1345+
response_without_ack: false,
1346+
send_async_ack_for_prev_msg: false,
1347+
};
1348+
let ibc2_timeout = mock_ibc2_packet_recv(&ibc2_msg).unwrap();
1349+
call_ibc2_packet_receive::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_timeout)
1350+
.unwrap()
1351+
.unwrap();
1352+
}
1353+
1354+
#[test]
1355+
fn call_ibc2_packet_timeout_works() {
1356+
// init
1357+
let mut instance = mock_instance(IBC2, &[]);
1358+
let info = mock_info("creator", &[]);
1359+
let instantiate_msg = br#"{}"#;
1360+
call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, instantiate_msg)
1361+
.unwrap()
1362+
.unwrap();
1363+
12991364
let ibc2_msg = br#"SomeRandomMsg"#;
1300-
let ibc2_msg = mock_ibc2_packet_recv(ibc2_msg).unwrap();
1301-
call_ibc2_packet_receive::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_msg)
1365+
let ibc2_msg = mock_ibc2_packet_timeout(ibc2_msg).unwrap();
1366+
call_ibc2_packet_timeout::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_msg)
13021367
.unwrap()
13031368
.unwrap();
13041369
}

packages/vm/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ pub use crate::calls::{
3333
call_sudo_raw,
3434
};
3535
#[cfg(feature = "ibc2")]
36-
pub use crate::calls::{call_ibc2_packet_receive, call_ibc2_packet_receive_raw};
36+
pub use crate::calls::{
37+
call_ibc2_packet_receive, call_ibc2_packet_receive_raw, call_ibc2_packet_timeout,
38+
call_ibc2_packet_timeout_raw,
39+
};
3740
#[cfg(feature = "stargate")]
3841
pub use crate::calls::{
3942
call_ibc_channel_close, call_ibc_channel_close_raw, call_ibc_channel_connect,

packages/vm/src/static_analysis.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub enum Entrypoint {
3939
IbcDestinationCallback,
4040
#[strum(serialize = "ibc2_packet_receive")]
4141
Ibc2PacketReceive,
42+
#[strum(serialize = "ibc2_packet_timeout")]
43+
Ibc2PacketTimeout,
4244
}
4345

4446
// sort entrypoints by their &str representation
@@ -62,7 +64,8 @@ pub const REQUIRED_IBC_EXPORTS: &[Entrypoint] = &[
6264
Entrypoint::IbcPacketTimeout,
6365
];
6466

65-
pub const REQUIRED_IBC2_EXPORT: &Entrypoint = &Entrypoint::Ibc2PacketReceive;
67+
pub const REQUIRED_IBC2_EXPORT: &[Entrypoint] =
68+
&[Entrypoint::Ibc2PacketReceive, Entrypoint::Ibc2PacketTimeout];
6669

6770
/// A trait that allows accessing shared functionality of `parity_wasm::elements::Module`
6871
/// and `wasmer::Module` in a shared fashion.

packages/vm/testdata/ibc2.wasm

76.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)