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