github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/vm/wasmtime/instance_runtime.go (about)

     1  package wasmtime
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  
     7  	"github.com/bytecodealliance/wasmtime-go/v17"
     8  	"github.com/pkg/errors"
     9  
    10  	"github.com/machinefi/w3bstream/pkg/depends/kit/logr"
    11  	"github.com/machinefi/w3bstream/pkg/types"
    12  )
    13  
    14  var (
    15  	ErrNotLinked           = errors.New("not linked")
    16  	ErrAlreadyInstantiated = errors.New("already instantiated")
    17  	ErrNotInstantiated     = errors.New("not instantiated")
    18  	ErrFuncNotImported     = errors.New("func not imported")
    19  	ErrAlreadyLinked       = errors.New("already linked")
    20  )
    21  
    22  type (
    23  	Runtime struct {
    24  		module   *wasmtime.Module
    25  		linker   *wasmtime.Linker
    26  		store    *wasmtime.Store
    27  		instance *wasmtime.Instance
    28  		engine   *wasmtime.Engine
    29  	}
    30  )
    31  
    32  func NewRuntime() *Runtime {
    33  	config := wasmtime.NewConfig()
    34  	config.SetConsumeFuel(true)
    35  	engine := wasmtime.NewEngineWithConfig(config)
    36  	return &Runtime{
    37  		engine: engine,
    38  	}
    39  }
    40  
    41  func (rt *Runtime) Link(lk ABILinker, code []byte) error {
    42  	if rt.module != nil {
    43  		return ErrAlreadyLinked
    44  	}
    45  	linker := wasmtime.NewLinker(rt.engine)
    46  	if err := lk.LinkABI(func(module, name string, fn interface{}) error {
    47  		return linker.FuncWrap(module, name, fn)
    48  	}); err != nil {
    49  		return err
    50  	}
    51  	if err := linker.DefineWasi(); err != nil {
    52  		return err
    53  	}
    54  	rt.linker = linker
    55  	module, err := wasmtime.NewModule(rt.engine, code)
    56  	if err != nil {
    57  		return err
    58  	}
    59  	rt.module = module
    60  	return nil
    61  }
    62  
    63  func (rt *Runtime) Instantiate(ctx context.Context) error {
    64  	ctx, l := logr.Start(ctx, "modules.vm.wasmtime.Runtime.Instantiate")
    65  	defer l.End()
    66  
    67  	if rt.module == nil {
    68  		return ErrNotLinked
    69  	}
    70  	if rt.instance != nil {
    71  		return ErrAlreadyInstantiated
    72  	}
    73  	store := wasmtime.NewStore(rt.engine)
    74  	store.SetWasi(wasmtime.NewWasiConfig())
    75  	if fuel, _ := types.MaxWasmConsumeFuelFromContext(ctx); fuel > 0 {
    76  		if err := store.SetFuel(fuel); err != nil {
    77  			return err
    78  		}
    79  	}
    80  
    81  	instance, err := rt.linker.Instantiate(store, rt.module)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	rt.instance = instance
    86  	rt.store = store
    87  
    88  	return nil
    89  }
    90  
    91  func (rt *Runtime) Deinstantiate(ctx context.Context) {
    92  	ctx, l := logr.Start(ctx, "modules.vm.wasmtime.Runtime.Deinstantiate")
    93  	defer l.End()
    94  
    95  	rt.instance = nil
    96  	rt.store = nil
    97  }
    98  
    99  func (rt *Runtime) newMemory() []byte {
   100  	return rt.instance.GetExport(rt.store, "memory").Memory().UnsafeData(rt.store)
   101  }
   102  
   103  func (rt *Runtime) alloc(size int32) (int32, []byte, error) {
   104  	fn := rt.instance.GetExport(rt.store, "alloc")
   105  	if fn == nil {
   106  		return 0, nil, errors.New("alloc is nil")
   107  	}
   108  	result, err := fn.Func().Call(rt.store, size)
   109  	if err != nil {
   110  		return 0, nil, err
   111  	}
   112  	return result.(int32), rt.newMemory(), nil
   113  }
   114  
   115  func putUint32Le(buf []byte, vmAddr int32, val uint32) error {
   116  	if int32(len(buf)) < vmAddr+4 {
   117  		return errors.New("overflow")
   118  	}
   119  	binary.LittleEndian.PutUint32(buf[vmAddr:], val)
   120  	return nil
   121  }
   122  
   123  func (rt *Runtime) Call(ctx context.Context, name string, args ...interface{}) (interface{}, error) {
   124  	ctx, l := logr.Start(ctx, "modules.vm.wasmtime.Runtime.Call", "func", name)
   125  	defer l.End()
   126  
   127  	if rt.module == nil {
   128  		return nil, ErrNotLinked
   129  	}
   130  	if rt.instance == nil {
   131  		return nil, ErrNotInstantiated
   132  	}
   133  	fn := rt.instance.GetFunc(rt.store, name)
   134  	if fn == nil {
   135  		return nil, ErrFuncNotImported
   136  	}
   137  	return fn.Call(rt.store, args...)
   138  }
   139  
   140  func (rt *Runtime) Read(addr, size int32) ([]byte, error) {
   141  	if rt.module == nil {
   142  		return nil, ErrNotLinked
   143  	}
   144  	if rt.instance == nil {
   145  		return nil, ErrNotInstantiated
   146  	}
   147  	mem := rt.newMemory()
   148  	if addr > int32(len(mem)) || addr+size > int32(len(mem)) {
   149  		return nil, errors.New("overflow")
   150  	}
   151  	buf := make([]byte, size)
   152  	if copied := copy(buf, mem[addr:addr+size]); int32(copied) != size {
   153  		return nil, errors.New("overflow")
   154  	}
   155  	return buf, nil
   156  }
   157  
   158  func (rt *Runtime) Copy(hostData []byte, vmAddrPtr, vmSizePtr int32) error {
   159  	if rt.module == nil {
   160  		return ErrNotLinked
   161  	}
   162  	if rt.instance == nil {
   163  		return ErrNotInstantiated
   164  	}
   165  	size := len(hostData)
   166  	addr, mem, err := rt.alloc(int32(size))
   167  	if err != nil {
   168  		return err
   169  	}
   170  	if copied := copy(mem[addr:], hostData); copied != size {
   171  		return errors.New("fail to copy data")
   172  	}
   173  	if err = putUint32Le(mem, vmAddrPtr, uint32(addr)); err != nil {
   174  		return err
   175  	}
   176  
   177  	return putUint32Le(mem, vmSizePtr, uint32(size))
   178  }