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

     1  package wasmtime
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"fmt"
     9  	"math/rand"
    10  	"runtime/debug"
    11  	"strconv"
    12  	"strings"
    13  	"sync/atomic"
    14  	"time"
    15  	"unicode/utf8"
    16  
    17  	"github.com/ethereum/go-ethereum/crypto"
    18  	"github.com/google/uuid"
    19  	"github.com/pkg/errors"
    20  	"github.com/tidwall/gjson"
    21  	"golang.org/x/text/encoding/unicode"
    22  
    23  	base "github.com/machinefi/w3bstream/pkg/depends/base/types"
    24  	confid "github.com/machinefi/w3bstream/pkg/depends/conf/id"
    25  	conflog "github.com/machinefi/w3bstream/pkg/depends/conf/log"
    26  	confmqtt "github.com/machinefi/w3bstream/pkg/depends/conf/mqtt"
    27  	"github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/datatypes"
    28  	"github.com/machinefi/w3bstream/pkg/depends/x/mapx"
    29  	"github.com/machinefi/w3bstream/pkg/enums"
    30  	"github.com/machinefi/w3bstream/pkg/models"
    31  	"github.com/machinefi/w3bstream/pkg/modules/metrics"
    32  	"github.com/machinefi/w3bstream/pkg/modules/operator"
    33  	optypes "github.com/machinefi/w3bstream/pkg/modules/operator/pool/types"
    34  	wasmapi "github.com/machinefi/w3bstream/pkg/modules/vm/wasmapi/types"
    35  	"github.com/machinefi/w3bstream/pkg/types"
    36  	"github.com/machinefi/w3bstream/pkg/types/wasm"
    37  	"github.com/machinefi/w3bstream/pkg/types/wasm/sql_util"
    38  )
    39  
    40  type (
    41  	Import func(module, name string, f interface{}) error
    42  
    43  	ABILinker interface {
    44  		LinkABI(Import) error
    45  	}
    46  
    47  	ExportFuncs struct {
    48  		rt      *Runtime
    49  		prj     *models.Project
    50  		app     *models.Applet
    51  		ins     *models.Instance
    52  		ctxID   *atomic.Value
    53  		logs    []*models.WasmLog
    54  		res     *mapx.Map[uint32, []byte]
    55  		evs     *mapx.Map[uint32, []byte]
    56  		env     *wasm.Env
    57  		kvs     wasm.KVStore
    58  		db      *wasm.Database
    59  		logger  conflog.Logger
    60  		cl      *wasm.ChainClient
    61  		cf      *types.ChainConfig
    62  		ctx     context.Context
    63  		mq      *confmqtt.Client
    64  		metrics metrics.CustomMetrics
    65  		srv     wasmapi.Server
    66  		opPool  optypes.Pool
    67  	}
    68  )
    69  
    70  func NewExportFuncs(ctx context.Context, rt *Runtime) (*ExportFuncs, error) {
    71  	ef := &ExportFuncs{
    72  		prj:     types.MustProjectFromContext(ctx),
    73  		app:     types.MustAppletFromContext(ctx),
    74  		ins:     types.MustInstanceFromContext(ctx),
    75  		ctxID:   &atomic.Value{},
    76  		res:     wasm.MustRuntimeResourceFromContext(ctx),
    77  		evs:     wasm.MustRuntimeEventTypesFromContext(ctx),
    78  		kvs:     wasm.MustKVStoreFromContext(ctx),
    79  		logger:  wasm.MustLoggerFromContext(ctx),
    80  		srv:     types.MustWasmApiServerFromContext(ctx),
    81  		opPool:  types.MustOperatorPoolFromContext(ctx),
    82  		cl:      wasm.MustChainClientFromContext(ctx),
    83  		cf:      types.MustChainConfigFromContext(ctx),
    84  		db:      wasm.MustSQLStoreFromContext(ctx),
    85  		env:     wasm.MustEnvFromContext(ctx),
    86  		mq:      wasm.MustMQTTClientFromContext(ctx),
    87  		metrics: wasm.MustCustomMetricsFromContext(ctx),
    88  		rt:      rt,
    89  		ctx:     ctx,
    90  	}
    91  	ef.ctxID.Store("")
    92  
    93  	return ef, nil
    94  }
    95  
    96  var (
    97  	_     wasm.ABI = (*ExportFuncs)(nil)
    98  	_rand          = rand.New(rand.NewSource(time.Now().UnixNano()))
    99  )
   100  
   101  func (ef *ExportFuncs) LinkABI(impt Import) error {
   102  	for name, ff := range map[string]interface{}{
   103  		"abort":                    ef.Abort,
   104  		"trace":                    ef.Trace,
   105  		"seed":                     ef.Seed,
   106  		"ws_log":                   ef.Log,
   107  		"ws_get_data":              ef.GetData,
   108  		"ws_set_data":              ef.SetData,
   109  		"ws_get_db":                ef.GetDB,
   110  		"ws_set_db":                ef.SetDB,
   111  		"ws_send_tx":               ef.SendTX,
   112  		"ws_send_tx_with_operator": ef.SendTXWithOperator,
   113  		"ws_call_contract":         ef.CallContract,
   114  		"ws_set_sql_db":            ef.SetSQLDB,
   115  		"ws_get_sql_db":            ef.GetSQLDB,
   116  		"ws_get_env":               ef.GetEnv,
   117  		"ws_send_mqtt_msg":         ef.SendMqttMsg,
   118  		"ws_api_call":              ef.ApiCall,
   119  	} {
   120  		if err := impt("env", name, ff); err != nil {
   121  			return err
   122  		}
   123  	}
   124  
   125  	for name, ff := range map[string]interface{}{
   126  		"ws_submit_metrics": ef.StatSubmit,
   127  	} {
   128  		if err := impt("stat", name, ff); err != nil {
   129  			return err
   130  		}
   131  	}
   132  
   133  	return nil
   134  }
   135  
   136  func (ef *ExportFuncs) _log(level conflog.Level, src string, msg any) {
   137  	l := ef.logger.WithValues("@src", src)
   138  	switch level {
   139  	case conflog.DebugLevel:
   140  		l.Debug(msg.(string))
   141  	case conflog.InfoLevel:
   142  		l.Info(msg.(string))
   143  	case conflog.WarnLevel:
   144  		l.Warn(msg.(error))
   145  	case conflog.ErrorLevel:
   146  		l.Error(msg.(error))
   147  	default:
   148  		l.Trace(msg.(string))
   149  	}
   150  	_msg := subStringWithLength(fmt.Sprint(msg), enums.WasmLogMaxLength)
   151  	ef.logs = append(ef.logs, &models.WasmLog{
   152  		WasmLogInfo: models.WasmLogInfo{
   153  			ProjectName: ef.prj.Name,
   154  			AppletName:  ef.app.Name,
   155  			InstanceID:  ef.ins.InstanceID,
   156  			EventID:     ef.ContextID(),
   157  			Src:         src,
   158  			Level:       level.String(),
   159  			LogTime:     time.Now().UnixNano(),
   160  			Msg:         _msg,
   161  		},
   162  		OperationTimes: datatypes.OperationTimes{
   163  			CreatedAt: base.Timestamp{Time: time.Now()},
   164  			UpdatedAt: base.Timestamp{Time: time.Now()},
   165  		},
   166  	})
   167  }
   168  
   169  func (ef *ExportFuncs) WasmLog(lv conflog.Level, msg any) {
   170  	ef._log(lv, "wasm", msg)
   171  }
   172  
   173  func (ef *ExportFuncs) HostLog(lv conflog.Level, msg any) {
   174  	ef._log(lv, "host", msg)
   175  }
   176  
   177  func (ef *ExportFuncs) EntryContext(ctx context.Context, ctxID string, tpe, data []byte) (uint32, bool) {
   178  	if ef.ctxID.CompareAndSwap("", ctxID) {
   179  		rid := uuid.New().ID() % uint32(maxInt)
   180  		ef.evs.Store(rid, tpe)
   181  		ef.res.Store(rid, data)
   182  		return rid, true
   183  	}
   184  	return 0, false
   185  }
   186  
   187  func (ef *ExportFuncs) LeaveContext(ctx context.Context, ctxID string, rid uint32) bool {
   188  	if ef.ctxID.Load().(string) == ctxID {
   189  		idg := confid.MustSFIDGeneratorFromContext(ctx)
   190  		ids := idg.MustGenSFIDs(len(ef.logs))
   191  		d := types.MustMgrDBExecutorFromContext(ctx)
   192  
   193  		for i, l := range ef.logs {
   194  			l.WasmLogID = ids[i]
   195  		}
   196  		err := models.BatchCreateWasmLogs(d, ef.logs...)
   197  		if err != nil {
   198  			ef.HostLog(conflog.WarnLevel, err)
   199  		}
   200  		ef.logs = ef.logs[:0]
   201  		ef.evs.Remove(rid)
   202  		ef.res.Remove(rid)
   203  		return ef.ctxID.CompareAndSwap(ctxID, "")
   204  	}
   205  	return false
   206  }
   207  
   208  func (ef *ExportFuncs) ContextID() string {
   209  	return ef.ctxID.Load().(string)
   210  }
   211  
   212  func (ef *ExportFuncs) Log(level, ptr, size int32) int32 {
   213  	buf, err := ef.rt.Read(ptr, size)
   214  	if err != nil {
   215  		ef.HostLog(conflog.ErrorLevel, err)
   216  		return wasm.ResultStatusCode_Failed
   217  	}
   218  	ef.WasmLog(conflog.Level(level), string(buf))
   219  	return int32(wasm.ResultStatusCode_OK)
   220  }
   221  
   222  func (ef *ExportFuncs) ApiCall(kAddr, kSize, vmAddrPtr, vmSizePtr int32) int32 {
   223  	buf, err := ef.rt.Read(kAddr, kSize)
   224  	if err != nil {
   225  		ef.HostLog(conflog.ErrorLevel, err)
   226  		return int32(wasm.ResultStatusCode_TransDataFromVMFailed)
   227  	}
   228  
   229  	resp := ef.srv.Call(ef.ctx, buf)
   230  
   231  	respJson, err := json.Marshal(resp)
   232  	if err != nil {
   233  		ef.HostLog(conflog.ErrorLevel, err)
   234  		return int32(wasm.ResultStatusCode_HostInternal)
   235  	}
   236  
   237  	if err = ef.rt.Copy(respJson, vmAddrPtr, vmSizePtr); err != nil {
   238  		ef.HostLog(conflog.ErrorLevel, err)
   239  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   240  	}
   241  
   242  	return int32(wasm.ResultStatusCode_OK)
   243  }
   244  
   245  // Abort is reserved for imported func env.abort() which is auto-generated by assemblyScript
   246  func (ef *ExportFuncs) Abort(msgPtr int32, fileNamePtr int32, line int32, col int32) {
   247  	msg, err := ef.readString(msgPtr)
   248  	if err != nil {
   249  		ef.HostLog(conflog.ErrorLevel, errors.Wrap(err, "fail to decode arguments in env.abort"))
   250  		return
   251  	}
   252  	fileName, err := ef.readString(fileNamePtr)
   253  	if err != nil {
   254  		ef.HostLog(conflog.ErrorLevel, errors.Wrap(err, "fail to decode arguments in env.abort"))
   255  		return
   256  	}
   257  	ef.HostLog(conflog.ErrorLevel, errors.Errorf("abort: %s at %s:%d:%d", msg, fileName, line, col))
   258  }
   259  
   260  func (ef *ExportFuncs) readString(ptr int32) (string, error) {
   261  	if ptr < 4 {
   262  		return "", errors.Errorf("the pointer address %d is invalid", ptr)
   263  	}
   264  
   265  	decoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
   266  
   267  	lenData, err := ef.rt.Read(ptr-4, 4) // sizeof(uint32) is 4
   268  	if err != nil {
   269  		return "", err
   270  	}
   271  	len := binary.LittleEndian.Uint32(lenData)
   272  	data, err := ef.rt.Read(ptr, int32(len))
   273  	if err != nil {
   274  		return "", err
   275  	}
   276  	utf8bytes, err := decoder.Bytes(data)
   277  	if err != nil {
   278  		return "", err
   279  	}
   280  	return string(utf8bytes), nil
   281  }
   282  
   283  // Trace is reserved for imported func env.trace() which is auto-generated by assemblyScript
   284  func (ef *ExportFuncs) Trace(msgPtr int32, _ int32, arr ...float64) {
   285  	msg, err := ef.readString(msgPtr)
   286  	if err != nil {
   287  		ef.HostLog(conflog.ErrorLevel, errors.Wrap(err, "fail to decode arguments in env.abort"))
   288  		return
   289  	}
   290  
   291  	str := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(arr)), ", "), "[]")
   292  	if len(str) > 0 {
   293  		str = " " + str
   294  	}
   295  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("trace: %s%s", msg, str))
   296  }
   297  
   298  // Seed is reserved for imported func env.seed() which is auto-generated by assemblyScript
   299  func (ef *ExportFuncs) Seed() float64 {
   300  	return _rand.Float64() * float64(time.Now().UnixNano())
   301  }
   302  
   303  func (ef *ExportFuncs) GetData(rid, vmAddrPtr, vmSizePtr int32) int32 {
   304  	data, ok := ef.res.Load(uint32(rid))
   305  	if !ok {
   306  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   307  	}
   308  
   309  	if err := ef.rt.Copy(data, vmAddrPtr, vmSizePtr); err != nil {
   310  		ef.HostLog(conflog.ErrorLevel, err)
   311  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   312  	}
   313  
   314  	return int32(wasm.ResultStatusCode_OK)
   315  }
   316  
   317  // TODO SetData if rid not exist, should be assigned by wasm?
   318  func (ef *ExportFuncs) SetData(rid, addr, size int32) int32 {
   319  	buf, err := ef.rt.Read(addr, size)
   320  	if err != nil {
   321  		ef.HostLog(conflog.ErrorLevel, err)
   322  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   323  	}
   324  	ef.res.Store(uint32(rid), buf)
   325  	return int32(wasm.ResultStatusCode_OK)
   326  }
   327  
   328  func (ef *ExportFuncs) GetDB(kAddr, kSize int32, vmAddrPtr, vmSizePtr int32) int32 {
   329  	key, err := ef.rt.Read(kAddr, kSize)
   330  	if err != nil {
   331  		ef.HostLog(conflog.ErrorLevel, err)
   332  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   333  	}
   334  
   335  	val, exist := ef.kvs.Get(string(key))
   336  	if exist != nil || val == nil {
   337  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   338  	}
   339  
   340  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("host.GetDB %s:%s", string(key), strconv.Quote(string(val))))
   341  
   342  	if err = ef.rt.Copy(val, vmAddrPtr, vmSizePtr); err != nil {
   343  		ef.HostLog(conflog.ErrorLevel, err)
   344  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   345  	}
   346  
   347  	return int32(wasm.ResultStatusCode_OK)
   348  }
   349  
   350  func (ef *ExportFuncs) SetDB(kAddr, kSize, vAddr, vSize int32) int32 {
   351  	key, err := ef.rt.Read(kAddr, kSize)
   352  	if err != nil {
   353  		ef.HostLog(conflog.ErrorLevel, err)
   354  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   355  	}
   356  	val, err := ef.rt.Read(vAddr, vSize)
   357  	if err != nil {
   358  		ef.HostLog(conflog.ErrorLevel, err)
   359  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   360  	}
   361  
   362  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("host.SetDB %s:%s", string(key), strconv.Quote(string(val))))
   363  
   364  	err = ef.kvs.Set(string(key), val)
   365  	if err != nil {
   366  		ef.HostLog(conflog.ErrorLevel, err)
   367  		return int32(wasm.ResultStatusCode_Failed)
   368  	}
   369  	return int32(wasm.ResultStatusCode_OK)
   370  }
   371  
   372  func (ef *ExportFuncs) SetSQLDB(addr, size int32) int32 {
   373  	if ef.db == nil {
   374  		return int32(wasm.ResultStatusCode_NoDBContext)
   375  	}
   376  	data, err := ef.rt.Read(addr, size)
   377  	if err != nil {
   378  		ef.HostLog(conflog.ErrorLevel, err)
   379  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   380  	}
   381  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("GetSQLDB: [query: %s]", string(data)))
   382  
   383  	prestate, params, err := sql_util.ParseQuery(data)
   384  	if err != nil {
   385  		ef.HostLog(conflog.ErrorLevel, err)
   386  		return wasm.ResultStatusCode_Failed
   387  	}
   388  
   389  	db, err := ef.db.WithDefaultSchema()
   390  	if err != nil {
   391  		ef.HostLog(conflog.ErrorLevel, err)
   392  		return wasm.ResultStatusCode_Failed
   393  	}
   394  	_, err = db.ExecContext(context.Background(), prestate, params...)
   395  	if err != nil {
   396  		ef.HostLog(conflog.ErrorLevel, err)
   397  		return wasm.ResultStatusCode_Failed
   398  	}
   399  
   400  	return int32(wasm.ResultStatusCode_OK)
   401  }
   402  
   403  func (ef *ExportFuncs) GetSQLDB(addr, size int32, vmAddrPtr, vmSizePtr int32) int32 {
   404  	if ef.db == nil {
   405  		return int32(wasm.ResultStatusCode_NoDBContext)
   406  	}
   407  	data, err := ef.rt.Read(addr, size)
   408  	if err != nil {
   409  		ef.HostLog(conflog.ErrorLevel, err)
   410  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   411  	}
   412  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("GetSQLDB: [query: %s]", string(data)))
   413  
   414  	prestate, params, err := sql_util.ParseQuery(data)
   415  	if err != nil {
   416  		ef.HostLog(conflog.ErrorLevel, err)
   417  		return wasm.ResultStatusCode_Failed
   418  	}
   419  
   420  	db, err := ef.db.WithDefaultSchema()
   421  	if err != nil {
   422  		ef.HostLog(conflog.ErrorLevel, err)
   423  		return wasm.ResultStatusCode_Failed
   424  	}
   425  	rows, err := db.QueryContext(context.Background(), prestate, params...)
   426  	if err != nil {
   427  		ef.HostLog(conflog.ErrorLevel, err)
   428  		return wasm.ResultStatusCode_Failed
   429  	}
   430  
   431  	ret, err := sql_util.JsonifyRows(rows)
   432  	if err != nil {
   433  		ef.HostLog(conflog.ErrorLevel, err)
   434  		return wasm.ResultStatusCode_Failed
   435  	}
   436  
   437  	if err = ef.rt.Copy(ret, vmAddrPtr, vmSizePtr); err != nil {
   438  		ef.HostLog(conflog.ErrorLevel, err)
   439  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   440  	}
   441  
   442  	return int32(wasm.ResultStatusCode_OK)
   443  }
   444  
   445  // TODO: make sendTX async, and add callback if possible
   446  func (ef *ExportFuncs) SendTX(chainID int32, offset, size, vmAddrPtr, vmSizePtr int32) (result int32) {
   447  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("offset %d size %d vmAddrPtr %d vmSizePtr %d", offset, size, vmAddrPtr, vmSizePtr))
   448  	if ef.cl == nil {
   449  		ef.HostLog(conflog.ErrorLevel, errors.New("eth client doesn't exist"))
   450  		return wasm.ResultStatusCode_Failed
   451  	}
   452  	buf, err := ef.rt.Read(offset, size)
   453  	if err != nil {
   454  		ef.HostLog(conflog.ErrorLevel, err)
   455  		return wasm.ResultStatusCode_Failed
   456  	}
   457  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("input data: %s", string(buf)))
   458  	ret := gjson.Parse(string(buf))
   459  	to := ret.Get("to").String()
   460  	value := ret.Get("value").String()
   461  	data := ret.Get("data").String()
   462  
   463  	op, err := ef.opPool.Get(ef.prj.AccountID, operator.DefaultOperatorName)
   464  	if err != nil {
   465  		ef.HostLog(conflog.ErrorLevel, fmt.Sprintf("failed to get operator [account: %s] [operator: %s]", ef.prj.AccountID, operator.DefaultOperatorName))
   466  	}
   467  	prvkey, err := crypto.HexToECDSA(op.Op.PrivateKey)
   468  	if err != nil {
   469  		ef.HostLog(conflog.ErrorLevel, fmt.Sprintf("failed to parse private key [account: %s] [operator: %s]", ef.prj.AccountID, operator.DefaultOperatorName))
   470  	}
   471  	from := crypto.PubkeyToAddress(prvkey.PublicKey)
   472  
   473  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("send tx: [from %s] [to %s] [value %s] [data %s] [prj: %v]", from, to, value, data, ef.prj.Name))
   474  	defer func() {
   475  		if err := recover(); err != nil {
   476  			fmt.Printf("panic: %s; calltrace:%s\n", fmt.Sprint(err), string(debug.Stack()))
   477  			result = -1
   478  			return
   479  		}
   480  	}()
   481  	txHash, err := ef.cl.SendTX(ef.cf, uint64(chainID), "", to, value, data, ef.opPool, ef.prj)
   482  	if err != nil {
   483  		ef.HostLog(conflog.ErrorLevel, err)
   484  		return wasm.ResultStatusCode_Failed
   485  	}
   486  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("send tx [hash %s]", txHash))
   487  	if err := ef.rt.Copy([]byte(txHash), vmAddrPtr, vmSizePtr); err != nil {
   488  		ef.HostLog(conflog.ErrorLevel, err)
   489  		return wasm.ResultStatusCode_Failed
   490  	}
   491  	ef.HostLog(conflog.InfoLevel, "send tx done")
   492  	return int32(wasm.ResultStatusCode_OK)
   493  }
   494  
   495  func (ef *ExportFuncs) SendTXWithOperator(chainID int32, offset, size, vmAddrPtr, vmSizePtr int32) int32 {
   496  	if ef.cl == nil {
   497  		ef.HostLog(conflog.ErrorLevel, errors.New("eth client doesn't exist"))
   498  		return wasm.ResultStatusCode_Failed
   499  	}
   500  	buf, err := ef.rt.Read(offset, size)
   501  	if err != nil {
   502  		ef.HostLog(conflog.ErrorLevel, err)
   503  		return wasm.ResultStatusCode_Failed
   504  	}
   505  	ret := gjson.Parse(string(buf))
   506  
   507  	to := ret.Get("to").String()
   508  	value := ret.Get("value").String()
   509  	data := ret.Get("data").String()
   510  	operatorName := ret.Get("operatorName").String()
   511  
   512  	op, err := ef.opPool.Get(ef.prj.AccountID, operatorName)
   513  	if err != nil {
   514  		ef.HostLog(conflog.ErrorLevel, fmt.Sprintf("failed to get operator [account: %s] [operator: %s]", ef.prj.AccountID, operator.DefaultOperatorName))
   515  	}
   516  	prvkey, err := crypto.HexToECDSA(op.Op.PrivateKey)
   517  	if err != nil {
   518  		ef.HostLog(conflog.ErrorLevel, fmt.Sprintf("failed to parse private key [account: %s] [operator: %s]", ef.prj.AccountID, operator.DefaultOperatorName))
   519  	}
   520  	from := crypto.PubkeyToAddress(prvkey.PublicKey)
   521  
   522  	ef.HostLog(conflog.InfoLevel, fmt.Sprintf("send tx: [from %s] [to %s] [value %s] [data %s] [prj: %v]", from, to, value, data, ef.prj.Name))
   523  
   524  	txResp, err := ef.cl.SendTXWithOperator(ef.cf, uint64(chainID), "", to, value, data, operatorName, ef.opPool, ef.prj)
   525  	if err != nil {
   526  		ef.HostLog(conflog.ErrorLevel, err)
   527  		return wasm.ResultStatusCode_Failed
   528  	}
   529  	if err := ef.rt.Copy([]byte(txResp.Hash), vmAddrPtr, vmSizePtr); err != nil {
   530  		ef.HostLog(conflog.ErrorLevel, err)
   531  		return wasm.ResultStatusCode_Failed
   532  	}
   533  	return int32(wasm.ResultStatusCode_OK)
   534  }
   535  
   536  func (ef *ExportFuncs) SendMqttMsg(topicAddr, topicSize, msgAddr, msgSize int32) int32 {
   537  	if ef.mq == nil {
   538  		ef.HostLog(conflog.ErrorLevel, errors.New("mq client doesn't exist"))
   539  		return wasm.ResultStatusCode_Failed
   540  	}
   541  
   542  	var (
   543  		topicBuf []byte
   544  		msgBuf   []byte
   545  		err      error
   546  	)
   547  
   548  	topicBuf, err = ef.rt.Read(topicAddr, topicSize)
   549  	if err != nil {
   550  		ef.HostLog(conflog.ErrorLevel, err)
   551  		return wasm.ResultStatusCode_Failed
   552  	}
   553  	msgBuf, err = ef.rt.Read(msgAddr, msgSize)
   554  	if err != nil {
   555  		ef.HostLog(conflog.ErrorLevel, err)
   556  		return wasm.ResultStatusCode_Failed
   557  	}
   558  	err = ef.mq.WithTopic(string(topicBuf)).Publish(string(msgBuf))
   559  	if err != nil {
   560  		ef.HostLog(conflog.ErrorLevel, err)
   561  		return wasm.ResultStatusCode_Failed
   562  	}
   563  	return int32(wasm.ResultStatusCode_OK)
   564  }
   565  
   566  func (ef *ExportFuncs) CallContract(chainID int32, offset, size int32, vmAddrPtr, vmSizePtr int32) int32 {
   567  	if ef.cl == nil {
   568  		ef.HostLog(conflog.ErrorLevel, errors.New("eth client doesn't exist"))
   569  		return wasm.ResultStatusCode_Failed
   570  	}
   571  	buf, err := ef.rt.Read(offset, size)
   572  	if err != nil {
   573  		ef.HostLog(conflog.ErrorLevel, err)
   574  		return wasm.ResultStatusCode_Failed
   575  	}
   576  	ret := gjson.Parse(string(buf))
   577  	data, err := ef.cl.CallContract(ef.cf, uint64(chainID), "", ret.Get("to").String(), ret.Get("data").String())
   578  	if err != nil {
   579  		ef.HostLog(conflog.ErrorLevel, err)
   580  		return wasm.ResultStatusCode_Failed
   581  	}
   582  	if err = ef.rt.Copy(data, vmAddrPtr, vmSizePtr); err != nil {
   583  		ef.HostLog(conflog.ErrorLevel, err)
   584  		return wasm.ResultStatusCode_Failed
   585  	}
   586  	return int32(wasm.ResultStatusCode_OK)
   587  }
   588  
   589  func (ef *ExportFuncs) GetEnv(kAddr, kSize int32, vmAddrPtr, vmSizePtr int32) int32 {
   590  	if ef.env == nil {
   591  		return int32(wasm.ResultStatusCode_EnvKeyNotFound)
   592  	}
   593  	key, err := ef.rt.Read(kAddr, kSize)
   594  	if err != nil {
   595  		ef.HostLog(conflog.ErrorLevel, err)
   596  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   597  	}
   598  
   599  	val, ok := ef.env.Get(string(key))
   600  	if !ok {
   601  		return int32(wasm.ResultStatusCode_EnvKeyNotFound)
   602  	}
   603  
   604  	if err = ef.rt.Copy([]byte(val), vmAddrPtr, vmSizePtr); err != nil {
   605  		ef.HostLog(conflog.ErrorLevel, err)
   606  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   607  	}
   608  	return int32(wasm.ResultStatusCode_OK)
   609  }
   610  
   611  func (ef *ExportFuncs) GetEventType(rid, vmAddrPtr, vmSizePtr int32) int32 {
   612  	data, ok := ef.res.Load(uint32(rid))
   613  	if !ok {
   614  		return int32(wasm.ResultStatusCode_ResourceNotFound)
   615  	}
   616  
   617  	if err := ef.rt.Copy(data, vmAddrPtr, vmSizePtr); err != nil {
   618  		ef.HostLog(conflog.ErrorLevel, err)
   619  		return int32(wasm.ResultStatusCode_TransDataToVMFailed)
   620  	}
   621  	return int32(wasm.ResultStatusCode_OK)
   622  }
   623  
   624  func (ef *ExportFuncs) StatSubmit(vmAddrPtr, vmSizePtr int32) int32 {
   625  	buf, err := ef.rt.Read(vmAddrPtr, vmSizePtr)
   626  	if err != nil {
   627  		ef.HostLog(conflog.ErrorLevel, err)
   628  		return wasm.ResultStatusCode_Failed
   629  	}
   630  	str := string(buf)
   631  	if !gjson.Valid(str) {
   632  		err = errors.New("invalid json")
   633  		ef.HostLog(conflog.ErrorLevel, err)
   634  		return wasm.ResultStatusCode_Failed
   635  	}
   636  	object := gjson.Parse(str)
   637  	if object.IsArray() {
   638  		err = errors.New("json object should not be an array")
   639  		ef.HostLog(conflog.ErrorLevel, err)
   640  		return wasm.ResultStatusCode_Failed
   641  	}
   642  
   643  	if err = ef.metrics.Submit(object); err != nil {
   644  		ef.HostLog(conflog.ErrorLevel, err)
   645  		return wasm.ResultStatusCode_Failed
   646  	}
   647  	return int32(wasm.ResultStatusCode_OK)
   648  }
   649  
   650  func subStringWithLength(str string, length int) string {
   651  	if !utf8.ValidString(str) {
   652  		str = hex.EncodeToString([]byte(str))
   653  	}
   654  	if length < 0 {
   655  		return ""
   656  	}
   657  	rs := []rune(str)
   658  	strLen := len(rs)
   659  
   660  	if length > strLen {
   661  		return str
   662  	}
   663  	return string(rs[0:length])
   664  }