From 3391e19c63e315b7ed9f52ac2d67ae6e75cbd56c Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 23 Feb 2024 16:42:14 +0100 Subject: [PATCH 01/10] Add IBCSourceChainCallbackMsg type --- types/ibc.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/types/ibc.go b/types/ibc.go index 1e2b4b223..36bfcd81e 100644 --- a/types/ibc.go +++ b/types/ibc.go @@ -148,6 +148,26 @@ type IBCPacketTimeoutMsg struct { Relayer string `json:"relayer"` } +// The type of IBC callback that is being called. +// +// IBC callbacks are needed for cases where your contract triggers the sending of an IBC packet +// through some other message (i.e. not through [`IbcMsg::SendPacket`]) and needs to know whether +// or not the packet was successfully received on the other chain. A prominent example is the +// [`IbcMsg::Transfer`] message. +// Without callbacks, you cannot know whether the transfer was successful or not. +// +// Note that there are some prerequisites that need to be fulfilled to receive source chain callbacks: +// - The contract must implement the `ibc_source_chain_callback` entrypoint. +// - The module that sends the packet must be wrapped by an `IBCMiddleware` +// (i.e. the source chain needs to support callbacks for the message you are sending). +// - You have to add json-encoded [`IbcCallbackData`] to a specific field of the message. +// For `IbcMsg::Transfer`, this is the `memo` field. +// - The receiver of the callback must also be the sender of the message. +type IBCSourceChainCallbackMsg struct { + Acknowledgement *IBCPacketAckMsg `json:"Acknowledgement,omitempty"` + Timeout *IBCPacketTimeoutMsg `json:"Timeout,omitempty"` +} + // TODO: test what the sdk Order.String() represents and how to parse back // Proto files: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/core/channel/v1/channel.proto#L69-L80 // Auto-gen code: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/ibc/core/04-channel/types/channel.pb.go#L70-L101 From 698cee4147b9024a8ab132ea2a8f15ba029e960c Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Feb 2024 13:05:36 +0100 Subject: [PATCH 02/10] Add ibc_source_chain_callback entrypoint --- internal/api/bindings.h | 12 ++++++++++++ internal/api/lib.go | 42 +++++++++++++++++++++++++++++++++++++++++ lib_libwasmvm.go | 35 ++++++++++++++++++++++++++++++++++ libwasmvm/bindings.h | 12 ++++++++++++ libwasmvm/src/calls.rs | 35 ++++++++++++++++++++++++++++++++-- 5 files changed, 134 insertions(+), 2 deletions(-) diff --git a/internal/api/bindings.h b/internal/api/bindings.h index 6a1bdd86e..50273b95d 100644 --- a/internal/api/bindings.h +++ b/internal/api/bindings.h @@ -597,6 +597,18 @@ struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, struct GasReport *gas_report, struct UnmanagedVector *error_msg); +struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); + struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); void destroy_unmanaged_vector(struct UnmanagedVector v); diff --git a/internal/api/lib.go b/internal/api/lib.go index c73c55976..195a840e6 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -700,6 +700,48 @@ func IBCPacketTimeout( return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } +func IBCSourceChainCallback( + cache Cache, + checksum []byte, + env []byte, + msg []byte, + gasMeter *types.GasMeter, + store types.KVStore, + api *types.GoAPI, + querier *Querier, + gasLimit uint64, + printDebug bool, +) ([]byte, types.GasReport, error) { + cs := makeView(checksum) + defer runtime.KeepAlive(checksum) + e := makeView(env) + defer runtime.KeepAlive(env) + ac := makeView(msg) + defer runtime.KeepAlive(msg) + var pinner runtime.Pinner + pinner.Pin(gasMeter) + checkAndPinAPI(api, pinner) + checkAndPinQuerier(querier, pinner) + defer pinner.Unpin() + + callID := startCall() + defer endCall(callID) + + dbState := buildDBState(store, callID) + db := buildDB(&dbState, gasMeter) + a := buildAPI(api) + q := buildQuerier(querier) + var gasReport C.GasReport + errmsg := uninitializedUnmanagedVector() + + res, err := C.ibc_source_chain_callback(cache.ptr, cs, e, ac, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) + if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) + } + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil +} + func convertGasReport(report C.GasReport) types.GasReport { return types.GasReport{ Limit: uint64(report.limit), diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index 7a0c53e58..223fe1edd 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -532,6 +532,41 @@ func (vm *VM) IBCPacketTimeout( return &result, gasReport.UsedInternally, nil } +// IBCSourceChainCallback is available on IBC-enabled contracts with and is called when an +// the response for an outgoing packet (previously sent by this contract) +// is received +func (vm *VM) IBCSourceChainCallback( + checksum Checksum, + env types.Env, + msg types.IBCSourceChainCallbackMsg, + store KVStore, + goapi GoAPI, + querier Querier, + gasMeter GasMeter, + gasLimit uint64, + deserCost types.UFraction, +) (*types.IBCBasicResult, uint64, error) { + envBin, err := json.Marshal(env) + if err != nil { + return nil, 0, err + } + msgBin, err := json.Marshal(msg) + if err != nil { + return nil, 0, err + } + data, gasReport, err := api.IBCSourceChainCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + if err != nil { + return nil, gasReport.UsedInternally, err + } + + var result types.IBCBasicResult + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result) + if err != nil { + return nil, gasReport.UsedInternally, err + } + return &result, gasReport.UsedInternally, nil +} + func compileCost(code WasmCode) uint64 { // CostPerByte is how much CosmWasm gas is charged *per byte* for compiling WASM code. // Benchmarks and numbers (in SDK Gas) were discussed in: diff --git a/libwasmvm/bindings.h b/libwasmvm/bindings.h index 6a1bdd86e..50273b95d 100644 --- a/libwasmvm/bindings.h +++ b/libwasmvm/bindings.h @@ -597,6 +597,18 @@ struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, struct GasReport *gas_report, struct UnmanagedVector *error_msg); +struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); + struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); void destroy_unmanaged_vector(struct UnmanagedVector v); diff --git a/libwasmvm/src/calls.rs b/libwasmvm/src/calls.rs index 44d57b36d..a09112db9 100644 --- a/libwasmvm/src/calls.rs +++ b/libwasmvm/src/calls.rs @@ -9,8 +9,9 @@ use cosmwasm_std::Checksum; use cosmwasm_vm::{ call_execute_raw, call_ibc_channel_close_raw, call_ibc_channel_connect_raw, call_ibc_channel_open_raw, call_ibc_packet_ack_raw, call_ibc_packet_receive_raw, - call_ibc_packet_timeout_raw, call_instantiate_raw, call_migrate_raw, call_query_raw, - call_reply_raw, call_sudo_raw, Backend, Cache, Instance, InstanceOptions, VmResult, + call_ibc_packet_timeout_raw, call_ibc_source_chain_callback_raw, call_instantiate_raw, + call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend, Cache, Instance, + InstanceOptions, VmResult, }; use crate::api::GoApi; @@ -395,6 +396,36 @@ pub extern "C" fn ibc_packet_timeout( ) } +#[no_mangle] +pub extern "C" fn ibc_source_chain_callback( + cache: *mut cache_t, + checksum: ByteSliceView, + env: ByteSliceView, + msg: ByteSliceView, + db: Db, + api: GoApi, + querier: GoQuerier, + gas_limit: u64, + print_debug: bool, + gas_report: Option<&mut GasReport>, + error_msg: Option<&mut UnmanagedVector>, +) -> UnmanagedVector { + call_2_args( + call_ibc_source_chain_callback_raw, + cache, + checksum, + env, + msg, + db, + api, + querier, + gas_limit, + print_debug, + gas_report, + error_msg, + ) +} + type VmFn2Args = fn( instance: &mut Instance, arg1: &[u8], From cc508dbd3e8304c1b26afc9fd0d8082faa697a21 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Feb 2024 13:22:34 +0100 Subject: [PATCH 03/10] Add ibc-callbacks to deploy_to_git branch --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 70e94b7b3..21b7e670a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -505,3 +505,4 @@ workflows: only: - main - /^release\/.*/ + - ibc-callbacks # added temporarily From 32a1401d87c62acf517c2c3cd152fa853779dc8f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 4 Apr 2024 14:07:41 +0200 Subject: [PATCH 04/10] Add destination chain callback --- internal/api/bindings.h | 12 +++++++++++ internal/api/lib.go | 46 +++++++++++++++++++++++++++++++++++++++-- lib_libwasmvm.go | 40 ++++++++++++++++++++++++++++++++--- libwasmvm/Cargo.lock | 14 ++++++------- libwasmvm/bindings.h | 12 +++++++++++ libwasmvm/src/calls.rs | 38 ++++++++++++++++++++++++++++++---- types/ibc.go | 17 +++++++++++++++ 7 files changed, 163 insertions(+), 16 deletions(-) diff --git a/internal/api/bindings.h b/internal/api/bindings.h index 50273b95d..b8dd742bb 100644 --- a/internal/api/bindings.h +++ b/internal/api/bindings.h @@ -609,6 +609,18 @@ struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache, struct GasReport *gas_report, struct UnmanagedVector *error_msg); +struct UnmanagedVector ibc_destination_chain_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); + struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); void destroy_unmanaged_vector(struct UnmanagedVector v); diff --git a/internal/api/lib.go b/internal/api/lib.go index 195a840e6..7fc47be86 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -716,7 +716,7 @@ func IBCSourceChainCallback( defer runtime.KeepAlive(checksum) e := makeView(env) defer runtime.KeepAlive(env) - ac := makeView(msg) + msgBytes := makeView(msg) defer runtime.KeepAlive(msg) var pinner runtime.Pinner pinner.Pin(gasMeter) @@ -734,7 +734,49 @@ func IBCSourceChainCallback( var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_source_chain_callback(cache.ptr, cs, e, ac, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) + res, err := C.ibc_source_chain_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) + if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { + // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. + return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) + } + return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil +} + +func IBCDestinationChainCallback( + cache Cache, + checksum []byte, + env []byte, + msg []byte, + gasMeter *types.GasMeter, + store types.KVStore, + api *types.GoAPI, + querier *Querier, + gasLimit uint64, + printDebug bool, +) ([]byte, types.GasReport, error) { + cs := makeView(checksum) + defer runtime.KeepAlive(checksum) + e := makeView(env) + defer runtime.KeepAlive(env) + msgBytes := makeView(msg) + defer runtime.KeepAlive(msg) + var pinner runtime.Pinner + pinner.Pin(gasMeter) + checkAndPinAPI(api, pinner) + checkAndPinQuerier(querier, pinner) + defer pinner.Unpin() + + callID := startCall() + defer endCall(callID) + + dbState := buildDBState(store, callID) + db := buildDB(&dbState, gasMeter) + a := buildAPI(api) + q := buildQuerier(querier) + var gasReport C.GasReport + errmsg := uninitializedUnmanagedVector() + + res, err := C.ibc_destination_chain_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index 223fe1edd..cc0f45133 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -532,9 +532,9 @@ func (vm *VM) IBCPacketTimeout( return &result, gasReport.UsedInternally, nil } -// IBCSourceChainCallback is available on IBC-enabled contracts with and is called when an -// the response for an outgoing packet (previously sent by this contract) -// is received +// IBCSourceChainCallback is available on IBC-enabled contracts with the corresponding entrypoint +// and should be called when the response (ack or timeout) for an outgoing callbacks-enabled packet +// (previously sent by this contract) is received. func (vm *VM) IBCSourceChainCallback( checksum Checksum, env types.Env, @@ -567,6 +567,40 @@ func (vm *VM) IBCSourceChainCallback( return &result, gasReport.UsedInternally, nil } +// IBCDestinationChainCallback is available on IBC-enabled contracts with the corresponding entrypoint +// and should be called when an incoming callbacks-enabled IBC packet is received. +func (vm *VM) IBCDestinationChainCallback( + checksum Checksum, + env types.Env, + msg types.IBCDestinationChainCallbackMsg, + store KVStore, + goapi GoAPI, + querier Querier, + gasMeter GasMeter, + gasLimit uint64, + deserCost types.UFraction, +) (*types.IBCBasicResult, uint64, error) { + envBin, err := json.Marshal(env) + if err != nil { + return nil, 0, err + } + msgBin, err := json.Marshal(msg) + if err != nil { + return nil, 0, err + } + data, gasReport, err := api.IBCSourceChainCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + if err != nil { + return nil, gasReport.UsedInternally, err + } + + var result types.IBCBasicResult + err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result) + if err != nil { + return nil, gasReport.UsedInternally, err + } + return &result, gasReport.UsedInternally, nil +} + func compileCost(code WasmCode) uint64 { // CostPerByte is how much CosmWasm gas is charged *per byte* for compiling WASM code. // Benchmarks and numbers (in SDK Gas) were discussed in: diff --git a/libwasmvm/Cargo.lock b/libwasmvm/Cargo.lock index e30f7ffd0..a3525a2a1 100644 --- a/libwasmvm/Cargo.lock +++ b/libwasmvm/Cargo.lock @@ -1146,9 +1146,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1786,7 +1786,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -2067,7 +2067,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -2080,7 +2080,7 @@ version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -2326,7 +2326,7 @@ dependencies = [ "bytesize", "derive_builder", "hex", - "indexmap 2.2.5", + "indexmap 2.2.6", "schemars", "semver", "serde", @@ -2417,7 +2417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ "bitflags 2.4.2", - "indexmap 2.2.5", + "indexmap 2.2.6", "semver", ] diff --git a/libwasmvm/bindings.h b/libwasmvm/bindings.h index 50273b95d..b8dd742bb 100644 --- a/libwasmvm/bindings.h +++ b/libwasmvm/bindings.h @@ -609,6 +609,18 @@ struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache, struct GasReport *gas_report, struct UnmanagedVector *error_msg); +struct UnmanagedVector ibc_destination_chain_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); + struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); void destroy_unmanaged_vector(struct UnmanagedVector v); diff --git a/libwasmvm/src/calls.rs b/libwasmvm/src/calls.rs index a09112db9..89d57c2ec 100644 --- a/libwasmvm/src/calls.rs +++ b/libwasmvm/src/calls.rs @@ -8,10 +8,10 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime}; use cosmwasm_std::Checksum; use cosmwasm_vm::{ call_execute_raw, call_ibc_channel_close_raw, call_ibc_channel_connect_raw, - call_ibc_channel_open_raw, call_ibc_packet_ack_raw, call_ibc_packet_receive_raw, - call_ibc_packet_timeout_raw, call_ibc_source_chain_callback_raw, call_instantiate_raw, - call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend, Cache, Instance, - InstanceOptions, VmResult, + call_ibc_channel_open_raw, call_ibc_destination_chain_callback_raw, call_ibc_packet_ack_raw, + call_ibc_packet_receive_raw, call_ibc_packet_timeout_raw, call_ibc_source_chain_callback_raw, + call_instantiate_raw, call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend, + Cache, Instance, InstanceOptions, VmResult, }; use crate::api::GoApi; @@ -426,6 +426,36 @@ pub extern "C" fn ibc_source_chain_callback( ) } +#[no_mangle] +pub extern "C" fn ibc_destination_chain_callback( + cache: *mut cache_t, + checksum: ByteSliceView, + env: ByteSliceView, + msg: ByteSliceView, + db: Db, + api: GoApi, + querier: GoQuerier, + gas_limit: u64, + print_debug: bool, + gas_report: Option<&mut GasReport>, + error_msg: Option<&mut UnmanagedVector>, +) -> UnmanagedVector { + call_2_args( + call_ibc_destination_chain_callback_raw, + cache, + checksum, + env, + msg, + db, + api, + querier, + gas_limit, + print_debug, + gas_report, + error_msg, + ) +} + type VmFn2Args = fn( instance: &mut Instance, arg1: &[u8], diff --git a/types/ibc.go b/types/ibc.go index 36bfcd81e..39f7aa1dc 100644 --- a/types/ibc.go +++ b/types/ibc.go @@ -168,6 +168,23 @@ type IBCSourceChainCallbackMsg struct { Timeout *IBCPacketTimeoutMsg `json:"Timeout,omitempty"` } +// The message type of the IBC destination chain callback. +// This is just an alias for [`IbcPacketReceiveMsg`] to add some documentation. +// +// The IBC destination chain callback is needed for cases where someone triggers the sending of an +// IBC packet through some other message (i.e. not through [`IbcMsg::SendPacket`]) and +// your contract needs to know that it received this. +// A prominent example is the [`IbcMsg::Transfer`] message. Without callbacks, you cannot know +// that someone sent you IBC coins. +// +// Note that there are some prerequisites that need to be fulfilled to receive source chain callbacks: +// - The contract must implement the `ibc_destination_chain_callback` entrypoint. +// - The module that receives the packet must be wrapped by an `IBCMiddleware` +// (i.e. the destination chain needs to support callbacks for the message you are being sent). +// - You have to add json-encoded [`IbcCallbackData`] to a specific field of the message. +// For `IbcMsg::Transfer`, this is the `memo` field. +type IBCDestinationChainCallbackMsg = IBCPacketReceiveMsg + // TODO: test what the sdk Order.String() represents and how to parse back // Proto files: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/core/channel/v1/channel.proto#L69-L80 // Auto-gen code: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/ibc/core/04-channel/types/channel.pb.go#L70-L101 From c6a435ec70f9248ad7b4455585dbbae4c18ce2c5 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 Apr 2024 15:33:43 +0200 Subject: [PATCH 05/10] Update destination chain callback type --- types/ibc.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/types/ibc.go b/types/ibc.go index 39f7aa1dc..4c2911612 100644 --- a/types/ibc.go +++ b/types/ibc.go @@ -169,11 +169,11 @@ type IBCSourceChainCallbackMsg struct { } // The message type of the IBC destination chain callback. -// This is just an alias for [`IbcPacketReceiveMsg`] to add some documentation. // // The IBC destination chain callback is needed for cases where someone triggers the sending of an // IBC packet through some other message (i.e. not through [`IbcMsg::SendPacket`]) and // your contract needs to know that it received this. +// The callback is called after the packet was successfully acknowledged on the destination chain. // A prominent example is the [`IbcMsg::Transfer`] message. Without callbacks, you cannot know // that someone sent you IBC coins. // @@ -183,7 +183,10 @@ type IBCSourceChainCallbackMsg struct { // (i.e. the destination chain needs to support callbacks for the message you are being sent). // - You have to add json-encoded [`IbcCallbackData`] to a specific field of the message. // For `IbcMsg::Transfer`, this is the `memo` field. -type IBCDestinationChainCallbackMsg = IBCPacketReceiveMsg +type IBCDestinationChainCallbackMsg struct { + Ack IBCAcknowledgement `json:"ack"` + Packet IBCPacket `json:"packet"` +} // TODO: test what the sdk Order.String() represents and how to parse back // Proto files: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/core/channel/v1/channel.proto#L69-L80 From 628249fe0c4a36e427c6a1433003097d89fd069a Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 5 Apr 2024 17:06:11 +0200 Subject: [PATCH 06/10] Fix destination chain callback --- lib_libwasmvm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index cc0f45133..1832264bf 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -588,7 +588,7 @@ func (vm *VM) IBCDestinationChainCallback( if err != nil { return nil, 0, err } - data, gasReport, err := api.IBCSourceChainCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCDestinationChainCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { return nil, gasReport.UsedInternally, err } From bc9921fef78ffe6063948d1e5e546396e39c5819 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 16 Apr 2024 14:37:06 +0200 Subject: [PATCH 07/10] Adjust IBC Callback types --- types/ibc.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/types/ibc.go b/types/ibc.go index 4c2911612..18f1b60cb 100644 --- a/types/ibc.go +++ b/types/ibc.go @@ -164,8 +164,8 @@ type IBCPacketTimeoutMsg struct { // For `IbcMsg::Transfer`, this is the `memo` field. // - The receiver of the callback must also be the sender of the message. type IBCSourceChainCallbackMsg struct { - Acknowledgement *IBCPacketAckMsg `json:"Acknowledgement,omitempty"` - Timeout *IBCPacketTimeoutMsg `json:"Timeout,omitempty"` + Acknowledgement *IBCPacketAckMsg `json:"acknowledgement,omitempty"` + Timeout *IBCPacketTimeoutMsg `json:"timeout,omitempty"` } // The message type of the IBC destination chain callback. @@ -184,8 +184,16 @@ type IBCSourceChainCallbackMsg struct { // - You have to add json-encoded [`IbcCallbackData`] to a specific field of the message. // For `IbcMsg::Transfer`, this is the `memo` field. type IBCDestinationChainCallbackMsg struct { - Ack IBCAcknowledgement `json:"ack"` - Packet IBCPacket `json:"packet"` + Ack IBCFullAcknowledgement `json:"ack"` + Packet IBCPacket `json:"packet"` +} + +// The acknowledgement written by the module on the destination chain. It is different from the [`crate::IbcAcknowledgement`] as it can be unsuccessful. +type IBCFullAcknowledgement struct { + // The acknowledgement data returned by the module. + Data []byte `json:"data"` + // Whether the acknowledgement was successful or not. + Success bool `json:"success"` } // TODO: test what the sdk Order.String() represents and how to parse back From 3852424b5387f2f341e235b52c9246c760e5fb2c Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 23 Apr 2024 13:20:37 +0200 Subject: [PATCH 08/10] Update ibc callback types --- types/ibc.go | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/types/ibc.go b/types/ibc.go index 18f1b60cb..621720fc7 100644 --- a/types/ibc.go +++ b/types/ibc.go @@ -148,24 +148,25 @@ type IBCPacketTimeoutMsg struct { Relayer string `json:"relayer"` } -// The type of IBC callback that is being called. +// The type of IBC source chain callback that is being called. // -// IBC callbacks are needed for cases where your contract triggers the sending of an IBC packet -// through some other message (i.e. not through [`IbcMsg::SendPacket`]) and needs to know whether -// or not the packet was successfully received on the other chain. A prominent example is the -// [`IbcMsg::Transfer`] message. -// Without callbacks, you cannot know whether the transfer was successful or not. +// IBC source chain callbacks are needed for cases where your contract triggers the sending of an IBC packet through some other message (i.e. not through [`IbcMsg::SendPacket`]) and needs to know whether or not the packet was successfully received on the other chain. A prominent example is the [`IbcMsg::Transfer`] message. Without callbacks, you cannot know whether the transfer was successful or not. // -// Note that there are some prerequisites that need to be fulfilled to receive source chain callbacks: -// - The contract must implement the `ibc_source_chain_callback` entrypoint. -// - The module that sends the packet must be wrapped by an `IBCMiddleware` -// (i.e. the source chain needs to support callbacks for the message you are sending). -// - You have to add json-encoded [`IbcCallbackData`] to a specific field of the message. -// For `IbcMsg::Transfer`, this is the `memo` field. -// - The receiver of the callback must also be the sender of the message. +// Note that there are some prerequisites that need to be fulfilled to receive source chain callbacks: - The contract must implement the `ibc_source_chain_callback` entrypoint. - The IBC application in the source chain must have support for the callbacks middleware. - You have to add serialized [`IbcCallbackRequest`] to a specific field of the message. For `IbcMsg::Transfer`, this is the `memo` field and it needs to be json-encoded. - The receiver of the callback must also be the sender of the message. type IBCSourceChainCallbackMsg struct { - Acknowledgement *IBCPacketAckMsg `json:"acknowledgement,omitempty"` - Timeout *IBCPacketTimeoutMsg `json:"timeout,omitempty"` + Acknowledgement *IBCAckCallbackMsg `json:"acknowledgement,omitempty"` + Timeout *IBCTimeoutCallbackMsg `json:"timeout,omitempty"` +} + +type IBCAckCallbackMsg struct { + Acknowledgement IBCAcknowledgement `json:"acknowledgement"` + OriginalPacket IBCPacket `json:"original_packet"` + Relayer string `json:"relayer"` +} + +type IBCTimeoutCallbackMsg struct { + Packet IBCPacket `json:"packet"` + Relayer string `json:"relayer"` } // The message type of the IBC destination chain callback. From cbe0b07c91d7fefcad5e58ede502a68b118c7c77 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 10 May 2024 15:41:18 +0200 Subject: [PATCH 09/10] Update callback types --- internal/api/bindings.h | 46 ++++++++++++++++++++--------------------- internal/api/lib.go | 8 +++---- lib_libwasmvm.go | 16 +++++++------- libwasmvm/bindings.h | 46 ++++++++++++++++++++--------------------- libwasmvm/src/calls.rs | 12 +++++------ types/ibc.go | 30 ++++++++++----------------- 6 files changed, 75 insertions(+), 83 deletions(-) diff --git a/internal/api/bindings.h b/internal/api/bindings.h index b8dd742bb..6e778a96f 100644 --- a/internal/api/bindings.h +++ b/internal/api/bindings.h @@ -597,29 +597,29 @@ struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, struct GasReport *gas_report, struct UnmanagedVector *error_msg); -struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - struct GasReport *gas_report, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_destination_chain_callback(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - struct GasReport *gas_report, - struct UnmanagedVector *error_msg); +struct UnmanagedVector ibc_source_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); + +struct UnmanagedVector ibc_destination_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); diff --git a/internal/api/lib.go b/internal/api/lib.go index 7fc47be86..56924437e 100644 --- a/internal/api/lib.go +++ b/internal/api/lib.go @@ -700,7 +700,7 @@ func IBCPacketTimeout( return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } -func IBCSourceChainCallback( +func IBCSourceCallback( cache Cache, checksum []byte, env []byte, @@ -734,7 +734,7 @@ func IBCSourceChainCallback( var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_source_chain_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) + res, err := C.ibc_source_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) @@ -742,7 +742,7 @@ func IBCSourceChainCallback( return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil } -func IBCDestinationChainCallback( +func IBCDestinationCallback( cache Cache, checksum []byte, env []byte, @@ -776,7 +776,7 @@ func IBCDestinationChainCallback( var gasReport C.GasReport errmsg := uninitializedUnmanagedVector() - res, err := C.ibc_destination_chain_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) + res, err := C.ibc_destination_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg) if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success { // Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0. return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg) diff --git a/lib_libwasmvm.go b/lib_libwasmvm.go index 1832264bf..e4b28bd4a 100644 --- a/lib_libwasmvm.go +++ b/lib_libwasmvm.go @@ -532,13 +532,13 @@ func (vm *VM) IBCPacketTimeout( return &result, gasReport.UsedInternally, nil } -// IBCSourceChainCallback is available on IBC-enabled contracts with the corresponding entrypoint +// IBCSourceCallback is available on IBC-enabled contracts with the corresponding entrypoint // and should be called when the response (ack or timeout) for an outgoing callbacks-enabled packet // (previously sent by this contract) is received. -func (vm *VM) IBCSourceChainCallback( +func (vm *VM) IBCSourceCallback( checksum Checksum, env types.Env, - msg types.IBCSourceChainCallbackMsg, + msg types.IBCSourceCallbackMsg, store KVStore, goapi GoAPI, querier Querier, @@ -554,7 +554,7 @@ func (vm *VM) IBCSourceChainCallback( if err != nil { return nil, 0, err } - data, gasReport, err := api.IBCSourceChainCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCSourceCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { return nil, gasReport.UsedInternally, err } @@ -567,12 +567,12 @@ func (vm *VM) IBCSourceChainCallback( return &result, gasReport.UsedInternally, nil } -// IBCDestinationChainCallback is available on IBC-enabled contracts with the corresponding entrypoint +// IBCDestinationCallback is available on IBC-enabled contracts with the corresponding entrypoint // and should be called when an incoming callbacks-enabled IBC packet is received. -func (vm *VM) IBCDestinationChainCallback( +func (vm *VM) IBCDestinationCallback( checksum Checksum, env types.Env, - msg types.IBCDestinationChainCallbackMsg, + msg types.IBCDestinationCallbackMsg, store KVStore, goapi GoAPI, querier Querier, @@ -588,7 +588,7 @@ func (vm *VM) IBCDestinationChainCallback( if err != nil { return nil, 0, err } - data, gasReport, err := api.IBCDestinationChainCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) + data, gasReport, err := api.IBCDestinationCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug) if err != nil { return nil, gasReport.UsedInternally, err } diff --git a/libwasmvm/bindings.h b/libwasmvm/bindings.h index b8dd742bb..6e778a96f 100644 --- a/libwasmvm/bindings.h +++ b/libwasmvm/bindings.h @@ -597,29 +597,29 @@ struct UnmanagedVector ibc_packet_timeout(struct cache_t *cache, struct GasReport *gas_report, struct UnmanagedVector *error_msg); -struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - struct GasReport *gas_report, - struct UnmanagedVector *error_msg); - -struct UnmanagedVector ibc_destination_chain_callback(struct cache_t *cache, - struct ByteSliceView checksum, - struct ByteSliceView env, - struct ByteSliceView msg, - struct Db db, - struct GoApi api, - struct GoQuerier querier, - uint64_t gas_limit, - bool print_debug, - struct GasReport *gas_report, - struct UnmanagedVector *error_msg); +struct UnmanagedVector ibc_source_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); + +struct UnmanagedVector ibc_destination_callback(struct cache_t *cache, + struct ByteSliceView checksum, + struct ByteSliceView env, + struct ByteSliceView msg, + struct Db db, + struct GoApi api, + struct GoQuerier querier, + uint64_t gas_limit, + bool print_debug, + struct GasReport *gas_report, + struct UnmanagedVector *error_msg); struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length); diff --git a/libwasmvm/src/calls.rs b/libwasmvm/src/calls.rs index 89d57c2ec..38d8fbf99 100644 --- a/libwasmvm/src/calls.rs +++ b/libwasmvm/src/calls.rs @@ -8,8 +8,8 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime}; use cosmwasm_std::Checksum; use cosmwasm_vm::{ call_execute_raw, call_ibc_channel_close_raw, call_ibc_channel_connect_raw, - call_ibc_channel_open_raw, call_ibc_destination_chain_callback_raw, call_ibc_packet_ack_raw, - call_ibc_packet_receive_raw, call_ibc_packet_timeout_raw, call_ibc_source_chain_callback_raw, + call_ibc_channel_open_raw, call_ibc_destination_callback_raw, call_ibc_packet_ack_raw, + call_ibc_packet_receive_raw, call_ibc_packet_timeout_raw, call_ibc_source_callback_raw, call_instantiate_raw, call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend, Cache, Instance, InstanceOptions, VmResult, }; @@ -397,7 +397,7 @@ pub extern "C" fn ibc_packet_timeout( } #[no_mangle] -pub extern "C" fn ibc_source_chain_callback( +pub extern "C" fn ibc_source_callback( cache: *mut cache_t, checksum: ByteSliceView, env: ByteSliceView, @@ -411,7 +411,7 @@ pub extern "C" fn ibc_source_chain_callback( error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( - call_ibc_source_chain_callback_raw, + call_ibc_source_callback_raw, cache, checksum, env, @@ -427,7 +427,7 @@ pub extern "C" fn ibc_source_chain_callback( } #[no_mangle] -pub extern "C" fn ibc_destination_chain_callback( +pub extern "C" fn ibc_destination_callback( cache: *mut cache_t, checksum: ByteSliceView, env: ByteSliceView, @@ -441,7 +441,7 @@ pub extern "C" fn ibc_destination_chain_callback( error_msg: Option<&mut UnmanagedVector>, ) -> UnmanagedVector { call_2_args( - call_ibc_destination_chain_callback_raw, + call_ibc_destination_callback_raw, cache, checksum, env, diff --git a/types/ibc.go b/types/ibc.go index 621720fc7..4fc69acae 100644 --- a/types/ibc.go +++ b/types/ibc.go @@ -148,12 +148,12 @@ type IBCPacketTimeoutMsg struct { Relayer string `json:"relayer"` } -// The type of IBC source chain callback that is being called. +// The type of IBC source callback that is being called. // -// IBC source chain callbacks are needed for cases where your contract triggers the sending of an IBC packet through some other message (i.e. not through [`IbcMsg::SendPacket`]) and needs to know whether or not the packet was successfully received on the other chain. A prominent example is the [`IbcMsg::Transfer`] message. Without callbacks, you cannot know whether the transfer was successful or not. +// IBC source callbacks are needed for cases where your contract triggers the sending of an IBC packet through some other message (i.e. not through [`IbcMsg::SendPacket`]) and needs to know whether or not the packet was successfully received on the other chain. A prominent example is the [`IbcMsg::Transfer`] message. Without callbacks, you cannot know whether the transfer was successful or not. // -// Note that there are some prerequisites that need to be fulfilled to receive source chain callbacks: - The contract must implement the `ibc_source_chain_callback` entrypoint. - The IBC application in the source chain must have support for the callbacks middleware. - You have to add serialized [`IbcCallbackRequest`] to a specific field of the message. For `IbcMsg::Transfer`, this is the `memo` field and it needs to be json-encoded. - The receiver of the callback must also be the sender of the message. -type IBCSourceChainCallbackMsg struct { +// Note that there are some prerequisites that need to be fulfilled to receive source callbacks: - The contract must implement the `ibc_source_callback` entrypoint. - The IBC application in the source chain must have support for the callbacks middleware. - You have to add serialized [`IbcCallbackRequest`] to a specific field of the message. For `IbcMsg::Transfer`, this is the `memo` field and it needs to be json-encoded. - The receiver of the callback must also be the sender of the message. +type IBCSourceCallbackMsg struct { Acknowledgement *IBCAckCallbackMsg `json:"acknowledgement,omitempty"` Timeout *IBCTimeoutCallbackMsg `json:"timeout,omitempty"` } @@ -169,32 +169,24 @@ type IBCTimeoutCallbackMsg struct { Relayer string `json:"relayer"` } -// The message type of the IBC destination chain callback. +// The message type of the IBC destination callback. // -// The IBC destination chain callback is needed for cases where someone triggers the sending of an +// The IBC destination callback is needed for cases where someone triggers the sending of an // IBC packet through some other message (i.e. not through [`IbcMsg::SendPacket`]) and // your contract needs to know that it received this. // The callback is called after the packet was successfully acknowledged on the destination chain. // A prominent example is the [`IbcMsg::Transfer`] message. Without callbacks, you cannot know // that someone sent you IBC coins. // -// Note that there are some prerequisites that need to be fulfilled to receive source chain callbacks: -// - The contract must implement the `ibc_destination_chain_callback` entrypoint. +// Note that there are some prerequisites that need to be fulfilled to receive source callbacks: +// - The contract must implement the `ibc_destination_callback` entrypoint. // - The module that receives the packet must be wrapped by an `IBCMiddleware` // (i.e. the destination chain needs to support callbacks for the message you are being sent). // - You have to add json-encoded [`IbcCallbackData`] to a specific field of the message. // For `IbcMsg::Transfer`, this is the `memo` field. -type IBCDestinationChainCallbackMsg struct { - Ack IBCFullAcknowledgement `json:"ack"` - Packet IBCPacket `json:"packet"` -} - -// The acknowledgement written by the module on the destination chain. It is different from the [`crate::IbcAcknowledgement`] as it can be unsuccessful. -type IBCFullAcknowledgement struct { - // The acknowledgement data returned by the module. - Data []byte `json:"data"` - // Whether the acknowledgement was successful or not. - Success bool `json:"success"` +type IBCDestinationCallbackMsg struct { + Ack IBCAcknowledgement `json:"ack"` + Packet IBCPacket `json:"packet"` } // TODO: test what the sdk Order.String() represents and how to parse back From ea935a7dfb4a717d711f2ca5717298e8a5f4b952 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 25 Jun 2024 16:46:07 +0200 Subject: [PATCH 10/10] Remove branch from deploy_to_git job --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 21b7e670a..70e94b7b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -505,4 +505,3 @@ workflows: only: - main - /^release\/.*/ - - ibc-callbacks # added temporarily