mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 17:33:47 +00:00
cmd, core, eth/tracers: support fancier js tracing
This commit is contained in:
parent
4b939c23e4
commit
b6467c167a
11 changed files with 589 additions and 33 deletions
|
|
@ -19,6 +19,7 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -35,6 +36,10 @@ func NewJSONLogger(cfg *vm.LogConfig, writer io.Writer) *JSONLogger {
|
|||
return &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureState outputs state information on the logger.
|
||||
func (l *JSONLogger) 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 {
|
||||
log := vm.StructLog{
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package vm
|
|||
import (
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -171,7 +172,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
ret, err = run(evm, contract, input)
|
||||
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in homestead this also counts for code storage gas errors.
|
||||
|
|
@ -181,6 +188,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
contract.UseGas(contract.Gas)
|
||||
}
|
||||
}
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
}
|
||||
return ret, contract.Gas, err
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +348,14 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
|||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, contractAddr, gas, nil
|
||||
}
|
||||
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
ret, err = run(evm, contract, nil)
|
||||
|
||||
// check whether the max code size has been exceeded
|
||||
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
|
||||
// if the contract creation ran successfully and no errors were returned
|
||||
|
|
@ -367,6 +384,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
|
|||
if maxCodeSizeExceeded && err == nil {
|
||||
err = errMaxCodeSizeExceeded
|
||||
}
|
||||
if evm.vmConfig.Debug && evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
}
|
||||
return ret, contractAddr, contract.Gas, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ func (s *StructLog) OpName() string {
|
|||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
|
||||
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
|
||||
}
|
||||
|
|
@ -111,6 +112,10 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
|||
return logger
|
||||
}
|
||||
|
||||
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// CaptureState also tracks SSTORE ops to track dirty values.
|
||||
|
|
|
|||
13
eth/api.go
13
eth/api.go
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
|
|
@ -488,21 +489,23 @@ func (t *timeoutError) Error() string {
|
|||
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||
// and returns them as a JSON object.
|
||||
func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.Hash, config *TraceArgs) (interface{}, error) {
|
||||
var tracer vm.Tracer
|
||||
var (
|
||||
tracer vm.Tracer
|
||||
err error
|
||||
)
|
||||
if config != nil && config.Tracer != nil {
|
||||
timeout := defaultTraceTimeout
|
||||
if config.Timeout != nil {
|
||||
var err error
|
||||
if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if tracer, ok := tracers.Tracer(*config.Tracer); ok {
|
||||
*config.Tracer = tracer
|
||||
}
|
||||
if tracer, err = ethapi.NewJavascriptTracer(*config.Tracer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Handle timeouts and RPC cancellations
|
||||
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
go func() {
|
||||
|
|
|
|||
281
eth/tracers/internal/tracers/assets.go
Normal file
281
eth/tracers/internal/tracers/assets.go
Normal file
File diff suppressed because one or more lines are too long
117
eth/tracers/internal/tracers/call_tracer.js
Normal file
117
eth/tracers/internal/tracers/call_tracer.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// callTracer is a full blown transaction tracer that extracts and reports all
|
||||
// the internal calls made by a transaction, along with any useful information.
|
||||
{
|
||||
// invocations is a nested data structure containing all the internal contract
|
||||
// calls executed by a transaction.
|
||||
invocations: [],
|
||||
|
||||
// callstack is the current recursive call stack of the EVM execution.
|
||||
callstack: [],
|
||||
|
||||
// descended tracks whether we've just descended from an outer transaction into
|
||||
// an inner call.
|
||||
descended: false,
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) {
|
||||
// We only care about system opcodes, faster if we pre-check once
|
||||
var syscall = (log.op.toNumber() & 0xf0) == 0xf0;
|
||||
if (syscall) {
|
||||
var op = log.op.toString();
|
||||
}
|
||||
// If a new method invocation is being done, add to the call stack
|
||||
if (syscall && op != "RETURN" && op != "REVERT") {
|
||||
// Skip any pre-compile invocations, those are just fancy opcodes
|
||||
var to = toAddress(log.stack.peek(1).Bytes());
|
||||
if (isPrecompiled(to)) {
|
||||
return
|
||||
}
|
||||
// Otherwise gather some internal call details
|
||||
var off = (op == 'DELEGATECALL' ? 0 : 1);
|
||||
|
||||
var inOff = log.stack.peek(2 + off).Int64();
|
||||
var inEnd = inOff + log.stack.peek(3 + off).Int64();
|
||||
|
||||
// Assemble the internal call report and store for completion
|
||||
var call = {
|
||||
type: op,
|
||||
from: log.account,
|
||||
to: to,
|
||||
input: toHex(log.memory.slice(inOff, inEnd)),
|
||||
gasDiff: log.gas - log.cost,
|
||||
outOff: log.stack.peek(4 + off).Int64(),
|
||||
outLen: log.stack.peek(5 + off).Int64()
|
||||
};
|
||||
if (op != 'DELEGATECALL') {
|
||||
call.value = '0x' + log.stack.peek(2).Text(16);
|
||||
}
|
||||
this.callstack.push(call);
|
||||
this.descended = true
|
||||
return;
|
||||
}
|
||||
// If we've just descended into an inner call, retrieve it's true allowance. We
|
||||
// need to extract if from within the call as there may be funky gas dynamics
|
||||
// with regard to requested and actually given gas (2300 stipend, 63/64 rule).
|
||||
if (this.descended) {
|
||||
if (log.depth > this.callstack.length) {
|
||||
this.callstack[this.callstack.length - 1].gas = log.gas;
|
||||
} else {
|
||||
this.callstack[this.callstack.length - 1].gas = 2300; // TODO (karalabe): Erm...
|
||||
}
|
||||
this.descended = false;
|
||||
}
|
||||
// If an existing call is returning, pop off the call stack
|
||||
if (syscall && op == 'REVERT') {
|
||||
call.revert = true;
|
||||
return;
|
||||
}
|
||||
if (log.depth == this.callstack.length) {
|
||||
// Pop off the last call and get the execution results
|
||||
var call = this.callstack.pop();
|
||||
|
||||
call.gasUsed = '0x' + big.NewInt(call.gas - log.gas + call.gasDiff).Text(16);
|
||||
call.gas = '0x' + big.NewInt(call.gas).Text(16);
|
||||
delete call.gasDiff;
|
||||
|
||||
call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen));
|
||||
delete call.outOff;
|
||||
delete call.outLen;
|
||||
|
||||
// Append to the invocation list if topmost
|
||||
var left = this.callstack.length;
|
||||
|
||||
if (left == 0) {
|
||||
this.invocations.push(call);
|
||||
return
|
||||
}
|
||||
// Apparently it was a nested call, inject into the previous one
|
||||
if (this.callstack[left-1].calls === undefined) {
|
||||
this.callstack[left-1].calls = [];
|
||||
}
|
||||
this.callstack[left-1].calls.push(call);
|
||||
}
|
||||
},
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx) {
|
||||
var result = {
|
||||
type: ctx.type,
|
||||
from: toAddress(ctx.from),
|
||||
to: toAddress(ctx.to),
|
||||
value: '0x' + ctx.value.Text(16),
|
||||
gas: '0x' + big.NewInt(ctx.gas).Text(16),
|
||||
gasUsed: '0x' + big.NewInt(ctx.gasUsed).Text(16),
|
||||
input: toHex(ctx.input),
|
||||
output: toHex(ctx.output),
|
||||
time: ctx.time,
|
||||
};
|
||||
if (this.invocations.length > 0) {
|
||||
result.calls = this.invocations;
|
||||
}
|
||||
if (ctx.error !== undefined) {
|
||||
result.error = ctx.error;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
10
eth/tracers/internal/tracers/noop_tracer.js
Normal file
10
eth/tracers/internal/tracers/noop_tracer.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// noopTracer is just the barebone boilerplate code required from a JavaScript
|
||||
// object to be usable as a transaction tracer.
|
||||
{
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) { },
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx) { }
|
||||
}
|
||||
13
eth/tracers/internal/tracers/opcount_tracer.js
Normal file
13
eth/tracers/internal/tracers/opcount_tracer.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// opcountTracer is a sample tracer that just counts the number of instructions
|
||||
// executed by the EVM before the transaction terminated.
|
||||
{
|
||||
// count tracks the number of EVM instructions executed.
|
||||
count: 0,
|
||||
|
||||
// step is invoked for every opcode that the VM executes.
|
||||
step: function(log, db) { this.count++ },
|
||||
|
||||
// result is invoked when all the opcodes have been iterated over and returns
|
||||
// the final result of the tracing.
|
||||
result: function(ctx) { return this.count }
|
||||
}
|
||||
21
eth/tracers/internal/tracers/tracers.go
Normal file
21
eth/tracers/internal/tracers/tracers.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:generate go-bindata -nometadata -o assets.go -pkg tracers -ignore ((tracers)|(assets)).go ./...
|
||||
//go:generate gofmt -s -w assets.go
|
||||
|
||||
// Package tracers contains the actual JavaScript tracer assets.
|
||||
package tracers
|
||||
51
eth/tracers/tracers.go
Normal file
51
eth/tracers/tracers.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package tracers is a collection of JavaScript transaction tracers.
|
||||
package tracers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/internal/tracers"
|
||||
)
|
||||
|
||||
// all contains all the built in JavaScript tracers by name.
|
||||
var all = make(map[string]string)
|
||||
|
||||
// init retrieves the JavaScript transaction tracers included in go-ethereum.
|
||||
func init() {
|
||||
for _, file := range tracers.AssetNames() {
|
||||
// Convert the underscored tracer file name into a camelcase tracer name
|
||||
pieces := strings.Split(strings.TrimSuffix(file, ".js"), "_")
|
||||
for i := 1; i < len(pieces); i++ {
|
||||
pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:]
|
||||
}
|
||||
name := strings.Join(pieces, "")
|
||||
|
||||
// Retrieve and store the tracer
|
||||
all[name] = string(tracers.MustAsset(file))
|
||||
}
|
||||
}
|
||||
|
||||
// Tracer retrieves a specific JavaScript tracer by name.
|
||||
func Tracer(name string) (string, bool) {
|
||||
if tracer, ok := all[name]; ok {
|
||||
return tracer, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
|
@ -37,23 +37,23 @@ func (fb *fakeBig) NewInt(x int64) *big.Int {
|
|||
return big.NewInt(x)
|
||||
}
|
||||
|
||||
// OpCodeWrapper provides a JavaScript-friendly wrapper around OpCode, to convince Otto to treat it
|
||||
// opCodeWrapper provides a JavaScript-friendly wrapper around OpCode, to convince Otto to treat it
|
||||
// as an object, instead of a number.
|
||||
type opCodeWrapper struct {
|
||||
op vm.OpCode
|
||||
}
|
||||
|
||||
// toNumber returns the ID of this opcode as an integer
|
||||
// toNumber returns the ID of this opcode as an integer.
|
||||
func (ocw *opCodeWrapper) toNumber() int {
|
||||
return int(ocw.op)
|
||||
}
|
||||
|
||||
// toString returns the string representation of the opcode
|
||||
// toString returns the string representation of the opcode.
|
||||
func (ocw *opCodeWrapper) toString() string {
|
||||
return ocw.op.String()
|
||||
}
|
||||
|
||||
// isPush returns true if the op is a Push
|
||||
// isPush returns true if the op is a Push.
|
||||
func (ocw *opCodeWrapper) isPush() bool {
|
||||
return ocw.op.IsPush()
|
||||
}
|
||||
|
|
@ -200,8 +200,11 @@ func (c *contractWrapper) toValue(vm *otto.Otto) otto.Value {
|
|||
// JavascriptTracer provides an implementation of Tracer that evaluates a
|
||||
// Javascript function for each VM execution step.
|
||||
type JavascriptTracer struct {
|
||||
inited bool // Flag whether the context was already inited from the EVM
|
||||
|
||||
vm *otto.Otto // Javascript VM instance
|
||||
traceobj *otto.Object // User-supplied object to call
|
||||
ctx map[string]interface{} // Map for the execution `ctx` arg to `step`
|
||||
op *opCodeWrapper // Wrapper around the VM opcode
|
||||
log map[string]interface{} // (Reusable) map for the `log` arg to `step`
|
||||
logvalue otto.Value // JS view of `log`
|
||||
|
|
@ -218,14 +221,19 @@ type JavascriptTracer struct {
|
|||
// code specifies a Javascript snippet, which must evaluate to an expression
|
||||
// returning an object with 'step' and 'result' functions.
|
||||
func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
|
||||
vm := otto.New()
|
||||
vm.Interrupt = make(chan func(), 1)
|
||||
jsvm := otto.New()
|
||||
jsvm.Interrupt = make(chan func(), 1)
|
||||
|
||||
// Set up builtins for this environment
|
||||
vm.Set("big", &fakeBig{})
|
||||
vm.Set("toHex", hexutil.Encode)
|
||||
jsvm.Set("big", &fakeBig{})
|
||||
jsvm.Set("toHex", hexutil.Encode)
|
||||
jsvm.Set("toAddress", common.BytesToAddress)
|
||||
jsvm.Set("isPrecompiled", func(addr []byte) bool {
|
||||
_, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(addr)]
|
||||
return ok
|
||||
})
|
||||
|
||||
jstracer, err := vm.Object("(" + code + ")")
|
||||
jstracer, err := jsvm.Object("(" + code + ")")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -254,23 +262,24 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) {
|
|||
contract = new(contractWrapper)
|
||||
)
|
||||
log := map[string]interface{}{
|
||||
"op": op.toValue(vm),
|
||||
"memory": mem.toValue(vm),
|
||||
"stack": stack.toValue(vm),
|
||||
"contract": contract.toValue(vm),
|
||||
"op": op.toValue(jsvm),
|
||||
"memory": mem.toValue(jsvm),
|
||||
"stack": stack.toValue(jsvm),
|
||||
"contract": contract.toValue(jsvm),
|
||||
}
|
||||
logvalue, _ := vm.ToValue(log)
|
||||
logvalue, _ := jsvm.ToValue(log)
|
||||
|
||||
return &JavascriptTracer{
|
||||
vm: vm,
|
||||
vm: jsvm,
|
||||
traceobj: jstracer,
|
||||
ctx: make(map[string]interface{}),
|
||||
op: op,
|
||||
log: log,
|
||||
logvalue: logvalue,
|
||||
memory: mem,
|
||||
stack: stack,
|
||||
db: db,
|
||||
dbvalue: db.toValue(vm),
|
||||
dbvalue: db.toValue(jsvm),
|
||||
contract: contract,
|
||||
err: nil,
|
||||
}, nil
|
||||
|
|
@ -317,8 +326,27 @@ func wrapError(context string, err error) error {
|
|||
return fmt.Errorf("%v in server-side tracer function '%v'", message, context)
|
||||
}
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution
|
||||
// CaptureState implements the Tracer interface to initialize the tracing operation.
|
||||
func (jst *JavascriptTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
jst.ctx["type"] = "CALL"
|
||||
if create {
|
||||
jst.ctx["type"] = "CREATE"
|
||||
}
|
||||
jst.ctx["from"] = from
|
||||
jst.ctx["to"] = to
|
||||
jst.ctx["input"] = input
|
||||
jst.ctx["gas"] = gas
|
||||
jst.ctx["value"] = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
||||
func (jst *JavascriptTracer) 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 {
|
||||
if !jst.inited {
|
||||
jst.ctx["block"] = env.BlockNumber.Uint64()
|
||||
jst.inited = true
|
||||
}
|
||||
if jst.err == nil {
|
||||
jst.op.op = op
|
||||
jst.memory.memory = memory
|
||||
|
|
@ -344,21 +372,23 @@ func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
|
|||
return nil
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (jst *JavascriptTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
|
||||
//TODO! @Arachnid please figure out of there's anything we can use this method for
|
||||
jst.ctx["output"] = output
|
||||
jst.ctx["gasUsed"] = gasUsed
|
||||
jst.ctx["time"] = t.String()
|
||||
if err != nil {
|
||||
jst.ctx["error"] = err.Error()
|
||||
}
|
||||
ctxvalue, _ := jst.vm.ToValue(jst.ctx)
|
||||
jst.result, jst.err = jst.callSafely("result", ctxvalue, jst.dbvalue)
|
||||
if jst.err != nil {
|
||||
jst.err = wrapError("result", jst.err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
||||
func (jst *JavascriptTracer) GetResult() (result interface{}, err error) {
|
||||
if jst.err != nil {
|
||||
return nil, jst.err
|
||||
}
|
||||
|
||||
result, err = jst.callSafely("result")
|
||||
if err != nil {
|
||||
err = wrapError("result", err)
|
||||
}
|
||||
return
|
||||
return jst.result, jst.err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue