Skip to content

Commit 32a1401

Browse files
committed
Add destination chain callback
1 parent cc508db commit 32a1401

File tree

7 files changed

+163
-16
lines changed

7 files changed

+163
-16
lines changed

internal/api/bindings.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,18 @@ struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache,
609609
struct GasReport *gas_report,
610610
struct UnmanagedVector *error_msg);
611611

612+
struct UnmanagedVector ibc_destination_chain_callback(struct cache_t *cache,
613+
struct ByteSliceView checksum,
614+
struct ByteSliceView env,
615+
struct ByteSliceView msg,
616+
struct Db db,
617+
struct GoApi api,
618+
struct GoQuerier querier,
619+
uint64_t gas_limit,
620+
bool print_debug,
621+
struct GasReport *gas_report,
622+
struct UnmanagedVector *error_msg);
623+
612624
struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length);
613625

614626
void destroy_unmanaged_vector(struct UnmanagedVector v);

internal/api/lib.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ func IBCSourceChainCallback(
716716
defer runtime.KeepAlive(checksum)
717717
e := makeView(env)
718718
defer runtime.KeepAlive(env)
719-
ac := makeView(msg)
719+
msgBytes := makeView(msg)
720720
defer runtime.KeepAlive(msg)
721721
var pinner runtime.Pinner
722722
pinner.Pin(gasMeter)
@@ -734,7 +734,49 @@ func IBCSourceChainCallback(
734734
var gasReport C.GasReport
735735
errmsg := uninitializedUnmanagedVector()
736736

737-
res, err := C.ibc_source_chain_callback(cache.ptr, cs, e, ac, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg)
737+
res, err := C.ibc_source_chain_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg)
738+
if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success {
739+
// Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0.
740+
return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg)
741+
}
742+
return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil
743+
}
744+
745+
func IBCDestinationChainCallback(
746+
cache Cache,
747+
checksum []byte,
748+
env []byte,
749+
msg []byte,
750+
gasMeter *types.GasMeter,
751+
store types.KVStore,
752+
api *types.GoAPI,
753+
querier *Querier,
754+
gasLimit uint64,
755+
printDebug bool,
756+
) ([]byte, types.GasReport, error) {
757+
cs := makeView(checksum)
758+
defer runtime.KeepAlive(checksum)
759+
e := makeView(env)
760+
defer runtime.KeepAlive(env)
761+
msgBytes := makeView(msg)
762+
defer runtime.KeepAlive(msg)
763+
var pinner runtime.Pinner
764+
pinner.Pin(gasMeter)
765+
checkAndPinAPI(api, pinner)
766+
checkAndPinQuerier(querier, pinner)
767+
defer pinner.Unpin()
768+
769+
callID := startCall()
770+
defer endCall(callID)
771+
772+
dbState := buildDBState(store, callID)
773+
db := buildDB(&dbState, gasMeter)
774+
a := buildAPI(api)
775+
q := buildQuerier(querier)
776+
var gasReport C.GasReport
777+
errmsg := uninitializedUnmanagedVector()
778+
779+
res, err := C.ibc_destination_chain_callback(cache.ptr, cs, e, msgBytes, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg)
738780
if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success {
739781
// Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0.
740782
return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg)

lib_libwasmvm.go

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,9 +532,9 @@ func (vm *VM) IBCPacketTimeout(
532532
return &result, gasReport.UsedInternally, nil
533533
}
534534

535-
// IBCSourceChainCallback is available on IBC-enabled contracts with and is called when an
536-
// the response for an outgoing packet (previously sent by this contract)
537-
// is received
535+
// IBCSourceChainCallback is available on IBC-enabled contracts with the corresponding entrypoint
536+
// and should be called when the response (ack or timeout) for an outgoing callbacks-enabled packet
537+
// (previously sent by this contract) is received.
538538
func (vm *VM) IBCSourceChainCallback(
539539
checksum Checksum,
540540
env types.Env,
@@ -567,6 +567,40 @@ func (vm *VM) IBCSourceChainCallback(
567567
return &result, gasReport.UsedInternally, nil
568568
}
569569

570+
// IBCDestinationChainCallback is available on IBC-enabled contracts with the corresponding entrypoint
571+
// and should be called when an incoming callbacks-enabled IBC packet is received.
572+
func (vm *VM) IBCDestinationChainCallback(
573+
checksum Checksum,
574+
env types.Env,
575+
msg types.IBCDestinationChainCallbackMsg,
576+
store KVStore,
577+
goapi GoAPI,
578+
querier Querier,
579+
gasMeter GasMeter,
580+
gasLimit uint64,
581+
deserCost types.UFraction,
582+
) (*types.IBCBasicResult, uint64, error) {
583+
envBin, err := json.Marshal(env)
584+
if err != nil {
585+
return nil, 0, err
586+
}
587+
msgBin, err := json.Marshal(msg)
588+
if err != nil {
589+
return nil, 0, err
590+
}
591+
data, gasReport, err := api.IBCSourceChainCallback(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug)
592+
if err != nil {
593+
return nil, gasReport.UsedInternally, err
594+
}
595+
596+
var result types.IBCBasicResult
597+
err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result)
598+
if err != nil {
599+
return nil, gasReport.UsedInternally, err
600+
}
601+
return &result, gasReport.UsedInternally, nil
602+
}
603+
570604
func compileCost(code WasmCode) uint64 {
571605
// CostPerByte is how much CosmWasm gas is charged *per byte* for compiling WASM code.
572606
// Benchmarks and numbers (in SDK Gas) were discussed in:

libwasmvm/Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libwasmvm/bindings.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,18 @@ struct UnmanagedVector ibc_source_chain_callback(struct cache_t *cache,
609609
struct GasReport *gas_report,
610610
struct UnmanagedVector *error_msg);
611611

612+
struct UnmanagedVector ibc_destination_chain_callback(struct cache_t *cache,
613+
struct ByteSliceView checksum,
614+
struct ByteSliceView env,
615+
struct ByteSliceView msg,
616+
struct Db db,
617+
struct GoApi api,
618+
struct GoQuerier querier,
619+
uint64_t gas_limit,
620+
bool print_debug,
621+
struct GasReport *gas_report,
622+
struct UnmanagedVector *error_msg);
623+
612624
struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length);
613625

614626
void destroy_unmanaged_vector(struct UnmanagedVector v);

libwasmvm/src/calls.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use time::{format_description::well_known::Rfc3339, OffsetDateTime};
88
use cosmwasm_std::Checksum;
99
use cosmwasm_vm::{
1010
call_execute_raw, call_ibc_channel_close_raw, call_ibc_channel_connect_raw,
11-
call_ibc_channel_open_raw, call_ibc_packet_ack_raw, call_ibc_packet_receive_raw,
12-
call_ibc_packet_timeout_raw, call_ibc_source_chain_callback_raw, call_instantiate_raw,
13-
call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend, Cache, Instance,
14-
InstanceOptions, VmResult,
11+
call_ibc_channel_open_raw, call_ibc_destination_chain_callback_raw, call_ibc_packet_ack_raw,
12+
call_ibc_packet_receive_raw, call_ibc_packet_timeout_raw, call_ibc_source_chain_callback_raw,
13+
call_instantiate_raw, call_migrate_raw, call_query_raw, call_reply_raw, call_sudo_raw, Backend,
14+
Cache, Instance, InstanceOptions, VmResult,
1515
};
1616

1717
use crate::api::GoApi;
@@ -426,6 +426,36 @@ pub extern "C" fn ibc_source_chain_callback(
426426
)
427427
}
428428

