diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go index 777f2ac8d2..47daf7dbbc 100644 --- a/cmd/evm/json_logger.go +++ b/cmd/evm/json_logger.go @@ -61,6 +61,11 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos return l.encoder.Encode(log) } +// CaptureFault outputs state information on the logger. +func (l *JSONLogger) 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 { + return nil +} + // CaptureEnd is triggered at end of execution. func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { type endLog struct { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 455f970ddb..482e67a3a9 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -144,12 +144,17 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er ) contract.Input = input - defer func() { - if err != nil && !logged && in.cfg.Debug { - in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) - } - }() - + if in.cfg.Debug { + defer func() { + if err != nil { + if !logged { + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) + } else { + in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) + } + } + }() + } // The Interpreter main run loop (contextual). This loop runs until either an // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during // the execution of one of the operations or until the done flag is set by the diff --git a/core/vm/logger.go b/core/vm/logger.go index 5465beeb7a..1a6e43ee30 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -86,6 +86,7 @@ func (s *StructLog) OpName() string { 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 + CaptureFault(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 } @@ -166,6 +167,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui return nil } +func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { + return nil +} + func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { fmt.Printf("0x%x", output) if err != nil { diff --git a/eth/api.go b/eth/api.go index e170f78870..e10e0f2b9f 100644 --- a/eth/api.go +++ b/eth/api.go @@ -24,14 +24,12 @@ import ( "math/big" "os" "strings" - "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "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/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" @@ -40,8 +38,6 @@ import ( "github.com/ethereum/go-ethereum/trie" ) -const defaultTraceTimeout = 5 * time.Second - // PublicEthereumAPI provides an API to access Ethereum full node-related // information. type PublicEthereumAPI struct { @@ -345,13 +341,6 @@ func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebug return &PrivateDebugAPI{config: config, eth: eth} } -// TraceArgs holds extra parameters to trace functions -type TraceArgs struct { - *vm.LogConfig - Tracer *string - Timeout *string -} - // Preimage is a debug API function that returns the preimage for a sha3 hash, if known. func (api *PrivateDebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { db := core.PreimageTable(api.eth.ChainDb()) diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 1d473f9f0f..ebf37d798d 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -42,6 +42,24 @@ import ( "github.com/ethereum/go-ethereum/trie" ) +const ( + // defaultTraceTimeout is the amount of time a single transaction can execute + // by default before being forcefully aborted. + defaultTraceTimeout = 5 * time.Second + + // defaultTraceRewind is the number of blocks the tracer is willing to rewind + // and regenerate to produce the state necessary to run a specific transaction. + defaultTraceRewind = uint64(128) +) + +// TraceConfig holds extra parameters to trace functions. +type TraceConfig struct { + *vm.LogConfig + Tracer *string + Timeout *string + Rewind *uint64 +} + // txTraceResult is the result of a single transaction trace. type txTraceResult struct { Result interface{} `json:"result,omitempty"` // Trace results procuded by the tracer @@ -124,7 +142,7 @@ func (db *ephemeralDatabase) Prune(root common.Hash) { // TraceChain returns the structured logs created during the execution of EVM // between two blocks (excluding start) and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceArgs) (*rpc.Subscription, error) { +func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) { // Fetch the block interval that we want to trace var from, to *types.Block @@ -157,7 +175,7 @@ func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.Block // traceChain configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item // per transaction, dependent on the requestd tracer. -func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceArgs) (*rpc.Subscription, error) { +func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { // Tracing a chain is a **long** operation, only do with subscriptions notifier, supported := rpc.NotifierFromContext(ctx) if !supported { @@ -166,6 +184,8 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl sub := notifier.CreateSubscription() // Ensure we have a valid starting state before doing any work + origin := start.NumberU64() + memdb, _ := ethdb.NewMemDatabase() db := &ephemeralDatabase{ diskdb: api.eth.ChainDb(), @@ -173,10 +193,28 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl } statedb, err := state.New(start.Root(), state.NewDatabase(db)) if err != nil { - return nil, err + // If the starting state is missing, allow some number of blocks to be rewound + rewind := defaultTraceRewind + if config.Rewind != nil { + rewind = *config.Rewind + } + // Find the most recent block that has the state available + for i := uint64(0); i < rewind; i++ { + start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1) + if start == nil { + break + } + if statedb, err = state.New(start.Root(), state.NewDatabase(db)); err == nil { + break + } + } + // If we still don't have the state available, bail out + if err != nil { + return nil, err + } } // Execute all the transaction contained within the chain concurrently for each block - blocks := int(end.NumberU64() - start.NumberU64()) + blocks := int(end.NumberU64() - origin) threads := runtime.NumCPU() if threads > blocks { @@ -204,6 +242,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} + log.Warn("Tracing failed", "err", err) break } task.statedb.DeleteSuicides() @@ -254,7 +293,11 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl } // Print progress logs if long enough time elapsed if time.Since(logged) > 8*time.Second { - log.Info("Tracing chain segment", "start", start.NumberU64(), "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin)) + if number > origin { + log.Info("Tracing chain segment", "start", origin, "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin)) + } else { + log.Info("Preparing state for chain trace", "block", number, "start", origin, "elapsed", time.Since(begin)) + } logged = time.Now() } // Retrieve the next block to trace @@ -263,16 +306,19 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl failed = fmt.Errorf("block #%d not found", number) break } - // Send the block over to the concurrent tracers - txs := block.Transactions() + // Send the block over to the concurrent tracers (if not in the rewind phase) + if number > origin { + txs := block.Transactions() - select { - case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, results: make([]*txTraceResult, len(txs))}: - case <-notifier.Closed(): - return + select { + case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, results: make([]*txTraceResult, len(txs))}: + case <-notifier.Closed(): + return + } + traced += uint64(len(txs)) + } else { + atomic.StoreUint64(&complete, number) } - traced += uint64(len(txs)) - // Generate the next state snapshot fast without tracing _, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{}) if err != nil { @@ -314,7 +360,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl go func() { var ( done = make(map[uint64]*blockTraceResult) - next = start.NumberU64() + 1 + next = origin + 1 ) for res := range results { // Queue up next received result @@ -341,7 +387,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl // TraceBlockByNumber returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceArgs) ([]*txTraceResult, error) { +func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) { // Fetch the block that we want to trace var block *types.Block @@ -362,7 +408,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.B // TraceBlockByHash returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceArgs) ([]*txTraceResult, error) { +func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { block := api.eth.blockchain.GetBlockByHash(hash) if block == nil { return nil, fmt.Errorf("block #%x not found", hash) @@ -372,7 +418,7 @@ func (api *PrivateDebugAPI) TraceBlockByHash(ctx context.Context, hash common.Ha // TraceBlock returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob []byte, config *TraceArgs) ([]*txTraceResult, error) { +func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob []byte, config *TraceConfig) ([]*txTraceResult, error) { block := new(types.Block) if err := rlp.Decode(bytes.NewReader(blob), block); err != nil { return nil, fmt.Errorf("could not decode block: %v", err) @@ -382,7 +428,7 @@ func (api *PrivateDebugAPI) TraceBlock(ctx context.Context, blob []byte, config // TraceBlockFromFile returns the structured logs created during the execution of // EVM and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceArgs) ([]*txTraceResult, error) { +func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, config *TraceConfig) ([]*txTraceResult, error) { blob, err := ioutil.ReadFile(file) if err != nil { return nil, fmt.Errorf("could not read file: %v", err) @@ -393,7 +439,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, // traceBlock configures a new tracer according to the provided configuration, and // executes all the transactions contained within. The return value will be one item // per transaction, dependent on the requestd tracer. -func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceArgs) ([]*txTraceResult, error) { +func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) { // Create the parent state database if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil { return nil, err @@ -469,7 +515,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, // 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, hash common.Hash, config *TraceArgs) (interface{}, error) { +func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { // Retrieve the transaction and assemble its EVM context tx, blockHash, _, index := core.GetTransaction(api.eth.ChainDb(), hash) if tx == nil { @@ -486,7 +532,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceArgs) (interface{}, error) { +func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( tracer vm.Tracer diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/internal/tracers/assets.go index 07b0e64a29..7385bea216 100644 --- a/eth/tracers/internal/tracers/assets.go +++ b/eth/tracers/internal/tracers/assets.go @@ -93,7 +93,7 @@ func _4byte_tracerJs() (*asset, error) { return a, nil } -var _call_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x58\x4d\x6f\xe3\x38\x12\x3d\xdb\xbf\xa2\xfa\xd2\xb1\xb7\x1d\xd9\xe9\x9e\xed\x83\x03\xef\x22\x48\xbc\xb3\x01\xb2\x9b\x41\xc7\x33\x73\x68\xf4\x81\x96\x4a\x32\x27\x32\xa9\x21\x29\x7f\xa0\xc7\xff\x7d\x51\x45\x4a\x96\x65\x3b\x1b\x74\x03\x8b\x9d\x9b\x45\x56\x3d\x15\xeb\xe3\xf1\xc9\xc3\x21\xc4\x22\xcf\x67\x46\xc4\x68\x40\x5a\x10\x90\x96\x79\x0e\xf3\x5c\xaf\x15\x38\x23\x94\x15\xb1\x93\x9a\x7f\x93\x89\x5b\x08\x07\xb8\xa1\x27\x67\x41\xa8\x04\x0c\x16\xda\xd0\xef\x3c\xef\x0e\x87\xe0\x16\x08\x52\x39\x34\x4a\xe4\x8c\x6d\x61\x29\x12\x84\xf9\x16\x44\x13\x70\x00\x22\xd7\x2a\x83\xb5\x74\x0b\x10\x6a\x0b\xa5\xc5\xb4\xcc\x41\xaa\x54\x9b\xa5\x20\x93\xa8\xfb\xb5\xdb\x09\x11\x5a\x27\xe2\x67\x0a\x90\xf0\xe3\xd2\x18\x54\x0e\x0c\xc6\xa5\xb1\x72\x85\x6c\x02\xde\x46\xa7\x6c\x33\xfd\xe5\x5f\x80\x1b\x8c\x4b\x8f\xd4\xa9\x41\xc6\xf0\xf9\xeb\xee\xcb\xa0\xcb\xd0\x09\xda\x18\x55\x82\x09\x9f\xef\xd9\xc2\x7a\x81\x6e\x81\x06\xd6\x78\xb1\x42\xf8\xad\xb4\xae\x61\x93\x1a\xbd\x04\xa1\x40\x97\x8e\x52\xd1\xc8\x8e\x54\x4e\x33\xa0\xa0\xdf\x0a\x0d\x47\x14\x75\x3b\xb5\xf3\x18\x52\x91\x5b\x0c\xef\xb5\x0e\x0b\x3a\x8d\x54\x2b\xfd\x4c\xc8\xda\x00\xae\xd0\x6c\x41\x17\xb1\x4e\xd0\xe7\x99\xce\x51\x1f\x03\x6d\xd4\xed\x90\xdf\x18\xd2\x52\xf1\x6b\x7b\xb9\xce\x06\x90\xcc\xfb\xf0\xb5\xdb\x21\xd8\x5f\x11\xb4\xca\xb7\x10\x0b\x83\x20\xe6\xba\x74\x60\xb7\xd6\xe1\x32\xc0\xda\x01\xa4\xc2\x52\xf0\x32\x85\x35\x42\x61\xf0\x32\x5e\x20\x65\x4d\xc5\xd8\xed\x74\x56\xc2\x90\x07\xa7\x73\x02\x84\x1f\xe9\x22\x72\xfa\xdf\xe5\x72\x8e\xa6\xd7\x87\xb7\x30\xda\xa4\xa3\x3e\x4c\x26\xfc\xe3\xba\xdb\xe9\xc8\x14\x7a\xc1\xc7\x07\xc2\x28\xba\x80\x09\xd4\xfe\x4f\xce\x48\x95\xf5\xfa\x64\xbf\xf3\xb1\xde\xa7\x20\x40\xe1\x1a\x62\xad\xb8\x9d\x28\x1f\x73\x94\x2a\x83\xd8\xa0\x70\x98\x0c\x40\x24\x09\x38\xed\x6b\x5e\x57\xf8\xf0\x95\xf0\xf6\x2d\xbf\x6b\x02\x17\xb7\x9f\xa6\x37\xb3\xe9\x45\x23\x08\xa9\x1e\xd3\x34\xc4\xc1\xbe\x51\x81\xf8\xdc\xbb\xea\x47\x2b\x91\x97\xf8\x98\xfa\x88\x82\xed\x54\x25\x30\x09\x3e\xef\xda\x3e\xef\x0f\x7c\xc8\x69\x38\x84\x1b\x6b\x71\x39\xcf\xf1\xb8\xeb\xc3\x58\xf0\x84\x58\xa7\x0d\x72\x89\x63\xbd\x2c\x72\xa4\xd2\x55\x6f\x0d\x99\xe6\x88\x3b\x6e\x5b\xe0\x18\x00\x40\x17\x03\x5e\xa0\x86\xe3\x05\xa7\xff\x89\x1b\x2e\x47\x95\xad\x28\x43\x77\x93\x24\x06\xad\xed\xf5\xfb\xde\x5c\xaa\xa2\x74\xe3\x03\xf3\x25\x2e\xb5\xd9\x46\x36\x97\x31\xf6\xf8\x68\x03\x7f\xd2\xca\x27\x13\xf6\x5e\x91\x0f\x59\x67\xe8\x7e\x14\xb6\xb7\xdf\xba\xd5\xd6\x8d\xab\x2d\x7a\xa8\xf6\x38\x17\xe4\x76\x31\xda\x5c\x1c\x67\x6b\xd4\xdf\x17\xfd\xea\x63\x9f\x5c\x76\x9c\x69\xb7\x90\x36\xaa\x67\x31\x2a\x4a\xbb\xe8\x71\xe7\xec\x77\xf7\xf3\x36\x01\x67\x4a\x6a\xcb\x8e\x41\x57\x1a\xd5\xee\x9e\xe3\xce\xb1\x98\xa7\x34\xb0\xce\x94\x31\x77\x50\x26\x78\x9c\x79\x9c\x04\xd1\x9b\x2d\xe7\x9c\x73\xa7\xf5\xd9\x46\x7a\x9a\x3e\xfc\xe3\x6e\xfa\x34\xfb\xf4\xf3\xed\xac\xd9\x4e\x39\xa6\x8e\x82\x3a\x3c\x43\x8e\x2a\x73\x0b\x8e\x9f\xe0\x0e\x77\x3f\x93\xcf\xe5\xd5\x17\xbf\x02\x93\xc9\x04\x4a\x95\x60\x2a\x15\x26\xfd\xaa\xec\x2f\x7a\xc0\xe7\x2f\x8c\xbd\x3b\x4e\xdf\xa1\xa9\x4f\xe6\xe9\x4e\xda\xf9\xfc\xfa\x34\x9e\x98\xc1\x25\xba\x85\x4e\x98\x8e\x62\xe1\x19\xad\x4a\x69\xa2\x15\xbe\x7a\x12\x7b\xd5\x28\xde\x3c\x3c\x5c\xc0\x1f\x7f\x40\xe3\xf9\xf6\xf1\x6e\xda\x5c\xbb\x9b\x3e\x4c\x7f\xbc\x99\x4d\xdb\xb6\x4f\xb3\x9b\xd9\xfd\x2d\xaf\xf6\x43\x8a\x86\x43\x78\x7a\x96\x05\xdf\x13\xcc\x59\x7a\x59\xc8\x1c\x1b\xf1\xda\x01\xb8\x85\xb6\x08\xc4\x7c\x4c\xdb\xa9\x50\x71\x45\xa7\xb6\xaa\xa0\xd3\x54\x3f\x5d\x0d\xce\x31\x2f\x34\xbb\xb6\x5f\xd7\x54\xda\x9f\x0c\x86\x97\x26\x3d\xa7\xab\xb8\xf6\x09\xf5\xe5\x61\xe2\x63\xc6\xe9\xbd\xfe\x90\xf0\x77\x18\xc1\x18\xae\x02\xad\xbc\xc0\x5b\xef\xe1\x1d\xc1\x7f\x03\x7b\x7d\x38\xe1\xf9\xff\xc9\x61\x4e\xb3\x71\x65\xee\xf4\xff\x9e\xdb\x74\xe9\x1e\xd3\x74\x0c\xed\x24\xfe\x70\x94\xc4\xda\xfe\x01\xd5\xb1\xfd\x5f\x8f\xec\xf7\x3c\x48\x5d\xa5\x0b\x78\x73\xd4\x22\x9e\x85\xde\xb4\xe6\x20\x24\x97\x45\x05\xa3\xc1\xe4\x0c\xf3\xbe\x3f\xec\xe1\x73\xd4\xf1\x5d\xcc\x7b\x52\x1c\x91\x04\x3a\x94\x3f\x03\x30\xe8\x8c\xc4\x15\x82\x74\x17\x96\x21\x49\x26\xea\xb5\x50\x31\x46\xf0\x2b\x7a\x44\x85\xc8\xe4\x12\x64\x25\x69\x13\x56\x5a\x24\x0d\xa5\xda\x73\x8e\x60\xf5\x67\x10\x96\x62\x0b\x73\x24\x19\xf4\xbc\x85\x4c\x58\x48\xb6\x4a\x2c\x65\x6c\x3d\x1e\x4b\x4a\x83\x99\x30\x0c\x6b\xf0\xf7\x12\xad\xc3\x84\x1b\x59\xc4\xae\x14\x79\xbe\x85\x4c\xae\x50\xb1\x77\xef\xfd\x87\xd1\x08\xac\x93\x05\xaa\x64\x00\x1f\x3f\x0c\x3f\xfe\x00\xa6\xcc\xb1\x1f\x75\x1b\x9c\x5e\x1f\x35\x54\x83\x36\x42\xf7\xdc\x61\xe1\x16\xbd\x3e\xfc\xed\xcc\xe5\x70\x86\xe9\x4f\xda\xc2\x25\x5c\x7d\x89\x28\xae\xc9\x41\xdf\xfa\x4a\x02\xe6\x16\x03\xda\x70\x08\xb3\xc7\xbb\xc7\xde\xb3\x30\x22\x17\x73\xec\x8f\x61\x56\xe5\x6a\x2d\x82\xee\xa6\xa2\x40\x91\x0b\xa9\x40\xc4\xb1\x2e\x95\xa3\xc4\x57\x12\x3a\xdf\x12\xbf\x5f\xb8\x0a\x6f\x21\x56\x48\x76\x68\x6d\x45\xf7\x5c\x35\x0a\x47\x2c\xc9\x1b\xa4\xb2\x32\xc1\x46\x55\x88\x1d\x34\x53\x73\xb0\x58\xcb\x3c\xaf\x00\x97\xda\xd2\x4b\xe6\x08\x6b\x43\x72\xdf\x4a\x15\x53\x3b\x40\x82\x94\x6d\x0b\x5a\x81\x80\x5c\x3b\xd2\xed\x3c\xe3\x20\x4c\x66\x23\xcf\xf7\xf4\x5a\xe2\x1c\xa5\xd7\xd1\x61\x23\x37\x5b\x95\x85\x75\x4b\x1b\x28\xc0\x8d\xb4\x8e\xd5\x24\x45\x29\x2d\xf8\x4e\x96\x2a\x1b\x40\xa1\x0b\xe6\xe9\x57\x0a\xcb\x4f\xd3\x5f\xa6\x9f\x6a\x25\xf0\xfa\x22\x1a\x92\xf4\x2e\x0c\xd3\xf5\xf1\x34\x9d\xe8\xa0\xc9\x99\x0e\x22\xc0\xfd\x65\xf8\x53\x23\xfe\x5c\x58\xb7\xaf\x44\x86\xfe\xb3\xa1\xfe\xf4\x01\x83\xb6\xcc\x9d\x6d\x91\x75\x9b\x0d\x74\x51\x5d\x09\x14\x14\xf3\x0c\x31\xf9\x09\x5d\x1d\x32\xec\x9a\x9d\x26\xc0\xdb\x34\x26\x9e\xf7\x2b\x7d\x26\x3c\xc9\x73\x84\xba\x74\x54\x65\xba\x96\xf7\x9c\x96\x09\xfb\xb3\xe5\x62\x06\x56\x9b\xcb\xec\x5e\xb9\x5e\xb5\x79\xaf\xe0\x12\xaa\x07\xe2\x6a\xb8\x3c\x18\x8e\x13\xa4\xd7\x49\x30\x47\x87\xb0\x87\xb8\x86\xd6\x12\x01\xf9\x43\x73\x6a\x0c\xba\xe3\x3b\x77\x14\xd0\x28\x2d\x6f\x0c\xba\x08\x7f\x2f\x45\x6e\x7b\xa3\x5a\x03\xf8\x13\x38\xcd\xb7\xd6\xa4\xbe\xb7\xaa\x8b\x8d\x7c\x0e\x54\x45\x00\xf4\x6e\x21\x1b\x95\x5b\x32\xf7\x97\x51\x82\x2f\x22\x04\x88\x03\x36\xf0\x78\xc7\x2d\xe7\x87\xa6\x4d\x1c\x27\x2a\x78\xf3\xf0\xf0\x97\x56\x01\xdb\xb5\xaa\x7b\x83\x06\xf3\xcd\x29\x2d\xfb\xad\xe5\x7c\x57\x3f\xbe\xa2\xb2\xaf\x2e\x6d\x7d\xfc\xef\xaf\x6f\xab\x50\x47\xfa\xa3\x32\x62\x15\xd2\x78\xa8\x4e\xe6\x45\xc2\x37\x54\xee\xe0\xb0\x1e\xf3\xf0\xb4\x1e\x79\x7f\xd1\xff\xf7\x22\xd5\xbb\xe7\xea\x73\x4e\x43\x50\xe3\xa8\xdf\x30\x76\xfb\xe6\xe1\x6b\x9f\x9e\x0a\x83\x2b\xa9\x4b\x62\x74\xfc\x33\x7d\x30\xd5\x1a\x68\xd7\xed\xec\xc2\xdf\x33\x9e\x33\x9b\x7f\xd0\xac\x17\xa8\x80\xbf\x1b\x69\x2c\xfc\x47\x85\xbf\x2d\xe7\x88\x0a\xa4\x43\x23\x48\x62\xe8\x15\x9a\xf0\xb7\x18\x31\xbd\x65\x38\xf2\x49\x25\xc9\xea\x00\x1c\xfe\xa3\x22\x72\x94\x2a\x8b\xba\x1d\xbf\xde\xf8\x67\x27\x76\x1b\x7f\x5a\xdf\xb8\xec\x15\x24\x76\xad\xb0\x63\xb7\x61\x92\x66\x15\xda\x92\xd9\xb4\x47\x4b\x5e\xa2\xb6\x44\x35\x3b\x06\x61\xdd\xfe\x90\xa7\x3d\x5e\x3b\x68\x01\x36\xcd\x84\xf5\x30\xad\xa6\x71\x9b\xe3\x9e\xa9\x1c\x88\x06\xc6\xa7\x1d\x68\xeb\x84\x53\x4b\xe8\x93\x31\x2f\xf9\x5d\x3f\x86\xe3\xe6\xae\x5f\x0a\x07\x95\xcb\x46\x6e\xe4\x92\x73\xb3\xbb\xee\x9e\xec\xb4\x51\xd5\x32\xa7\x86\xc4\xe7\xbc\xee\xa9\x33\xae\xcd\x9b\x9c\x5e\x89\xc6\x68\xf3\x12\x9e\x37\x98\x40\x6d\xcc\xad\x1a\xc6\x39\xd8\xf8\x03\x35\xa1\x8f\xdf\xee\xd9\xe2\x10\xfb\x88\x41\xc8\xdd\xf7\x61\x80\xbe\xee\x76\x76\xdd\x5d\xf7\x3f\x01\x00\x00\xff\xff\xb1\x67\x2f\xbe\xf6\x15\x00\x00") +var _call_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x59\xdf\x73\xdb\x36\xf2\x7f\x96\xfe\x8a\x4d\x1e\x6a\x69\xa2\x48\x4a\xd2\x6f\x1e\xe4\xaf\x7a\xe3\x71\xd4\x5e\x66\x7c\xe7\x4e\xe2\xb6\x0f\x19\x3f\x40\xe4\x52\x42\x0c\x02\x2c\x00\x4a\xd6\xa5\xfe\xdf\x6f\x76\x01\x52\xa4\x44\x3b\xba\xdc\xdc\x4d\xef\x45\x23\x02\xbb\xcb\xc5\xee\x67\x7f\x81\x93\x09\x24\x42\xa9\x1b\x2b\x12\xb4\x20\x1d\x08\xc8\x4a\xa5\x60\xa9\xcc\x56\x83\xb7\x42\x3b\x91\x78\x69\xf8\x3f\x91\xf8\xb5\xf0\x80\xf7\xf4\xe4\x1d\x08\x9d\x82\xc5\xc2\x58\xfa\xaf\x54\x7f\x32\x01\xbf\x46\x90\xda\xa3\xd5\x42\xb1\x6c\x07\xb9\x48\x11\x96\x3b\x10\x4d\x81\x23\x10\xca\xe8\x15\x6c\xa5\x5f\x83\xd0\x3b\x28\x1d\x66\xa5\x02\xa9\x33\x63\x73\x41\x24\xe3\xfe\x97\x7e\x2f\x6a\xe8\xbc\x48\xee\x48\x41\x92\x9f\x94\xd6\xa2\xf6\x60\x31\x29\xad\x93\x1b\x64\x12\x08\x34\x26\x63\x9a\xc5\xaf\x7f\x03\xbc\xc7\xa4\x0c\x92\x7a\xb5\x90\x19\x7c\xfa\xf2\x70\x3b\xea\xb3\xe8\x14\x5d\x82\x3a\xc5\x94\xcf\x77\xe7\x60\xbb\x46\xbf\x46\x0b\x5b\x3c\xdb\x20\x7c\x2e\x9d\x6f\xd0\x64\xd6\xe4\x20\x34\x98\xd2\x93\x29\x1a\xd6\x91\xda\x1b\x16\x28\xe8\xbf\x46\xcb\x1a\x8d\xfb\xbd\x9a\x79\x06\x99\x50\x0e\xe3\x7b\x9d\xc7\x82\x4e\x23\xf5\xc6\xdc\x91\x64\x63\x01\x37\x68\x77\x60\x8a\xc4\xa4\x18\xec\x4c\xe7\xa8\x8f\x81\x6e\xdc\xef\x11\xdf\x0c\xb2\x52\xf3\x6b\x07\xca\xac\x46\x90\x2e\x87\xf0\xa5\xdf\x23\xb1\x97\xa2\xf0\xa5\x45\xb6\x27\x5a\x6b\xac\x03\x99\xe7\x98\x4a\xe1\x51\xed\xfa\xbd\xde\x46\xd8\xb0\x01\x73\x50\x66\x35\x5e\xa1\x5f\xd0\xe3\x60\x78\xde\xef\xf5\x64\x06\x83\xb0\xfb\x6c\x3e\x87\x52\xa7\x98\x49\x8d\x69\x10\xdf\xf3\x6b\xe9\xc6\x99\x28\x95\xaf\xdf\x4b\x4c\x3d\x8b\xbe\xb4\x9a\xfe\x3e\x04\x2d\x7e\x43\x30\x5a\xed\x20\x11\xa4\xca\xd2\x94\x1e\xdc\xce\x79\xcc\xe3\xe1\xdc\x08\x32\xe1\xc8\x84\x32\x83\x2d\x42\x61\xf1\x65\xb2\x46\xf2\x9d\x4e\x30\x6a\xe9\x76\x8e\x9d\x3a\x07\x7a\xdb\xd8\x14\x63\x6f\xfe\x5e\xe6\x4b\xb4\x83\x21\x7c\x07\xd3\xfb\x6c\x3a\x84\xf9\x9c\xff\x54\xba\x47\x9e\xa8\x2f\x49\x31\x45\x3c\x28\xf3\x7f\xf4\x56\xea\x55\x38\x6b\xd4\xf5\x7d\x06\x02\x34\x6e\x21\x31\x9a\x41\x4d\x5e\x59\xa2\xd4\x2b\x48\x2c\x0a\x8f\xe9\x08\x44\x9a\x82\x37\x01\x79\x35\xce\xda\xaf\x84\xef\xbe\xe3\x77\xcd\xe1\xec\xf2\xc3\xe2\xe2\x66\x71\xd6\x50\x42\xea\xeb\x2c\x8b\x7a\x30\xef\xb8\x40\xbc\x1b\xbc\x1a\x8e\x37\x42\x95\x78\x9d\x05\x8d\x22\xed\x42\xa7\x30\x8f\x3c\x2f\x0e\x79\x5e\xb7\x78\x88\x69\x32\x81\x0b\xe7\x30\x5f\x2a\x3c\x8e\xbd\x18\x9c\x1c\xa7\xce\x1b\x8b\x0c\xb4\xc4\xe4\x85\x42\x02\x50\xf5\xd6\x68\x69\xd6\xb8\xe7\x77\x05\xce\x00\x00\x4c\x31\xe2\x05\x82\x3d\x2f\x78\xf3\x57\xbc\x67\x77\x54\xd6\x22\x00\x5d\xa4\xa9\x45\xe7\x06\xc3\x61\x20\x97\xba\x28\xfd\xac\x45\x9e\x63\x6e\xec\x6e\xec\x94\x4c\x70\xc0\x47\x1b\x85\x93\x56\x3c\x2b\xe1\xde\x6b\xe2\x89\xa0\xfc\x49\xb8\xc1\x7e\xeb\xd2\x38\x3f\xab\xb6\xe8\xa1\xda\x63\x5b\x10\xdb\xd9\xf4\xfe\xec\xd8\x5a\xd3\xe1\xde\xe9\xaf\xde\x0e\x89\xe5\xe1\xbc\x86\x72\x9d\x11\xc6\x45\xe9\xd6\x03\x46\xce\x7e\x77\x1f\xf5\x73\xf0\xb6\xc4\x4e\xa4\x33\x7a\x8e\x91\xe3\x50\x65\x94\x36\xbc\x2d\x13\x46\xd0\x4a\x70\x52\xe1\xa0\x16\x94\x64\x5d\xb9\x64\x9b\x7b\x63\x1e\x05\xd2\xc7\xc5\xd5\x8f\xef\x16\x1f\x6f\x3e\xfc\x72\x79\xd3\x84\x93\xc2\xcc\x93\x52\xed\x33\x28\xd4\x2b\xbf\x66\xfd\x49\x5c\x7b\xf7\x13\xf1\xbc\x7c\x75\x1b\x56\x60\xde\x11\xdd\xbd\xa7\x39\xe0\xd3\x2d\xcb\x7e\x38\x36\x5f\x9b\x34\x18\xf3\x4b\x00\x91\x29\x1e\x9a\x39\xa2\x23\xec\x72\xf4\x6b\x93\x72\x1e\x4c\x44\x48\xa5\x95\x15\x53\xa3\xf1\xe4\xe0\x1b\x54\xd1\x77\x71\x75\x75\x06\x7f\xfc\x01\x8d\xe7\xcb\xeb\x77\x8b\xe6\xda\xbb\xc5\xd5\xe2\xa7\x8b\x9b\xc5\x21\xed\xc7\x9b\x8b\x9b\xf7\x97\xbc\x3a\x8c\x56\x99\x4c\xe0\xe3\x9d\x2c\x38\xa1\x72\x9a\x32\x79\x21\x15\x36\xf4\x75\x23\xf0\x6b\xe3\x10\x28\xd9\x71\xbd\xc8\x84\x4e\xaa\x3c\xee\x2a\xa7\x79\x43\x2e\x33\x55\xac\x1c\xa7\x82\x26\x50\x87\xb5\x1b\xa5\xfb\xd9\x62\x7c\x69\x3a\xf0\xa6\xd2\x6b\x6f\xd0\xe0\x11\xce\x75\x9c\x64\x06\xa7\x1f\x12\xfe\x02\x53\x98\xc1\xab\x98\x49\x9e\x48\x55\xaf\xe1\x05\x89\xff\x86\x84\xf5\xa6\x83\xf3\xcf\x99\xb6\xbc\x61\xe2\x8a\xdc\x9b\xff\x7e\x3a\x33\xa5\xbf\xce\xb2\x19\x1c\x1a\xf1\xfb\x23\x23\xd6\xf4\x57\xa8\x8f\xe9\xff\xef\x88\x7e\x9f\xfa\x08\x55\xa6\x80\x67\x47\x10\x09\x89\xe7\xd9\x41\x1c\x44\xe3\x72\x37\xc3\xd2\x60\xfe\x48\xb2\x7d\xdd\xc6\xf0\x63\xd9\xe2\xdf\x4a\xb6\x9d\x5d\x19\xf5\x5e\xed\xbe\x6b\x04\x16\xbd\x95\xb8\x41\x90\xfe\xcc\xb1\x48\xea\x4f\xcd\x56\xe8\x04\xc7\xf0\x1b\x06\x89\x1a\x91\x93\x4b\xec\x67\xa9\x1d\xe1\x16\x8f\x7a\x52\xa9\xf7\x39\x47\x70\xdb\x69\x11\x72\xb1\x83\x25\x52\xff\x75\xb7\x83\x95\x70\x90\xee\xb4\xc8\x65\xe2\x82\x3c\xee\x65\x2d\xae\x84\x65\xb1\x16\x7f\x2f\xd1\x79\x4c\x19\xc8\x22\xf1\xa5\x50\x6a\x07\x2b\xb9\x41\xcd\xdc\x83\xd7\x6f\xa6\x53\x70\x5e\x16\xa8\xd3\x11\xbc\x7d\x33\x79\xfb\x3d\xd8\x52\xe1\x70\xdc\x6f\xa4\xf1\xfa\xa8\xd1\x1b\xb4\x11\xd1\xf3\x0e\x0b\xbf\x1e\x0c\xe1\x87\x47\xea\xc1\x23\xc9\xbd\x93\x16\x5e\xc2\xab\xdb\x31\xe9\x35\x6f\xe1\x36\x78\x12\x50\x39\x8c\xd2\x26\x13\xb8\xb9\x7e\x77\x3d\xb8\x13\x56\x28\xb1\xc4\xe1\x0c\x6e\x2a\x5b\x6d\x45\x6c\xf8\xc9\x29\x50\x28\x21\x35\x88\x24\x31\xa5\xf6\x64\xf8\xaa\x77\x57\x3b\xca\xef\x67\xbe\x92\xb7\x16\x1b\x24\x3a\x74\xae\x4a\xf7\xec\x35\x52\x47\xe4\xc4\x0d\x52\x3b\x99\x62\xc3\x2b\x94\x1d\x0c\xa7\xe6\x48\xb1\x95\x4a\x55\x02\x73\xe3\xe8\x25\x4b\x84\xad\xa5\x39\xc3\x49\x9d\x10\x1c\x20\x45\xb2\xb6\x03\xa3\x41\x80\x32\x9e\x06\x06\x8e\x71\x10\x76\xe5\xc6\x21\xdf\xd3\x6b\x29\xe7\x68\xb3\x1d\xb7\x81\xdc\x84\x2a\x77\xf4\x07\xed\x80\x06\xbc\x97\xce\x73\x03\x49\x5a\x4a\x07\x01\xc9\x52\xaf\x46\x50\x98\x82\xf3\xf4\x89\xbd\xe4\x87\xc5\xaf\x8b\x0f\x75\xf1\x3f\xdd\x89\x55\x8b\xff\xbc\x9e\x80\xc0\xd2\x78\xe1\x31\x7d\xde\xd1\xb3\x77\x00\x6a\xfe\x08\xa0\x48\xfe\xbe\x36\xfe\xdc\x38\x8e\x12\xce\xef\x1d\xb3\xc2\x30\xbe\x34\x15\x70\xa5\xf2\xee\x20\x77\x1f\x26\x07\x53\x54\x15\x82\x94\xe2\xb4\x43\x89\xbd\xa3\xb3\x8e\x06\xf7\x4d\xe0\x09\x08\x34\x8d\x04\xc0\xfb\x55\x87\x26\x42\xce\x67\x0d\x4d\xe9\xc9\xe9\x54\xa5\xf7\x29\x6e\x25\xdc\x2f\x8e\x7d\x1b\x93\xdc\x52\xae\xde\x6b\x3f\xa8\x36\xdf\x6b\x78\x09\xd5\x03\xa5\x6e\x78\xd9\x8a\x95\x8e\x1c\xd8\x4b\x51\xa1\x47\xd8\x8b\x38\x87\x83\x25\x12\x14\x0e\xcd\xa6\xb1\xe8\x8f\x4b\xf0\x34\x4a\x23\xb3\x3c\xb3\xe8\xc7\xf8\x7b\x29\x94\x1b\x4c\xeb\x96\x20\x9c\xc0\x1b\x2e\x62\xf3\xba\x8c\x55\x75\x8e\x78\x5a\x4d\x46\x14\x18\xd8\xa2\x35\x2a\xb6\x74\x19\x6a\x53\x8a\x4f\x4a\x88\x22\x62\x72\xa8\x3d\x16\xe1\xd7\xd5\x65\xf6\x9a\x04\xf0\xbc\x2e\xfb\x99\x90\xaa\xb4\xf8\xfc\x1c\x3a\x92\x8b\x2b\x6d\x26\x12\xf6\xa5\x43\xe0\x11\xd4\x81\x33\x39\xae\xcd\x36\x28\xd0\x95\xa2\x8e\xc1\x51\xe3\xe0\xa0\x48\x10\x19\x45\x7c\xe9\xc4\x0a\x1b\xe0\xa8\x0d\x5e\x39\xaa\x73\x2e\xfe\x66\xe8\xbc\xa8\x1f\xbf\x82\xa2\xf0\x96\xaf\x42\xe3\x29\x6c\x74\x7a\xf9\xa8\x97\xa9\x88\xb8\xa3\x69\x3c\x54\xaa\x86\x86\xa3\x46\xce\xbf\xe2\xf7\xff\x8c\xe3\x83\xe7\xe3\xef\xa9\x81\x76\x48\x1b\xce\xd8\x26\x0e\x27\xdd\x37\x31\x5f\x47\x41\xbd\xfb\x18\x00\x1e\xeb\x8f\x08\xaa\xfa\x33\x26\x7e\x0f\x57\x6e\x69\xe8\xa9\xb0\xb8\x91\xa6\xa4\x6a\x85\xff\x4b\xf3\x5f\xdd\xdf\x3d\xf4\x7b\x0f\xf1\xce\x8b\xdd\xd7\xbc\xf4\xda\xae\x31\x34\x59\xa1\x35\x6a\xd4\x0a\xc3\x85\x34\x5e\x85\x05\xb7\x8f\xfb\x3d\xfe\xf3\xc4\xed\x57\x0c\x78\x6f\x0a\x2a\xfe\xb1\x16\x29\x8b\x22\xdd\xd5\xe5\x6f\x14\xda\x0e\x58\x0b\x9d\xc6\xd1\x43\xa4\xa9\x24\x79\x0c\x46\x52\x51\xac\x84\xd4\xfd\x4e\x3b\x7e\xb5\xe6\x76\x41\xe3\xa8\x93\x6d\x96\xcd\x38\x32\xd2\x7c\xc7\x1a\xf7\x4f\x28\x8f\x07\xc1\x74\x78\x91\x17\xef\x02\x8d\x76\x65\xce\x7d\x2f\x88\x8d\x90\x4a\xd0\xac\xc5\xfd\x94\x4e\x21\x51\x28\x34\xf7\x4e\xe4\x3d\xb3\x41\xeb\xfa\x27\xa0\xfc\x5b\x40\x7e\x90\x1d\xab\xc7\x68\x8e\xd3\x83\xf6\xd4\x90\x0d\xc7\xff\x51\x09\xef\x23\xbe\x1a\xe6\x0d\xa1\x25\xbd\x83\x42\x50\x1f\xda\x3f\x2d\xa6\xb8\x43\x22\x9a\x1f\x60\xda\xe8\xc2\xff\x2c\x51\x76\x0c\xb1\xab\xba\x1b\x8b\x87\xf7\xc6\x8c\x40\xa1\xe0\x99\x08\xe2\x74\x53\x75\x9f\x4f\x8d\x68\x55\xf8\x86\xfe\xed\x28\x7e\xf9\x16\x6b\x8d\xd5\x7d\x47\x68\xe4\x97\x88\x1a\xa4\x47\x2b\x68\xfa\x21\x74\xc5\x4f\x05\xa4\xa5\x63\x71\xec\x17\x49\x41\x17\x05\xc7\x7b\x7b\x2a\xd0\x52\xaf\xc6\xfd\x5e\x58\x6f\xc4\x7b\xe2\xef\x83\x19\x43\x25\x64\xae\x38\xfd\xd7\xc3\x7f\xe2\xef\xb9\x61\xe4\x01\xf9\xe0\x06\x80\xf6\x68\x29\x4c\xcf\x07\xf3\x3e\x33\xc6\x99\xff\xf0\x5a\x91\xf6\x78\xad\x05\x6e\x26\x5d\x09\x17\xc4\x1c\x84\x83\xbf\x3f\x8e\x86\x8a\x81\x02\x61\xd6\xcd\x40\x5b\x1d\x4c\x07\x77\x10\x44\xcc\x4b\x61\x37\x54\xf5\x59\x73\x37\x2c\xc5\x83\xca\xbc\x61\x1b\x99\xb3\x6d\x1e\xce\xbb\x13\xdc\xb4\xc2\x62\x77\x22\x23\x9b\xd7\x60\x7d\x84\xb5\x39\x55\x1c\x93\x3c\x95\x26\x59\x7a\x95\xd5\x1e\x61\x65\xe9\x8d\xbe\xc3\xdf\x9f\x2e\xb2\x26\x6e\xaa\xd8\xa2\xe9\x12\x12\x73\x4c\xa4\x0b\x96\xad\x04\x04\x44\x07\x5d\x19\xcd\xf2\x1f\x18\x25\x36\x63\xa7\xda\x02\x8b\xe1\xab\x02\x77\xa3\x14\x3a\x66\xc9\x95\xbf\x74\x34\x30\xee\x63\x22\x45\x27\x2d\xa6\x90\x49\x54\x29\x98\x14\x2d\x8f\xa3\x9f\x9d\xd1\xe1\xfb\x11\x5a\x49\x12\xc3\x77\x32\xb8\x59\x53\x58\x92\x50\x2d\x13\xf4\x3b\xc8\x50\xf0\x87\x20\x6f\xa0\x10\xce\x41\x8e\x82\x06\xd0\xac\x54\x6a\x07\xc6\xa6\x48\xc2\xeb\x89\x8c\xc2\xd1\x40\xe9\xd0\x3a\xd8\xae\x4d\x2c\x91\xdc\xa2\x15\xd4\x71\x4a\x3f\x8a\x97\x2e\xd2\x15\x4a\xec\x40\x7a\x2a\xc7\xf1\x50\xcd\x08\xad\xbf\xbe\xf0\x27\x1c\x43\x15\xf7\x38\x44\xab\xa1\xae\x1d\xa3\xbc\x4c\x4f\xed\xe8\x8c\x43\x4d\x3b\x2e\xf7\xd7\x51\xed\x20\xac\x4a\x46\x3b\xd2\x9a\x05\xa8\x1d\x4e\xbc\xc3\x4f\xed\x40\x6a\x34\xcb\xbc\xc1\xe0\xa8\x19\xf8\xe9\x20\xb4\x58\xcb\x18\x5b\xe1\x5b\x63\x4d\xce\x4f\xa3\x08\x18\xf2\xe2\x80\x8c\x73\x87\x3b\xca\xc2\xc1\x46\x8d\x92\x12\x16\x3e\xdd\xe1\xee\xb6\xbb\x82\x44\x38\x36\xe8\xea\x92\x51\x41\x3a\xec\x3d\x11\xc8\xb5\x16\x72\x3e\x3d\x07\xf9\xff\x4d\x86\xaa\xea\x81\x7c\xf1\xa2\x7a\x67\x73\xff\x93\xbc\xad\xa2\xb3\x46\xfc\xc1\xfe\xb0\xa5\x51\x8c\x91\x40\x43\x41\xd1\x7f\xe8\xff\x33\x00\x00\xff\xff\xfe\xdb\x25\x4e\x78\x1e\x00\x00") func call_tracerJsBytes() ([]byte, error) { return bindataRead( diff --git a/eth/tracers/internal/tracers/call_tracer.js b/eth/tracers/internal/tracers/call_tracer.js index eeb643e986..1f3c78e910 100644 --- a/eth/tracers/internal/tracers/call_tracer.js +++ b/eth/tracers/internal/tracers/call_tracer.js @@ -10,6 +10,12 @@ // step is invoked for every opcode that the VM executes. step: function(log, db) { + // Capture any errors immediately + var error = log.getError(); + if (error !== undefined) { + this.fault(log, db); + return; + } // We only care about system opcodes, faster if we pre-check once var syscall = (log.op.toNumber() & 0xf0) == 0xf0; if (syscall) { @@ -39,9 +45,7 @@ if (this.callstack[left-1].calls === undefined) { this.callstack[left-1].calls = []; } - this.callstack[left-1].calls.push({ - type: op, - }); + this.callstack[left-1].calls.push({type: op}); return } // If a new method invocation is being done, add to the call stack @@ -89,7 +93,7 @@ } // If an existing call is returning, pop off the call stack if (syscall && op == 'REVERT') { - this.callstack[this.callstack.length - 1].revert = true; + this.callstack[this.callstack.length - 1].error = "execution reverted"; return; } if (log.getDepth() == this.callstack.length - 1) { @@ -105,21 +109,22 @@ if (!ret.equals(0)) { call.to = toHex(toAddress(ret.toString(16))); call.output = toHex(db.getCode(toAddress(ret.toString(16)))); - } else { - call.revert = true; + } else if (call.error === undefined) { + call.error = "internal failure"; // TODO(karalabe): surface these faults somehow } } else { - // If the call was a CALL*, retrieve the output code + // If the call was a contract call, retrieve the gas usage and output if (call.gas !== undefined) { call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost + call.gas - log.getGas()).toString(16); - delete call.gasIn; delete call.gasCost; - } - var ret = log.stack.peek(0); - if (!ret.equals(0)) { - call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen)); - } else { - call.revert = true; + + var ret = log.stack.peek(0); + if (!ret.equals(0)) { + call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen)); + } else if (call.error === undefined) { + call.error = "internal failure"; // TODO(karalabe): surface these faults somehow + } } + delete call.gasIn; delete call.gasCost; delete call.outOff; delete call.outLen; } if (call.gas !== undefined) { @@ -134,6 +139,37 @@ } }, + // fault is invoked when the actual execution of an opcode fails. + fault: function(log, db) { + // If the topmost call already reverted, don't handle the additional fault again + if (this.callstack[this.callstack.length - 1].error !== undefined) { + return; + } + // Pop off the just failed call + var call = this.callstack.pop(); + call.error = log.getError(); + + // Consume all available gas and clean any leftovers + if (call.gas !== undefined) { + call.gas = '0x' + bigInt(call.gas).toString(16); + call.gasUsed = call.gas + } + delete call.gasIn; delete call.gasCost; + delete call.outOff; delete call.outLen; + + // Flatten the failed call into its parent + var left = this.callstack.length; + if (left > 0) { + if (this.callstack[left-1].calls === undefined) { + this.callstack[left-1].calls = []; + } + this.callstack[left-1].calls.push(call); + return; + } + // Last call failed too, leave it in the stack + this.callstack.push(call); + }, + // result is invoked when all the opcodes have been iterated over and returns // the final result of the tracing. result: function(ctx) { @@ -151,13 +187,44 @@ if (this.callstack[0].calls !== undefined) { result.calls = this.callstack[0].calls; } - if (ctx.error !== undefined) { + if (this.callstack[0].error !== undefined) { + result.error = this.callstack[0].error; + } else if (ctx.error !== undefined) { result.error = ctx.error; + } + if (result.error !== undefined) { delete result.output; } - if (this.callstack[0].revert) { - result.revert = true; + return this.finalize(result); + }, + + // finalize recreates a call object using the final desired field oder for json + // serialization. This is a nicety feature to pass meaningfully ordered results + // to users who don't interpret it, just display it. + finalize: function(call) { + var sorted = { + type: call.type, + from: call.from, + to: call.to, + value: call.value, + gas: call.gas, + gasUsed: call.gasUsed, + input: call.input, + output: call.output, + error: call.error, + time: call.time, + calls: call.calls, } - return result; + for (var key in sorted) { + if (sorted[key] === undefined) { + delete sorted[key]; + } + } + if (sorted.calls !== undefined) { + for (var i=0; i