diff --git a/internal/api/lib_test.go b/internal/api/lib_test.go index c99d6484e..880ef7646 100644 --- a/internal/api/lib_test.go +++ b/internal/api/lib_test.go @@ -66,8 +66,9 @@ func TestInitCacheWorksForNonExistentDir(t *testing.T) { func TestInitCacheErrorsForBrokenDir(t *testing.T) { // Use colon to make this fail on Windows // https://gist.github.com/doctaphred/d01d05291546186941e1b7ddc02034d3 - // On Unix we should not have permission to create this. - cannotBeCreated := "/foo:bar" + // On Unix we should not have permission to create inside /sys + // which is mounted read-only in most environments. + cannotBeCreated := "/sys/foo:bar" config := types.VMConfig{ Cache: types.CacheOptions{ BaseDir: cannotBeCreated, diff --git a/internal/wazeroimpl/runtime.go b/internal/wazeroimpl/runtime.go index b872246a9..e18fe752a 100644 --- a/internal/wazeroimpl/runtime.go +++ b/internal/wazeroimpl/runtime.go @@ -3,6 +3,7 @@ package wazeroimpl import ( "context" "encoding/hex" + "encoding/json" "fmt" "os" "runtime" @@ -106,11 +107,70 @@ func (c *Cache) registerHost(ctx context.Context, store types.KVStore, apiImpl * store.Delete(key) }), []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("db_remove") - // query_external - simplified: returns 0 length + // query_external - calls into the Go querier builder.NewFunctionBuilder().WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) { - resPtr := uint32(stack[2]) - _ = m.Memory().WriteUint32Le(resPtr, 0) - }), []api.ValueType{api.ValueTypeI64, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("query_external") + gasLimit := uint64(stack[0]) + reqPtr := uint32(stack[1]) + reqLen := uint32(stack[2]) + outPtr := uint32(stack[3]) + mem := m.Memory() + req, _ := mem.Read(reqPtr, reqLen) + if q != nil { + res := types.RustQuery(*q, req, gasLimit) + bz, _ := json.Marshal(res) + _ = mem.WriteUint32Le(outPtr, uint32(len(bz))) + mem.Write(outPtr+4, bz) + } else { + _ = mem.WriteUint32Le(outPtr, 0) + } + }), []api.ValueType{api.ValueTypeI64, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("query_external") + + // humanize_address + builder.NewFunctionBuilder().WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) { + srcPtr := uint32(stack[0]) + srcLen := uint32(stack[1]) + outPtr := uint32(stack[2]) + mem := m.Memory() + addr, _ := mem.Read(srcPtr, srcLen) + if apiImpl != nil { + res, _, err := apiImpl.HumanizeAddress(addr) + if err == nil { + _ = mem.WriteUint32Le(outPtr, uint32(len(res))) + mem.Write(outPtr+4, []byte(res)) + return + } + } + _ = mem.WriteUint32Le(outPtr, 0) + }), []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("humanize_address") + + // canonicalize_address + builder.NewFunctionBuilder().WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) { + srcPtr := uint32(stack[0]) + srcLen := uint32(stack[1]) + outPtr := uint32(stack[2]) + mem := m.Memory() + addrBytes, _ := mem.Read(srcPtr, srcLen) + if apiImpl != nil { + res, _, err := apiImpl.CanonicalizeAddress(string(addrBytes)) + if err == nil { + _ = mem.WriteUint32Le(outPtr, uint32(len(res))) + mem.Write(outPtr+4, res) + return + } + } + _ = mem.WriteUint32Le(outPtr, 0) + }), []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("canonicalize_address") + + // validate_address + builder.NewFunctionBuilder().WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) { + srcPtr := uint32(stack[0]) + srcLen := uint32(stack[1]) + mem := m.Memory() + addrBytes, _ := mem.Read(srcPtr, srcLen) + if apiImpl != nil { + _, _ = apiImpl.ValidateAddress(string(addrBytes)) + } + }), []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("validate_address") return builder.Instantiate(ctx) } @@ -154,3 +214,87 @@ func (c *Cache) Execute(ctx context.Context, checksum types.Checksum, env, info, } return err } + +// callFunc instantiates the module and executes the given exported function name. +func (c *Cache) callFunc(ctx context.Context, checksum types.Checksum, funcName string, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + compiled, ok := c.getModule(checksum) + if !ok { + return fmt.Errorf("module not found") + } + _, err := c.registerHost(ctx, store, apiImpl, q, gm) + if err != nil { + return err + } + mod, err := c.runtime.InstantiateModule(ctx, compiled, wazero.NewModuleConfig()) + if err != nil { + return err + } + if fn := mod.ExportedFunction(funcName); fn != nil { + _, err = fn.Call(ctx) + } + return err +} + +func (c *Cache) Query(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "query", store, apiImpl, q, gm) +} + +func (c *Cache) Migrate(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "migrate", store, apiImpl, q, gm) +} + +func (c *Cache) Sudo(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "sudo", store, apiImpl, q, gm) +} + +func (c *Cache) Reply(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "reply", store, apiImpl, q, gm) +} + +func (c *Cache) IBCChannelOpen(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_channel_open", store, apiImpl, q, gm) +} + +func (c *Cache) IBCChannelConnect(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_channel_connect", store, apiImpl, q, gm) +} + +func (c *Cache) IBCChannelClose(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_channel_close", store, apiImpl, q, gm) +} + +func (c *Cache) IBCPacketReceive(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_packet_receive", store, apiImpl, q, gm) +} + +func (c *Cache) IBCPacketAck(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_packet_ack", store, apiImpl, q, gm) +} + +func (c *Cache) IBCPacketTimeout(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_packet_timeout", store, apiImpl, q, gm) +} + +func (c *Cache) IBCSourceCallback(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_source_callback", store, apiImpl, q, gm) +} + +func (c *Cache) IBCDestinationCallback(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc_destination_callback", store, apiImpl, q, gm) +} + +func (c *Cache) IBC2PacketReceive(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc2_packet_receive", store, apiImpl, q, gm) +} + +func (c *Cache) IBC2PacketAck(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc2_packet_ack", store, apiImpl, q, gm) +} + +func (c *Cache) IBC2PacketTimeout(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc2_packet_timeout", store, apiImpl, q, gm) +} + +func (c *Cache) IBC2PacketSend(ctx context.Context, checksum types.Checksum, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) error { + return c.callFunc(ctx, checksum, "ibc2_packet_send", store, apiImpl, q, gm) +} diff --git a/lib_libwasmvm_wazero.go b/lib_libwasmvm_wazero.go index 77fade170..c949a8201 100644 --- a/lib_libwasmvm_wazero.go +++ b/lib_libwasmvm_wazero.go @@ -122,69 +122,121 @@ func (vm *VM) Execute(checksum Checksum, env types.Env, info types.MessageInfo, } func (vm *VM) Query(checksum Checksum, env types.Env, queryMsg []byte, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.QueryResult, uint64, error) { - return nil, 0, fmt.Errorf("Query not supported in wazero VM") + if err := vm.cache.Query(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.QueryResult{}, 0, nil } func (vm *VM) Migrate(checksum Checksum, env types.Env, migrateMsg []byte, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.ContractResult, uint64, error) { - return nil, 0, fmt.Errorf("Migrate not supported in wazero VM") + if err := vm.cache.Migrate(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.ContractResult{}, 0, nil } func (vm *VM) MigrateWithInfo(checksum Checksum, env types.Env, migrateMsg []byte, migrateInfo types.MigrateInfo, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.ContractResult, uint64, error) { - return nil, 0, fmt.Errorf("MigrateWithInfo not supported in wazero VM") + // reuse migrate for now + if err := vm.cache.Migrate(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.ContractResult{}, 0, nil } func (vm *VM) Sudo(checksum Checksum, env types.Env, sudoMsg []byte, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.ContractResult, uint64, error) { - return nil, 0, fmt.Errorf("Sudo not supported in wazero VM") + if err := vm.cache.Sudo(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.ContractResult{}, 0, nil } func (vm *VM) Reply(checksum Checksum, env types.Env, reply types.Reply, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.ContractResult, uint64, error) { - return nil, 0, fmt.Errorf("Reply not supported in wazero VM") + if err := vm.cache.Reply(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.ContractResult{}, 0, nil } func (vm *VM) IBCChannelOpen(checksum Checksum, env types.Env, msg types.IBCChannelOpenMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCChannelOpenResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCChannelOpen not supported in wazero VM") + if err := vm.cache.IBCChannelOpen(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCChannelOpenResult{}, 0, nil } func (vm *VM) IBCChannelConnect(checksum Checksum, env types.Env, msg types.IBCChannelConnectMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCChannelConnect not supported in wazero VM") + if err := vm.cache.IBCChannelConnect(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBCChannelClose(checksum Checksum, env types.Env, msg types.IBCChannelCloseMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCChannelClose not supported in wazero VM") + if err := vm.cache.IBCChannelClose(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBCPacketReceive(checksum Checksum, env types.Env, msg types.IBCPacketReceiveMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCReceiveResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCPacketReceive not supported in wazero VM") + if err := vm.cache.IBCPacketReceive(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCReceiveResult{}, 0, nil } func (vm *VM) IBCPacketAck(checksum Checksum, env types.Env, msg types.IBCPacketAckMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCPacketAck not supported in wazero VM") + if err := vm.cache.IBCPacketAck(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBCPacketTimeout(checksum Checksum, env types.Env, msg types.IBCPacketTimeoutMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCPacketTimeout not supported in wazero VM") + if err := vm.cache.IBCPacketTimeout(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBCSourceCallback(checksum Checksum, env types.Env, msg types.IBCSourceCallbackMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCSourceCallback not supported in wazero VM") + if err := vm.cache.IBCSourceCallback(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBCDestinationCallback(checksum Checksum, env types.Env, msg types.IBCDestinationCallbackMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBCDestinationCallback not supported in wazero VM") + if err := vm.cache.IBCDestinationCallback(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBC2PacketAck(checksum Checksum, env types.Env, msg types.IBC2AcknowledgeMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBC2PacketAck not supported in wazero VM") + if err := vm.cache.IBC2PacketAck(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBC2PacketReceive(checksum Checksum, env types.Env, msg types.IBC2PacketReceiveMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCReceiveResult, uint64, error) { - return nil, 0, fmt.Errorf("IBC2PacketReceive not supported in wazero VM") + if err := vm.cache.IBC2PacketReceive(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCReceiveResult{}, 0, nil } func (vm *VM) IBC2PacketTimeout(checksum Checksum, env types.Env, msg types.IBC2PacketTimeoutMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBC2PacketTimeout not supported in wazero VM") + if err := vm.cache.IBC2PacketTimeout(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil } func (vm *VM) IBC2PacketSend(checksum Checksum, env types.Env, msg types.IBC2PacketSendMsg, store KVStore, goapi GoAPI, querier Querier, gasMeter GasMeter, gasLimit uint64, deserCost types.UFraction) (*types.IBCBasicResult, uint64, error) { - return nil, 0, fmt.Errorf("IBC2PacketSend not supported in wazero VM") + if err := vm.cache.IBC2PacketSend(context.Background(), checksum, store, &goapi, &querier, gasMeter); err != nil { + return nil, 0, err + } + return &types.IBCBasicResult{}, 0, nil }