429+
#[no_mangle]
430+
pub extern "C" fn ibc_destination_chain_callback(
431+
cache: *mut cache_t,
432+
checksum: ByteSliceView,
433+
env: ByteSliceView,
434+
msg: ByteSliceView,
435+
db: Db,
436+
api: GoApi,
437+
querier: GoQuerier,
438+
gas_limit: u64,
439+
print_debug: bool,
440+
gas_report: Option<&mut GasReport>,
441+
error_msg: Option<&mut UnmanagedVector>,
442+
) -> UnmanagedVector {
443+
call_2_args(
444+
call_ibc_destination_chain_callback_raw,
445+
cache,
446+
checksum,
447+
env,
448+
msg,
449+
db,
450+
api,
451+
querier,
452+
gas_limit,
453+
print_debug,
454+
gas_report,
455+
error_msg,
456+
)
457+
}
458+
429459
type VmFn2Args = fn(
430460
instance: &mut Instance<GoApi, GoStorage, GoQuerier>,
431461
arg1: &[u8],

types/ibc.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,23 @@ type IBCSourceChainCallbackMsg struct {
168168
Timeout *IBCPacketTimeoutMsg `json:"Timeout,omitempty"`
169169
}
170170

171+
// The message type of the IBC destination chain callback.
172+
// This is just an alias for [`IbcPacketReceiveMsg`] to add some documentation.
173+
//
174+
// The IBC destination chain callback is needed for cases where someone triggers the sending of an
175+
// IBC packet through some other message (i.e. not through [`IbcMsg::SendPacket`]) and
176+
// your contract needs to know that it received this.
177+
// A prominent example is the [`IbcMsg::Transfer`] message. Without callbacks, you cannot know
178+
// that someone sent you IBC coins.
179+
//
180+
// Note that there are some prerequisites that need to be fulfilled to receive source chain callbacks:
181+
// - The contract must implement the `ibc_destination_chain_callback` entrypoint.
182+
// - The module that receives the packet must be wrapped by an `IBCMiddleware`
183+
// (i.e. the destination chain needs to support callbacks for the message you are being sent).
184+
// - You have to add json-encoded [`IbcCallbackData`] to a specific field of the message.
185+
// For `IbcMsg::Transfer`, this is the `memo` field.
186+
type IBCDestinationChainCallbackMsg = IBCPacketReceiveMsg
187+
171188
// TODO: test what the sdk Order.String() represents and how to parse back
172189
// Proto files: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/core/channel/v1/channel.proto#L69-L80
173190
// Auto-gen code: https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/x/ibc/core/04-channel/types/channel.pb.go#L70-L101

0 commit comments

Comments
 (0)