github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nvm/engine_v8.go (about)

     1  // Copyright (C) 2017 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package nvm
    20  
    21  /*
    22  #include <stdlib.h>
    23  #cgo CFLAGS:
    24  #cgo LDFLAGS: -L${SRCDIR}/native-lib -lnebulasv8
    25  
    26  #include "v8/engine.h"
    27  
    28  // Forward declaration.
    29  void V8Log_cgo(int level, const char *msg);
    30  
    31  char *RequireDelegateFunc_cgo(void *handler, const char *filename, size_t *lineOffset);
    32  char *AttachLibVersionDelegateFunc_cgo(void *handler, const char *libname);
    33  
    34  char *StorageGetFunc_cgo(void *handler, const char *key, size_t *gasCnt);
    35  int StoragePutFunc_cgo(void *handler, const char *key, const char *value, size_t *gasCnt);
    36  int StorageDelFunc_cgo(void *handler, const char *key, size_t *gasCnt);
    37  
    38  char *GetTxByHashFunc_cgo(void *handler, const char *hash, size_t *gasCnt);
    39  char *GetAccountStateFunc_cgo(void *handler, const char *address, size_t *gasCnt, char **result, char **info);
    40  int TransferFunc_cgo(void *handler, const char *to, const char *value, size_t *gasCnt);
    41  int VerifyAddressFunc_cgo(void *handler, const char *address, size_t *gasCnt);
    42  char *GetPreBlockHashFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info);
    43  char *GetPreBlockSeedFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info);
    44  char *GetLatestNebulasRankFunc_cgo(void *handler, const char *address, size_t *gasCnt, char **result, char **info);
    45  char *GetLatestNebulasRankSummaryFunc_cgo(void *handler, size_t *gasCnt, char **result, char **info);
    46  
    47  char *Sha256Func_cgo(const char *data, size_t *gasCnt);
    48  char *Sha3256Func_cgo(const char *data, size_t *gasCnt);
    49  char *Ripemd160Func_cgo(const char *data, size_t *gasCnt);
    50  char *RecoverAddressFunc_cgo(int alg, const char *data, const char *sign, size_t *gasCnt);
    51  char *Md5Func_cgo(const char *data, size_t *gasCnt);
    52  char *Base64Func_cgo(const char *data, size_t *gasCnt);
    53  char *GetContractSourceFunc_cgo(void *handler, const char *address);
    54  char *InnerContractFunc_cgo(void *handler, const char *address, const char *funcName, const char * v, const char *args, size_t *gasCnt);
    55  
    56  char *GetTxRandomFunc_cgo(void *handler, size_t *gasCnt, char **result, char **exceptionInfo);
    57  
    58  void EventTriggerFunc_cgo(void *handler, const char *topic, const char *data, size_t *gasCnt);
    59  
    60  */
    61  import "C"
    62  import (
    63  	"fmt"
    64  	"strings"
    65  	"sync"
    66  	"unsafe"
    67  
    68  	"encoding/json"
    69  
    70  	lru "github.com/hashicorp/golang-lru"
    71  	"github.com/nebulasio/go-nebulas/core"
    72  	"github.com/nebulasio/go-nebulas/crypto/hash"
    73  	"github.com/nebulasio/go-nebulas/util/byteutils"
    74  	"github.com/nebulasio/go-nebulas/util/logging"
    75  	"github.com/sirupsen/logrus"
    76  )
    77  
    78  const (
    79  	ExecutionFailedErr  = 1
    80  	ExecutionTimeOutErr = 2
    81  
    82  	// ExecutionTimeout max v8 execution timeout.
    83  	ExecutionTimeout                 = 15 * 1000 * 1000
    84  	OriginExecutionTimeout           = 5 * 1000 * 1000
    85  	CompatibleExecutionTimeout       = 20 * 1000 * 1000
    86  	TimeoutGasLimitCost              = 100000000
    87  	MaxLimitsOfExecutionInstructions = 10000000 // TODO: set max gasLimit with execution 5s *0.8
    88  )
    89  
    90  // const (
    91  // 	ExecutionFailedErr   = 1
    92  // 	ExecutionInnerNvmErr = 2
    93  // 	ExecutionTimeOutErr  = 3
    94  // )
    95  
    96  //engine_v8 private data
    97  var (
    98  	v8engineOnce              = sync.Once{}
    99  	storages                  = make(map[uint64]*V8Engine, 1024)
   100  	storagesIdx               = uint64(0)
   101  	storagesLock              = sync.RWMutex{}
   102  	engines                   = make(map[*C.V8Engine]*V8Engine, 1024)
   103  	enginesLock               = sync.RWMutex{}
   104  	sourceModuleCache, _      = lru.New(40960)
   105  	instructionCounterVersion = "1.0.0"
   106  )
   107  
   108  // V8Engine v8 engine.
   109  type V8Engine struct {
   110  	ctx                                     *Context
   111  	modules                                 Modules
   112  	v8engine                                *C.V8Engine
   113  	strictDisallowUsageOfInstructionCounter int
   114  	enableLimits                            bool
   115  	limitsOfExecutionInstructions           uint64
   116  	limitsOfTotalMemorySize                 uint64
   117  	actualCountOfExecutionInstructions      uint64
   118  	actualTotalMemorySize                   uint64
   119  	lcsHandler                              uint64
   120  	gcsHandler                              uint64
   121  	innerErrMsg                             string
   122  	innerErr                                error
   123  }
   124  
   125  type sourceModuleItem struct {
   126  	source                    string
   127  	sourceLineOffset          int
   128  	traceableSource           string
   129  	traceableSourceLineOffset int
   130  }
   131  
   132  // InitV8Engine initialize the v8 engine.
   133  func InitV8Engine() {
   134  	C.Initialize()
   135  
   136  	// Logger.
   137  	C.InitializeLogger((C.LogFunc)(unsafe.Pointer(C.V8Log_cgo)))
   138  
   139  	// Require.
   140  	C.InitializeRequireDelegate((C.RequireDelegate)(unsafe.Pointer(C.RequireDelegateFunc_cgo)), (C.AttachLibVersionDelegate)(unsafe.Pointer(C.AttachLibVersionDelegateFunc_cgo)))
   141  
   142  	// execution_env require
   143  	C.InitializeExecutionEnvDelegate((C.AttachLibVersionDelegate)(unsafe.Pointer(C.AttachLibVersionDelegateFunc_cgo)))
   144  
   145  	// Storage.
   146  	C.InitializeStorage((C.StorageGetFunc)(unsafe.Pointer(C.StorageGetFunc_cgo)),
   147  		(C.StoragePutFunc)(unsafe.Pointer(C.StoragePutFunc_cgo)),
   148  		(C.StorageDelFunc)(unsafe.Pointer(C.StorageDelFunc_cgo)))
   149  
   150  	// Blockchain.
   151  	C.InitializeBlockchain((C.GetTxByHashFunc)(unsafe.Pointer(C.GetTxByHashFunc_cgo)),
   152  		(C.GetAccountStateFunc)(unsafe.Pointer(C.GetAccountStateFunc_cgo)),
   153  		(C.TransferFunc)(unsafe.Pointer(C.TransferFunc_cgo)),
   154  		(C.VerifyAddressFunc)(unsafe.Pointer(C.VerifyAddressFunc_cgo)),
   155  		(C.GetPreBlockHashFunc)(unsafe.Pointer(C.GetPreBlockHashFunc_cgo)),
   156  		(C.GetPreBlockSeedFunc)(unsafe.Pointer(C.GetPreBlockSeedFunc_cgo)),
   157  		(C.GetContractSourceFunc)(unsafe.Pointer(C.GetContractSourceFunc_cgo)),
   158  		(C.InnerContractFunc)(unsafe.Pointer(C.InnerContractFunc_cgo)),
   159  		(C.GetLatestNebulasRankFunc)(unsafe.Pointer(C.GetLatestNebulasRankFunc_cgo)),
   160  		(C.GetLatestNebulasRankSummaryFunc)(unsafe.Pointer(C.GetLatestNebulasRankSummaryFunc_cgo)))
   161  
   162  	// random.
   163  	C.InitializeRandom((C.GetTxRandomFunc)(unsafe.Pointer(C.GetTxRandomFunc_cgo)))
   164  
   165  	// Event.
   166  	C.InitializeEvent((C.EventTriggerFunc)(unsafe.Pointer(C.EventTriggerFunc_cgo)))
   167  
   168  	// Crypto
   169  	C.InitializeCrypto((C.Sha256Func)(unsafe.Pointer(C.Sha256Func_cgo)),
   170  		(C.Sha3256Func)(unsafe.Pointer(C.Sha3256Func_cgo)),
   171  		(C.Ripemd160Func)(unsafe.Pointer(C.Ripemd160Func_cgo)),
   172  		(C.RecoverAddressFunc)(unsafe.Pointer(C.RecoverAddressFunc_cgo)),
   173  		(C.Md5Func)(unsafe.Pointer(C.Md5Func_cgo)),
   174  		(C.Base64Func)(unsafe.Pointer(C.Base64Func_cgo)))
   175  }
   176  
   177  // DisposeV8Engine dispose the v8 engine.
   178  func DisposeV8Engine() {
   179  	C.Dispose()
   180  }
   181  
   182  // NewV8Engine return new V8Engine instance.
   183  func NewV8Engine(ctx *Context) *V8Engine {
   184  	v8engineOnce.Do(func() {
   185  		InitV8Engine()
   186  	})
   187  
   188  	engine := &V8Engine{
   189  		ctx:      ctx,
   190  		modules:  NewModules(),
   191  		v8engine: C.CreateEngine(),
   192  		strictDisallowUsageOfInstructionCounter: 1, // enable by default.
   193  		enableLimits:                            true,
   194  		limitsOfExecutionInstructions:           0,
   195  		limitsOfTotalMemorySize:                 0,
   196  		actualCountOfExecutionInstructions:      0,
   197  		actualTotalMemorySize:                   0,
   198  	}
   199  
   200  	(func() {
   201  		enginesLock.Lock()
   202  		defer enginesLock.Unlock()
   203  		engines[engine.v8engine] = engine
   204  	})()
   205  
   206  	(func() {
   207  		storagesLock.Lock()
   208  		defer storagesLock.Unlock()
   209  
   210  		storagesIdx++
   211  		engine.lcsHandler = storagesIdx
   212  		storagesIdx++
   213  		engine.gcsHandler = storagesIdx
   214  
   215  		storages[engine.lcsHandler] = engine
   216  		storages[engine.gcsHandler] = engine
   217  	})()
   218  	// engine.v8engine.lcs = C.uintptr_t(engine.lcsHandler)
   219  	// engine.v8engine.gcs = C.uintptr_t(engine.gcsHandler)
   220  	if core.NvmGasLimitWithoutTimeoutAtHeight(ctx.block.Height()) {
   221  		engine.SetTimeOut(ExecutionTimeout)
   222  	} else {
   223  		timeoutMark := core.NvmExeTimeoutAtHeight(ctx.block.Height())
   224  		if timeoutMark {
   225  			engine.SetTimeOut(OriginExecutionTimeout)
   226  		} else {
   227  			engine.SetTimeOut(CompatibleExecutionTimeout)
   228  		}
   229  	}
   230  
   231  	if core.EnableInnerContractAtHeight(ctx.block.Height()) {
   232  		engine.EnableInnerContract()
   233  	}
   234  	return engine
   235  }
   236  
   237  // SetEnableLimit eval switch
   238  func (e *V8Engine) SetEnableLimit(isLimit bool) {
   239  	e.enableLimits = isLimit
   240  }
   241  
   242  // Dispose dispose all resources.
   243  func (e *V8Engine) Dispose() {
   244  	storagesLock.Lock()
   245  	delete(storages, e.lcsHandler)
   246  	delete(storages, e.gcsHandler)
   247  	storagesLock.Unlock()
   248  
   249  	enginesLock.Lock()
   250  	delete(engines, e.v8engine)
   251  	enginesLock.Unlock()
   252  
   253  	C.DeleteEngine(e.v8engine)
   254  }
   255  
   256  // Context returns engine context
   257  func (e *V8Engine) Context() *Context {
   258  	return e.ctx
   259  }
   260  
   261  // SetTestingFlag set testing flag, default is False.
   262  func (e *V8Engine) SetTestingFlag(flag bool) {
   263  	// deprecated.
   264  	/*if flag {
   265  		e.v8engine.testing = C.int(1)
   266  	} else {
   267  		e.v8engine.testing = C.int(0)
   268  	}*/
   269  }
   270  
   271  // SetTimeOut set nvm timeout, if not set, the default is 5*1000*1000
   272  func (e *V8Engine) SetTimeOut(timeout uint64) {
   273  	e.v8engine.timeout = C.int(timeout) //TODO:
   274  }
   275  func (e *V8Engine) EnableInnerContract() {
   276  	C.EnableInnerContract(e.v8engine)
   277  }
   278  
   279  // SetExecutionLimits set execution limits of V8 Engine, prevent Halting Problem.
   280  func (e *V8Engine) SetExecutionLimits(limitsOfExecutionInstructions, limitsOfTotalMemorySize uint64) error {
   281  
   282  	e.v8engine.limits_of_executed_instructions = C.size_t(limitsOfExecutionInstructions)
   283  	e.v8engine.limits_of_total_memory_size = C.size_t(limitsOfTotalMemorySize)
   284  
   285  	logging.VLog().WithFields(logrus.Fields{
   286  		"limits_of_executed_instructions": limitsOfExecutionInstructions,
   287  		"limits_of_total_memory_size":     limitsOfTotalMemorySize,
   288  	}).Debug("set execution limits.")
   289  
   290  	e.limitsOfExecutionInstructions = limitsOfExecutionInstructions
   291  	e.limitsOfTotalMemorySize = limitsOfTotalMemorySize
   292  
   293  	if limitsOfExecutionInstructions == 0 || limitsOfTotalMemorySize == 0 {
   294  		logging.VLog().Debugf("limit args has empty. limitsOfExecutionInstructions:%v,limitsOfTotalMemorySize:%d", limitsOfExecutionInstructions, limitsOfTotalMemorySize)
   295  		return ErrLimitHasEmpty
   296  	}
   297  	// V8 needs at least 6M heap memory.
   298  	if limitsOfTotalMemorySize > 0 && limitsOfTotalMemorySize < 6000000 {
   299  		logging.VLog().Debugf("V8 needs at least 6M (6000000) heap memory, your limitsOfTotalMemorySize (%d) is too low.", limitsOfTotalMemorySize)
   300  		return ErrSetMemorySmall
   301  	}
   302  	return nil
   303  }
   304  
   305  // ExecutionInstructions returns the execution instructions
   306  func (e *V8Engine) ExecutionInstructions() uint64 {
   307  	return e.actualCountOfExecutionInstructions
   308  }
   309  
   310  // TranspileTypeScript transpile typescript to javascript and return it.
   311  func (e *V8Engine) TranspileTypeScript(source string) (string, int, error) {
   312  	cSource := C.CString(source)
   313  	defer C.free(unsafe.Pointer(cSource))
   314  
   315  	lineOffset := C.int(0)
   316  	jsSource := C.TranspileTypeScriptModuleThread(e.v8engine, cSource, &lineOffset)
   317  	if jsSource == nil {
   318  		return "", 0, ErrTranspileTypeScriptFailed
   319  	}
   320  
   321  	defer C.free(unsafe.Pointer(jsSource))
   322  	return C.GoString(jsSource), int(lineOffset), nil
   323  
   324  }
   325  
   326  // InjectTracingInstructions process the source to inject tracing instructions.
   327  func (e *V8Engine) InjectTracingInstructions(source string) (string, int, error) {
   328  	cSource := C.CString(source)
   329  	defer C.free(unsafe.Pointer(cSource))
   330  
   331  	lineOffset := C.int(0)
   332  
   333  	traceableCSource := C.InjectTracingInstructionsThread(e.v8engine, cSource, &lineOffset, C.int(e.strictDisallowUsageOfInstructionCounter))
   334  	if traceableCSource == nil {
   335  		return "", 0, ErrInjectTracingInstructionFailed
   336  	}
   337  
   338  	defer C.free(unsafe.Pointer(traceableCSource))
   339  	return C.GoString(traceableCSource), int(lineOffset), nil
   340  }
   341  
   342  // CollectTracingStats collect tracing data from v8 engine.
   343  func (e *V8Engine) CollectTracingStats() {
   344  	// read memory stats.
   345  	C.ReadMemoryStatistics(e.v8engine)
   346  
   347  	e.actualCountOfExecutionInstructions = uint64(e.v8engine.stats.count_of_executed_instructions)
   348  	e.actualTotalMemorySize = uint64(e.v8engine.stats.total_memory_size)
   349  }
   350  
   351  // GetNVMLeftResources return current NVM verb total resource
   352  func (e *V8Engine) GetNVMLeftResources() (uint64, uint64) {
   353  	e.CollectTracingStats()
   354  	instruction := uint64(0)
   355  	mem := uint64(0)
   356  	if e.limitsOfExecutionInstructions >= e.actualCountOfExecutionInstructions {
   357  		instruction = e.limitsOfExecutionInstructions - e.actualCountOfExecutionInstructions
   358  	}
   359  
   360  	if e.limitsOfTotalMemorySize >= e.actualTotalMemorySize {
   361  		mem = e.limitsOfTotalMemorySize - e.actualTotalMemorySize
   362  	}
   363  
   364  	return instruction, mem
   365  }
   366  
   367  // RunScriptSource run js source.
   368  func (e *V8Engine) RunScriptSource(source string, sourceLineOffset int) (string, error) {
   369  	cSource := C.CString(source)
   370  	defer C.free(unsafe.Pointer(cSource))
   371  
   372  	var (
   373  		result  string
   374  		err     error
   375  		ret     C.int
   376  		cResult *C.char
   377  	)
   378  	ctx := e.Context()
   379  	if ctx == nil || ctx.block == nil {
   380  		logging.VLog().WithFields(logrus.Fields{
   381  			"ctx": ctx,
   382  		}).Error("Unexpected: Failed to get current height")
   383  		err = core.ErrUnexpected
   384  		return "", err
   385  	}
   386  	// done := make(chan bool, 1)
   387  	// go func() {
   388  	// 	ret = C.RunScriptSource(&cResult, e.v8engine, cSource, C.int(sourceLineOffset), C.uintptr_t(e.lcsHandler),
   389  	// 		C.uintptr_t(e.gcsHandler))
   390  	// 	done <- true
   391  	// }()
   392  
   393  	ret = C.RunScriptSourceThread(&cResult, e.v8engine, cSource, C.int(sourceLineOffset), C.uintptr_t(e.lcsHandler),
   394  		C.uintptr_t(e.gcsHandler))
   395  	e.CollectTracingStats()
   396  
   397  	if e.innerErr != nil {
   398  		if e.innerErrMsg == "" { //the first call of muti-nvm
   399  			result = "Inner Contract: \"\""
   400  		} else {
   401  			result = "Inner Contract: " + e.innerErrMsg
   402  		}
   403  		err := e.innerErr
   404  		if cResult != nil {
   405  			C.free(unsafe.Pointer(cResult))
   406  		}
   407  		if e.actualCountOfExecutionInstructions > e.limitsOfExecutionInstructions {
   408  			e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions
   409  		}
   410  		return result, err
   411  	}
   412  
   413  	if ret == C.NVM_EXE_TIMEOUT_ERR { //TODO: errcode in v8
   414  		err = ErrExecutionTimeout
   415  		if core.NvmGasLimitWithoutTimeoutAtHeight(ctx.block.Height()) {
   416  			err = core.ErrUnexpected
   417  		} else if core.NewNvmExeTimeoutConsumeGasAtHeight(ctx.block.Height()) {
   418  			if TimeoutGasLimitCost > e.limitsOfExecutionInstructions {
   419  				e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions
   420  			} else {
   421  				e.actualCountOfExecutionInstructions = TimeoutGasLimitCost
   422  			}
   423  		}
   424  	} else if ret == C.NVM_UNEXPECTED_ERR {
   425  		err = core.ErrUnexpected
   426  	} else if ret == C.NVM_INNER_EXE_ERR {
   427  		err = core.ErrInnerExecutionFailed
   428  		if e.limitsOfExecutionInstructions < e.actualCountOfExecutionInstructions {
   429  			logging.VLog().WithFields(logrus.Fields{
   430  				"actualGas": e.actualCountOfExecutionInstructions,
   431  				"limitGas":  e.limitsOfExecutionInstructions,
   432  			}).Error("Unexpected error: actual gas exceed the limit")
   433  		}
   434  	} else {
   435  		if ret != C.NVM_SUCCESS {
   436  			err = core.ErrExecutionFailed
   437  		}
   438  		if e.limitsOfExecutionInstructions > 0 &&
   439  			e.limitsOfExecutionInstructions < e.actualCountOfExecutionInstructions {
   440  			// Reach instruction limits.
   441  			err = ErrInsufficientGas
   442  			e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions
   443  		} else if e.limitsOfTotalMemorySize > 0 && e.limitsOfTotalMemorySize < e.actualTotalMemorySize {
   444  			// reach memory limits.
   445  			err = ErrExceedMemoryLimits
   446  			e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions
   447  		}
   448  	}
   449  
   450  	//set result
   451  	if cResult != nil {
   452  		result = C.GoString(cResult)
   453  		C.free(unsafe.Pointer(cResult))
   454  	} else if ret == C.NVM_SUCCESS {
   455  		result = "\"\"" // default JSON String.
   456  	}
   457  
   458  	return result, err
   459  }
   460  
   461  // DeployAndInit a contract
   462  func (e *V8Engine) DeployAndInit(source, sourceType, args string) (string, error) {
   463  	return e.RunContractScript(source, sourceType, "init", args)
   464  }
   465  
   466  // Call function in a script
   467  func (e *V8Engine) Call(source, sourceType, function, args string) (string, error) {
   468  	if core.PublicFuncNameChecker.MatchString(function) == false {
   469  		logging.VLog().Debugf("Invalid function: %v", function)
   470  		return "", ErrDisallowCallNotStandardFunction
   471  	}
   472  	if strings.EqualFold("init", function) == true {
   473  		return "", ErrDisallowCallPrivateFunction
   474  	}
   475  	return e.RunContractScript(source, sourceType, function, args)
   476  }
   477  
   478  // RunContractScript execute script in Smart Contract's way.
   479  func (e *V8Engine) RunContractScript(source, sourceType, function, args string) (string, error) {
   480  	var runnableSource string
   481  	var sourceLineOffset int
   482  	var err error
   483  
   484  	switch sourceType {
   485  	case core.SourceTypeJavaScript:
   486  		runnableSource, sourceLineOffset, err = e.prepareRunnableContractScript(source, function, args)
   487  	case core.SourceTypeTypeScript:
   488  		// transpile to javascript.
   489  		jsSource, _, err := e.TranspileTypeScript(source)
   490  		if err != nil {
   491  			return "", err
   492  		}
   493  		runnableSource, sourceLineOffset, err = e.prepareRunnableContractScript(jsSource, function, args)
   494  	default:
   495  		return "", ErrUnsupportedSourceType
   496  	}
   497  
   498  	if err != nil {
   499  		return "", err
   500  	}
   501  	if core.NvmMemoryLimitWithoutInjectAtHeight(e.ctx.block.Height()) {
   502  		e.CollectTracingStats()
   503  		mem := e.actualTotalMemorySize + core.DefaultLimitsOfTotalMemorySize
   504  		logging.VLog().WithFields(logrus.Fields{
   505  			"actualTotalMemorySize": e.actualTotalMemorySize,
   506  			"limit":                 mem,
   507  			"tx.hash":               e.ctx.tx.Hash(),
   508  		}).Debug("mem limit")
   509  		if err := e.SetExecutionLimits(e.limitsOfExecutionInstructions, mem); err != nil {
   510  			return "", err
   511  		}
   512  	}
   513  
   514  	if core.NvmGasLimitWithoutTimeoutAtHeight(e.ctx.block.Height()) {
   515  		if e.limitsOfExecutionInstructions > MaxLimitsOfExecutionInstructions {
   516  			e.SetExecutionLimits(MaxLimitsOfExecutionInstructions, e.limitsOfTotalMemorySize)
   517  		}
   518  	}
   519  	result, err := e.RunScriptSource(runnableSource, sourceLineOffset)
   520  
   521  	if core.NvmGasLimitWithoutTimeoutAtHeight(e.ctx.block.Height()) {
   522  		if e.limitsOfExecutionInstructions == MaxLimitsOfExecutionInstructions && err == ErrInsufficientGas {
   523  			err = ErrExecutionTimeout
   524  			result = "\"null\""
   525  		}
   526  	}
   527  	return result, err
   528  }
   529  
   530  // ClearModuleCache ..
   531  func ClearSourceModuleCache() {
   532  	sourceModuleCache.Purge()
   533  }
   534  
   535  // AddModule add module.
   536  func (e *V8Engine) AddModule(id, source string, sourceLineOffset int) error {
   537  	// inject tracing instruction when enable limits.
   538  	if e.enableLimits {
   539  		var item *sourceModuleItem
   540  		sourceHash := byteutils.Hex(hash.Sha3256([]byte(source)))
   541  
   542  		// try read from cache.
   543  		if sourceModuleCache.Contains(sourceHash) { //ToDo cache whether need into db
   544  			value, _ := sourceModuleCache.Get(sourceHash)
   545  			item = value.(*sourceModuleItem)
   546  		}
   547  
   548  		if item == nil {
   549  			traceableSource, lineOffset, err := e.InjectTracingInstructions(source)
   550  			if err != nil {
   551  				logging.VLog().WithFields(logrus.Fields{
   552  					"err": err,
   553  				}).Debug("Failed to inject tracing instruction.")
   554  				return err
   555  			}
   556  
   557  			item = &sourceModuleItem{
   558  				source:                    source,
   559  				sourceLineOffset:          sourceLineOffset,
   560  				traceableSource:           traceableSource,
   561  				traceableSourceLineOffset: lineOffset,
   562  			}
   563  
   564  			// put to cache.
   565  			sourceModuleCache.Add(sourceHash, item)
   566  		}
   567  
   568  		source = item.traceableSource
   569  		sourceLineOffset = item.traceableSourceLineOffset
   570  	}
   571  	e.modules.Add(NewModule(id, source, sourceLineOffset))
   572  	return nil
   573  }
   574  
   575  func (e *V8Engine) prepareRunnableContractScript(source, function, args string) (string, int, error) {
   576  	sourceLineOffset := 0
   577  
   578  	counterVersion := core.GetNearestInstructionCounterVersionAtHeight(e.ctx.block.Height())
   579  	if counterVersion != instructionCounterVersion {
   580  		instructionCounterVersion = counterVersion
   581  		ClearSourceModuleCache()
   582  		logging.VLog().WithFields(logrus.Fields{
   583  			"height":  e.ctx.block.Height(),
   584  			"version": instructionCounterVersion,
   585  		}).Info("Clear source module cache.")
   586  	}
   587  
   588  	// add module.
   589  	const ModuleID string = "contract.js"
   590  	if err := e.AddModule(ModuleID, source, sourceLineOffset); err != nil {
   591  		return "", 0, err
   592  	}
   593  
   594  	// prepare for execute.
   595  	block := toSerializableBlock(e.ctx.block)
   596  	blockJSON, err := json.Marshal(block)
   597  	if err != nil {
   598  		return "", 0, err
   599  	}
   600  	tx := toSerializableTransaction(e.ctx.tx)
   601  	txJSON, err := json.Marshal(tx)
   602  	if err != nil {
   603  		return "", 0, err
   604  	}
   605  
   606  	var runnableSource string
   607  	var argsInput []byte
   608  	if len(args) > 0 {
   609  		var argsObj []interface{}
   610  		if err := json.Unmarshal([]byte(args), &argsObj); err != nil {
   611  			return "", 0, ErrArgumentsFormat
   612  		}
   613  		if argsInput, err = json.Marshal(argsObj); err != nil {
   614  			return "", 0, ErrArgumentsFormat
   615  		}
   616  
   617  	} else {
   618  		argsInput = []byte("[]")
   619  	}
   620  	runnableSource = fmt.Sprintf(`Blockchain.blockParse("%s");
   621  									Blockchain.transactionParse("%s");
   622  									var __contract = require("%s");
   623  									var __instance = new __contract();
   624  									__instance["%s"].apply(__instance, JSON.parse("%s"));`,
   625  		formatArgs(string(blockJSON)), formatArgs(string(txJSON)),
   626  		ModuleID, function, formatArgs(string(argsInput))) //TODO: freeze?
   627  	return runnableSource, 0, nil
   628  }
   629  
   630  func getEngineByStorageHandler(handler uint64) (*V8Engine, Account) {
   631  	storagesLock.RLock()
   632  	engine := storages[handler]
   633  	storagesLock.RUnlock()
   634  
   635  	if engine == nil {
   636  		logging.VLog().WithFields(logrus.Fields{
   637  			"wantedHandler": handler,
   638  		}).Error("wantedHandler is not found.")
   639  		return nil, nil
   640  	}
   641  
   642  	if engine.lcsHandler == handler {
   643  		return engine, engine.ctx.contract
   644  	} else if engine.gcsHandler == handler {
   645  		// disable gcs according to issue https://github.com/nebulasio/go-nebulas/issues/23.
   646  		return nil, nil
   647  		// return engine, engine.ctx.owner
   648  	} else {
   649  		logging.VLog().WithFields(logrus.Fields{
   650  			"lcsHandler":    engine.lcsHandler,
   651  			"gcsHandler":    engine.gcsHandler,
   652  			"wantedHandler": handler,
   653  		}).Error("in-consistent storage handler.")
   654  		return nil, nil
   655  	}
   656  }
   657  
   658  func getEngineByEngineHandler(handler unsafe.Pointer) *V8Engine {
   659  	v8engine := (*C.V8Engine)(handler)
   660  	enginesLock.RLock()
   661  	defer enginesLock.RUnlock()
   662  
   663  	return engines[v8engine]
   664  }
   665  
   666  func formatArgs(s string) string {
   667  	s = strings.Replace(s, "\\", "\\\\", -1)
   668  	s = strings.Replace(s, "\n", "\\n", -1)
   669  	s = strings.Replace(s, "\r", "\\r", -1)
   670  	s = strings.Replace(s, "\"", "\\\"", -1)
   671  	return s
   672  }