Skip to content

Commit d1409e6

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

File tree

16 files changed

+432
-15
lines changed

16 files changed

+432
-15
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 `Ibc2PacketSendMsg` message - ([#2477])
31+
- cosmwasm-vm: Add `ibc2_packet_send` 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/schema/ibc2.json

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@
5353
"format": "uint32",
5454
"minimum": 0.0
5555
},
56+
"last_packet_sent": {
57+
"anyOf": [
58+
{
59+
"$ref": "#/definitions/Ibc2PacketSendMsg"
60+
},
61+
{
62+
"type": "null"
63+
}
64+
]
65+
},
5666
"last_packet_seq": {
5767
"type": "integer",
5868
"format": "uint64",
@@ -62,7 +72,99 @@
6272
"type": "string"
6373
}
6474
},
65-
"additionalProperties": false
75+
"additionalProperties": false,
76+
"definitions": {
77+
"Addr": {
78+
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
79+
"type": "string"
80+
},
81+
"Binary": {
82+
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.",
83+
"type": "string"
84+
},
85+
"Ibc2PacketSendMsg": {
86+
"description": "Ibc2PacketSendMsg represents a payload sent event in the IBC2 protocol. Since sending IBCv2 packets are permissionless, the IBC protocol indtroduce the extra entry point, where application can verify the message sent from a given port ID.\n\nIt includes details about the source and destination clients, and the sequence number of the packet.",
87+
"type": "object",
88+
"required": [
89+
"destination_client",
90+
"packet_sequence",
91+
"payload",
92+
"signer",
93+
"source_client"
94+
],
95+
"properties": {
96+
"destination_client": {
97+
"description": "The identifier of the client that was the intended recipient.",
98+
"type": "string"
99+
},
100+
"packet_sequence": {
101+
"description": "The sequence number of the sent packet.",
102+
"type": "integer",
103+
"format": "uint64",
104+
"minimum": 0.0
105+
},
106+
"payload": {
107+
"description": "The payload to be sent.",
108+
"allOf": [
109+
{
110+
"$ref": "#/definitions/Ibc2Payload"
111+
}
112+
]
113+
},
114+
"signer": {
115+
"description": "The address of the signer that sent the packet.",
116+
"allOf": [
117+
{
118+
"$ref": "#/definitions/Addr"
119+
}
120+
]
121+
},
122+
"source_client": {
123+
"description": "The identifier of the client that originally sent the packet.",
124+
"type": "string"
125+
}
126+
},
127+
"additionalProperties": false
128+
},
129+
"Ibc2Payload": {
130+
"description": "Payload value should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
131+
"type": "object",
132+
"required": [
133+
"destination_port",
134+
"encoding",
135+
"source_port",
136+
"value",
137+
"version"
138+
],
139+
"properties": {
140+
"destination_port": {
141+
"description": "The port id on the chain where the packet is sent to.",
142+
"type": "string"
143+
},
144+
"encoding": {
145+
"description": "Encoding used to serialize the [Ibc2Payload::value].",
146+
"type": "string"
147+
},
148+
"source_port": {
149+
"description": "The port id on the chain where the packet is sent from.",
150+
"type": "string"
151+
},
152+
"value": {
153+
"description": "Encoded payload data.",
154+
"allOf": [
155+
{
156+
"$ref": "#/definitions/Binary"
157+
}
158+
]
159+
},
160+
"version": {
161+
"description": "Version of the receiving contract.",
162+
"type": "string"
163+
}
164+
},
165+
"additionalProperties": false
166+
}
167+
}
66168
}
67169
}
68170
}

