github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/eth/tracers/js/goja.go (about)

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