From 0983d02aa906394fd2be83ebda104730e32c593b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 10 Dec 2018 12:32:40 +0200 Subject: [PATCH] eth, internal/web3ext: tiny polishes in tracers --- eth/api_tracer.go | 172 ++++++++++++++++++++---------------- internal/web3ext/web3ext.go | 2 + 2 files changed, 97 insertions(+), 77 deletions(-) diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 77d52d9441..0b8f8aa00b 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -66,7 +66,7 @@ type TraceConfig struct { type StdTraceConfig struct { *vm.LogConfig Reexec *uint64 - TxHash *common.Hash + TxHash common.Hash } // txTraceResult is the result of a single transaction trace. @@ -375,7 +375,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.B 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) + return nil, fmt.Errorf("block %#x not found", hash) } return api.traceBlock(ctx, block, config) } @@ -400,37 +400,41 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string, return api.TraceBlock(ctx, blob, config) } -// TraceBadBlockByHash returns the structured logs created during the execution of a block -func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, blockHash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { +// TraceBadBlockByHash returns the structured logs created during the execution of +// EVM against a block pulled from the pool of bad ones and returns them as a JSON +// object. +func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { blocks := api.eth.blockchain.BadBlocks() for _, block := range blocks { - if block.Hash() == blockHash { + if block.Hash() == hash { return api.traceBlock(ctx, block, config) } } - return nil, fmt.Errorf("hash not found among bad blocks") + return nil, fmt.Errorf("bad block %#x not found", hash) } -// StandardTraceBadBlockToFile dumps the standard-json logs to files on the local filesystem, -// and returns a list of files to the caller. -func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, blockHash common.Hash, stdConfig *StdTraceConfig) ([]string, error) { - blocks := api.eth.blockchain.BadBlocks() - for _, block := range blocks { - if block.Hash() == blockHash { - return api.standardTraceBlockToFile(ctx, block, stdConfig) - } +// StandardTraceBlockToFile dumps the structured logs created during the +// execution of EVM to the local file system and returns a list of files +// to the caller. +func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { + block := api.eth.blockchain.GetBlockByHash(hash) + if block == nil { + return nil, fmt.Errorf("block %#x not found", hash) } - return nil, fmt.Errorf("hash not found among bad blocks") + return api.standardTraceBlockToFile(ctx, block, config) } -// StandardTraceBlockToFile dumps the standard-json logs to files on the local filesystem, -// and returns a list of files to the caller. -func (api *PrivateDebugAPI) StandardTraceBlockToFile(ctx context.Context, blockHash common.Hash, stdConfig *StdTraceConfig) ([]string, error) { - block := api.eth.blockchain.GetBlockByHash(blockHash) - if block == nil { - return nil, fmt.Errorf("block #%x not found", blockHash) +// StandardTraceBadBlockToFile dumps the structured logs created during the +// execution of EVM against a block pulled from the pool of bad ones to the +// local file system and returns a list of files to the caller. +func (api *PrivateDebugAPI) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { + blocks := api.eth.blockchain.BadBlocks() + for _, block := range blocks { + if block.Hash() == hash { + return api.standardTraceBlockToFile(ctx, block, config) + } } - return api.standardTraceBlockToFile(ctx, block, stdConfig) + return nil, fmt.Errorf("bad block %#x not found", hash) } // traceBlock configures a new tracer according to the provided configuration, and @@ -443,7 +447,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, } parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, fmt.Errorf("parent %x not found", block.ParentHash()) + return nil, fmt.Errorf("parent %#x not found", block.ParentHash()) } reexec := defaultTraceReexec if config != nil && config.Reexec != nil { @@ -514,90 +518,104 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block, return results, nil } -// standardTraceBlockToFile configures a new tracer which uses standard-json output, and -// traces either a full block or an individual transaction. The return value will be one filename -// per transaction traced. -func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, stdConfig *StdTraceConfig) ([]string, error) { +// standardTraceBlockToFile configures a new tracer which uses standard JSON output, +// and traces either a full block or an individual transaction. The return value will +// be one filename per transaction traced. +func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) { + // If we're tracing a single transaction, make sure it's present + if config != nil && config.TxHash != (common.Hash{}) { + var exists bool + for _, tx := range block.Transactions() { + if exists = (tx.Hash() == config.TxHash); exists { + break + } + } + if !exists { + return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash) + } + } // Create the parent state database if err := api.eth.engine.VerifyHeader(api.eth.blockchain, block.Header(), true); err != nil { return nil, err } parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, fmt.Errorf("parent %x not found", block.ParentHash()) + return nil, fmt.Errorf("parent %#x not found", block.ParentHash()) } - var ( - signer = types.MakeSigner(api.config, block.Number()) - done = false - blockPrefix = fmt.Sprintf("block_0x%x", block.Hash().Bytes()[:4]) - usedLogConfig = &vm.LogConfig{Debug: true} - files []string - reExec_val = defaultTraceReexec - txHash *common.Hash - ) - if stdConfig != nil { - if stdConfig.Reexec != nil { - reExec_val = *stdConfig.Reexec - } - if stdConfig.LogConfig != nil { - usedLogConfig.DisableMemory = stdConfig.LogConfig.DisableMemory - usedLogConfig.DisableStack = stdConfig.LogConfig.DisableStack - usedLogConfig.DisableStorage = stdConfig.LogConfig.DisableStorage - usedLogConfig.Limit = stdConfig.LogConfig.Limit - } - txHash = stdConfig.TxHash + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec } - statedb, err := api.computeStateDB(parent, reExec_val) + statedb, err := api.computeStateDB(parent, reexec) if err != nil { return nil, err } + // Retrieve the tracing configurations, or use default values + var ( + logConfig vm.LogConfig + txHash common.Hash + ) + if config != nil { + if config.LogConfig != nil { + logConfig = *config.LogConfig + } + txHash = config.TxHash + } + logConfig.Debug = true + // Execute transaction, either tracing all or just the requested one + var ( + signer = types.MakeSigner(api.config, block.Number()) + dumps []string + ) for i, tx := range block.Transactions() { + // Prepare the trasaction for un-traced execution var ( - outfile *os.File - err error + msg, _ = tx.AsMessage(signer) + vmctx = core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) + + vmConf vm.Config + dump *os.File + err error ) - msg, _ := tx.AsMessage(signer) - vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil) - vmConf := vm.Config{} - if txHash == nil || bytes.Equal(txHash.Bytes(), tx.Hash().Bytes()) { - prefix := fmt.Sprintf("%v-%d-0x%x-", blockPrefix, i, tx.Hash().Bytes()[:4]) - // Open a file to dump trace into - outfile, err = ioutil.TempFile(os.TempDir(), prefix) + // If the transaction needs tracing, swap out the configs + if tx.Hash() == txHash || txHash == (common.Hash{}) { + // Generate a unique temporary file to dump it into + prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4]) + + dump, err = ioutil.TempFile(os.TempDir(), prefix) if err != nil { return nil, err } - files = append(files, outfile.Name()) + dumps = append(dumps, dump.Name()) + + // Swap out the noop logger to the standard tracer vmConf = vm.Config{ Debug: true, - Tracer: vm.NewJSONLogger(usedLogConfig, bufio.NewWriter(outfile)), + Tracer: vm.NewJSONLogger(&logConfig, bufio.NewWriter(dump)), EnablePreimageRecording: true, } - if txHash != nil { // Only one tx to trace - done = true - } } + // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, statedb, api.config, vmConf) _, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) - if outfile != nil { - outfile.Close() - log.Info("Wrote trace", "file", outfile.Name()) + if dump != nil { + dump.Close() + log.Info("Wrote standard trace", "file", dump.Name()) } if err != nil { - return files, err + return dumps, err } // Finalize the state so any modifications are written to the trie statedb.Finalise(true) - if done { + // If we've traced the transaction we were looking for, abort + if tx.Hash() == txHash { break } } - if txHash != nil && !done { - return nil, fmt.Errorf("transaction hash not found in block") - } - return files, nil + return dumps, nil } // computeStateDB retrieves the state database associated with a certain block. @@ -675,7 +693,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha // Retrieve the transaction and assemble its EVM context tx, blockHash, _, index := rawdb.ReadTransaction(api.eth.ChainDb(), hash) if tx == nil { - return nil, fmt.Errorf("transaction %x not found", hash) + return nil, fmt.Errorf("transaction %#x not found", hash) } reexec := defaultTraceReexec if config != nil && config.Reexec != nil { @@ -755,11 +773,11 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree // Create the parent state database block := api.eth.blockchain.GetBlockByHash(blockHash) if block == nil { - return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash) + return nil, vm.Context{}, nil, fmt.Errorf("block %#x not found", blockHash) } parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash()) + return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash()) } statedb, err := api.computeStateDB(parent, reexec) if err != nil { @@ -778,10 +796,10 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{}) if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { - return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err) + return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } // Ensure any modifications are committed to the state statedb.Finalise(true) } - return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash) + return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash) } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index f36835d8ac..06bfcef69e 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -388,11 +388,13 @@ web3._extend({ name: 'standardTraceBadBlockToFile', call: 'debug_standardTraceBadBlockToFile', params: 2, + inputFormatter: [null, null] }), new web3._extend.Method({ name: 'standardTraceBlockToFile', call: 'debug_standardTraceBlockToFile', params: 2, + inputFormatter: [null, null] }), new web3._extend.Method({ name: 'traceBlockByNumber',