Skip to content

Commit 71df726

Browse files
committed
feat: Add IBCv2 timeout entrypoint
1 parent bef437e commit 71df726

File tree

8 files changed

+268
-97
lines changed

8 files changed

+268
-97
lines changed

internal/api/bindings.h

+17
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ typedef struct AnalysisReport {
215215
* This does not guarantee they are functional or even have the correct signatures.
216216
*/
217217
bool has_ibc_entry_points;
218+
/**
219+
* `true` if and only if all required ibc2 exports exist as exported functions.
220+
* This does not guarantee they are functional or even have the correct signatures.
221+
*/
222+
bool has_ibc2_entry_points;
218223
/**
219224
* A UTF-8 encoded comma separated list of all entrypoints that
220225
* are exported by the contract.
@@ -645,6 +650,18 @@ struct UnmanagedVector ibc2_packet_receive(struct cache_t *cache,
645650
struct GasReport *gas_report,
646651
struct UnmanagedVector *error_msg);
647652

653+
struct UnmanagedVector ibc2_packet_timeout(struct cache_t *cache,
654+
struct ByteSliceView checksum,
655+
struct ByteSliceView env,
656+
struct ByteSliceView msg,
657+
struct Db db,
658+
struct GoApi api,
659+
struct GoQuerier querier,
660+
uint64_t gas_limit,
661+
bool print_debug,
662+
struct GasReport *gas_report,
663+
struct UnmanagedVector *error_msg);
664+
648665
struct UnmanagedVector new_unmanaged_vector(bool nil, const uint8_t *ptr, uintptr_t length);
649666

650667
void destroy_unmanaged_vector(struct UnmanagedVector v);

internal/api/lib.go

+43-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"os"
1111
"path/filepath"
1212
"runtime"
13-
"slices"
1413
"strings"
1514
"syscall"
1615

@@ -164,11 +163,10 @@ func AnalyzeCode(cache Cache, checksum []byte) (*types.AnalysisReport, error) {
164163
requiredCapabilities := string(copyAndDestroyUnmanagedVector(report.required_capabilities))
165164
entrypoints := string(copyAndDestroyUnmanagedVector(report.entrypoints))
166165
entrypoints_array := strings.Split(entrypoints, ",")
167-
hasIBC2EntryPoints := slices.Contains(entrypoints_array, "ibc2_packet_receive")
168166

169167
res := types.AnalysisReport{
170168
HasIBCEntryPoints: bool(report.has_ibc_entry_points),
171-
HasIBC2EntryPoints: hasIBC2EntryPoints,
169+
HasIBC2EntryPoints: bool(report.has_ibc2_entry_points),
172170
RequiredCapabilities: requiredCapabilities,
173171
Entrypoints: entrypoints_array,
174172
ContractMigrateVersion: optionalU64ToPtr(report.contract_migrate_version),
@@ -723,6 +721,48 @@ func IBC2PacketReceive(
723721
return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil
724722
}
725723

724+
func IBC2PacketTimeout(
725+
cache Cache,
726+
checksum []byte,
727+
env []byte,
728+
payload []byte,
729+
gasMeter *types.GasMeter,
730+
store types.KVStore,
731+
api *types.GoAPI,
732+
querier *Querier,
733+
gasLimit uint64,
734+
printDebug bool,
735+
) ([]byte, types.GasReport, error) {
736+
cs := makeView(checksum)
737+
defer runtime.KeepAlive(checksum)
738+
e := makeView(env)
739+
defer runtime.KeepAlive(env)
740+
pa := makeView(payload)
741+
defer runtime.KeepAlive(payload)
742+
var pinner runtime.Pinner
743+
pinner.Pin(gasMeter)
744+
checkAndPinAPI(api, pinner)
745+
checkAndPinQuerier(querier, pinner)
746+
defer pinner.Unpin()
747+
748+
callID := startCall()
749+
defer endCall(callID)
750+
751+
dbState := buildDBState(store, callID)
752+
db := buildDB(&dbState, gasMeter)
753+
a := buildAPI(api)
754+
q := buildQuerier(querier)
755+
var gasReport C.GasReport
756+
errmsg := uninitializedUnmanagedVector()
757+
758+
res, err := C.ibc2_packet_timeout(cache.ptr, cs, e, pa, db, a, q, cu64(gasLimit), cbool(printDebug), &gasReport, &errmsg)
759+
if err != nil && err.(syscall.Errno) != C.ErrnoValue_Success {
760+
// Depending on the nature of the error, `gasUsed` will either have a meaningful value, or just 0.
761+
return nil, convertGasReport(gasReport), errorWithMessage(err, errmsg)
762+
}
763+
return copyAndDestroyUnmanagedVector(res), convertGasReport(gasReport), nil
764+
}
765+
726766
func IBCPacketAck(
727767
cache Cache,
728768
checksum []byte,

lib_libwasmvm.go

+35
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,41 @@ func (vm *VM) IBC2PacketReceive(
712712
return &result, gasReport.UsedInternally, nil
713713
}
714714

715+
// IBC2PacketTimeout is available on IBCv2-enabled contracts and is called when an
716+
// outgoing packet (previously sent by this contract) will provably never be executed.
717+
// Usually handled like ack returning an error
718+
func (vm *VM) IBC2PacketTimeout(
719+
checksum Checksum,
720+
env types.Env,
721+
msg types.IBC2PacketTimeoutMsg,
722+
store KVStore,
723+
goapi GoAPI,
724+
querier Querier,
725+
gasMeter GasMeter,
726+
gasLimit uint64,
727+
deserCost types.UFraction,
728+
) (*types.IBCBasicResult, uint64, error) {
729+
envBin, err := json.Marshal(env)
730+
if err != nil {
731+
return nil, 0, err
732+
}
733+
msgBin, err := json.Marshal(msg)
734+
if err != nil {
735+
return nil, 0, err
736+
}
737+
data, gasReport, err := api.IBC2PacketTimeout(vm.cache, checksum, envBin, msgBin, &gasMeter, store, &goapi, &querier, gasLimit, vm.printDebug)
738+
if err != nil {
739+
return nil, gasReport.UsedInternally, err
740+
}
741+
742+
var result types.IBCBasicResult
743+
err = DeserializeResponse(gasLimit, deserCost, &gasReport, data, &result)
744+
if err != nil {
745+
return nil, gasReport.UsedInternally, err
746+
}
747+
return &result, gasReport.UsedInternally, nil
748+
}
749+
715750
func compileCost(code WasmCode) uint64 {
716751
// CostPerByte is how much CosmWasm gas is charged *per byte* for compiling WASM code.
717752
// Benchmarks and numbers (in SDK Gas) were discussed in:

0 commit comments

Comments
 (0)