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