From 781915f183c6e09474c6192d1aaba8e99c4990de Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 19 Aug 2016 15:19:51 +0100 Subject: core/vm: Refactor tracing to make Tracer the main interface This CL makes several refactors: - Define a Tracer interface, implementing the `CaptureState` method - Add the VM environment as the first argument of `Tracer.CaptureState` - Rename existing functionality `StructLogger` an make it an implementation of `Tracer` - Delete `StructLogCollector` and make `StructLogger` collect the logs directly - Change all callers to use the new `StructLogger` where necessary and extract logs from that. - Deletes the apparently obsolete and likely nonfunctional 'TraceCall' from the eth API. Callers that only wish accumulated logs can use the `StructLogger` implementation straightforwardly. Callers that wish to efficiently capture VM traces and operate on them without excessive copying can now implement the `Tracer` interface to receive VM state at each step and do with it as they wish. This CL also removes the accumulation of logs from the vm.Environment; this was necessary as part of the refactor, but also simplifies it by removing a responsibility that doesn't directly belong to the Environment. --- eth/api.go | 56 ++++++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) (limited to 'eth') diff --git a/eth/api.go b/eth/api.go index 3b7abb69a..a89b5162e 100644 --- a/eth/api.go +++ b/eth/api.go @@ -319,7 +319,7 @@ type BlockTraceResult struct { // TraceBlock processes the given block's RLP but does not import the block in to // the chain. -func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.LogConfig) BlockTraceResult { var block types.Block err := rlp.Decode(bytes.NewReader(blockRlp), &block) if err != nil { @@ -336,7 +336,7 @@ func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) Block // TraceBlockFromFile loads the block's RLP from the given file name and attempts to // process it but does not import the block in to the chain. -func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.LogConfig) BlockTraceResult { blockRlp, err := ioutil.ReadFile(file) if err != nil { return BlockTraceResult{Error: fmt.Sprintf("could not read file: %v", err)} @@ -345,7 +345,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) B } // TraceBlockByNumber processes the block by canonical block number. -func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.LogConfig) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByNumber(number) if block == nil { @@ -361,7 +361,7 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) } // TraceBlockByHash processes the block by hash. -func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config) BlockTraceResult { +func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.LogConfig) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByHash(hash) if block == nil { @@ -376,49 +376,38 @@ func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config } } -// TraceCollector collects EVM structered logs. -// -// TraceCollector implements vm.Collector -type TraceCollector struct { - traces []vm.StructLog -} - -// AddStructLog adds a structered log. -func (t *TraceCollector) AddStructLog(slog vm.StructLog) { - t.traces = append(t.traces, slog) -} - // traceBlock processes the given block but does not save the state. -func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (bool, []vm.StructLog, error) { +func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConfig) (bool, []vm.StructLog, error) { // Validate and reprocess the block var ( blockchain = api.eth.BlockChain() validator = blockchain.Validator() processor = blockchain.Processor() - collector = &TraceCollector{} ) - if config == nil { - config = new(vm.Config) + + structLogger := vm.NewStructLogger(logConfig) + + config := vm.Config{ + Debug: true, + Tracer: structLogger, } - config.Debug = true // make sure debug is set. - config.Logger.Collector = collector if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash(), block.NumberU64()-1), true, false); err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } statedb, err := state.New(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), api.eth.ChainDb()) if err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - receipts, _, usedGas, err := processor.Process(block, statedb, *config) + receipts, _, usedGas, err := processor.Process(block, statedb, config) if err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } if err := validator.ValidateState(block, blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas); err != nil { - return false, collector.traces, err + return false, structLogger.StructLogs(), err } - return true, collector.traces, nil + return true, structLogger.StructLogs(), nil } // callmsg is the message type used for call transations. @@ -452,10 +441,9 @@ func formatError(err error) string { // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ethapi.ExecutionResult, error) { - if logger == nil { - logger = new(vm.LogConfig) - } +func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logConfig *vm.LogConfig) (*ethapi.ExecutionResult, error) { + logger := vm.NewStructLogger(logConfig) + // Retrieve the tx from the chain and the containing block tx, blockHash, _, txIndex := core.GetTransaction(api.eth.ChainDb(), txHash) if tx == nil { @@ -500,7 +488,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC continue } // Otherwise trace the transaction and return - vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger}) + vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: logger}) ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) @@ -508,7 +496,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC return ðapi.ExecutionResult{ Gas: gas, ReturnValue: fmt.Sprintf("%x", ret), - StructLogs: ethapi.FormatLogs(vmenv.StructLogs()), + StructLogs: ethapi.FormatLogs(logger.StructLogs()), }, nil } return nil, errors.New("database inconsistency") -- cgit v1.2.3