github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/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/vm"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	tracers2 "github.com/ethereum/go-ethereum/eth/tracers"
    36  	"github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
    37  	"github.com/ethereum/go-ethereum/log"
    38  	"gopkg.in/olebedev/go-duktape.v3"
    39  )
    40  
    41  // camel converts a snake cased input string into a camel cased output.
    42  func camel(str string) string {
    43  	pieces := strings.Split(str, "_")
    44  	for i := 1; i < len(pieces); i++ {
    45  		pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
    46  	}
    47  	return strings.Join(pieces, "")
    48  }
    49  
    50  var assetTracers = make(map[string]string)
    51  
    52  // init retrieves the JavaScript transaction tracers included in go-ethereum.
    53  func init() {
    54  	for _, file := range tracers.AssetNames() {
    55  		name := camel(strings.TrimSuffix(file, ".js"))
    56  		assetTracers[name] = string(tracers.MustAsset(file))
    57  	}
    58  	tracers2.RegisterLookup(true, newJsTracer)
    59  }
    60  
    61  // makeSlice convert an unsafe memory pointer with the given type into a Go byte
    62  // slice.
    63  //
    64  // Note, the returned slice uses the same memory area as the input arguments.
    65  // If those are duktape stack items, popping them off **will** make the slice
    66  // contents change.
    67  func makeSlice(ptr unsafe.Pointer, size uint) []byte {
    68  	var sl = struct {
    69  		addr uintptr
    70  		len  int
    71  		cap  int
    72  	}{uintptr(ptr), int(size), int(size)}
    73  
    74  	return *(*[]byte)(unsafe.Pointer(&sl))
    75  }
    76  
    77  // popSlice pops a buffer off the JavaScript stack and returns it as a slice.
    78  func popSlice(ctx *duktape.Context) []byte {
    79  	blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
    80  	ctx.Pop()
    81  	return blob
    82  }
    83  
    84  // pushBigInt create a JavaScript BigInteger in the VM.
    85  func pushBigInt(n *big.Int, ctx *duktape.Context) {
    86  	ctx.GetGlobalString("bigInt")
    87  	ctx.PushString(n.String())
    88  	ctx.Call(1)
    89  }
    90  
    91  // opWrapper provides a JavaScript wrapper around OpCode.
    92  type opWrapper struct {
    93  	op vm.OpCode
    94  }
    95  
    96  // pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
    97  // onto the VM stack.
    98  func (ow *opWrapper) pushObject(vm *duktape.Context) {
    99  	obj := vm.PushObject()
   100  
   101  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
   102  	vm.PutPropString(obj, "toNumber")
   103  
   104  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
   105  	vm.PutPropString(obj, "toString")
   106  
   107  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
   108  	vm.PutPropString(obj, "isPush")
   109  }
   110  
   111  // memoryWrapper provides a JavaScript wrapper around vm.Memory.
   112  type memoryWrapper struct {
   113  	memory *vm.Memory
   114  }
   115  
   116  // slice returns the requested range of memory as a byte slice.
   117  func (mw *memoryWrapper) slice(begin, end int64) []byte {
   118  	if end == begin {
   119  		return []byte{}
   120  	}
   121  	if end < begin || begin < 0 {
   122  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   123  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   124  		log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end)
   125  		return nil
   126  	}
   127  	if mw.memory.Len() < int(end) {
   128  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   129  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   130  		log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
   131  		return nil
   132  	}
   133  	return mw.memory.GetCopy(begin, end-begin)
   134  }
   135  
   136  // getUint returns the 32 bytes at the specified address interpreted as a uint.
   137  func (mw *memoryWrapper) getUint(addr int64) *big.Int {
   138  	if mw.memory.Len() < int(addr)+32 || addr < 0 {
   139  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   140  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   141  		log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
   142  		return new(big.Int)
   143  	}
   144  	return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
   145  }
   146  
   147  // pushObject assembles a JSVM object wrapping a swappable memory and pushes it
   148  // onto the VM stack.
   149  func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
   150  	obj := vm.PushObject()
   151  
   152  	// Generate the `slice` method which takes two ints and returns a buffer
   153  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   154  		blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
   155  		ctx.Pop2()
   156  
   157  		ptr := ctx.PushFixedBuffer(len(blob))
   158  		copy(makeSlice(ptr, uint(len(blob))), blob)
   159  		return 1
   160  	})
   161  	vm.PutPropString(obj, "slice")
   162  
   163  	// Generate the `getUint` method which takes an int and returns a bigint
   164  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   165  		offset := int64(ctx.GetInt(-1))
   166  		ctx.Pop()
   167  
   168  		pushBigInt(mw.getUint(offset), ctx)
   169  		return 1
   170  	})
   171  	vm.PutPropString(obj, "getUint")
   172  }
   173  
   174  // stackWrapper provides a JavaScript wrapper around vm.Stack.
   175  type stackWrapper struct {
   176  	stack *vm.Stack
   177  }
   178  
   179  // peek returns the nth-from-the-top element of the stack.
   180  func (sw *stackWrapper) peek(idx int) *big.Int {
   181  	if len(sw.stack.Data()) <= idx || idx < 0 {
   182  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   183  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   184  		log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
   185  		return new(big.Int)
   186  	}
   187  	return sw.stack.Back(idx).ToBig()
   188  }
   189  
   190  // pushObject assembles a JSVM object wrapping a swappable stack and pushes it
   191  // onto the VM stack.
   192  func (sw *stackWrapper) pushObject(vm *duktape.Context) {
   193  	obj := vm.PushObject()
   194  
   195  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
   196  	vm.PutPropString(obj, "length")
   197  
   198  	// Generate the `peek` method which takes an int and returns a bigint
   199  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   200  		offset := ctx.GetInt(-1)
   201  		ctx.Pop()
   202  
   203  		pushBigInt(sw.peek(offset), ctx)
   204  		return 1
   205  	})
   206  	vm.PutPropString(obj, "peek")
   207  }
   208  
   209  // dbWrapper provides a JavaScript wrapper around vm.Database.
   210  type dbWrapper struct {
   211  	db vm.StateDB
   212  }
   213  
   214  // pushObject assembles a JSVM object wrapping a swappable database and pushes it
   215  // onto the VM stack.
   216  func (dw *dbWrapper) pushObject(vm *duktape.Context) {
   217  	obj := vm.PushObject()
   218  
   219  	// Push the wrapper for statedb.GetBalance
   220  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   221  		pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
   222  		return 1
   223  	})
   224  	vm.PutPropString(obj, "getBalance")
   225  
   226  	// Push the wrapper for statedb.GetNonce
   227  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   228  		ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
   229  		return 1
   230  	})
   231  	vm.PutPropString(obj, "getNonce")
   232  
   233  	// Push the wrapper for statedb.GetCode
   234  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   235  		code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
   236  
   237  		ptr := ctx.PushFixedBuffer(len(code))
   238  		copy(makeSlice(ptr, uint(len(code))), code)
   239  		return 1
   240  	})
   241  	vm.PutPropString(obj, "getCode")
   242  
   243  	// Push the wrapper for statedb.GetState
   244  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   245  		hash := popSlice(ctx)
   246  		addr := popSlice(ctx)
   247  
   248  		state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
   249  
   250  		ptr := ctx.PushFixedBuffer(len(state))
   251  		copy(makeSlice(ptr, uint(len(state))), state[:])
   252  		return 1
   253  	})
   254  	vm.PutPropString(obj, "getState")
   255  
   256  	// Push the wrapper for statedb.Exists
   257  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   258  		ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
   259  		return 1
   260  	})
   261  	vm.PutPropString(obj, "exists")
   262  }
   263  
   264  // contractWrapper provides a JavaScript wrapper around vm.Contract
   265  type contractWrapper struct {
   266  	contract *vm.Contract
   267  }
   268  
   269  // pushObject assembles a JSVM object wrapping a swappable contract and pushes it
   270  // onto the VM stack.
   271  func (cw *contractWrapper) pushObject(vm *duktape.Context) {
   272  	obj := vm.PushObject()
   273  
   274  	// Push the wrapper for contract.Caller
   275  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   276  		ptr := ctx.PushFixedBuffer(20)
   277  		copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
   278  		return 1
   279  	})
   280  	vm.PutPropString(obj, "getCaller")
   281  
   282  	// Push the wrapper for contract.Address
   283  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   284  		ptr := ctx.PushFixedBuffer(20)
   285  		copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
   286  		return 1
   287  	})
   288  	vm.PutPropString(obj, "getAddress")
   289  
   290  	// Push the wrapper for contract.Value
   291  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   292  		pushBigInt(cw.contract.Value(), ctx)
   293  		return 1
   294  	})
   295  	vm.PutPropString(obj, "getValue")
   296  
   297  	// Push the wrapper for contract.Input
   298  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   299  		blob := cw.contract.Input
   300  
   301  		ptr := ctx.PushFixedBuffer(len(blob))
   302  		copy(makeSlice(ptr, uint(len(blob))), blob)
   303  		return 1
   304  	})
   305  	vm.PutPropString(obj, "getInput")
   306  }
   307  
   308  type frame struct {
   309  	typ   *string
   310  	from  *common.Address
   311  	to    *common.Address
   312  	input []byte
   313  	gas   *uint
   314  	value *big.Int
   315  }
   316  
   317  func newFrame() *frame {
   318  	return &frame{
   319  		typ:  new(string),
   320  		from: new(common.Address),
   321  		to:   new(common.Address),
   322  		gas:  new(uint),
   323  	}
   324  }
   325  
   326  func (f *frame) pushObject(vm *duktape.Context) {
   327  	obj := vm.PushObject()
   328  
   329  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.typ); return 1 })
   330  	vm.PutPropString(obj, "getType")
   331  
   332  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.from); return 1 })
   333  	vm.PutPropString(obj, "getFrom")
   334  
   335  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.to); return 1 })
   336  	vm.PutPropString(obj, "getTo")
   337  
   338  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, f.input); return 1 })
   339  	vm.PutPropString(obj, "getInput")
   340  
   341  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *f.gas); return 1 })
   342  	vm.PutPropString(obj, "getGas")
   343  
   344  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   345  		if f.value != nil {
   346  			pushValue(ctx, f.value)
   347  		} else {
   348  			ctx.PushUndefined()
   349  		}
   350  		return 1
   351  	})
   352  	vm.PutPropString(obj, "getValue")
   353  }
   354  
   355  type frameResult struct {
   356  	gasUsed    *uint
   357  	output     []byte
   358  	errorValue *string
   359  }
   360  
   361  func newFrameResult() *frameResult {
   362  	return &frameResult{
   363  		gasUsed: new(uint),
   364  	}
   365  }
   366  
   367  func (r *frameResult) pushObject(vm *duktape.Context) {
   368  	obj := vm.PushObject()
   369  
   370  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, *r.gasUsed); return 1 })
   371  	vm.PutPropString(obj, "getGasUsed")
   372  
   373  	vm.PushGoFunction(func(ctx *duktape.Context) int { pushValue(ctx, r.output); return 1 })
   374  	vm.PutPropString(obj, "getOutput")
   375  
   376  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   377  		if r.errorValue != nil {
   378  			pushValue(ctx, *r.errorValue)
   379  		} else {
   380  			ctx.PushUndefined()
   381  		}
   382  		return 1
   383  	})
   384  	vm.PutPropString(obj, "getError")
   385  }
   386  
   387  // jsTracer provides an implementation of Tracer that evaluates a Javascript
   388  // function for each VM execution step.
   389  type jsTracer struct {
   390  	vm  *duktape.Context // Javascript VM instance
   391  	env *vm.EVM          // EVM instance executing the code being traced
   392  
   393  	tracerObject int // Stack index of the tracer JavaScript object
   394  	stateObject  int // Stack index of the global state to pull arguments from
   395  
   396  	opWrapper       *opWrapper       // Wrapper around the VM opcode
   397  	stackWrapper    *stackWrapper    // Wrapper around the VM stack
   398  	memoryWrapper   *memoryWrapper   // Wrapper around the VM memory
   399  	contractWrapper *contractWrapper // Wrapper around the contract object
   400  	dbWrapper       *dbWrapper       // Wrapper around the VM environment
   401  
   402  	pcValue     *uint   // Swappable pc value wrapped by a log accessor
   403  	gasValue    *uint   // Swappable gas value wrapped by a log accessor
   404  	costValue   *uint   // Swappable cost value wrapped by a log accessor
   405  	depthValue  *uint   // Swappable depth value wrapped by a log accessor
   406  	errorValue  *string // Swappable error value wrapped by a log accessor
   407  	refundValue *uint   // Swappable refund value wrapped by a log accessor
   408  
   409  	frame       *frame       // Represents entry into call frame. Fields are swappable
   410  	frameResult *frameResult // Represents exit from a call frame. Fields are swappable
   411  
   412  	ctx map[string]interface{} // Transaction context gathered throughout execution
   413  	err error                  // Error, if one has occurred
   414  
   415  	interrupt uint32 // Atomic flag to signal execution interruption
   416  	reason    error  // Textual reason for the interruption
   417  
   418  	activePrecompiles []common.Address // Updated on CaptureStart based on given rules
   419  	traceSteps        bool             // When true, will invoke step() on each opcode
   420  	traceCallFrames   bool             // When true, will invoke enter() and exit() js funcs
   421  	gasLimit          uint64           // Amount of gas bought for the whole tx
   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  // CaptureTxStart implements the Tracer interface and is invoked at the beginning of
   683  // transaction processing.
   684  func (jst *jsTracer) CaptureTxStart(gasLimit uint64) {
   685  	jst.gasLimit = gasLimit
   686  }
   687  
   688  // CaptureTxStart implements the Tracer interface and is invoked at the end of
   689  // transaction processing.
   690  func (*jsTracer) CaptureTxEnd(restGas uint64) {}
   691  
   692  // CaptureStart implements the Tracer interface and is invoked before executing the
   693  // top-level call frame of a transaction.
   694  func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
   695  	jst.env = env
   696  	jst.ctx["type"] = "CALL"
   697  	if create {
   698  		jst.ctx["type"] = "CREATE"
   699  	}
   700  	jst.ctx["from"] = from
   701  	jst.ctx["to"] = to
   702  	jst.ctx["input"] = input
   703  	jst.ctx["gas"] = gas
   704  	jst.ctx["gasPrice"] = env.TxContext.GasPrice
   705  	jst.ctx["value"] = value
   706  
   707  	// Initialize the context
   708  	jst.ctx["block"] = env.Context.BlockNumber.Uint64()
   709  	jst.dbWrapper.db = env.StateDB
   710  	// Update list of precompiles based on current block
   711  	rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil)
   712  	jst.activePrecompiles = vm.ActivePrecompiles(rules)
   713  
   714  	// Intrinsic costs are the only things reduced from initial gas to this point
   715  	jst.ctx["intrinsicGas"] = jst.gasLimit - gas
   716  }
   717  
   718  // CaptureState implements the Tracer interface to trace a single step of VM execution.
   719  func (jst *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
   720  	if !jst.traceSteps {
   721  		return
   722  	}
   723  	if jst.err != nil {
   724  		return
   725  	}
   726  	// If tracing was interrupted, set the error and stop
   727  	if atomic.LoadUint32(&jst.interrupt) > 0 {
   728  		jst.err = jst.reason
   729  		jst.env.Cancel()
   730  		return
   731  	}
   732  	jst.opWrapper.op = op
   733  	jst.stackWrapper.stack = scope.Stack
   734  	jst.memoryWrapper.memory = scope.Memory
   735  	jst.contractWrapper.contract = scope.Contract
   736  
   737  	*jst.pcValue = uint(pc)
   738  	*jst.gasValue = uint(gas)
   739  	*jst.costValue = uint(cost)
   740  	*jst.depthValue = uint(depth)
   741  	*jst.refundValue = uint(jst.env.StateDB.GetRefund())
   742  
   743  	jst.errorValue = nil
   744  	if err != nil {
   745  		jst.errorValue = new(string)
   746  		*jst.errorValue = err.Error()
   747  	}
   748  
   749  	if _, err := jst.call(true, "step", "log", "db"); err != nil {
   750  		jst.err = wrapError("step", err)
   751  	}
   752  }
   753  
   754  // CaptureFault implements the Tracer interface to trace an execution fault
   755  func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
   756  	if jst.err != nil {
   757  		return
   758  	}
   759  	// Apart from the error, everything matches the previous invocation
   760  	jst.errorValue = new(string)
   761  	*jst.errorValue = err.Error()
   762  
   763  	if _, err := jst.call(true, "fault", "log", "db"); err != nil {
   764  		jst.err = wrapError("fault", err)
   765  	}
   766  }
   767  
   768  // CaptureEnd is called after the top-level call finishes.
   769  func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
   770  	jst.ctx["output"] = output
   771  	jst.ctx["time"] = t.String()
   772  	jst.ctx["gasUsed"] = gasUsed
   773  
   774  	if err != nil {
   775  		jst.ctx["error"] = err.Error()
   776  	}
   777  }
   778  
   779  // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
   780  func (jst *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   781  	if !jst.traceCallFrames {
   782  		return
   783  	}
   784  	if jst.err != nil {
   785  		return
   786  	}
   787  	// If tracing was interrupted, set the error and stop
   788  	if atomic.LoadUint32(&jst.interrupt) > 0 {
   789  		jst.err = jst.reason
   790  		return
   791  	}
   792  
   793  	*jst.frame.typ = typ.String()
   794  	*jst.frame.from = from
   795  	*jst.frame.to = to
   796  	jst.frame.input = common.CopyBytes(input)
   797  	*jst.frame.gas = uint(gas)
   798  	jst.frame.value = nil
   799  	if value != nil {
   800  		jst.frame.value = new(big.Int).SetBytes(value.Bytes())
   801  	}
   802  
   803  	if _, err := jst.call(true, "enter", "frame"); err != nil {
   804  		jst.err = wrapError("enter", err)
   805  	}
   806  }
   807  
   808  // CaptureExit is called when EVM exits a scope, even if the scope didn't
   809  // execute any code.
   810  func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
   811  	if !jst.traceCallFrames {
   812  		return
   813  	}
   814  	// If tracing was interrupted, set the error and stop
   815  	if atomic.LoadUint32(&jst.interrupt) > 0 {
   816  		jst.err = jst.reason
   817  		return
   818  	}
   819  
   820  	jst.frameResult.output = common.CopyBytes(output)
   821  	*jst.frameResult.gasUsed = uint(gasUsed)
   822  	jst.frameResult.errorValue = nil
   823  	if err != nil {
   824  		jst.frameResult.errorValue = new(string)
   825  		*jst.frameResult.errorValue = err.Error()
   826  	}
   827  
   828  	if _, err := jst.call(true, "exit", "frameResult"); err != nil {
   829  		jst.err = wrapError("exit", err)
   830  	}
   831  }
   832  
   833  // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
   834  func (jst *jsTracer) GetResult() (json.RawMessage, error) {
   835  	// Transform the context into a JavaScript object and inject into the state
   836  	obj := jst.vm.PushObject()
   837  
   838  	for key, val := range jst.ctx {
   839  		jst.addToObj(obj, key, val)
   840  	}
   841  	jst.vm.PutPropString(jst.stateObject, "ctx")
   842  
   843  	// Finalize the trace and return the results
   844  	result, err := jst.call(false, "result", "ctx", "db")
   845  	if err != nil {
   846  		jst.err = wrapError("result", err)
   847  	}
   848  	// Clean up the JavaScript environment
   849  	jst.vm.DestroyHeap()
   850  	jst.vm.Destroy()
   851  
   852  	return result, jst.err
   853  }
   854  
   855  // addToObj pushes a field to a JS object.
   856  func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {
   857  	pushValue(jst.vm, val)
   858  	jst.vm.PutPropString(obj, key)
   859  }
   860  
   861  func pushValue(ctx *duktape.Context, val interface{}) {
   862  	switch val := val.(type) {
   863  	case uint64:
   864  		ctx.PushUint(uint(val))
   865  	case string:
   866  		ctx.PushString(val)
   867  	case []byte:
   868  		ptr := ctx.PushFixedBuffer(len(val))
   869  		copy(makeSlice(ptr, uint(len(val))), val)
   870  	case common.Address:
   871  		ptr := ctx.PushFixedBuffer(20)
   872  		copy(makeSlice(ptr, 20), val[:])
   873  	case *big.Int:
   874  		pushBigInt(val, ctx)
   875  	case int:
   876  		ctx.PushInt(val)
   877  	case uint:
   878  		ctx.PushUint(val)
   879  	case common.Hash:
   880  		ptr := ctx.PushFixedBuffer(32)
   881  		copy(makeSlice(ptr, 32), val[:])
   882  	default:
   883  		panic(fmt.Sprintf("unsupported type: %T", val))
   884  	}
   885  }