Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ a.out

# direnv Nix stuff
.direnv/

reference-code/
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
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 v0.0.0
golang.org/x/sys v0.16.0
)

replace github.com/tetratelabs/wazero => ./reference-code/wazero

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.3.1 // indirect
Expand Down
145 changes: 145 additions & 0 deletions internal/wazeroimpl/runtime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package wazeroimpl

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

"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
memLimit uint32 // in wasm pages
}

// InitCache creates a new wazero Runtime with memory limits similar to api.InitCache.
func InitCache(config types.VMConfig) (*Cache, error) {
ctx := context.Background()
r := wazero.NewRuntime(ctx)
limitPages := config.Cache.InstanceMemoryLimitBytes.Uint32() / 65536

Check failure on line 25 in internal/wazeroimpl/runtime.go

View workflow job for this annotation

GitHub Actions / lint

config.Cache.InstanceMemoryLimitBytes.Uint32 undefined (type types.Size has no field or method Uint32, but does have unexported field uint32) (typecheck)
return &Cache{
runtime: r,
modules: make(map[string]wazero.CompiledModule),
memLimit: limitPages,
}, 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(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(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(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(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().WithMemoryLimitPages(c.memLimit))
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().WithMemoryLimitPages(c.memLimit))
if err != nil {
return err
}
if fn := mod.ExportedFunction("execute"); fn != nil {
_, err = fn.Call(ctx)
}
return err
}
Loading