Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ a.out

# direnv Nix stuff
.direnv/

reference-code/
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
module github.com/CosmWasm/wasmvm/v3

go 1.22
go 1.22.0

toolchain go1.23.8

require (
github.com/google/btree v1.0.0
github.com/shamaton/msgpack/v2 v2.2.0
github.com/stretchr/testify v1.8.1
github.com/tetratelabs/wazero v1.9.0
golang.org/x/sys v0.16.0
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
144 changes: 144 additions & 0 deletions internal/wazeroimpl/runtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package wazeroimpl

import (
"context"
"encoding/hex"
"fmt"
"unsafe"

"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"

"github.com/CosmWasm/wasmvm/v3/types"
)

// Cache manages a wazero runtime and compiled modules.
type Cache struct {
runtime wazero.Runtime
modules map[string]wazero.CompiledModule
}

// InitCache creates a new wazero Runtime with memory limits similar to api.InitCache.
func InitCache(config types.VMConfig) (*Cache, error) {
ctx := context.Background()
limitBytes := *(*uint32)(unsafe.Pointer(&config.Cache.InstanceMemoryLimitBytes))
r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().WithMemoryLimitPages(limitBytes/65536))
return &Cache{
runtime: r,
modules: make(map[string]wazero.CompiledModule),
}, nil
}

// Close releases all resources of the runtime.
func (c *Cache) Close(ctx context.Context) error {
if c.runtime != nil {
return c.runtime.Close(ctx)
}
return nil
}

// Compile stores a compiled module under the given checksum.
func (c *Cache) Compile(ctx context.Context, checksum types.Checksum, wasm []byte) error {
mod, err := c.runtime.CompileModule(ctx, wasm)
if err != nil {
return err
}
c.modules[hex.EncodeToString(checksum)] = mod
return nil
}

// getModule returns the compiled module for the checksum.
func (c *Cache) getModule(checksum types.Checksum) (wazero.CompiledModule, bool) {
mod, ok := c.modules[hex.EncodeToString(checksum)]
return mod, ok
}

// registerHost builds an env module with callbacks for the given state.
func (c *Cache) registerHost(ctx context.Context, store types.KVStore, apiImpl *types.GoAPI, q *types.Querier, gm types.GasMeter) (api.Module, error) {
builder := c.runtime.NewHostModuleBuilder("env")

// db_read
builder.NewFunctionBuilder().WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) {
keyPtr := uint32(stack[0])
keyLen := uint32(stack[1])
outPtr := uint32(stack[2])
mem := m.Memory()
key, _ := mem.Read(keyPtr, keyLen)
value := store.Get(key)
if value == nil {
_ = mem.WriteUint32Le(outPtr, 0)
return
}
_ = mem.WriteUint32Le(outPtr, uint32(len(value)))
mem.Write(outPtr+4, value)
}), []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("db_read")

// db_write
builder.NewFunctionBuilder().WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) {
keyPtr := uint32(stack[0])
keyLen := uint32(stack[1])
valPtr := uint32(stack[2])
valLen := uint32(stack[3])
mem := m.Memory()
key, _ := mem.Read(keyPtr, keyLen)
val, _ := mem.Read(valPtr, valLen)
store.Set(key, val)
}), []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("db_write")

// db_remove
builder.NewFunctionBuilder().WithGoModuleFunction(api.GoModuleFunc(func(ctx context.Context, m api.Module, stack []uint64) {
keyPtr := uint32(stack[0])
keyLen := uint32(stack[1])
mem := m.Memory()
key, _ := mem.Read(keyPtr, keyLen)
store.Delete(key)
}), []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{}).Export("db_remove")

// query_external - simplified: returns 0 length
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")

return builder.Instantiate(ctx)
}

// Instantiate loads and runs the contract's instantiate function.
func (c *Cache) Instantiate(ctx context.Context, checksum types.Checksum, env, info, msg []byte, 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("instantiate"); fn != nil {
_, err = fn.Call(ctx)
}
return err
}

// Execute runs the contract's execute function.
func (c *Cache) Execute(ctx context.Context, checksum types.Checksum, env, info, msg []byte, 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("execute"); fn != nil {
_, err = fn.Call(ctx)
}
return err
}
Loading