contracts/ibc2/schema/raw/response_to_query_state.json

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919
"format": "uint32",
2020
"minimum": 0.0
2121
},
22+
"last_packet_sent": {
23+
"anyOf": [
24+
{
25+
"$ref": "#/definitions/Ibc2PacketSendMsg"
26+
},
27+
{
28+
"type": "null"
29+
}
30+
]
31+
},
2232
"last_packet_seq": {
2333
"type": "integer",
2434
"format": "uint64",
@@ -28,5 +38,97 @@
2838
"type": "string"
2939
}
3040
},
31-
"additionalProperties": false
41+
"additionalProperties": false,
42+
"definitions": {
43+
"Addr": {
44+
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
45+
"type": "string"
46+
},
47+
"Binary": {
48+
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.",
49+
"type": "string"
50+
},
51+
"Ibc2PacketSendMsg": {
52+
"description": "Ibc2PacketSendMsg represents a payload sent event in the IBC2 protocol. Since sending IBCv2 packets are permissionless, the IBC protocol indtroduce the extra entry point, where application can verify the message sent from a given port ID.\n\nIt includes details about the source and destination clients, and the sequence number of the packet.",
53+
"type": "object",
54+
"required": [
55+
"destination_client",
56+
"packet_sequence",
57+
"payload",
58+
"signer",
59+
"source_client"
60+
],
61+
"properties": {
62+
"destination_client": {
63+
"description": "The identifier of the client that was the intended recipient.",
64+
"type": "string"
65+
},
66+
"packet_sequence": {
67+
"description": "The sequence number of the sent packet.",
68+
"type": "integer",
69+
"format": "uint64",
70+
"minimum": 0.0
71+
},
72+
"payload": {
73+
"description": "The payload to be sent.",
74+
"allOf": [
75+
{
76+
"$ref": "#/definitions/Ibc2Payload"
77+
}
78+
]
79+
},
80+
"signer": {
81+
"description": "The address of the signer that sent the packet.",
82+
"allOf": [
83+
{
84+
"$ref": "#/definitions/Addr"
85+
}
86+
]
87+
},
88+
"source_client": {
89+
"description": "The identifier of the client that originally sent the packet.",
90+
"type": "string"
91+
}
92+
},
93+
"additionalProperties": false
94+
},
95+
"Ibc2Payload": {
96+
"description": "Payload value should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.",
97+
"type": "object",
98+
"required": [
99+
"destination_port",
100+
"encoding",
101+
"source_port",
102+
"value",
103+
"version"
104+
],
105+
"properties": {
106+
"destination_port": {
107+
"description": "The port id on the chain where the packet is sent to.",
108+
"type": "string"
109+
},
110+
"encoding": {
111+
"description": "Encoding used to serialize the [Ibc2Payload::value].",
112+
"type": "string"
113+
},
114+
"source_port": {
115+
"description": "The port id on the chain where the packet is sent from.",
116+
"type": "string"
117+
},
118+
"value": {
119+
"description": "Encoded payload data.",
120+
"allOf": [
121+
{
122+
"$ref": "#/definitions/Binary"
123+
}
124+
]
125+
},
126+
"version": {
127+
"description": "Version of the receiving contract.",
128+
"type": "string"
129+
}
130+
},
131+
"additionalProperties": false
132+
}
133+
}
32134
}

contracts/ibc2/src/contract.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use cosmwasm_std::{
22
entry_point, from_json, to_json_vec, Binary, Deps, DepsMut, Empty, Env, Ibc2Msg,
3-
Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg, Ibc2Payload, IbcAcknowledgement, IbcBasicResponse,
4-
IbcReceiveResponse, MessageInfo, QueryResponse, Response, StdAck, StdError, StdResult,
3+
Ibc2PacketReceiveMsg, Ibc2PacketSendMsg, Ibc2PacketTimeoutMsg, Ibc2Payload, IbcAcknowledgement,
4+
IbcBasicResponse, IbcReceiveResponse, MessageInfo, QueryResponse, Response, StdAck, StdError,
5+
StdResult,
56
};
67

78
use crate::msg::{IbcPayload, QueryMsg};
@@ -21,6 +22,7 @@ pub fn instantiate(
2122
ibc2_packet_timeout_counter: 0,
2223
last_source_client: "".to_owned(),
2324
last_packet_seq: 0,
25+
last_packet_sent: None,
2426
})?,
2527
);
2628

@@ -120,3 +122,26 @@ pub fn ibc2_packet_timeout(
120122

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

contracts/ibc2/src/state.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use cosmwasm_std::Ibc2PacketSendMsg;
12
use schemars::JsonSchema;
23
use serde::{Deserialize, Serialize};
34

@@ -7,6 +8,7 @@ pub struct State {
78
pub ibc2_packet_timeout_counter: u32,
89
pub last_source_client: String,
910
pub last_packet_seq: u64,
11+
pub last_packet_sent: Option<Ibc2PacketSendMsg>,
1012
}
1113

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

packages/std/src/exports/exports.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::ibc::{
2626
};
2727
use crate::ibc::{IbcChannelOpenMsg, IbcChannelOpenResponse};
2828
#[cfg(feature = "ibc2")]
29-
use crate::ibc2::{Ibc2PacketReceiveMsg, Ibc2PacketTimeoutMsg};
29+
use crate::ibc2::{Ibc2PacketReceiveMsg, Ibc2PacketSendMsg, Ibc2PacketTimeoutMsg};
3030
use crate::query::CustomQuery;
3131
use crate::results::{ContractResult, QueryResponse, Reply, Response};
3232
use crate::serde::{from_json, to_json_vec};
@@ -583,6 +583,36 @@ where
583583
Region::from_vec(v).to_heap_ptr() as u32
584584
}
585585

586+
/// do_ibc2_packet_send 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_send<Q, C, E>(
597+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketSendMsg) -> 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_send(
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_send<Q, C, E>(
1019+
contract_fn: &dyn Fn(DepsMut<Q>, Env, Ibc2PacketSendMsg) -> Result<IbcBasicResponse<C>, E>,
1020+
env_ptr: *mut Region<Owned>,
1021+
msg_ptr: *mut Region<Owned>,
1022+
) -> ContractResult<IbcBasicResponse<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: Ibc2PacketSendMsg = 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_send, do_ibc2_packet_timeout};
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,

0 commit comments

Comments
 (0)