github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/eth/tracers/js/tracer.go (about)

     1  // Copyright 2017 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 is a collection of tracers written in javascript.
    18  package js
    19  
    20  import (
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"math/big"
    25  	"strings"
    26  	"sync/atomic"
    27  	"time"
    28  	"unicode"
    29  	"unsafe"
    30  
    31  	"github.com/ethereum/go-ethereum/common"
    32  	"github.com/ethereum/go-ethereum/common/hexutil"
    33  	"github.com/ethereum/go-ethereum/core"
    34  	"github.com/ethereum/go-ethereum/core/vm"
    35  	"github.com/ethereum/go-ethereum/crypto"
    36  	tracers2 "github.com/ethereum/go-ethereum/eth/tracers"
    37  	"github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
    38  	"github.com/ethereum/go-ethereum/log"
    39  	"gopkg.in/olebedev/go-duktape.v3"
    40  )
    41  
    42  // camel converts a snake cased input string into a camel cased output.
    43  func camel(str string) string {
    44  	pieces := strings.Split(str, "_")
    45  	for i := 1; i < len(pieces); i++ {
    46  		pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
    47  	}
    48  	return strings.Join(pieces, "")
    49  }
    50  
    51  var assetTracers = make(map[string]string)
    52  
    53  // init retrieves the JavaScript transaction tracers included in go-ethereum.
    54  func init() {
    55  	for _, file := range tracers.AssetNames() {
    56  		name := camel(strings.TrimSuffix(file, ".js"))
    57  		assetTracers[name] = string(tracers.MustAsset(file))
    58  	}
    59  	tracers2.RegisterLookup(true, newJsTracer)
    60  }
    61  
    62  // makeSlice convert an unsafe memory pointer with the given type into a Go byte
    63  // slice.
    64  //
    65  // Note, the returned slice uses the same memory area as the input arguments.
    66  // If those are duktape stack items, popping them off **will** make the slice
    67  // contents change.
    68  func makeSlice(ptr unsafe.Pointer, size uint) []byte {
    69  	var sl = struct {
    70  		addr uintptr
    71  		len  int
    72  		cap  int
    73  	}{uintptr(ptr), int(size), int(size)}
    74  
    75  	return *(*[]byte)(unsafe.Pointer(&sl))
    76  }
    77  
    78  // popSlice pops a buffer off the JavaScript stack and returns it as a slice.
    79  func popSlice(ctx *duktape.Context) []byte {
    80  	blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
    81  	ctx.Pop()
    82  	return blob
    83  }
    84  
    85  // pushBigInt create a JavaScript BigInteger in the VM.
    86  func pushBigInt(n *big.Int, ctx *duktape.Context) {
    87  	ctx.GetGlobalString("bigInt")
    88  	ctx.PushString(n.String())
    89  	ctx.Call(1)
    90  }
    91  
    92  // opWrapper provides a JavaScript wrapper around OpCode.
    93  type opWrapper struct {
    94  	op vm.OpCode
    95  }
    96  
    97  // pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
    98  // onto the VM stack.
    99  func (ow *opWrapper) pushObject(vm *duktape.Context) {
   100  	obj := vm.PushObject()
   101  
   102  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
   103  	vm.PutPropString(obj, "toNumber")
   104  
   105  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
   106  	vm.PutPropString(obj, "toString")
   107  
   108  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
   109  	vm.PutPropString(obj, "isPush")
   110  }
   111  
   112  // memoryWrapper provides a JavaScript wrapper around vm.Memory.
   113  type memoryWrapper struct {
   114  	memory *vm.Memory
   115  }
   116  
   117  // slice returns the requested range of memory as a byte slice.
   118  func (mw *memoryWrapper) slice(begin, end int64) []byte {
   119  	if end == begin {
   120  		return []byte{}
   121  	}
   122  	if end < begin || begin < 0 {
   123  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   124  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   125  		log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end)
   126  		return nil
   127  	}
   128  	if mw.memory.Len() < int(end) {
   129  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   130  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   131  		log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
   132  		return nil
   133  	}
   134  	return mw.memory.GetCopy(begin, end-begin)
   135  }
   136  
   137  // getUint returns the 32 bytes at the specified address interpreted as a uint.
   138  func (mw *memoryWrapper) getUint(addr int64) *big.Int {
   139  	if mw.memory.Len() < int(addr)+32 || addr < 0 {
   140  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   141  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   142  		log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
   143  		return new(big.Int)
   144  	}
   145  	return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
   146  }
   147  
   148  // pushObject assembles a JSVM object wrapping a swappable memory and pushes it
   149  // onto the VM stack.
   150  func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
   151  	obj := vm.PushObject()
   152  
   153  	// Generate the `slice` method which takes two ints and returns a buffer
   154  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   155  		blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
   156  		ctx.Pop2()
   157  
   158  		ptr := ctx.PushFixedBuffer(len(blob))
   159  		copy(makeSlice(ptr, uint(len(blob))), blob)
   160  		return 1
   161  	})
   162  	vm.PutPropString(obj, "slice")
   163  
   164  	// Generate the `getUint` method which takes an int and returns a bigint
   165  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   166  		offset := int64(ctx.GetInt(-1))
   167  		ctx.Pop()
   168  
   169  		pushBigInt(mw.getUint(offset), ctx)
   170  		return 1
   171  	})
   172  	vm.PutPropString(obj, "getUint")
   173  }
   174  
   175  // stackWrapper provides a JavaScript wrapper around vm.Stack.
   176  type stackWrapper struct {
   177  	stack *vm.Stack
   178  }
   179  
   180  // peek returns the nth-from-the-top element of the stack.
   181  func (sw *stackWrapper) peek(idx int) *big.Int {
   182  	if len(sw.stack.Data()) <= idx || idx < 0 {
   183  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   184  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   185  		log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
   186  		return new(big.Int)
   187  	}
   188  	return sw.stack.Back(idx).ToBig()
   189  }
   190  
   191  // pushObject assembles a JSVM object wrapping a swappable stack and pushes it
   192  // onto the VM stack.
   193  func (sw *stackWrapper) pushObject(vm *duktape.Context) {
   194  	obj := vm.PushObject()
   195  
   196  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
   197  	vm.PutPropString(obj, "length")
   198  
   199  	// Generate the `peek` method which takes an int and returns a bigint
   200  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   201  		offset := ctx.GetInt(-1)
   202  		ctx.Pop()
   203  
   204  		pushBigInt(sw.peek(offset), ctx)
   205  		return 1
   206  	})
   207  	vm.PutPropString(obj, "peek")
   208  }
   209  
   210  // dbWrapper provides a JavaScript wrapper around vm.Database.
   211  type dbWrapper struct {
   212  	db vm.StateDB
   213  }
   214  
   215  // pushObject assembles a JSVM object wrapping a swappable database and pushes it
   216  // onto the VM stack.
   217  func (dw *dbWrapper) pushObject(vm *duktape.Context) {
   218  	obj := vm.PushObject()
   219  
   220  	// Push the wrapper for statedb.GetBalance
   221  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   222  		pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
   223  		return 1
   224  	})
   225  	vm.PutPropString(obj, "getBalance")
   226  
   227  	// Push the wrapper for statedb.GetNonce
   228  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   229  		ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
   230  		return 1
   231  	})
   232  	vm.PutPropString(obj, "getNonce")
   233  
   234  	// Push the wrapper for statedb.GetCode
   235  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   236  		code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
   237  
   238  		ptr := ctx.PushFixedBuffer(len(code))
   239  		copy(makeSlice(ptr, uint(len(code))), code)
   240  		return 1
   241  	})
   242  	vm.PutPropString(obj, "getCode")
   243  
   244  	// Push the wrapper for statedb.GetState
   245  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   246  		hash := popSlice(ctx)
   247  		addr := popSlice(ctx)
   248  
   249  		state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
   250  
   251  		ptr := ctx.PushFixedBuffer(len(state))
   252  		copy(makeSlice(ptr, uint(len(state))), state[:])
   253  		return 1
   254  	})
   255  	vm.PutPropString(obj, "getState")
   256  
   257  	// Push the wrapper for statedb.Exists
   258  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   259  		ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
   260  		return 1
   261  	})
   262  	vm.PutPropString(obj, "exists")
   263  }
   264  
   265  // contractWrapper provides a JavaScript wrapper around vm.Contract
   266  type contractWrapper struct {
   267  	contract *vm.Contract
   268  }
   269  
   270  // pushObject assembles a JSVM object wrapping a swappable contract and pushes it
   271  // onto the VM stack.
   272  func (cw *contractWrapper) pushObject(vm *duktape.Context) {
   273  	obj := vm.PushObject()
   274  
   275  	// Push the wrapper for contract.Caller
   276  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   277  		ptr := ctx.PushFixedBuffer(20)
   278  		copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
   279  		return 1
   280  	})
   281  	vm.PutPropString(obj, "getCaller")
   282  
   283  	// Push the wrapper for contract.Address
   284  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   285  		ptr := ctx.PushFixedBuffer(20)
   286  		copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
   287  		return 1
   288  	})
   289  	vm.PutPropString(obj, "getAddress")
   290  
   291  	// Push the wrapper for contract.Value
   292  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   293  		pushBigInt(cw.contract.Value(), ctx)
   294  		return 1
   295  	})
   296  	vm.PutPropString(obj, "getValue")
   297  
   298  	// Push the wrapper for contract.Input
   299  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   300  		blob := cw.contract.Input
   301  
   302  		ptr := ctx.PushFixedBuffer(len(blob))
   303  		copy(makeSlice(ptr, uint(len(blob))), blob)
   304  		return 1
   305  	})
   306  	vm.PutPropString(obj, "getInput")
   307  }
   308  
   309  type frame struct {
   310  	typ   *string
   311  	from  *common.Address
   312  	to    *common.Address
   313  	input []byte
   314  	gas   *uint
   315  	value *big.Int
   316  }
   317  
   318  func newFrame() *frame {
   319  	return &frame{
   320  		typ:  new(string),
   321  		from: new(common.Address),
   322  		to:   new(common.Address),
   323  		gas:  new(uint),
   324  	}
   325  }
   326  
   327  func (f *frame) pushObject(vm *duktape.Context) {
   328  	obj := vm.PushObject()
   329  
   330  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 })
   331  	vm.PutPropString(obj, "getType")
   332  
   333  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 })
   334  	vm.PutPropString(obj, "getFrom")
   335  
   336  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 })
   337  	vm.PutPropString(obj, "getTo")
   338  
   339  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 })
   340  	vm.PutPropString(obj, "getInput")
   341  
   342  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 })
   343  	vm.PutPropString(obj, "getGas")
   344  
   345  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   346  		if f.value != nil {
   347  			pushValue(ctx, f.value)
   348  		} else {
   349  			ctx.PushUndefined()
   350  		}
   351  		return 1
   352  	})
   353  	vm.PutPropString(obj, "getValue")
   354  }
   355  
   356  type frameResult struct {
   357  	gasUsed    *uint
   358  	output     []byte
   359  	errorValue *string
   360  }
   361  
   362  func newFrameResult() *frameResult {
   363  	return &frameResult{
   364  		gasUsed: new(uint),
   365  	}
   366  }
   367  
   368  func (r *frameResult) pushObject(vm *duktape.Context) {
   369  	obj := vm.PushObject()
   370  
   371  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 })
   372  	vm.PutPropString(obj, "getGasUsed")
   373  
   374  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 })
   375  	vm.PutPropString(obj, "getOutput")
   376  
   377  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   378  		if r.errorValue != nil {
   379  			pushValue(ctx, *r.errorValue)
   380  		} else {
   381  			ctx.PushUndefined()
   382  		}
   383  		return 1
   384  	})
   385  	vm.PutPropString(obj, "getError")
   386  }
   387  
   388  // jsTracer provides an implementation of Tracer that evaluates a Javascript
   389  // function for each VM execution step.
   390  type jsTracer struct {
   391  	vm  *duktape.Context // Javascript VM instance
   392  	env *vm.EVM          // EVM instance executing the code being traced
   393  
   394  	tracerObject int // Stack index of the tracer JavaScript object
   395  	stateObject  int // Stack index of the global state to pull arguments from
   396  
   397  	opWrapper       *opWrapper       // Wrapper around the VM opcode
   398  	stackWrapper    *stackWrapper    // Wrapper around the VM stack
   399  	memoryWrapper   *memoryWrapper   // Wrapper around the VM memory
   400  	contractWrapper *contractWrapper // Wrapper around the contract object
   401  	dbWrapper       *dbWrapper       // Wrapper around the VM environment
   402  
   403  	pcValue     *uint   // Swappable pc value wrapped by a log accessor
   404  	gasValue    *uint   // Swappable gas value wrapped by a log accessor
   405  	costValue   *uint   // Swappable cost value wrapped by a log accessor
   406  	depthValue  *uint   // Swappable depth value wrapped by a log accessor
   407  	errorValue  *string // Swappable error value wrapped by a log accessor
   408  	refundValue *uint   // Swappable refund value wrapped by a log accessor
   409  
   410  	frame       *frame       // Represents entry into call frame. Fields are swappable
   411  	frameResult *frameResult // Represents exit from a call frame. Fields are swappable
   412  
   413  	ctx map[string]interface{} // Transaction context gathered throughout execution
   414  	err error                  // Error, if one has occurred
   415  
   416  	interrupt uint32 // Atomic flag to signal execution interruption
   417  	reason    error  // Textual reason for the interruption
   418  
   419  	activePrecompiles []common.Address // Updated on CaptureStart based on given rules
   420  	traceSteps        bool             // When true, will invoke step() on each opcode
   421  	traceCallFrames   bool             // When true, will invoke enter() and exit() js funcs
   422  }
   423  
   424  // New instantiates a new tracer instance. code specifies a Javascript snippet,
   425  // which must evaluate to an expression returning an object with 'step', 'fault'
   426  // and 'result' functions.
   427  func newJsTracer(code string, ctx *tracers2.Context) (tracers2.Tracer, error) {
   428  	if c, ok := assetTracers[code]; ok {
   429  		code = c
   430  	}
   431  	if ctx == nil {
   432  		ctx = new(tracers2.Context)
   433  	}
   434  	tracer := &jsTracer{
   435  		vm:              duktape.New(),
   436  		ctx:             make(map[string]interface{}),
   437  		opWrapper:       new(opWrapper),
   438  		stackWrapper:    new(stackWrapper),
   439  		memoryWrapper:   new(memoryWrapper),
   440  		contractWrapper: new(contractWrapper),
   441  		dbWrapper:       new(dbWrapper),
   442  		pcValue:         new(uint),
   443  		gasValue:        new(uint),
   444  		costValue:       new(uint),
   445  		depthValue:      new(uint),
   446  		refundValue:     new(uint),
   447  		frame:           newFrame(),
   448  		frameResult:     newFrameResult(),
   449  	}
   450  	if ctx.BlockHash != (common.Hash{}) {
   451  		tracer.ctx["blockHash"] = ctx.BlockHash
   452  
   453  		if ctx.TxHash != (common.Hash{}) {
   454  			tracer.ctx["txIndex"] = ctx.TxIndex
   455  			tracer.ctx["txHash"] = ctx.TxHash
   456  		}
   457  	}
   458  	// Set up builtins for this environment
   459  	tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
   460  		ctx.PushString(hexutil.Encode(popSlice(ctx)))
   461  		return 1
   462  	})
   463  	tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
   464  		var word common.Hash
   465  		if ptr, size := ctx.GetBuffer(-1); ptr != nil {
   466  			word = common.BytesToHash(makeSlice(ptr, size))
   467  		} else {
   468  			word = common.HexToHash(ctx.GetString(-1))
   469  		}
   470  		ctx.Pop()
   471  		copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
   472  		return 1
   473  	})
   474  	tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
   475  		var addr common.Address
   476  		if ptr, size := ctx.GetBuffer(-1); ptr != nil {
   477  			addr = common.BytesToAddress(makeSlice(ptr, size))
   478  		} else {
   479  			addr = common.HexToAddress(ctx.GetString(-1))
   480  		}
   481  		ctx.Pop()
   482  		copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
   483  		return 1
   484  	})
   485  	tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
   486  		var from common.Address
   487  		if ptr, size := ctx.GetBuffer(-2); ptr != nil {
   488  			from = common.BytesToAddress(makeSlice(ptr, size))
   489  		} else {
   490  			from = common.HexToAddress(ctx.GetString(-2))
   491  		}
   492  		nonce := uint64(ctx.GetInt(-1))
   493  		ctx.Pop2()
   494  
   495  		contract := crypto.CreateAddress(from, nonce)
   496  		copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
   497  		return 1
   498  	})
   499  	tracer.vm.PushGlobalGoFunction("toContract2", func(ctx *duktape.Context) int {
   500  		var from common.Address
   501  		if ptr, size := ctx.GetBuffer(-3); ptr != nil {
   502  			from = common.BytesToAddress(makeSlice(ptr, size))
   503  		} else {
   504  			from = common.HexToAddress(ctx.GetString(-3))
   505  		}
   506  		// Retrieve salt hex string from js stack
   507  		salt := common.HexToHash(ctx.GetString(-2))
   508  		// Retrieve code slice from js stack
   509  		var code []byte
   510  		if ptr, size := ctx.GetBuffer(-1); ptr != nil {
   511  			code = common.CopyBytes(makeSlice(ptr, size))
   512  		} else {
   513  			code = common.FromHex(ctx.GetString(-1))
   514  		}
   515  		codeHash := crypto.Keccak256(code)
   516  		ctx.Pop3()
   517  		contract := crypto.CreateAddress2(from, salt, codeHash)
   518  		copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
   519  		return 1
   520  	})
   521  	tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
   522  		addr := common.BytesToAddress(popSlice(ctx))
   523  		for _, p := range tracer.activePrecompiles {
   524  			if p == addr {
   525  				ctx.PushBoolean(true)
   526  				return 1
   527  			}
   528  		}
   529  		ctx.PushBoolean(false)
   530  		return 1
   531  	})
   532  	tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
   533  		start, end := ctx.GetInt(-2), ctx.GetInt(-1)
   534  		ctx.Pop2()
   535  
   536  		blob := popSlice(ctx)
   537  		size := end - start
   538  
   539  		if start < 0 || start > end || end > len(blob) {
   540  			// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   541  			// runtime goes belly up https://github.com/golang/go/issues/15639.
   542  			log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
   543  			ctx.PushFixedBuffer(0)
   544  			return 1
   545  		}
   546  		copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
   547  		return 1
   548  	})
   549  	// Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
   550  	if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
   551  		log.Warn("Failed to compile tracer", "err", err)
   552  		return nil, err
   553  	}
   554  	tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
   555  
   556  	hasStep := tracer.vm.GetPropString(tracer.tracerObject, "step")
   557  	tracer.vm.Pop()
   558  
   559  	if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
   560  		return nil, fmt.Errorf("trace object must expose a function fault()")
   561  	}
   562  	tracer.vm.Pop()
   563  
   564  	if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
   565  		return nil, fmt.Errorf("trace object must expose a function result()")
   566  	}
   567  	tracer.vm.Pop()
   568  
   569  	hasEnter := tracer.vm.GetPropString(tracer.tracerObject, "enter")
   570  	tracer.vm.Pop()
   571  	hasExit := tracer.vm.GetPropString(tracer.tracerObject, "exit")
   572  	tracer.vm.Pop()
   573  	if hasEnter != hasExit {
   574  		return nil, fmt.Errorf("trace object must expose either both or none of enter() and exit()")
   575  	}
   576  	tracer.traceCallFrames = hasEnter && hasExit
   577  	tracer.traceSteps = hasStep
   578  
   579  	// Tracer is valid, inject the big int library to access large numbers
   580  	tracer.vm.EvalString(bigIntegerJS)
   581  	tracer.vm.PutGlobalString("bigInt")
   582  
   583  	// Push the global environment state as object #1 into the JSVM stack
   584  	tracer.stateObject = tracer.vm.PushObject()
   585  
   586  	logObject := tracer.vm.PushObject()
   587  
   588  	tracer.opWrapper.pushObject(tracer.vm)
   589  	tracer.vm.PutPropString(logObject, "op")
   590  
   591  	tracer.stackWrapper.pushObject(tracer.vm)
   592  	tracer.vm.PutPropString(logObject, "stack")
   593  
   594  	tracer.memoryWrapper.pushObject(tracer.vm)
   595  	tracer.vm.PutPropString(logObject, "memory")
   596  
   597  	tracer.contractWrapper.pushObject(tracer.vm)
   598  	tracer.vm.PutPropString(logObject, "contract")
   599  
   600  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
   601  	tracer.vm.PutPropString(logObject, "getPC")
   602  
   603  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
   604  	tracer.vm.PutPropString(logObject, "getGas")
   605  
   606  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
   607  	tracer.vm.PutPropString(logObject, "getCost")
   608  
   609  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
   610  	tracer.vm.PutPropString(logObject, "getDepth")
   611  
   612  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.refundValue); return 1 })
   613  	tracer.vm.PutPropString(logObject, "getRefund")
   614  
   615  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
   616  		if tracer.errorValue != nil {
   617  			ctx.PushString(*tracer.errorValue)
   618  		} else {
   619  			ctx.PushUndefined()
   620  		}
   621  		return 1
   622  	})
   623  	tracer.vm.PutPropString(logObject, "getError")
   624  
   625  	tracer.vm.PutPropString(tracer.stateObject, "log")
   626  
   627  	tracer.frame.pushObject(tracer.vm)
   628  	tracer.vm.PutPropString(tracer.stateObject, "frame")
   629  
   630  	tracer.frameResult.pushObject(tracer.vm)
   631  	tracer.vm.PutPropString(tracer.stateObject, "frameResult")
   632  
   633  	tracer.dbWrapper.pushObject(tracer.vm)
   634  	tracer.vm.PutPropString(tracer.stateObject, "db")
   635  
   636  	return tracer, nil
   637  }
   638  
   639  // Stop terminates execution of the tracer at the first opportune moment.
   640  func (jst *jsTracer) Stop(err error) {
   641  	jst.reason = err
   642  	atomic.StoreUint32(&jst.interrupt, 1)
   643  }
   644  
   645  // call executes a method on a JS object, catching any errors, formatting and
   646  // returning them as error objects.
   647  func (jst *jsTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) {
   648  	// Execute the JavaScript call and return any error
   649  	jst.vm.PushString(method)
   650  	for _, arg := range args {
   651  		jst.vm.GetPropString(jst.stateObject, arg)
   652  	}
   653  	code := jst.vm.PcallProp(jst.tracerObject, len(args))
   654  	defer jst.vm.Pop()
   655  
   656  	if code != 0 {
   657  		err := jst.vm.SafeToString(-1)
   658  		return nil, errors.New(err)
   659  	}
   660  	// No error occurred, extract return value and return
   661  	if noret {
   662  		return nil, nil
   663  	}
   664  	// Push a JSON marshaller onto the stack. We can't marshal from the out-
   665  	// side because duktape can crash on large nestings and we can't catch
   666  	// C++ exceptions ourselves from Go. TODO(karalabe): Yuck, why wrap?!
   667  	jst.vm.PushString("(JSON.stringify)")
   668  	jst.vm.Eval()
   669  
   670  	jst.vm.Swap(-1, -2)
   671  	if code = jst.vm.Pcall(1); code != 0 {
   672  		err := jst.vm.SafeToString(-1)
   673  		return nil, errors.New(err)
   674  	}
   675  	return json.RawMessage(jst.vm.SafeToString(-1)), nil
   676  }
   677  
   678  func wrapError(context string, err error) error {
   679  	return fmt.Errorf("%v    in server-side tracer function '%v'", err, context)
   680  }
   681  
   682  // CaptureStart implements the Tracer interface to initialize the tracing operation.
   683  func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
   684  	jst.env = env
   685  	jst.ctx["type"] = "CALL"
   686  	if create {
   687  		jst.ctx["type"] = "CREATE"
   688  	}
   689  	jst.ctx["from"] = from
   690  	jst.ctx["to"] = to
   691  	jst.ctx["input"] = input
   692  	jst.ctx["gas"] = gas
   693  	jst.ctx["gasPrice"] = env.TxContext.GasPrice
   694  	jst.ctx["value"] = value
   695  
   696  	// Initialize the context
   697  	jst.ctx["block"] = env.Context.BlockNumber.Uint64()
   698  	jst.dbWrapper.db = env.StateDB
   699  	// Update list of precompiles based on current block
   700  	rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil)
   701  	jst.activePrecompiles = vm.ActivePrecompiles(rules)
   702  
   703  	// Compute intrinsic gas
   704  	isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
   705  	isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
   706  	intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
   707  	if err != nil {
   708  		return
   709  	}
   710  	jst.ctx["intrinsicGas"] = intrinsicGas
   711  }
   712  
   713  // CaptureState implements the Tracer interface to trace a single step of VM execution.
   714  func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
   715  	if !jst.traceSteps {
   716  		return
   717  	}
   718  	if jst.err != nil {
   719  		return
   720  	}
   721  	// If tracing was interrupted, set the error and stop
   722  	if atomic.LoadUint32(&jst.interrupt) > 0 {
   723  		jst.err = jst.reason
   724  		jst.env.Cancel()
   725  		return
   726  	}
   727  	jst.opWrapper.op = op
   728  	jst.stackWrapper.stack = scope.Stack
   729  	jst.memoryWrapper.memory = scope.Memory
   730  	jst.contractWrapper.contract = scope.Contract
   731  
   732  	*jst.pcValue = uint(pc)
   733  	*jst.gasValue = uint(gas)
   734  	*jst.costValue = uint(cost)
   735  	*jst.depthValue = uint(depth)
   736  	*jst.refundValue = uint(jst.env.StateDB.GetRefund())
   737  
   738  	jst.errorValue = nil
   739  	if err != nil {
   740  		jst.errorValue = new(string)
   741  		*jst.errorValue = err.Error()
   742  	}
   743  
   744  	if _, err := jst.call(true, "step", "log", "db"); err != nil {
   745  		jst.err = wrapError("step", err)
   746  	}
   747  }
   748  
   749  // CaptureFault implements the Tracer interface to trace an execution fault
   750  func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
   751  	if jst.err != nil {
   752  		return
   753  	}
   754  	// Apart from the error, everything matches the previous invocation
   755  	jst.errorValue = new(string)
   756  	*jst.errorValue = err.Error()
   757  
   758  	if _, err := jst.call(true, "fault", "log", "db"); err != nil {
   759  		jst.err = wrapError("fault", err)
   760  	}
   761  }
   762  
   763  // CaptureEnd is called after the call finishes to finalize the tracing.
   764  func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
   765  	jst.ctx["output"] = output
   766  	jst.ctx["time"] = t.String()
   767  	jst.ctx["gasUsed"] = gasUsed
   768  
   769  	if err != nil {
   770  		jst.ctx["error"] = err.Error()
   771  	}
   772  }
   773  
   774  // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
   775  func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   776  	if !jst.traceCallFrames {
   777  		return
   778  	}
   779  	if jst.err != nil {
   780  		return
   781  	}
   782  	// If tracing was interrupted, set the error and stop
   783  	if atomic.LoadUint32(&jst.interrupt) > 0 {
   784  		jst.err = jst.reason
   785  		return
   786  	}
   787  
   788  	*jst.frame.typ = typ.String()
   789  	*jst.frame.from = from
   790  	*jst.frame.to = to
   791  	jst.frame.input = common.CopyBytes(input)
   792  	*jst.frame.gas = uint(gas)
   793  	jst.frame.value = nil
   794  	if value != nil {
   795  		jst.frame.value = new(big.Int).SetBytes(value.Bytes())
   796  	}
   797  
   798  	if _, err := jst.call(true, "enter", "frame"); err != nil {
   799  		jst.err = wrapError("enter", err)
   800  	}
   801  }
   802  
   803  // CaptureExit is called when EVM exits a scope, even if the scope didn't
   804  // execute any code.
   805  func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
   806  	if !jst.traceCallFrames {
   807  		return
   808  	}
   809  	// If tracing was interrupted, set the error and stop
   810  	if atomic.LoadUint32(&jst.interrupt) > 0 {
   811  		jst.err = jst.reason
   812  		return
   813  	}
   814  
   815  	jst.frameResult.output = common.CopyBytes(output)
   816  	*jst.frameResult.gasUsed = uint(gasUsed)
   817  	jst.frameResult.errorValue = nil
   818  	if err != nil {
   819  		jst.frameResult.errorValue = new(string)
   820  		*jst.frameResult.errorValue = err.Error()
   821  	}
   822  
   823  	if _, err := jst.call(true, "exit", "frameResult"); err != nil {
   824  		jst.err = wrapError("exit", err)
   825  	}
   826  }
   827  
   828  // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
   829  func (jst *jsTracer) GetResult() (json.RawMessage, error) {
   830  	// Transform the context into a JavaScript object and inject into the state
   831  	obj := jst.vm.PushObject()
   832  
   833  	for key, val := range jst.ctx {
   834  		jst.addToObj(obj, key, val)
   835  	}
   836  	jst.vm.PutPropString(jst.stateObject, "ctx")
   837  
   838  	// Finalize the trace and return the results
   839  	result, err := jst.call(false, "result", "ctx", "db")
   840  	if err != nil {
   841  		jst.err = wrapError("result", err)
   842  	}
   843  	// Clean up the JavaScript environment
   844  	jst.vm.DestroyHeap()
   845  	jst.vm.Destroy()
   846  
   847  	return result, jst.err
   848  }
   849  
   850  // addToObj pushes a field to a JS object.
   851  func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {
   852  	pushValue(jst.vm, val)
   853  	jst.vm.PutPropString(obj, key)
   854  }
   855  
   856  func pushValue(ctx *duktape.Context, val interface{}) {
   857  	switch val := val.(type) {
   858  	case uint64:
   859  		ctx.PushUint(uint(val))
   860  	case string:
   861  		ctx.PushString(val)
   862  	case []byte:
   863  		ptr := ctx.PushFixedBuffer(len(val))
   864  		copy(makeSlice(ptr, uint(len(val))), val)
   865  	case common.Address:
   866  		ptr := ctx.PushFixedBuffer(20)
   867  		copy(makeSlice(ptr, 20), val[:])
   868  	case *big.Int:
   869  		pushBigInt(val, ctx)
   870  	case int:
   871  		ctx.PushInt(val)
   872  	case uint:
   873  		ctx.PushUint(val)
   874  	case common.Hash:
   875  		ptr := ctx.PushFixedBuffer(32)
   876  		copy(makeSlice(ptr, 32), val[:])
   877  	default:
   878  		panic(fmt.Sprintf("unsupported type: %T", val))
   879  	}
   880  }