github.com/aquanetwork/aquachain@v1.7.8/aqua/tracers/tracer_cgo.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  // +build gccgo cgo
    18  
    19  package tracers
    20  
    21  import (
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"math/big"
    26  	"sync/atomic"
    27  	"time"
    28  	"unsafe"
    29  
    30  	"gitlab.com/aquachain/aquachain/common"
    31  	"gitlab.com/aquachain/aquachain/common/hexutil"
    32  	"gitlab.com/aquachain/aquachain/common/log"
    33  	"gitlab.com/aquachain/aquachain/core/vm"
    34  	"gitlab.com/aquachain/aquachain/crypto"
    35  	duktape "gopkg.in/olebedev/go-duktape.v3"
    36  )
    37  
    38  // makeSlice convert an unsafe memory pointer with the given type into a Go byte
    39  // slice.
    40  //
    41  // Note, the returned slice uses the same memory area as the input arguments.
    42  // If those are duktape stack items, popping them off **will** make the slice
    43  // contents change.
    44  func makeSlice(ptr unsafe.Pointer, size uint) []byte {
    45  	var sl = struct {
    46  		addr uintptr
    47  		len  int
    48  		cap  int
    49  	}{uintptr(ptr), int(size), int(size)}
    50  
    51  	return *(*[]byte)(unsafe.Pointer(&sl))
    52  }
    53  
    54  // popSlice pops a buffer off the JavaScript stack and returns it as a slice.
    55  func popSlice(ctx *duktape.Context) []byte {
    56  	blob := common.CopyBytes(makeSlice(ctx.GetBuffer(-1)))
    57  	ctx.Pop()
    58  	return blob
    59  }
    60  
    61  // pushBigInt create a JavaScript BigInteger in the VM.
    62  func pushBigInt(n *big.Int, ctx *duktape.Context) {
    63  	ctx.GetGlobalString("bigInt")
    64  	ctx.PushString(n.String())
    65  	ctx.Call(1)
    66  }
    67  
    68  // opWrapper provides a JavaScript wrapper around OpCode.
    69  type opWrapper struct {
    70  	op vm.OpCode
    71  }
    72  
    73  // pushObject assembles a JSVM object wrapping a swappable opcode and pushes it
    74  // onto the VM stack.
    75  func (ow *opWrapper) pushObject(vm *duktape.Context) {
    76  	obj := vm.PushObject()
    77  
    78  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(int(ow.op)); return 1 })
    79  	vm.PutPropString(obj, "toNumber")
    80  
    81  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushString(ow.op.String()); return 1 })
    82  	vm.PutPropString(obj, "toString")
    83  
    84  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushBoolean(ow.op.IsPush()); return 1 })
    85  	vm.PutPropString(obj, "isPush")
    86  }
    87  
    88  // memoryWrapper provides a JavaScript wrapper around vm.Memory.
    89  type memoryWrapper struct {
    90  	memory *vm.Memory
    91  }
    92  
    93  // slice returns the requested range of memory as a byte slice.
    94  func (mw *memoryWrapper) slice(begin, end int64) []byte {
    95  	if mw.memory.Len() < int(end) {
    96  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
    97  		// runtime goes belly up https://github.com/golang/go/issues/15639.
    98  		log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin)
    99  		return nil
   100  	}
   101  	return mw.memory.Get(begin, end-begin)
   102  }
   103  
   104  // getUint returns the 32 bytes at the specified address interpreted as a uint.
   105  func (mw *memoryWrapper) getUint(addr int64) *big.Int {
   106  	if mw.memory.Len() < int(addr)+32 {
   107  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   108  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   109  		log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32)
   110  		return new(big.Int)
   111  	}
   112  	return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32))
   113  }
   114  
   115  // pushObject assembles a JSVM object wrapping a swappable memory and pushes it
   116  // onto the VM stack.
   117  func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
   118  	obj := vm.PushObject()
   119  
   120  	// Generate the `slice` method which takes two ints and returns a buffer
   121  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   122  		blob := mw.slice(int64(ctx.GetInt(-2)), int64(ctx.GetInt(-1)))
   123  		ctx.Pop2()
   124  
   125  		ptr := ctx.PushFixedBuffer(len(blob))
   126  		copy(makeSlice(ptr, uint(len(blob))), blob[:])
   127  		return 1
   128  	})
   129  	vm.PutPropString(obj, "slice")
   130  
   131  	// Generate the `getUint` method which takes an int and returns a bigint
   132  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   133  		offset := int64(ctx.GetInt(-1))
   134  		ctx.Pop()
   135  
   136  		pushBigInt(mw.getUint(offset), ctx)
   137  		return 1
   138  	})
   139  	vm.PutPropString(obj, "getUint")
   140  }
   141  
   142  // stackWrapper provides a JavaScript wrapper around vm.Stack.
   143  type stackWrapper struct {
   144  	stack *vm.Stack
   145  }
   146  
   147  // peek returns the nth-from-the-top element of the stack.
   148  func (sw *stackWrapper) peek(idx int) *big.Int {
   149  	if len(sw.stack.Data()) <= idx {
   150  		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   151  		// runtime goes belly up https://github.com/golang/go/issues/15639.
   152  		log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
   153  		return new(big.Int)
   154  	}
   155  	return sw.stack.Data()[len(sw.stack.Data())-idx-1]
   156  }
   157  
   158  // pushObject assembles a JSVM object wrapping a swappable stack and pushes it
   159  // onto the VM stack.
   160  func (sw *stackWrapper) pushObject(vm *duktape.Context) {
   161  	obj := vm.PushObject()
   162  
   163  	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
   164  	vm.PutPropString(obj, "length")
   165  
   166  	// Generate the `peek` method which takes an int and returns a bigint
   167  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   168  		offset := ctx.GetInt(-1)
   169  		ctx.Pop()
   170  
   171  		pushBigInt(sw.peek(offset), ctx)
   172  		return 1
   173  	})
   174  	vm.PutPropString(obj, "peek")
   175  }
   176  
   177  // dbWrapper provides a JavaScript wrapper around vm.Database.
   178  type dbWrapper struct {
   179  	db vm.StateDB
   180  }
   181  
   182  // pushObject assembles a JSVM object wrapping a swappable database and pushes it
   183  // onto the VM stack.
   184  func (dw *dbWrapper) pushObject(vm *duktape.Context) {
   185  	obj := vm.PushObject()
   186  
   187  	// Push the wrapper for statedb.GetBalance
   188  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   189  		pushBigInt(dw.db.GetBalance(common.BytesToAddress(popSlice(ctx))), ctx)
   190  		return 1
   191  	})
   192  	vm.PutPropString(obj, "getBalance")
   193  
   194  	// Push the wrapper for statedb.GetNonce
   195  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   196  		ctx.PushInt(int(dw.db.GetNonce(common.BytesToAddress(popSlice(ctx)))))
   197  		return 1
   198  	})
   199  	vm.PutPropString(obj, "getNonce")
   200  
   201  	// Push the wrapper for statedb.GetCode
   202  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   203  		code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
   204  
   205  		ptr := ctx.PushFixedBuffer(len(code))
   206  		copy(makeSlice(ptr, uint(len(code))), code[:])
   207  		return 1
   208  	})
   209  	vm.PutPropString(obj, "getCode")
   210  
   211  	// Push the wrapper for statedb.GetState
   212  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   213  		hash := popSlice(ctx)
   214  		addr := popSlice(ctx)
   215  
   216  		state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash))
   217  
   218  		ptr := ctx.PushFixedBuffer(len(state))
   219  		copy(makeSlice(ptr, uint(len(state))), state[:])
   220  		return 1
   221  	})
   222  	vm.PutPropString(obj, "getState")
   223  
   224  	// Push the wrapper for statedb.Exists
   225  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   226  		ctx.PushBoolean(dw.db.Exist(common.BytesToAddress(popSlice(ctx))))
   227  		return 1
   228  	})
   229  	vm.PutPropString(obj, "exists")
   230  }
   231  
   232  // contractWrapper provides a JavaScript wrapper around vm.Contract
   233  type contractWrapper struct {
   234  	contract *vm.Contract
   235  }
   236  
   237  // pushObject assembles a JSVM object wrapping a swappable contract and pushes it
   238  // onto the VM stack.
   239  func (cw *contractWrapper) pushObject(vm *duktape.Context) {
   240  	obj := vm.PushObject()
   241  
   242  	// Push the wrapper for contract.Caller
   243  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   244  		ptr := ctx.PushFixedBuffer(20)
   245  		copy(makeSlice(ptr, 20), cw.contract.Caller().Bytes())
   246  		return 1
   247  	})
   248  	vm.PutPropString(obj, "getCaller")
   249  
   250  	// Push the wrapper for contract.Address
   251  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   252  		ptr := ctx.PushFixedBuffer(20)
   253  		copy(makeSlice(ptr, 20), cw.contract.Address().Bytes())
   254  		return 1
   255  	})
   256  	vm.PutPropString(obj, "getAddress")
   257  
   258  	// Push the wrapper for contract.Value
   259  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   260  		pushBigInt(cw.contract.Value(), ctx)
   261  		return 1
   262  	})
   263  	vm.PutPropString(obj, "getValue")
   264  
   265  	// Push the wrapper for contract.Input
   266  	vm.PushGoFunction(func(ctx *duktape.Context) int {
   267  		blob := cw.contract.Input
   268  
   269  		ptr := ctx.PushFixedBuffer(len(blob))
   270  		copy(makeSlice(ptr, uint(len(blob))), blob[:])
   271  		return 1
   272  	})
   273  	vm.PutPropString(obj, "getInput")
   274  }
   275  
   276  // Tracer provides an implementation of Tracer that evaluates a Javascript
   277  // function for each VM execution step.
   278  type Tracer struct {
   279  	inited bool // Flag whether the context was already inited from the EVM
   280  
   281  	vm *duktape.Context // Javascript VM instance
   282  
   283  	tracerObject int // Stack index of the tracer JavaScript object
   284  	stateObject  int // Stack index of the global state to pull arguments from
   285  
   286  	opWrapper       *opWrapper       // Wrapper around the VM opcode
   287  	stackWrapper    *stackWrapper    // Wrapper around the VM stack
   288  	memoryWrapper   *memoryWrapper   // Wrapper around the VM memory
   289  	contractWrapper *contractWrapper // Wrapper around the contract object
   290  	dbWrapper       *dbWrapper       // Wrapper around the VM environment
   291  
   292  	pcValue    *uint   // Swappable pc value wrapped by a log accessor
   293  	gasValue   *uint   // Swappable gas value wrapped by a log accessor
   294  	costValue  *uint   // Swappable cost value wrapped by a log accessor
   295  	depthValue *uint   // Swappable depth value wrapped by a log accessor
   296  	errorValue *string // Swappable error value wrapped by a log accessor
   297  
   298  	ctx map[string]interface{} // Transaction context gathered throughout execution
   299  	err error                  // Error, if one has occurred
   300  
   301  	interrupt uint32 // Atomic flag to signal execution interruption
   302  	reason    error  // Textual reason for the interruption
   303  }
   304  
   305  // New instantiates a new tracer instance. code specifies a Javascript snippet,
   306  // which must evaluate to an expression returning an object with 'step', 'fault'
   307  // and 'result' functions.
   308  func New(code string) (*Tracer, error) {
   309  	// Resolve any tracers by name and assemble the tracer object
   310  	if tracer, ok := tracer(code); ok {
   311  		code = tracer
   312  	}
   313  	tracer := &Tracer{
   314  		vm:              duktape.New(),
   315  		ctx:             make(map[string]interface{}),
   316  		opWrapper:       new(opWrapper),
   317  		stackWrapper:    new(stackWrapper),
   318  		memoryWrapper:   new(memoryWrapper),
   319  		contractWrapper: new(contractWrapper),
   320  		dbWrapper:       new(dbWrapper),
   321  		pcValue:         new(uint),
   322  		gasValue:        new(uint),
   323  		costValue:       new(uint),
   324  		depthValue:      new(uint),
   325  	}
   326  	// Set up builtins for this environment
   327  	tracer.vm.PushGlobalGoFunction("toHex", func(ctx *duktape.Context) int {
   328  		ctx.PushString(hexutil.Encode(popSlice(ctx)))
   329  		return 1
   330  	})
   331  	tracer.vm.PushGlobalGoFunction("toWord", func(ctx *duktape.Context) int {
   332  		var word common.Hash
   333  		if ptr, size := ctx.GetBuffer(-1); ptr != nil {
   334  			word = common.BytesToHash(makeSlice(ptr, size))
   335  		} else {
   336  			word = common.HexToHash(ctx.GetString(-1))
   337  		}
   338  		ctx.Pop()
   339  		copy(makeSlice(ctx.PushFixedBuffer(32), 32), word[:])
   340  		return 1
   341  	})
   342  	tracer.vm.PushGlobalGoFunction("toAddress", func(ctx *duktape.Context) int {
   343  		var addr common.Address
   344  		if ptr, size := ctx.GetBuffer(-1); ptr != nil {
   345  			addr = common.BytesToAddress(makeSlice(ptr, size))
   346  		} else {
   347  			addr = common.HexToAddress(ctx.GetString(-1))
   348  		}
   349  		ctx.Pop()
   350  		copy(makeSlice(ctx.PushFixedBuffer(20), 20), addr[:])
   351  		return 1
   352  	})
   353  	tracer.vm.PushGlobalGoFunction("toContract", func(ctx *duktape.Context) int {
   354  		var from common.Address
   355  		if ptr, size := ctx.GetBuffer(-2); ptr != nil {
   356  			from = common.BytesToAddress(makeSlice(ptr, size))
   357  		} else {
   358  			from = common.HexToAddress(ctx.GetString(-2))
   359  		}
   360  		nonce := uint64(ctx.GetInt(-1))
   361  		ctx.Pop2()
   362  
   363  		contract := crypto.CreateAddress(from, nonce)
   364  		copy(makeSlice(ctx.PushFixedBuffer(20), 20), contract[:])
   365  		return 1
   366  	})
   367  	tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int {
   368  		_, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(popSlice(ctx))]
   369  		ctx.PushBoolean(ok)
   370  		return 1
   371  	})
   372  	tracer.vm.PushGlobalGoFunction("slice", func(ctx *duktape.Context) int {
   373  		start, end := ctx.GetInt(-2), ctx.GetInt(-1)
   374  		ctx.Pop2()
   375  
   376  		blob := popSlice(ctx)
   377  		size := end - start
   378  
   379  		if start < 0 || start > end || end > len(blob) {
   380  			// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
   381  			// runtime goes belly up https://github.com/golang/go/issues/15639.
   382  			log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size)
   383  			ctx.PushFixedBuffer(0)
   384  			return 1
   385  		}
   386  		copy(makeSlice(ctx.PushFixedBuffer(size), uint(size)), blob[start:end])
   387  		return 1
   388  	})
   389  	// Push the JavaScript tracer as object #0 onto the JSVM stack and validate it
   390  	if err := tracer.vm.PevalString("(" + code + ")"); err != nil {
   391  		log.Warn("Failed to compile tracer", "err", err)
   392  		return nil, err
   393  	}
   394  	tracer.tracerObject = 0 // yeah, nice, eval can't return the index itself
   395  
   396  	if !tracer.vm.GetPropString(tracer.tracerObject, "step") {
   397  		return nil, fmt.Errorf("Trace object must expose a function step()")
   398  	}
   399  	tracer.vm.Pop()
   400  
   401  	if !tracer.vm.GetPropString(tracer.tracerObject, "fault") {
   402  		return nil, fmt.Errorf("Trace object must expose a function fault()")
   403  	}
   404  	tracer.vm.Pop()
   405  
   406  	if !tracer.vm.GetPropString(tracer.tracerObject, "result") {
   407  		return nil, fmt.Errorf("Trace object must expose a function result()")
   408  	}
   409  	tracer.vm.Pop()
   410  
   411  	// Tracer is valid, inject the big int library to access large numbers
   412  	tracer.vm.EvalString(bigIntegerJS)
   413  	tracer.vm.PutGlobalString("bigInt")
   414  
   415  	// Push the global environment state as object #1 into the JSVM stack
   416  	tracer.stateObject = tracer.vm.PushObject()
   417  
   418  	logObject := tracer.vm.PushObject()
   419  
   420  	tracer.opWrapper.pushObject(tracer.vm)
   421  	tracer.vm.PutPropString(logObject, "op")
   422  
   423  	tracer.stackWrapper.pushObject(tracer.vm)
   424  	tracer.vm.PutPropString(logObject, "stack")
   425  
   426  	tracer.memoryWrapper.pushObject(tracer.vm)
   427  	tracer.vm.PutPropString(logObject, "memory")
   428  
   429  	tracer.contractWrapper.pushObject(tracer.vm)
   430  	tracer.vm.PutPropString(logObject, "contract")
   431  
   432  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.pcValue); return 1 })
   433  	tracer.vm.PutPropString(logObject, "getPC")
   434  
   435  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.gasValue); return 1 })
   436  	tracer.vm.PutPropString(logObject, "getGas")
   437  
   438  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.costValue); return 1 })
   439  	tracer.vm.PutPropString(logObject, "getCost")
   440  
   441  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushUint(*tracer.depthValue); return 1 })
   442  	tracer.vm.PutPropString(logObject, "getDepth")
   443  
   444  	tracer.vm.PushGoFunction(func(ctx *duktape.Context) int {
   445  		if tracer.errorValue != nil {
   446  			ctx.PushString(*tracer.errorValue)
   447  		} else {
   448  			ctx.PushUndefined()
   449  		}
   450  		return 1
   451  	})
   452  	tracer.vm.PutPropString(logObject, "getError")
   453  
   454  	tracer.vm.PutPropString(tracer.stateObject, "log")
   455  
   456  	tracer.dbWrapper.pushObject(tracer.vm)
   457  	tracer.vm.PutPropString(tracer.stateObject, "db")
   458  
   459  	return tracer, nil
   460  }
   461  
   462  // Stop terminates execution of the tracer at the first opportune moment.
   463  func (jst *Tracer) Stop(err error) {
   464  	jst.reason = err
   465  	atomic.StoreUint32(&jst.interrupt, 1)
   466  }
   467  
   468  // call executes a method on a JS object, catching any errors, formatting and
   469  // returning them as error objects.
   470  func (jst *Tracer) call(method string, args ...string) (json.RawMessage, error) {
   471  	// Execute the JavaScript call and return any error
   472  	jst.vm.PushString(method)
   473  	for _, arg := range args {
   474  		jst.vm.GetPropString(jst.stateObject, arg)
   475  	}
   476  	code := jst.vm.PcallProp(jst.tracerObject, len(args))
   477  	defer jst.vm.Pop()
   478  
   479  	if code != 0 {
   480  		err := jst.vm.SafeToString(-1)
   481  		return nil, errors.New(err)
   482  	}
   483  	// No error occurred, extract return value and return
   484  	return json.RawMessage(jst.vm.JsonEncode(-1)), nil
   485  }
   486  
   487  func wrapError(context string, err error) error {
   488  	var message string
   489  	switch err := err.(type) {
   490  	default:
   491  		message = err.Error()
   492  	}
   493  	return fmt.Errorf("%v    in server-side tracer function '%v'", message, context)
   494  }
   495  
   496  // CaptureStart implements the Tracer interface to initialize the tracing operation.
   497  func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
   498  	jst.ctx["type"] = "CALL"
   499  	if create {
   500  		jst.ctx["type"] = "CREATE"
   501  	}
   502  	jst.ctx["from"] = from
   503  	jst.ctx["to"] = to
   504  	jst.ctx["input"] = input
   505  	jst.ctx["gas"] = gas
   506  	jst.ctx["value"] = value
   507  
   508  	return nil
   509  }
   510  
   511  // CaptureState implements the Tracer interface to trace a single step of VM execution.
   512  func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
   513  	if jst.err == nil {
   514  		// Initialize the context if it wasn't done yet
   515  		if !jst.inited {
   516  			jst.ctx["block"] = env.BlockNumber.Uint64()
   517  			jst.inited = true
   518  		}
   519  		// If tracing was interrupted, set the error and stop
   520  		if atomic.LoadUint32(&jst.interrupt) > 0 {
   521  			jst.err = jst.reason
   522  			return nil
   523  		}
   524  		jst.opWrapper.op = op
   525  		jst.stackWrapper.stack = stack
   526  		jst.memoryWrapper.memory = memory
   527  		jst.contractWrapper.contract = contract
   528  		jst.dbWrapper.db = env.StateDB
   529  
   530  		*jst.pcValue = uint(pc)
   531  		*jst.gasValue = uint(gas)
   532  		*jst.costValue = uint(cost)
   533  		*jst.depthValue = uint(depth)
   534  
   535  		jst.errorValue = nil
   536  		if err != nil {
   537  			jst.errorValue = new(string)
   538  			*jst.errorValue = err.Error()
   539  		}
   540  		_, err := jst.call("step", "log", "db")
   541  		if err != nil {
   542  			jst.err = wrapError("step", err)
   543  		}
   544  	}
   545  	return nil
   546  }
   547  
   548  // CaptureFault implements the Tracer interface to trace an execution fault
   549  // while running an opcode.
   550  func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
   551  	if jst.err == nil {
   552  		// Apart from the error, everything matches the previous invocation
   553  		jst.errorValue = new(string)
   554  		*jst.errorValue = err.Error()
   555  
   556  		_, err := jst.call("fault", "log", "db")
   557  		if err != nil {
   558  			jst.err = wrapError("fault", err)
   559  		}
   560  	}
   561  	return nil
   562  }
   563  
   564  // CaptureEnd is called after the call finishes to finalize the tracing.
   565  func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
   566  	jst.ctx["output"] = output
   567  	jst.ctx["gasUsed"] = gasUsed
   568  	jst.ctx["time"] = t.String()
   569  
   570  	if err != nil {
   571  		jst.ctx["error"] = err.Error()
   572  	}
   573  	return nil
   574  }
   575  
   576  // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
   577  func (jst *Tracer) GetResult() (json.RawMessage, error) {
   578  	// Transform the context into a JavaScript object and inject into the state
   579  	obj := jst.vm.PushObject()
   580  
   581  	for key, val := range jst.ctx {
   582  		switch val := val.(type) {
   583  		case uint64:
   584  			jst.vm.PushUint(uint(val))
   585  
   586  		case string:
   587  			jst.vm.PushString(val)
   588  
   589  		case []byte:
   590  			ptr := jst.vm.PushFixedBuffer(len(val))
   591  			copy(makeSlice(ptr, uint(len(val))), val[:])
   592  
   593  		case common.Address:
   594  			ptr := jst.vm.PushFixedBuffer(20)
   595  			copy(makeSlice(ptr, 20), val[:])
   596  
   597  		case *big.Int:
   598  			pushBigInt(val, jst.vm)
   599  
   600  		default:
   601  			panic(fmt.Sprintf("unsupported type: %T", val))
   602  		}
   603  		jst.vm.PutPropString(obj, key)
   604  	}
   605  	jst.vm.PutPropString(jst.stateObject, "ctx")
   606  
   607  	// Finalize the trace and return the results
   608  	result, err := jst.call("result", "ctx", "db")
   609  	if err != nil {
   610  		jst.err = wrapError("result", err)
   611  	}
   612  	// Clean up the JavaScript environment
   613  	jst.vm.DestroyHeap()
   614  	jst.vm.Destroy()
   615  
   616  	return result, jst.err
   617  }