github.com/amazechain/amc@v0.1.3/internal/tracers/js/goja.go (about)

     1  // Copyright 2023 The AmazeChain Authors
     2  // This file is part of the AmazeChain library.
     3  //
     4  // The AmazeChain library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The AmazeChain library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the AmazeChain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package js
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"github.com/amazechain/amc/internal/vm/evmtypes"
    24  	"github.com/holiman/uint256"
    25  	"math/big"
    26  
    27  	"github.com/dop251/goja"
    28  
    29  	"github.com/amazechain/amc/common/crypto"
    30  	"github.com/amazechain/amc/common/hexutil"
    31  	common "github.com/amazechain/amc/common/types"
    32  	"github.com/amazechain/amc/internal/tracers"
    33  	jsassets "github.com/amazechain/amc/internal/tracers/js/internal/tracers"
    34  	"github.com/amazechain/amc/internal/vm"
    35  	"github.com/amazechain/amc/internal/vm/stack"
    36  )
    37  
    38  const (
    39  	memoryPadLimit = 1024 * 1024
    40  )
    41  
    42  var assetTracers = make(map[string]string)
    43  
    44  // init retrieves the JavaScript transaction tracers included in go-ethereum.
    45  func init() {
    46  	var err error
    47  	assetTracers, err = jsassets.Load()
    48  	if err != nil {
    49  		panic(err)
    50  	}
    51  	type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
    52  	lookup := func(code string) ctorFn {
    53  		return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
    54  			return newJsTracer(code, ctx, cfg)
    55  		}
    56  	}
    57  	for name, code := range assetTracers {
    58  		tracers.DefaultDirectory.Register(name, lookup(code), true)
    59  	}
    60  	tracers.DefaultDirectory.RegisterJSEval(newJsTracer)
    61  }
    62  
    63  // bigIntProgram is compiled once and the exported function mostly invoked to convert
    64  // hex strings into big ints.
    65  var bigIntProgram = goja.MustCompile("bigInt", bigIntegerJS, false)
    66  
    67  type toBigFn = func(vm *goja.Runtime, val string) (goja.Value, error)
    68  type toBufFn = func(vm *goja.Runtime, val []byte) (goja.Value, error)
    69  type fromBufFn = func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byte, error)
    70  
    71  func toBuf(vm *goja.Runtime, bufType goja.Value, val []byte) (goja.Value, error) {
    72  	// bufType is usually Uint8Array. This is equivalent to `new Uint8Array(val)` in JS.
    73  	return vm.New(bufType, vm.ToValue(vm.NewArrayBuffer(val)))
    74  }
    75  
    76  func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString bool) ([]byte, error) {
    77  	obj := buf.ToObject(vm)
    78  	switch obj.ClassName() {
    79  	case "String":
    80  		if !allowString {
    81  			break
    82  		}
    83  		return common.FromHex2Bytes(obj.String()), nil
    84  
    85  	case "Array":
    86  		var b []byte
    87  		if err := vm.ExportTo(buf, &b); err != nil {
    88  			return nil, err
    89  		}
    90  		return b, nil
    91  
    92  	case "Object":
    93  		if !obj.Get("constructor").SameAs(bufType) {
    94  			break
    95  		}
    96  		b := obj.Get("buffer").Export().(goja.ArrayBuffer).Bytes()
    97  		return b, nil
    98  	}
    99  	return nil, fmt.Errorf("invalid buffer type")
   100  }
   101  
   102  // jsTracer is an implementation of the Tracer interface which evaluates
   103  // JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
   104  type jsTracer struct {
   105  	vm                *goja.Runtime
   106  	env               *vm.EVM
   107  	toBig             toBigFn               // Converts a hex string into a JS bigint
   108  	toBuf             toBufFn               // Converts a []byte into a JS buffer
   109  	fromBuf           fromBufFn             // Converts an array, hex string or Uint8Array to a []byte
   110  	ctx               map[string]goja.Value // KV-bag passed to JS in `result`
   111  	activePrecompiles []common.Address      // List of active precompiles at current block
   112  	traceStep         bool                  // True if tracer object exposes a `step()` method
   113  	traceFrame        bool                  // True if tracer object exposes the `enter()` and `exit()` methods
   114  	gasLimit          uint64                // Amount of gas bought for the whole tx
   115  	err               error                 // Any error that should stop tracing
   116  	obj               *goja.Object          // Trace object
   117  
   118  	// Methods exposed by tracer
   119  	result goja.Callable
   120  	fault  goja.Callable
   121  	step   goja.Callable
   122  	enter  goja.Callable
   123  	exit   goja.Callable
   124  
   125  	// Underlying structs being passed into JS
   126  	log         *steplog
   127  	frame       *callframe
   128  	frameResult *callframeResult
   129  
   130  	// Goja-wrapping of types prepared for JS consumption
   131  	logValue         goja.Value
   132  	dbValue          goja.Value
   133  	frameValue       goja.Value
   134  	frameResultValue goja.Value
   135  }
   136  
   137  // newJsTracer instantiates a new JS tracer instance. code is a
   138  // Javascript snippet which evaluates to an expression returning
   139  // an object with certain methods:
   140  //
   141  // The methods `result` and `fault` are required to be present.
   142  // The methods `step`, `enter`, and `exit` are optional, but note that
   143  // `enter` and `exit` always go together.
   144  func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
   145  	vm := goja.New()
   146  	// By default field names are exported to JS as is, i.e. capitalized.
   147  	vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
   148  	t := &jsTracer{
   149  		vm:  vm,
   150  		ctx: make(map[string]goja.Value),
   151  	}
   152  	if ctx == nil {
   153  		ctx = new(tracers.Context)
   154  	}
   155  	if ctx.BlockHash != (common.Hash{}) {
   156  		t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes())
   157  		if ctx.TxHash != (common.Hash{}) {
   158  			t.ctx["txIndex"] = vm.ToValue(ctx.TxIndex)
   159  			t.ctx["txHash"] = vm.ToValue(ctx.TxHash.Bytes())
   160  		}
   161  	}
   162  
   163  	t.setTypeConverters()
   164  	t.setBuiltinFunctions()
   165  	ret, err := vm.RunString("(" + code + ")")
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	// Check tracer's interface for required and optional methods.
   170  	obj := ret.ToObject(vm)
   171  	result, ok := goja.AssertFunction(obj.Get("result"))
   172  	if !ok {
   173  		return nil, errors.New("trace object must expose a function result()")
   174  	}
   175  	fault, ok := goja.AssertFunction(obj.Get("fault"))
   176  	if !ok {
   177  		return nil, errors.New("trace object must expose a function fault()")
   178  	}
   179  	step, ok := goja.AssertFunction(obj.Get("step"))
   180  	t.traceStep = ok
   181  	enter, hasEnter := goja.AssertFunction(obj.Get("enter"))
   182  	exit, hasExit := goja.AssertFunction(obj.Get("exit"))
   183  	if hasEnter != hasExit {
   184  		return nil, errors.New("trace object must expose either both or none of enter() and exit()")
   185  	}
   186  	t.traceFrame = hasEnter
   187  	t.obj = obj
   188  	t.step = step
   189  	t.enter = enter
   190  	t.exit = exit
   191  	t.result = result
   192  	t.fault = fault
   193  
   194  	// Pass in config
   195  	if setup, ok := goja.AssertFunction(obj.Get("setup")); ok {
   196  		cfgStr := "{}"
   197  		if cfg != nil {
   198  			cfgStr = string(cfg)
   199  		}
   200  		if _, err := setup(obj, vm.ToValue(cfgStr)); err != nil {
   201  			return nil, err
   202  		}
   203  	}
   204  	// Setup objects carrying data to JS. These are created once and re-used.
   205  	t.log = &steplog{
   206  		vm:       vm,
   207  		op:       &opObj{vm: vm},
   208  		memory:   &memoryObj{vm: vm, toBig: t.toBig, toBuf: t.toBuf},
   209  		stack:    &stackObj{vm: vm, toBig: t.toBig},
   210  		contract: &contractObj{vm: vm, toBig: t.toBig, toBuf: t.toBuf},
   211  	}
   212  	t.frame = &callframe{vm: vm, toBig: t.toBig, toBuf: t.toBuf}
   213  	t.frameResult = &callframeResult{vm: vm, toBuf: t.toBuf}
   214  	t.frameValue = t.frame.setupObject()
   215  	t.frameResultValue = t.frameResult.setupObject()
   216  	t.logValue = t.log.setupObject()
   217  	return t, nil
   218  }
   219  
   220  // CaptureTxStart implements the Tracer interface and is invoked at the beginning of
   221  // transaction processing.
   222  func (t *jsTracer) CaptureTxStart(gasLimit uint64) {
   223  	t.gasLimit = gasLimit
   224  }
   225  
   226  // CaptureTxEnd implements the Tracer interface and is invoked at the end of
   227  // transaction processing.
   228  func (t *jsTracer) CaptureTxEnd(restGas uint64) {
   229  	t.ctx["gasUsed"] = t.vm.ToValue(t.gasLimit - restGas)
   230  }
   231  
   232  // CaptureStart implements the Tracer interface to initialize the tracing operation.
   233  func (t *jsTracer) CaptureStart(env vm.VMInterface, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *uint256.Int) {
   234  	t.env = env.(*vm.EVM)
   235  	db := &dbObj{db: env.IntraBlockState(), vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
   236  	t.dbValue = db.setupObject()
   237  	if create {
   238  		t.ctx["type"] = t.vm.ToValue("CREATE")
   239  	} else {
   240  		t.ctx["type"] = t.vm.ToValue("CALL")
   241  	}
   242  	t.ctx["from"] = t.vm.ToValue(from.Bytes())
   243  	t.ctx["to"] = t.vm.ToValue(to.Bytes())
   244  	t.ctx["input"] = t.vm.ToValue(input)
   245  	t.ctx["gas"] = t.vm.ToValue(gas)
   246  	t.ctx["gasPrice"] = t.vm.ToValue(env.TxContext().GasPrice)
   247  	valueBig, err := t.toBig(t.vm, value.ToBig().String())
   248  	if err != nil {
   249  		t.err = err
   250  		return
   251  	}
   252  	t.ctx["value"] = valueBig
   253  	t.ctx["block"] = t.vm.ToValue(env.Context().BlockNumber)
   254  	// Update list of precompiles based on current block
   255  	rules := env.ChainConfig().Rules(env.Context().BlockNumber)
   256  	t.activePrecompiles = vm.ActivePrecompiles(rules)
   257  }
   258  
   259  // CaptureState implements the Tracer interface to trace a single step of VM execution.
   260  func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
   261  	if !t.traceStep {
   262  		return
   263  	}
   264  	if t.err != nil {
   265  		return
   266  	}
   267  
   268  	log := t.log
   269  	log.op.op = op
   270  	log.memory.memory = scope.Memory
   271  	log.stack.stack = scope.Stack
   272  	log.contract.contract = scope.Contract
   273  	log.pc = pc
   274  	log.gas = gas
   275  	log.cost = cost
   276  	log.refund = t.env.IntraBlockState().GetRefund()
   277  	log.depth = depth
   278  	log.err = err
   279  	if _, err := t.step(t.obj, t.logValue, t.dbValue); err != nil {
   280  		t.onError("step", err)
   281  	}
   282  }
   283  
   284  // CaptureFault implements the Tracer interface to trace an execution fault
   285  func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
   286  	if t.err != nil {
   287  		return
   288  	}
   289  	// Other log fields have been already set as part of the last CaptureState.
   290  	t.log.err = err
   291  	if _, err := t.fault(t.obj, t.logValue, t.dbValue); err != nil {
   292  		t.onError("fault", err)
   293  	}
   294  }
   295  
   296  // CaptureEnd is called after the call finishes to finalize the tracing.
   297  func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
   298  	t.ctx["output"] = t.vm.ToValue(output)
   299  	if err != nil {
   300  		t.ctx["error"] = t.vm.ToValue(err.Error())
   301  	}
   302  }
   303  
   304  // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
   305  func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *uint256.Int) {
   306  	if !t.traceFrame {
   307  		return
   308  	}
   309  	if t.err != nil {
   310  		return
   311  	}
   312  
   313  	t.frame.typ = typ.String()
   314  	t.frame.from = from
   315  	t.frame.to = to
   316  	t.frame.input = common.CopyBytes(input)
   317  	t.frame.gas = uint(gas)
   318  	t.frame.value = nil
   319  	if value != nil {
   320  		t.frame.value = new(big.Int).SetBytes(value.ToBig().Bytes())
   321  	}
   322  
   323  	if _, err := t.enter(t.obj, t.frameValue); err != nil {
   324  		t.onError("enter", err)
   325  	}
   326  }
   327  
   328  // CaptureExit is called when EVM exits a scope, even if the scope didn't
   329  // execute any code.
   330  func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
   331  	if !t.traceFrame {
   332  		return
   333  	}
   334  
   335  	t.frameResult.gasUsed = uint(gasUsed)
   336  	t.frameResult.output = common.CopyBytes(output)
   337  	t.frameResult.err = err
   338  
   339  	if _, err := t.exit(t.obj, t.frameResultValue); err != nil {
   340  		t.onError("exit", err)
   341  	}
   342  }
   343  
   344  // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
   345  func (t *jsTracer) GetResult() (json.RawMessage, error) {
   346  	ctx := t.vm.ToValue(t.ctx)
   347  	res, err := t.result(t.obj, ctx, t.dbValue)
   348  	if err != nil {
   349  		return nil, wrapError("result", err)
   350  	}
   351  	encoded, err := json.Marshal(res)
   352  	if err != nil {
   353  		return nil, err
   354  	}
   355  	return json.RawMessage(encoded), t.err
   356  }
   357  
   358  // Stop terminates execution of the tracer at the first opportune moment.
   359  func (t *jsTracer) Stop(err error) {
   360  	t.vm.Interrupt(err)
   361  }
   362  
   363  // onError is called anytime the running JS code is interrupted
   364  // and returns an error. It in turn pings the EVM to cancel its
   365  // execution.
   366  func (t *jsTracer) onError(context string, err error) {
   367  	t.err = wrapError(context, err)
   368  	// `env` is set on CaptureStart which comes before any JS execution.
   369  	// So it should be non-nil.
   370  	t.env.Cancel()
   371  }
   372  
   373  func wrapError(context string, err error) error {
   374  	return fmt.Errorf("%v    in server-side tracer function '%v'", err, context)
   375  }
   376  
   377  // setBuiltinFunctions injects Go functions which are available to tracers into the environment.
   378  // It depends on type converters having been set up.
   379  func (t *jsTracer) setBuiltinFunctions() {
   380  	vm := t.vm
   381  	// TODO: load console from goja-nodejs
   382  	vm.Set("toHex", func(v goja.Value) string {
   383  		b, err := t.fromBuf(vm, v, false)
   384  		if err != nil {
   385  			vm.Interrupt(err)
   386  			return ""
   387  		}
   388  		return hexutil.Encode(b)
   389  	})
   390  	vm.Set("toWord", func(v goja.Value) goja.Value {
   391  		// TODO: add test with []byte len < 32 or > 32
   392  		b, err := t.fromBuf(vm, v, true)
   393  		if err != nil {
   394  			vm.Interrupt(err)
   395  			return nil
   396  		}
   397  		b = common.BytesToHash(b).Bytes()
   398  		res, err := t.toBuf(vm, b)
   399  		if err != nil {
   400  			vm.Interrupt(err)
   401  			return nil
   402  		}
   403  		return res
   404  	})
   405  	vm.Set("toAddress", func(v goja.Value) goja.Value {
   406  		a, err := t.fromBuf(vm, v, true)
   407  		if err != nil {
   408  			vm.Interrupt(err)
   409  			return nil
   410  		}
   411  		a = common.BytesToAddress(a).Bytes()
   412  		res, err := t.toBuf(vm, a)
   413  		if err != nil {
   414  			vm.Interrupt(err)
   415  			return nil
   416  		}
   417  		return res
   418  	})
   419  	vm.Set("toContract", func(from goja.Value, nonce uint) goja.Value {
   420  		a, err := t.fromBuf(vm, from, true)
   421  		if err != nil {
   422  			vm.Interrupt(err)
   423  			return nil
   424  		}
   425  		addr := common.BytesToAddress(a)
   426  		b := crypto.CreateAddress(addr, uint64(nonce)).Bytes()
   427  		res, err := t.toBuf(vm, b)
   428  		if err != nil {
   429  			vm.Interrupt(err)
   430  			return nil
   431  		}
   432  		return res
   433  	})
   434  	vm.Set("toContract2", func(from goja.Value, salt string, initcode goja.Value) goja.Value {
   435  		a, err := t.fromBuf(vm, from, true)
   436  		if err != nil {
   437  			vm.Interrupt(err)
   438  			return nil
   439  		}
   440  		addr := common.BytesToAddress(a)
   441  		code, err := t.fromBuf(vm, initcode, true)
   442  		if err != nil {
   443  			vm.Interrupt(err)
   444  			return nil
   445  		}
   446  		code = common.CopyBytes(code)
   447  		codeHash := crypto.Keccak256(code)
   448  		b := crypto.CreateAddress2(addr, common.HexToHash(salt), codeHash).Bytes()
   449  		res, err := t.toBuf(vm, b)
   450  		if err != nil {
   451  			vm.Interrupt(err)
   452  			return nil
   453  		}
   454  		return res
   455  	})
   456  	vm.Set("isPrecompiled", func(v goja.Value) bool {
   457  		a, err := t.fromBuf(vm, v, true)
   458  		if err != nil {
   459  			vm.Interrupt(err)
   460  			return false
   461  		}
   462  		addr := common.BytesToAddress(a)
   463  		for _, p := range t.activePrecompiles {
   464  			if p == addr {
   465  				return true
   466  			}
   467  		}
   468  		return false
   469  	})
   470  	vm.Set("slice", func(slice goja.Value, start, end int) goja.Value {
   471  		b, err := t.fromBuf(vm, slice, false)
   472  		if err != nil {
   473  			vm.Interrupt(err)
   474  			return nil
   475  		}
   476  		if start < 0 || start > end || end > len(b) {
   477  			vm.Interrupt(fmt.Sprintf("Tracer accessed out of bound memory: available %d, offset %d, size %d", len(b), start, end-start))
   478  			return nil
   479  		}
   480  		res, err := t.toBuf(vm, b[start:end])
   481  		if err != nil {
   482  			vm.Interrupt(err)
   483  			return nil
   484  		}
   485  		return res
   486  	})
   487  }
   488  
   489  // setTypeConverters sets up utilities for converting Go types into those
   490  // suitable for JS consumption.
   491  func (t *jsTracer) setTypeConverters() error {
   492  	// Inject bigint logic.
   493  	// TODO: To be replaced after goja adds support for native JS bigint.
   494  	toBigCode, err := t.vm.RunProgram(bigIntProgram)
   495  	if err != nil {
   496  		return err
   497  	}
   498  	// Used to create JS bigint objects from go.
   499  	toBigFn, ok := goja.AssertFunction(toBigCode)
   500  	if !ok {
   501  		return errors.New("failed to bind bigInt func")
   502  	}
   503  	toBigWrapper := func(vm *goja.Runtime, val string) (goja.Value, error) {
   504  		return toBigFn(goja.Undefined(), vm.ToValue(val))
   505  	}
   506  	t.toBig = toBigWrapper
   507  	// NOTE: We need this workaround to create JS buffers because
   508  	// goja doesn't at the moment expose constructors for typed arrays.
   509  	//
   510  	// Cache uint8ArrayType once to be used every time for less overhead.
   511  	uint8ArrayType := t.vm.Get("Uint8Array")
   512  	toBufWrapper := func(vm *goja.Runtime, val []byte) (goja.Value, error) {
   513  		return toBuf(vm, uint8ArrayType, val)
   514  	}
   515  	t.toBuf = toBufWrapper
   516  	fromBufWrapper := func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byte, error) {
   517  		return fromBuf(vm, uint8ArrayType, buf, allowString)
   518  	}
   519  	t.fromBuf = fromBufWrapper
   520  	return nil
   521  }
   522  
   523  type opObj struct {
   524  	vm *goja.Runtime
   525  	op vm.OpCode
   526  }
   527  
   528  func (o *opObj) ToNumber() int {
   529  	return int(o.op)
   530  }
   531  
   532  func (o *opObj) ToString() string {
   533  	return o.op.String()
   534  }
   535  
   536  func (o *opObj) IsPush() bool {
   537  	return o.op.IsPush()
   538  }
   539  
   540  func (o *opObj) setupObject() *goja.Object {
   541  	obj := o.vm.NewObject()
   542  	obj.Set("toNumber", o.vm.ToValue(o.ToNumber))
   543  	obj.Set("toString", o.vm.ToValue(o.ToString))
   544  	obj.Set("isPush", o.vm.ToValue(o.IsPush))
   545  	return obj
   546  }
   547  
   548  type memoryObj struct {
   549  	memory *vm.Memory
   550  	vm     *goja.Runtime
   551  	toBig  toBigFn
   552  	toBuf  toBufFn
   553  }
   554  
   555  func (mo *memoryObj) Slice(begin, end int64) goja.Value {
   556  	b, err := mo.slice(begin, end)
   557  	if err != nil {
   558  		mo.vm.Interrupt(err)
   559  		return nil
   560  	}
   561  	res, err := mo.toBuf(mo.vm, b)
   562  	if err != nil {
   563  		mo.vm.Interrupt(err)
   564  		return nil
   565  	}
   566  	return res
   567  }
   568  
   569  // slice returns the requested range of memory as a byte slice.
   570  func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
   571  	if end == begin {
   572  		return []byte{}, nil
   573  	}
   574  	if end < begin || begin < 0 {
   575  		return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
   576  	}
   577  	mlen := mo.memory.Len()
   578  	if end-int64(mlen) > memoryPadLimit {
   579  		return nil, fmt.Errorf("tracer reached limit for padding memory slice: end %d, memorySize %d", end, mlen)
   580  	}
   581  	slice := make([]byte, end-begin)
   582  	end = min(end, int64(mo.memory.Len()))
   583  	ptr := mo.memory.GetPtr(begin, end-begin)
   584  	copy(slice[:], ptr[:])
   585  	return slice, nil
   586  }
   587  
   588  func (mo *memoryObj) GetUint(addr int64) goja.Value {
   589  	value, err := mo.getUint(addr)
   590  	if err != nil {
   591  		mo.vm.Interrupt(err)
   592  		return nil
   593  	}
   594  	res, err := mo.toBig(mo.vm, value.String())
   595  	if err != nil {
   596  		mo.vm.Interrupt(err)
   597  		return nil
   598  	}
   599  	return res
   600  }
   601  
   602  // getUint returns the 32 bytes at the specified address interpreted as a uint.
   603  func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
   604  	if mo.memory.Len() < int(addr)+32 || addr < 0 {
   605  		return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
   606  	}
   607  	return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil
   608  }
   609  
   610  func (mo *memoryObj) Length() int {
   611  	return mo.memory.Len()
   612  }
   613  
   614  func (m *memoryObj) setupObject() *goja.Object {
   615  	o := m.vm.NewObject()
   616  	o.Set("slice", m.vm.ToValue(m.Slice))
   617  	o.Set("getUint", m.vm.ToValue(m.GetUint))
   618  	o.Set("length", m.vm.ToValue(m.Length))
   619  	return o
   620  }
   621  
   622  type stackObj struct {
   623  	stack *stack.Stack
   624  	vm    *goja.Runtime
   625  	toBig toBigFn
   626  }
   627  
   628  func (s *stackObj) Peek(idx int) goja.Value {
   629  	value, err := s.peek(idx)
   630  	if err != nil {
   631  		s.vm.Interrupt(err)
   632  		return nil
   633  	}
   634  	res, err := s.toBig(s.vm, value.String())
   635  	if err != nil {
   636  		s.vm.Interrupt(err)
   637  		return nil
   638  	}
   639  	return res
   640  }
   641  
   642  // peek returns the nth-from-the-top element of the stack.
   643  func (s *stackObj) peek(idx int) (*big.Int, error) {
   644  	if len(s.stack.Data) <= idx || idx < 0 {
   645  		return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data), idx)
   646  	}
   647  	return s.stack.Back(idx).ToBig(), nil
   648  }
   649  
   650  func (s *stackObj) Length() int {
   651  	return len(s.stack.Data)
   652  }
   653  
   654  func (s *stackObj) setupObject() *goja.Object {
   655  	o := s.vm.NewObject()
   656  	o.Set("peek", s.vm.ToValue(s.Peek))
   657  	o.Set("length", s.vm.ToValue(s.Length))
   658  	return o
   659  }
   660  
   661  type dbObj struct {
   662  	db      evmtypes.IntraBlockState
   663  	vm      *goja.Runtime
   664  	toBig   toBigFn
   665  	toBuf   toBufFn
   666  	fromBuf fromBufFn
   667  }
   668  
   669  func (do *dbObj) GetBalance(addrSlice goja.Value) goja.Value {
   670  	a, err := do.fromBuf(do.vm, addrSlice, false)
   671  	if err != nil {
   672  		do.vm.Interrupt(err)
   673  		return nil
   674  	}
   675  	addr := common.BytesToAddress(a)
   676  	value := do.db.GetBalance(addr)
   677  	res, err := do.toBig(do.vm, value.String())
   678  	if err != nil {
   679  		do.vm.Interrupt(err)
   680  		return nil
   681  	}
   682  	return res
   683  }
   684  
   685  func (do *dbObj) GetNonce(addrSlice goja.Value) uint64 {
   686  	a, err := do.fromBuf(do.vm, addrSlice, false)
   687  	if err != nil {
   688  		do.vm.Interrupt(err)
   689  		return 0
   690  	}
   691  	addr := common.BytesToAddress(a)
   692  	return do.db.GetNonce(addr)
   693  }
   694  
   695  func (do *dbObj) GetCode(addrSlice goja.Value) goja.Value {
   696  	a, err := do.fromBuf(do.vm, addrSlice, false)
   697  	if err != nil {
   698  		do.vm.Interrupt(err)
   699  		return nil
   700  	}
   701  	addr := common.BytesToAddress(a)
   702  	code := do.db.GetCode(addr)
   703  	res, err := do.toBuf(do.vm, code)
   704  	if err != nil {
   705  		do.vm.Interrupt(err)
   706  		return nil
   707  	}
   708  	return res
   709  }
   710  
   711  func (do *dbObj) GetState(addrSlice goja.Value, hashSlice goja.Value) goja.Value {
   712  	a, err := do.fromBuf(do.vm, addrSlice, false)
   713  	if err != nil {
   714  		do.vm.Interrupt(err)
   715  		return nil
   716  	}
   717  	addr := common.BytesToAddress(a)
   718  	h, err := do.fromBuf(do.vm, hashSlice, false)
   719  	if err != nil {
   720  		do.vm.Interrupt(err)
   721  		return nil
   722  	}
   723  	hash := common.BytesToHash(h)
   724  	var out uint256.Int
   725  	do.db.GetState(addr, &hash, &out)
   726  	state := out.Bytes()
   727  	res, err := do.toBuf(do.vm, state)
   728  	if err != nil {
   729  		do.vm.Interrupt(err)
   730  		return nil
   731  	}
   732  	return res
   733  }
   734  
   735  func (do *dbObj) Exists(addrSlice goja.Value) bool {
   736  	a, err := do.fromBuf(do.vm, addrSlice, false)
   737  	if err != nil {
   738  		do.vm.Interrupt(err)
   739  		return false
   740  	}
   741  	addr := common.BytesToAddress(a)
   742  	return do.db.Exist(addr)
   743  }
   744  
   745  func (do *dbObj) setupObject() *goja.Object {
   746  	o := do.vm.NewObject()
   747  	o.Set("getBalance", do.vm.ToValue(do.GetBalance))
   748  	o.Set("getNonce", do.vm.ToValue(do.GetNonce))
   749  	o.Set("getCode", do.vm.ToValue(do.GetCode))
   750  	o.Set("getState", do.vm.ToValue(do.GetState))
   751  	o.Set("exists", do.vm.ToValue(do.Exists))
   752  	return o
   753  }
   754  
   755  type contractObj struct {
   756  	contract *vm.Contract
   757  	vm       *goja.Runtime
   758  	toBig    toBigFn
   759  	toBuf    toBufFn
   760  }
   761  
   762  func (co *contractObj) GetCaller() goja.Value {
   763  	caller := co.contract.Caller().Bytes()
   764  	res, err := co.toBuf(co.vm, caller)
   765  	if err != nil {
   766  		co.vm.Interrupt(err)
   767  		return nil
   768  	}
   769  	return res
   770  }
   771  
   772  func (co *contractObj) GetAddress() goja.Value {
   773  	addr := co.contract.Address().Bytes()
   774  	res, err := co.toBuf(co.vm, addr)
   775  	if err != nil {
   776  		co.vm.Interrupt(err)
   777  		return nil
   778  	}
   779  	return res
   780  }
   781  
   782  func (co *contractObj) GetValue() goja.Value {
   783  	value := co.contract.Value()
   784  	res, err := co.toBig(co.vm, value.String())
   785  	if err != nil {
   786  		co.vm.Interrupt(err)
   787  		return nil
   788  	}
   789  	return res
   790  }
   791  
   792  func (co *contractObj) GetInput() goja.Value {
   793  	input := common.CopyBytes(co.contract.Input)
   794  	res, err := co.toBuf(co.vm, input)
   795  	if err != nil {
   796  		co.vm.Interrupt(err)
   797  		return nil
   798  	}
   799  	return res
   800  }
   801  
   802  func (c *contractObj) setupObject() *goja.Object {
   803  	o := c.vm.NewObject()
   804  	o.Set("getCaller", c.vm.ToValue(c.GetCaller))
   805  	o.Set("getAddress", c.vm.ToValue(c.GetAddress))
   806  	o.Set("getValue", c.vm.ToValue(c.GetValue))
   807  	o.Set("getInput", c.vm.ToValue(c.GetInput))
   808  	return o
   809  }
   810  
   811  type callframe struct {
   812  	vm    *goja.Runtime
   813  	toBig toBigFn
   814  	toBuf toBufFn
   815  
   816  	typ   string
   817  	from  common.Address
   818  	to    common.Address
   819  	input []byte
   820  	gas   uint
   821  	value *big.Int
   822  }
   823  
   824  func (f *callframe) GetType() string {
   825  	return f.typ
   826  }
   827  
   828  func (f *callframe) GetFrom() goja.Value {
   829  	from := f.from.Bytes()
   830  	res, err := f.toBuf(f.vm, from)
   831  	if err != nil {
   832  		f.vm.Interrupt(err)
   833  		return nil
   834  	}
   835  	return res
   836  }
   837  
   838  func (f *callframe) GetTo() goja.Value {
   839  	to := f.to.Bytes()
   840  	res, err := f.toBuf(f.vm, to)
   841  	if err != nil {
   842  		f.vm.Interrupt(err)
   843  		return nil
   844  	}
   845  	return res
   846  }
   847  
   848  func (f *callframe) GetInput() goja.Value {
   849  	input := f.input
   850  	res, err := f.toBuf(f.vm, input)
   851  	if err != nil {
   852  		f.vm.Interrupt(err)
   853  		return nil
   854  	}
   855  	return res
   856  }
   857  
   858  func (f *callframe) GetGas() uint {
   859  	return f.gas
   860  }
   861  
   862  func (f *callframe) GetValue() goja.Value {
   863  	if f.value == nil {
   864  		return goja.Undefined()
   865  	}
   866  	res, err := f.toBig(f.vm, f.value.String())
   867  	if err != nil {
   868  		f.vm.Interrupt(err)
   869  		return nil
   870  	}
   871  	return res
   872  }
   873  
   874  func (f *callframe) setupObject() *goja.Object {
   875  	o := f.vm.NewObject()
   876  	o.Set("getType", f.vm.ToValue(f.GetType))
   877  	o.Set("getFrom", f.vm.ToValue(f.GetFrom))
   878  	o.Set("getTo", f.vm.ToValue(f.GetTo))
   879  	o.Set("getInput", f.vm.ToValue(f.GetInput))
   880  	o.Set("getGas", f.vm.ToValue(f.GetGas))
   881  	o.Set("getValue", f.vm.ToValue(f.GetValue))
   882  	return o
   883  }
   884  
   885  type callframeResult struct {
   886  	vm    *goja.Runtime
   887  	toBuf toBufFn
   888  
   889  	gasUsed uint
   890  	output  []byte
   891  	err     error
   892  }
   893  
   894  func (r *callframeResult) GetGasUsed() uint {
   895  	return r.gasUsed
   896  }
   897  
   898  func (r *callframeResult) GetOutput() goja.Value {
   899  	res, err := r.toBuf(r.vm, r.output)
   900  	if err != nil {
   901  		r.vm.Interrupt(err)
   902  		return nil
   903  	}
   904  	return res
   905  }
   906  
   907  func (r *callframeResult) GetError() goja.Value {
   908  	if r.err != nil {
   909  		return r.vm.ToValue(r.err.Error())
   910  	}
   911  	return goja.Undefined()
   912  }
   913  
   914  func (r *callframeResult) setupObject() *goja.Object {
   915  	o := r.vm.NewObject()
   916  	o.Set("getGasUsed", r.vm.ToValue(r.GetGasUsed))
   917  	o.Set("getOutput", r.vm.ToValue(r.GetOutput))
   918  	o.Set("getError", r.vm.ToValue(r.GetError))
   919  	return o
   920  }
   921  
   922  type steplog struct {
   923  	vm *goja.Runtime
   924  
   925  	op       *opObj
   926  	memory   *memoryObj
   927  	stack    *stackObj
   928  	contract *contractObj
   929  
   930  	pc     uint64
   931  	gas    uint64
   932  	cost   uint64
   933  	depth  int
   934  	refund uint64
   935  	err    error
   936  }
   937  
   938  func (l *steplog) GetPC() uint64     { return l.pc }
   939  func (l *steplog) GetGas() uint64    { return l.gas }
   940  func (l *steplog) GetCost() uint64   { return l.cost }
   941  func (l *steplog) GetDepth() int     { return l.depth }
   942  func (l *steplog) GetRefund() uint64 { return l.refund }
   943  
   944  func (l *steplog) GetError() goja.Value {
   945  	if l.err != nil {
   946  		return l.vm.ToValue(l.err.Error())
   947  	}
   948  	return goja.Undefined()
   949  }
   950  
   951  func (l *steplog) setupObject() *goja.Object {
   952  	o := l.vm.NewObject()
   953  	// Setup basic fields.
   954  	o.Set("getPC", l.vm.ToValue(l.GetPC))
   955  	o.Set("getGas", l.vm.ToValue(l.GetGas))
   956  	o.Set("getCost", l.vm.ToValue(l.GetCost))
   957  	o.Set("getDepth", l.vm.ToValue(l.GetDepth))
   958  	o.Set("getRefund", l.vm.ToValue(l.GetRefund))
   959  	o.Set("getError", l.vm.ToValue(l.GetError))
   960  	// Setup nested objects.
   961  	o.Set("op", l.op.setupObject())
   962  	o.Set("stack", l.stack.setupObject())
   963  	o.Set("memory", l.memory.setupObject())
   964  	o.Set("contract", l.contract.setupObject())
   965  	return o
   966  }
   967  
   968  func min(a, b int64) int64 {
   969  	if a < b {
   970  		return a
   971  	}
   972  	return b
   973  }