Skip to content

Commit 287243a

Browse files
committed
feat: Add support for IBCv2 Sent entry point
1 parent 7689ea4 commit 287243a

File tree

10 files changed

+191
-4
lines changed

10 files changed

+191
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ and this project adheres to
2727
- cosmwasm-std: Add `cosmwasm_std::testing::Envs`, which is an `Env` factory for
2828
testing environments. It auto-increments block heights and timestamps. It
2929
allows for advanced configurations such as custom address prefixes. ([#2442])
30+
- cosmwasm-std: Add `Ibc2PacketSentMsg` message - ([#2477])
31+
- cosmwasm-vm: Add `ibc2_packet_sent` entrypoint ([#2477])
3032

3133
## Changed
3234

@@ -125,6 +127,7 @@ and this project adheres to
125127
[#2467]: https://github.com/CosmWasm/cosmwasm/pull/2467
126128
[#2472]: https://github.com/CosmWasm/cosmwasm/pull/2472
127129
[#2473]: https://github.com/CosmWasm/cosmwasm/pull/2473
130+
[#2477]: https://github.com/CosmWasm/cosmwasm/pull/2477
128131

129132
## [2.2.0] - 2024-12-17
130133

contracts/ibc2/src/contract.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,26 @@ pub fn ibc2_packet_timeout(
120120

121121
Ok(IbcBasicResponse::default())
122122
}
123+
124+
#[entry_point]
125+
pub fn ibc2_packet_sent(
126+
deps: DepsMut,
127+
_env: Env,
128+
msg: Ibc2PacketSentMsg,
129+
) -> StdResult<IbcBasicResponse> {
130+
let data = deps
131+
.storage
132+
.get(STATE_KEY)
133+
.ok_or_else(|| StdError::generic_err("State not found."))?;
134+
let state: State = from_json(data)?;
135+
136+
deps.storage.set(
137+
STATE_KEY,
138+
&to_json_vec(&State {
139+
last_packet_sent: msg,
140+
..state
141+
})?,
142+
);
143+
144+
Ok(IbcBasicResponse::default())
145+
}

contracts/ibc2/src/state.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub struct State {
77
pub ibc2_packet_timeout_counter: u32,
88
pub last_source_client: String,
99
pub last_packet_seq: u64,
10+
pub last_packet_sent: Ibc2PacketSentMsg,
1011
}
1112

1213
pub const STATE_KEY: &[u8] = b"state";

packages/std/src/exports/exports.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,36 @@ where
583583
Region::from_vec(v).to_heap_ptr() as u32
584584
}
585585

586+
/// do_ibc2_packet_sent is designed for use with #[entry_point] to make a "C" extern
587+
///
588+
/// contract_fn is called when there is an Ibc2 payload on port belonging
589+
/// to this contract waiting to be verified before sending it to another
590+
/// blockchain.
591+
///
592+
/// - `Q`: custom query type (see QueryRequest)
593+
/// - `C`: custom response message type (see CosmosMsg)
594+
/// - `E`: error type for responses
595+
#[cfg(feature = "ibc2")]
596+
pub fn do_ibc2_packet_sent<Q, C, E>(
597+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketSentMsg) -> Result<IbcBasicResponse<C>, E>,
598+
env_ptr: u32,
599+
msg_ptr: u32,
600+
) -> u32
601+
where
602+
Q: CustomQuery,
603+
C: CustomMsg,
604+
E: ToString,
605+
{
606+
install_panic_handler();
607+
let res = _do_ibc2_packet_sent(
608+
contract_fn,
609+
env_ptr as *mut Region<Owned>,
610+
msg_ptr as *mut Region<Owned>,
611+
);
612+
let v = to_json_vec(&res).unwrap();
613+
Region::from_vec(v).to_heap_ptr() as u32
614+
}
615+
586616
fn _do_instantiate<Q, M, C, E>(
587617
instantiate_fn: &dyn Fn(DepsMut<Q>, Env, MessageInfo, M) -> Result<Response<C>, E>,
588618
env_ptr: *mut Region<Owned>,
@@ -984,6 +1014,29 @@ where
9841014
contract_fn(deps.as_mut(), env, msg).into()
9851015
}
9861016

1017+
#[cfg(feature = "ibc2")]
1018+
fn _do_ibc2_packet_sent<Q, C, E>(
1019+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketSentMsg) -> Result<IbcBasicResponse<C>, E>,
1020+
env_ptr: *mut Region<Owned>,
1021+
msg_ptr: *mut Region<Owned>,
1022+
) -> ContractResult<IbcReceiveResponse<C>>
1023+
where
1024+
Q: CustomQuery,
1025+
C: CustomMsg,
1026+
E: ToString,
1027+
{
1028+
let env: Vec<u8> =
1029+
unsafe { Region::from_heap_ptr(ptr::NonNull::new(env_ptr).unwrap()).into_vec() };
1030+
let msg: Vec<u8> =
1031+
unsafe { Region::from_heap_ptr(ptr::NonNull::new(msg_ptr).unwrap()).into_vec() };
1032+
1033+
let env: Env = try_into_contract_result!(from_json(env));
1034+
let msg: Ibc2PacketSentMsg = try_into_contract_result!(from_json(msg));
1035+
1036+
let mut deps = deps_from_imports();
1037+
contract_fn(deps.as_mut(), env, msg).into()
1038+
}
1039+
9871040
/// Makes all bridges to external dependencies (i.e. Wasm imports) that are injected by the VM
9881041
fn deps_from_imports<Q>() -> OwnedDeps<ExternalStorage, ExternalApi, ExternalQuerier, Q>
9891042
where

packages/std/src/exports/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub use exports::{
1010
do_query, do_reply, do_sudo,
1111
};
1212
#[cfg(feature = "ibc2")]
13-
pub use exports::{do_ibc2_packet_receive, do_ibc2_packet_timeout};
13+
pub use exports::{do_ibc2_packet_receive, do_ibc2_packet_timeout, do_ibc2_packet_sent};
1414
#[cfg(feature = "stargate")]
1515
pub use exports::{
1616
do_ibc_channel_close, do_ibc_channel_connect, do_ibc_channel_open, do_ibc_packet_ack,

packages/std/src/ibc2.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,46 @@ impl Ibc2PacketTimeoutMsg {
132132
}
133133
}
134134

135+
/// Ibc2PacketSentMsg represents a payload sent event in the IBC2 protocol.
136+
/// Since sending IBCv2 packets are permissionless, the IBC protocol indtroduce
137+
/// the extra entry point, where application can verify the message sent from
138+
/// a given port ID.
139+
///
140+
/// It includes details about the source and destination clients, and the sequence
141+
/// number of the packet.
142+
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
143+
#[non_exhaustive]
144+
pub struct Ibc2PacketSentMsg {
145+
/// The payload to be sent.
146+
pub payload: Ibc2Payload,
147+
/// The identifier of the client that originally sent the packet.
148+
pub source_client: String,
149+
/// The identifier of the client that was the intended recipient.
150+
pub destination_client: String,
151+
/// The sequence number of the sent packet.
152+
pub packet_sequence: u64,
153+
/// The address of the signer that sent the packet.
154+
pub signer: Addr,
155+
}
156+
157+
impl Ibc2PacketSentMsg {
158+
pub fn new(
159+
payload: Ibc2Payload,
160+
source_client: String,
161+
destination_client: String,
162+
packet_sequence: u64,
163+
signer: Addr,
164+
) -> Self {
165+
Self {
166+
payload,
167+
source_client,
168+
destination_client,
169+
packet_sequence,
170+
signer,
171+
}
172+
}
173+
}
174+
135175
#[cfg(test)]
136176
mod tests {
137177
use serde_json::to_string;

packages/std/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub use crate::exports::{
129129
do_query, do_reply, do_sudo,
130130
};
131131
#[cfg(all(feature = "exports", target_arch = "wasm32", feature = "ibc2"))]
132-
pub use crate::exports::{do_ibc2_packet_receive, do_ibc2_packet_timeout};
132+
pub use crate::exports::{do_ibc2_packet_receive, do_ibc2_packet_timeout, do_ibc2_packet_sent};
133133
#[cfg(all(feature = "exports", target_arch = "wasm32", feature = "stargate"))]
134134
pub use crate::exports::{
135135
do_ibc_channel_close, do_ibc_channel_connect, do_ibc_channel_open, do_ibc_packet_ack,

packages/vm/src/calls.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ mod read_limits {
6767
pub const RESULT_IBC_SOURCE_CALLBACK: usize = 64 * MI;
6868
/// Max length (in bytes) of the result data from a ibc_destination_callback call.
6969
pub const RESULT_IBC_DESTINATION_CALLBACK: usize = 64 * MI;
70+
#[cfg(feature = "ibc2")]
71+
pub const RESULT_IBC2_PACKET_SENT: usize = 64 * MI;
7072
}
7173

7274
/// The limits for the JSON deserialization.
@@ -110,6 +112,9 @@ mod deserialization_limits {
110112
pub const RESULT_IBC_SOURCE_CALLBACK: usize = 256 * KI;
111113
/// Max length (in bytes) of the result data from a ibc_destination_callback call.
112114
pub const RESULT_IBC_DESTINATION_CALLBACK: usize = 256 * KI;
115+
/// Max length (in bytes) of the result data from a ibc_packet_receive call.
116+
#[cfg(any(feature = "ibc2"))]
117+
pub const RESULT_IBC2_PACKET_SENT: usize = 256 * KI;
113118
}
114119

115120
pub fn call_instantiate<A, S, Q, U>(
@@ -693,7 +698,7 @@ where
693698
instance,
694699
"ibc2_packet_receive",
695700
&[env, msg],
696-
read_limits::RESULT_IBC_PACKET_RECEIVE,
701+
read_limits::RESULT_IBC2_PACKET_SENT,
697702
)
698703
}
699704

@@ -736,6 +741,46 @@ where
736741
)
737742
}
738743

744+
745+
#[cfg(feature = "ibc2")]
746+
pub fn call_ibc2_packet_sent_raw<A, S, Q>(
747+
instance: &mut Instance<A, S, Q>,
748+
env: &[u8],
749+
msg: &[u8],
750+
) -> VmResult<Vec<u8>>
751+
where
752+
A: BackendApi + 'static,
753+
S: Storage + 'static,
754+
Q: Querier + 'static,
755+
{
756+
instance.set_storage_readonly(false);
757+
call_raw(
758+
instance,
759+
"ibc2_packet_sent",
760+
&[env, msg],
761+
read_limits::RESULT_IBC2_PACKET_SENT,
762+
)
763+
}
764+
765+
#[cfg(feature = "ibc2")]
766+
pub fn call_ibc2_packet_sent<A, S, Q, U>(
767+
instance: &mut Instance<A, S, Q>,
768+
env: &Env,
769+
msg: &Ibc2PacketSentMsg,
770+
) -> VmResult<ContractResult<IbcBasicResponse<U>>>
771+
where
772+
A: BackendApi + 'static,
773+
S: Storage + 'static,
774+
Q: Querier + 'static,
775+
U: DeserializeOwned + CustomMsg,
776+
{
777+
let env = to_vec(env)?;
778+
let msg = to_vec(msg)?;
779+
let data = call_ibc2_packet_sent_raw(instance, &env, &msg)?;
780+
let result = from_slice(&data, deserialization_limits::RESULT_IBC2_PACKET_SENT)?;
781+
Ok(result)
782+
}
783+
739784
pub fn call_ibc_source_callback_raw<A, S, Q>(
740785
instance: &mut Instance<A, S, Q>,
741786
env: &[u8],
@@ -1367,5 +1412,25 @@ mod tests {
13671412
.unwrap()
13681413
.unwrap();
13691414
}
1415+
1416+
#[test]
1417+
fn call_ibc2_packet_sent_works() {
1418+
// init
1419+
let mut instance = mock_instance(IBC2, &[]);
1420+
let info = mock_info("creator", &[]);
1421+
let instantiate_msg = br#"{}"#;
1422+
call_instantiate::<_, _, _, Empty>(&mut instance, &mock_env(), &info, instantiate_msg)
1423+
.unwrap()
1424+
.unwrap();
1425+
1426+
let ibc2_msg = IbcPayload {
1427+
response_without_ack: false,
1428+
send_async_ack_for_prev_msg: false,
1429+
};
1430+
let ibc2_sent = mock_ibc2_packet_sent(&ibc2_msg).unwrap();
1431+
call_ibc2_packet_sent::<_, _, _, Empty>(&mut instance, &mock_env(), &ibc2_sent)
1432+
.unwrap()
1433+
.unwrap();
1434+
}
13701435
}
13711436
}

packages/vm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub use crate::calls::{
3535
#[cfg(feature = "ibc2")]
3636
pub use crate::calls::{
3737
call_ibc2_packet_receive, call_ibc2_packet_receive_raw, call_ibc2_packet_timeout,
38-
call_ibc2_packet_timeout_raw,
38+
call_ibc2_packet_timeout_raw, call_ibc2_packet_sent, call_ibc2_packet_sent_raw,
3939
};
4040
#[cfg(feature = "stargate")]
4141
pub use crate::calls::{

packages/vm/src/static_analysis.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub enum Entrypoint {
4141
Ibc2PacketReceive,
4242
#[strum(serialize = "ibc2_packet_timeout")]
4343
Ibc2PacketTimeout,
44+
#[strum(serialize = "ibc2_packet_sent")]
45+
Ibc2PacketSent,
4446
}
4547

4648
// sort entrypoints by their &str representation

0 commit comments

Comments
 (0)