From 1876cb443b36c51fde0f5f5717b74834f9887fa2 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <1591639+s1na@users.noreply.github.com> Date: Thu, 25 Nov 2021 13:17:09 +0100 Subject: [PATCH] all: move loggers to eth/tracers (#23892) * all: mv loggers to eth/tracers * core/vm: minor * eth/tracers: tmp comment out testStoreCapture * eth/tracers: uncomment and fix logger test * eth/tracers: simplify test * core/vm: re-add license * core/vm: minor * rename LogConfig to Config --- cmd/evm/internal/t8ntool/transition.go | 5 +- cmd/evm/runner.go | 15 +- cmd/evm/staterunner.go | 13 +- core/blockchain_test.go | 5 +- core/vm/logger.go | 324 +--------------- core/vm/runtime/runtime_test.go | 9 +- eth/tracers/api.go | 29 +- .../tracers/logger}/access_list_tracer.go | 25 +- .../tracers/logger}/gen_structlog.go | 7 +- eth/tracers/logger/logger.go | 349 ++++++++++++++++++ .../vm => eth/tracers/logger}/logger_json.go | 21 +- .../vm => eth/tracers/logger}/logger_test.go | 25 +- eth/tracers/tracers_test.go | 3 +- internal/ethapi/api.go | 9 +- tests/state_test.go | 3 +- 15 files changed, 441 insertions(+), 401 deletions(-) rename {core/vm => eth/tracers/logger}/access_list_tracer.go (81%) rename {core/vm => eth/tracers/logger}/gen_structlog.go (94%) create mode 100644 eth/tracers/logger/logger.go rename {core/vm => eth/tracers/logger}/logger_json.go (76%) rename {core/vm => eth/tracers/logger}/logger_test.go (75%) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index edb439425e..11d71e4ce5 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -112,7 +113,7 @@ func Transition(ctx *cli.Context) error { log.Warn(fmt.Sprintf("--%s has been deprecated in favour of --%s", TraceDisableReturnDataFlag.Name, TraceEnableReturnDataFlag.Name)) } // Configure the EVM logger - logConfig := &vm.LogConfig{ + logConfig := &logger.Config{ DisableStack: ctx.Bool(TraceDisableStackFlag.Name), EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name) || ctx.Bool(TraceEnableMemoryFlag.Name), EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name) || ctx.Bool(TraceEnableReturnDataFlag.Name), @@ -134,7 +135,7 @@ func Transition(ctx *cli.Context) error { return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } prevFile = traceFile - return vm.NewJSONLogger(logConfig, traceFile), nil + return logger.NewJSONLogger(logConfig, traceFile), nil } } else { getTracer = func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error) { diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 447bb2c2e6..889de43e0a 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "gopkg.in/urfave/cli.v1" @@ -107,7 +108,7 @@ func runCmd(ctx *cli.Context) error { glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) log.Root().SetHandler(glogger) - logconfig := &vm.LogConfig{ + logconfig := &logger.Config{ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name), DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), @@ -117,7 +118,7 @@ func runCmd(ctx *cli.Context) error { var ( tracer vm.EVMLogger - debugLogger *vm.StructLogger + debugLogger *logger.StructLogger statedb *state.StateDB chainConfig *params.ChainConfig sender = common.BytesToAddress([]byte("sender")) @@ -125,12 +126,12 @@ func runCmd(ctx *cli.Context) error { genesisConfig *core.Genesis ) if ctx.GlobalBool(MachineFlag.Name) { - tracer = vm.NewJSONLogger(logconfig, os.Stdout) + tracer = logger.NewJSONLogger(logconfig, os.Stdout) } else if ctx.GlobalBool(DebugFlag.Name) { - debugLogger = vm.NewStructLogger(logconfig) + debugLogger = logger.NewStructLogger(logconfig) tracer = debugLogger } else { - debugLogger = vm.NewStructLogger(logconfig) + debugLogger = logger.NewStructLogger(logconfig) } if ctx.GlobalString(GenesisFlag.Name) != "" { gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) @@ -288,10 +289,10 @@ func runCmd(ctx *cli.Context) error { if ctx.GlobalBool(DebugFlag.Name) { if debugLogger != nil { fmt.Fprintln(os.Stderr, "#### TRACE ####") - vm.WriteTrace(os.Stderr, debugLogger.StructLogs()) + logger.WriteTrace(os.Stderr, debugLogger.StructLogs()) } fmt.Fprintln(os.Stderr, "#### LOGS ####") - vm.WriteLogs(os.Stderr, statedb.Logs()) + logger.WriteLogs(os.Stderr, statedb.Logs()) } if bench || ctx.GlobalBool(StatDumpFlag.Name) { diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 5e9bf696b6..90596d9b3c 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/tests" @@ -58,7 +59,7 @@ func stateTestCmd(ctx *cli.Context) error { log.Root().SetHandler(glogger) // Configure the EVM logger - config := &vm.LogConfig{ + config := &logger.Config{ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name), DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name), @@ -66,18 +67,18 @@ func stateTestCmd(ctx *cli.Context) error { } var ( tracer vm.EVMLogger - debugger *vm.StructLogger + debugger *logger.StructLogger ) switch { case ctx.GlobalBool(MachineFlag.Name): - tracer = vm.NewJSONLogger(config, os.Stderr) + tracer = logger.NewJSONLogger(config, os.Stderr) case ctx.GlobalBool(DebugFlag.Name): - debugger = vm.NewStructLogger(config) + debugger = logger.NewStructLogger(config) tracer = debugger default: - debugger = vm.NewStructLogger(config) + debugger = logger.NewStructLogger(config) } // Load the test content from the input file src, err := ioutil.ReadFile(ctx.Args().First()) @@ -118,7 +119,7 @@ func stateTestCmd(ctx *cli.Context) error { if ctx.GlobalBool(DebugFlag.Name) { if debugger != nil { fmt.Fprintln(os.Stderr, "#### TRACE ####") - vm.WriteTrace(os.Stderr, debugger.StructLogs()) + logger.WriteTrace(os.Stderr, debugger.StructLogs()) } } } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 80d07eb30a..0b73197422 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -2690,7 +2691,7 @@ func TestDeleteRecreateSlots(t *testing.T) { gspec.MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{ Debug: true, - Tracer: vm.NewJSONLogger(nil, os.Stdout), + Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -2770,7 +2771,7 @@ func TestDeleteRecreateAccount(t *testing.T) { gspec.MustCommit(diskdb) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{ Debug: true, - Tracer: vm.NewJSONLogger(nil, os.Stdout), + Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) diff --git a/core/vm/logger.go b/core/vm/logger.go index 98ff967eed..3af5aec199 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// Copyright 2021 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 @@ -17,87 +17,12 @@ package vm import ( - "encoding/hex" - "fmt" - "io" "math/big" - "strings" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" ) -// Storage represents a contract's storage. -type Storage map[common.Hash]common.Hash - -// Copy duplicates the current storage. -func (s Storage) Copy() Storage { - cpy := make(Storage) - for key, value := range s { - cpy[key] = value - } - return cpy -} - -// LogConfig are the configuration options for structured logger the EVM -type LogConfig struct { - EnableMemory bool // enable memory capture - DisableStack bool // disable stack capture - DisableStorage bool // disable storage capture - EnableReturnData bool // enable return data capture - Debug bool // print output during capture end - Limit int // maximum length of output, but zero means unlimited - // Chain overrides, can be used to execute a trace using future fork rules - Overrides *params.ChainConfig `json:"overrides,omitempty"` -} - -//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go - -// StructLog is emitted to the EVM each cycle and lists information about the current internal state -// prior to the execution of the statement. -type StructLog struct { - Pc uint64 `json:"pc"` - Op OpCode `json:"op"` - Gas uint64 `json:"gas"` - GasCost uint64 `json:"gasCost"` - Memory []byte `json:"memory"` - MemorySize int `json:"memSize"` - Stack []uint256.Int `json:"stack"` - ReturnData []byte `json:"returnData"` - Storage map[common.Hash]common.Hash `json:"-"` - Depth int `json:"depth"` - RefundCounter uint64 `json:"refund"` - Err error `json:"-"` -} - -// overrides for gencodec -type structLogMarshaling struct { - Gas math.HexOrDecimal64 - GasCost math.HexOrDecimal64 - Memory hexutil.Bytes - ReturnData hexutil.Bytes - OpName string `json:"opName"` // adds call to OpName() in MarshalJSON - ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON -} - -// OpName formats the operand name in a human-readable format. -func (s *StructLog) OpName() string { - return s.Op.String() -} - -// ErrorString formats the log's error as a string. -func (s *StructLog) ErrorString() string { - if s.Err != nil { - return s.Err.Error() - } - return "" -} - // EVMLogger is used to collect execution traces from an EVM transaction // execution. CaptureState is called for each step of the VM with the // current VM state. @@ -111,250 +36,3 @@ type EVMLogger interface { CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) } - -// StructLogger is an EVM state logger and implements EVMLogger. -// -// StructLogger can capture state based on the given Log configuration and also keeps -// a track record of modified storage which is used in reporting snapshots of the -// contract their storage. -type StructLogger struct { - cfg LogConfig - env *EVM - - storage map[common.Address]Storage - logs []StructLog - output []byte - err error -} - -// NewStructLogger returns a new logger -func NewStructLogger(cfg *LogConfig) *StructLogger { - logger := &StructLogger{ - storage: make(map[common.Address]Storage), - } - if cfg != nil { - logger.cfg = *cfg - } - return logger -} - -// Reset clears the data held by the logger. -func (l *StructLogger) Reset() { - l.storage = make(map[common.Address]Storage) - l.output = make([]byte, 0) - l.logs = l.logs[:0] - l.err = nil -} - -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - l.env = env -} - -// CaptureState logs a new structured log message and pushes it out to the environment -// -// CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { - memory := scope.Memory - stack := scope.Stack - contract := scope.Contract - // check if already accumulated the specified number of logs - if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { - return - } - // Copy a snapshot of the current memory state to a new buffer - var mem []byte - if l.cfg.EnableMemory { - mem = make([]byte, len(memory.Data())) - copy(mem, memory.Data()) - } - // Copy a snapshot of the current stack state to a new buffer - var stck []uint256.Int - if !l.cfg.DisableStack { - stck = make([]uint256.Int, len(stack.Data())) - for i, item := range stack.Data() { - stck[i] = item - } - } - // Copy a snapshot of the current storage to a new container - var storage Storage - if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) { - // initialise new changed values storage container for this contract - // if not present. - if l.storage[contract.Address()] == nil { - l.storage[contract.Address()] = make(Storage) - } - // capture SLOAD opcodes and record the read entry in the local storage - if op == SLOAD && stack.len() >= 1 { - var ( - address = common.Hash(stack.data[stack.len()-1].Bytes32()) - value = l.env.StateDB.GetState(contract.Address(), address) - ) - l.storage[contract.Address()][address] = value - storage = l.storage[contract.Address()].Copy() - } else if op == SSTORE && stack.len() >= 2 { - // capture SSTORE opcodes and record the written entry in the local storage. - var ( - value = common.Hash(stack.data[stack.len()-2].Bytes32()) - address = common.Hash(stack.data[stack.len()-1].Bytes32()) - ) - l.storage[contract.Address()][address] = value - storage = l.storage[contract.Address()].Copy() - } - } - var rdata []byte - if l.cfg.EnableReturnData { - rdata = make([]byte, len(rData)) - copy(rdata, rData) - } - // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} - l.logs = append(l.logs, log) -} - -// CaptureFault implements the EVMLogger interface to trace an execution fault -// while running an opcode. -func (l *StructLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { -} - -// CaptureEnd is called after the call finishes to finalize the tracing. -func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { - l.output = output - l.err = err - if l.cfg.Debug { - fmt.Printf("0x%x\n", output) - if err != nil { - fmt.Printf(" error: %v\n", err) - } - } -} - -func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} - -// StructLogs returns the captured log entries. -func (l *StructLogger) StructLogs() []StructLog { return l.logs } - -// Error returns the VM error captured by the trace. -func (l *StructLogger) Error() error { return l.err } - -// Output returns the VM return value captured by the trace. -func (l *StructLogger) Output() []byte { return l.output } - -// WriteTrace writes a formatted trace to the given writer -func WriteTrace(writer io.Writer, logs []StructLog) { - for _, log := range logs { - fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost) - if log.Err != nil { - fmt.Fprintf(writer, " ERROR: %v", log.Err) - } - fmt.Fprintln(writer) - - if len(log.Stack) > 0 { - fmt.Fprintln(writer, "Stack:") - for i := len(log.Stack) - 1; i >= 0; i-- { - fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex()) - } - } - if len(log.Memory) > 0 { - fmt.Fprintln(writer, "Memory:") - fmt.Fprint(writer, hex.Dump(log.Memory)) - } - if len(log.Storage) > 0 { - fmt.Fprintln(writer, "Storage:") - for h, item := range log.Storage { - fmt.Fprintf(writer, "%x: %x\n", h, item) - } - } - if len(log.ReturnData) > 0 { - fmt.Fprintln(writer, "ReturnData:") - fmt.Fprint(writer, hex.Dump(log.ReturnData)) - } - fmt.Fprintln(writer) - } -} - -// WriteLogs writes vm logs in a readable format to the given writer -func WriteLogs(writer io.Writer, logs []*types.Log) { - for _, log := range logs { - fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex) - - for i, topic := range log.Topics { - fmt.Fprintf(writer, "%08d %x\n", i, topic) - } - - fmt.Fprint(writer, hex.Dump(log.Data)) - fmt.Fprintln(writer) - } -} - -type mdLogger struct { - out io.Writer - cfg *LogConfig - env *EVM -} - -// NewMarkdownLogger creates a logger which outputs information in a format adapted -// for human readability, and is also a valid markdown table -func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger { - l := &mdLogger{out: writer, cfg: cfg} - if l.cfg == nil { - l.cfg = &LogConfig{} - } - return l -} - -func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.env = env - if !create { - fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", - from.String(), to.String(), - input, gas, value) - } else { - fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", - from.String(), to.String(), - input, gas, value) - } - - fmt.Fprintf(t.out, ` -| Pc | Op | Cost | Stack | RStack | Refund | -|-------|-------------|------|-----------|-----------|---------| -`) -} - -// CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (t *mdLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { - stack := scope.Stack - fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) - - if !t.cfg.DisableStack { - // format stack - var a []string - for _, elem := range stack.data { - a = append(a, elem.Hex()) - } - b := fmt.Sprintf("[%v]", strings.Join(a, ",")) - fmt.Fprintf(t.out, "%10v |", b) - } - fmt.Fprintf(t.out, "%10v |", t.env.StateDB.GetRefund()) - fmt.Fprintln(t.out, "") - if err != nil { - fmt.Fprintf(t.out, "Error: %v\n", err) - } -} - -func (t *mdLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { - fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) -} - -func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) { - fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n", - output, gasUsed, err) -} - -func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index fea7817ff7..97673b4906 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -34,6 +34,7 @@ import ( "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/eth/tracers/logger" "github.com/ethereum/go-ethereum/params" // force-load js tracers to trigger registration @@ -326,7 +327,7 @@ func TestBlockhash(t *testing.T) { } type stepCounter struct { - inner *vm.JSONLogger + inner *logger.JSONLogger steps int } @@ -493,7 +494,7 @@ func BenchmarkSimpleLoop(b *testing.B) { byte(vm.JUMP), } - //tracer := vm.NewJSONLogger(nil, os.Stdout) + //tracer := logger.NewJSONLogger(nil, os.Stdout) //Execute(loopingCode, nil, &Config{ // EVMConfig: vm.Config{ // Debug: true, @@ -536,7 +537,7 @@ func TestEip2929Cases(t *testing.T) { Execute(code, nil, &Config{ EVMConfig: vm.Config{ Debug: true, - Tracer: vm.NewMarkdownLogger(nil, os.Stdout), + Tracer: logger.NewMarkdownLogger(nil, os.Stdout), ExtraEips: []int{2929}, }, }) @@ -686,7 +687,7 @@ func TestColdAccountAccessCost(t *testing.T) { want: 7600, }, } { - tracer := vm.NewStructLogger(nil) + tracer := logger.NewStructLogger(nil) Execute(tc.code, nil, &Config{ EVMConfig: vm.Config{ Debug: true, diff --git a/eth/tracers/api.go b/eth/tracers/api.go index f8d61fa063..5e553c114e 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -36,6 +36,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/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" @@ -165,7 +166,7 @@ func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber // TraceConfig holds extra parameters to trace functions. type TraceConfig struct { - *vm.LogConfig + *logger.Config Tracer *string Timeout *string Reexec *uint64 @@ -174,7 +175,7 @@ type TraceConfig struct { // TraceCallConfig is the config for traceCall API. It holds one more // field to override the state for tracing. type TraceCallConfig struct { - *vm.LogConfig + *logger.Config Tracer *string Timeout *string Reexec *uint64 @@ -183,7 +184,7 @@ type TraceCallConfig struct { // StdTraceConfig holds extra parameters to standard-json trace functions. type StdTraceConfig struct { - vm.LogConfig + logger.Config Reexec *uint64 TxHash common.Hash } @@ -669,11 +670,11 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block } // Retrieve the tracing configurations, or use default values var ( - logConfig vm.LogConfig + logConfig logger.Config txHash common.Hash ) if config != nil { - logConfig = config.LogConfig + logConfig = config.Config txHash = config.TxHash } logConfig.Debug = true @@ -698,7 +699,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block chainConfigCopy := new(params.ChainConfig) *chainConfigCopy = *chainConfig chainConfig = chainConfigCopy - if berlin := config.LogConfig.Overrides.BerlinBlock; berlin != nil { + if berlin := config.Config.Overrides.BerlinBlock; berlin != nil { chainConfig.BerlinBlock = berlin canon = false } @@ -730,7 +731,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block writer = bufio.NewWriter(dump) vmConf = vm.Config{ Debug: true, - Tracer: vm.NewJSONLogger(&logConfig, writer), + Tracer: logger.NewJSONLogger(&logConfig, writer), EnablePreimageRecording: true, } } @@ -847,10 +848,10 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc var traceConfig *TraceConfig if config != nil { traceConfig = &TraceConfig{ - LogConfig: config.LogConfig, - Tracer: config.Tracer, - Timeout: config.Timeout, - Reexec: config.Reexec, + Config: config.Config, + Tracer: config.Tracer, + Timeout: config.Timeout, + Reexec: config.Reexec, } } return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig) @@ -868,7 +869,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex ) switch { case config == nil: - tracer = vm.NewStructLogger(nil) + tracer = logger.NewStructLogger(nil) case config.Tracer != nil: // Define a meaningful timeout of a single transaction trace timeout := defaultTraceTimeout @@ -891,7 +892,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex tracer = t } default: - tracer = vm.NewStructLogger(config.LogConfig) + tracer = logger.NewStructLogger(config.Config) } // Run the transaction with tracing enabled. vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true}) @@ -906,7 +907,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex // Depending on the tracer type, format and return the output. switch tracer := tracer.(type) { - case *vm.StructLogger: + case *logger.StructLogger: // If the result contains a revert reason, return it. returnVal := fmt.Sprintf("%x", result.Return()) if len(result.Revert()) > 0 { diff --git a/core/vm/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go similarity index 81% rename from core/vm/access_list_tracer.go rename to eth/tracers/logger/access_list_tracer.go index 1368e4c99d..181fc47acb 100644 --- a/core/vm/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package logger import ( "math/big" @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" ) // accessList is an accumulator for the set of accounts and storage slots an EVM @@ -137,36 +138,38 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi } } -func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (a *AccessListTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { } // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. -func (a *AccessListTracer) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { stack := scope.Stack - if (op == SLOAD || op == SSTORE) && stack.len() >= 1 { - slot := common.Hash(stack.data[stack.len()-1].Bytes32()) + stackData := stack.Data() + stackLen := len(stackData) + if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 { + slot := common.Hash(stackData[stackLen-1].Bytes32()) a.list.addSlot(scope.Contract.Address(), slot) } - if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.len() >= 1 { - addr := common.Address(stack.data[stack.len()-1].Bytes20()) + if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 { + addr := common.Address(stackData[stackLen-1].Bytes20()) if _, ok := a.excl[addr]; !ok { a.list.addAddress(addr) } } - if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.len() >= 5 { - addr := common.Address(stack.data[stack.len()-2].Bytes20()) + if (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE) && stackLen >= 5 { + addr := common.Address(stackData[stackLen-2].Bytes20()) if _, ok := a.excl[addr]; !ok { a.list.addAddress(addr) } } } -func (*AccessListTracer) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { +func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { } func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {} -func (*AccessListTracer) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {} diff --git a/core/vm/gen_structlog.go b/eth/tracers/logger/gen_structlog.go similarity index 94% rename from core/vm/gen_structlog.go rename to eth/tracers/logger/gen_structlog.go index 365f3b7914..9e71b555cb 100644 --- a/core/vm/gen_structlog.go +++ b/eth/tracers/logger/gen_structlog.go @@ -1,6 +1,6 @@ // Code generated by github.com/fjl/gencodec. DO NOT EDIT. -package vm +package logger import ( "encoding/json" @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/vm" "github.com/holiman/uint256" ) @@ -17,7 +18,7 @@ var _ = (*structLogMarshaling)(nil) func (s StructLog) MarshalJSON() ([]byte, error) { type StructLog struct { Pc uint64 `json:"pc"` - Op OpCode `json:"op"` + Op vm.OpCode `json:"op"` Gas math.HexOrDecimal64 `json:"gas"` GasCost math.HexOrDecimal64 `json:"gasCost"` Memory hexutil.Bytes `json:"memory"` @@ -53,7 +54,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) { func (s *StructLog) UnmarshalJSON(input []byte) error { type StructLog struct { Pc *uint64 `json:"pc"` - Op *OpCode `json:"op"` + Op *vm.OpCode `json:"op"` Gas *math.HexOrDecimal64 `json:"gas"` GasCost *math.HexOrDecimal64 `json:"gasCost"` Memory *hexutil.Bytes `json:"memory"` diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go new file mode 100644 index 0000000000..8461935822 --- /dev/null +++ b/eth/tracers/logger/logger.go @@ -0,0 +1,349 @@ +// Copyright 2015 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 . + +package logger + +import ( + "encoding/hex" + "fmt" + "io" + "math/big" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +// Storage represents a contract's storage. +type Storage map[common.Hash]common.Hash + +// Copy duplicates the current storage. +func (s Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range s { + cpy[key] = value + } + return cpy +} + +// Config are the configuration options for structured logger the EVM +type Config struct { + EnableMemory bool // enable memory capture + DisableStack bool // disable stack capture + DisableStorage bool // disable storage capture + EnableReturnData bool // enable return data capture + Debug bool // print output during capture end + Limit int // maximum length of output, but zero means unlimited + // Chain overrides, can be used to execute a trace using future fork rules + Overrides *params.ChainConfig `json:"overrides,omitempty"` +} + +//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go + +// StructLog is emitted to the EVM each cycle and lists information about the current internal state +// prior to the execution of the statement. +type StructLog struct { + Pc uint64 `json:"pc"` + Op vm.OpCode `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Memory []byte `json:"memory"` + MemorySize int `json:"memSize"` + Stack []uint256.Int `json:"stack"` + ReturnData []byte `json:"returnData"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth int `json:"depth"` + RefundCounter uint64 `json:"refund"` + Err error `json:"-"` +} + +// overrides for gencodec +type structLogMarshaling struct { + Gas math.HexOrDecimal64 + GasCost math.HexOrDecimal64 + Memory hexutil.Bytes + ReturnData hexutil.Bytes + OpName string `json:"opName"` // adds call to OpName() in MarshalJSON + ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON +} + +// OpName formats the operand name in a human-readable format. +func (s *StructLog) OpName() string { + return s.Op.String() +} + +// ErrorString formats the log's error as a string. +func (s *StructLog) ErrorString() string { + if s.Err != nil { + return s.Err.Error() + } + return "" +} + +// StructLogger is an EVM state logger and implements EVMLogger. +// +// StructLogger can capture state based on the given Log configuration and also keeps +// a track record of modified storage which is used in reporting snapshots of the +// contract their storage. +type StructLogger struct { + cfg Config + env *vm.EVM + + storage map[common.Address]Storage + logs []StructLog + output []byte + err error +} + +// NewStructLogger returns a new logger +func NewStructLogger(cfg *Config) *StructLogger { + logger := &StructLogger{ + storage: make(map[common.Address]Storage), + } + if cfg != nil { + logger.cfg = *cfg + } + return logger +} + +// Reset clears the data held by the logger. +func (l *StructLogger) Reset() { + l.storage = make(map[common.Address]Storage) + l.output = make([]byte, 0) + l.logs = l.logs[:0] + l.err = nil +} + +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. +func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + l.env = env +} + +// CaptureState logs a new structured log message and pushes it out to the environment +// +// CaptureState also tracks SLOAD/SSTORE ops to track storage change. +func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + memory := scope.Memory + stack := scope.Stack + contract := scope.Contract + // check if already accumulated the specified number of logs + if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { + return + } + // Copy a snapshot of the current memory state to a new buffer + var mem []byte + if l.cfg.EnableMemory { + mem = make([]byte, len(memory.Data())) + copy(mem, memory.Data()) + } + // Copy a snapshot of the current stack state to a new buffer + var stck []uint256.Int + if !l.cfg.DisableStack { + stck = make([]uint256.Int, len(stack.Data())) + for i, item := range stack.Data() { + stck[i] = item + } + } + stackData := stack.Data() + stackLen := len(stackData) + // Copy a snapshot of the current storage to a new container + var storage Storage + if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) { + // initialise new changed values storage container for this contract + // if not present. + if l.storage[contract.Address()] == nil { + l.storage[contract.Address()] = make(Storage) + } + // capture SLOAD opcodes and record the read entry in the local storage + if op == vm.SLOAD && stackLen >= 1 { + var ( + address = common.Hash(stackData[stackLen-1].Bytes32()) + value = l.env.StateDB.GetState(contract.Address(), address) + ) + l.storage[contract.Address()][address] = value + storage = l.storage[contract.Address()].Copy() + } else if op == vm.SSTORE && stackLen >= 2 { + // capture SSTORE opcodes and record the written entry in the local storage. + var ( + value = common.Hash(stackData[stackLen-2].Bytes32()) + address = common.Hash(stackData[stackLen-1].Bytes32()) + ) + l.storage[contract.Address()][address] = value + storage = l.storage[contract.Address()].Copy() + } + } + var rdata []byte + if l.cfg.EnableReturnData { + rdata = make([]byte, len(rData)) + copy(rdata, rData) + } + // create a new snapshot of the EVM. + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} + l.logs = append(l.logs, log) +} + +// CaptureFault implements the EVMLogger interface to trace an execution fault +// while running an opcode. +func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { + l.output = output + l.err = err + if l.cfg.Debug { + fmt.Printf("0x%x\n", output) + if err != nil { + fmt.Printf(" error: %v\n", err) + } + } +} + +func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +} + +func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} + +// StructLogs returns the captured log entries. +func (l *StructLogger) StructLogs() []StructLog { return l.logs } + +// Error returns the VM error captured by the trace. +func (l *StructLogger) Error() error { return l.err } + +// Output returns the VM return value captured by the trace. +func (l *StructLogger) Output() []byte { return l.output } + +// WriteTrace writes a formatted trace to the given writer +func WriteTrace(writer io.Writer, logs []StructLog) { + for _, log := range logs { + fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost) + if log.Err != nil { + fmt.Fprintf(writer, " ERROR: %v", log.Err) + } + fmt.Fprintln(writer) + + if len(log.Stack) > 0 { + fmt.Fprintln(writer, "Stack:") + for i := len(log.Stack) - 1; i >= 0; i-- { + fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex()) + } + } + if len(log.Memory) > 0 { + fmt.Fprintln(writer, "Memory:") + fmt.Fprint(writer, hex.Dump(log.Memory)) + } + if len(log.Storage) > 0 { + fmt.Fprintln(writer, "Storage:") + for h, item := range log.Storage { + fmt.Fprintf(writer, "%x: %x\n", h, item) + } + } + if len(log.ReturnData) > 0 { + fmt.Fprintln(writer, "ReturnData:") + fmt.Fprint(writer, hex.Dump(log.ReturnData)) + } + fmt.Fprintln(writer) + } +} + +// WriteLogs writes vm logs in a readable format to the given writer +func WriteLogs(writer io.Writer, logs []*types.Log) { + for _, log := range logs { + fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex) + + for i, topic := range log.Topics { + fmt.Fprintf(writer, "%08d %x\n", i, topic) + } + + fmt.Fprint(writer, hex.Dump(log.Data)) + fmt.Fprintln(writer) + } +} + +type mdLogger struct { + out io.Writer + cfg *Config + env *vm.EVM +} + +// NewMarkdownLogger creates a logger which outputs information in a format adapted +// for human readability, and is also a valid markdown table +func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { + l := &mdLogger{out: writer, cfg: cfg} + if l.cfg == nil { + l.cfg = &Config{} + } + return l +} + +func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + t.env = env + if !create { + fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", + from.String(), to.String(), + input, gas, value) + } else { + fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n", + from.String(), to.String(), + input, gas, value) + } + + fmt.Fprintf(t.out, ` +| Pc | Op | Cost | Stack | RStack | Refund | +|-------|-------------|------|-----------|-----------|---------| +`) +} + +// CaptureState also tracks SLOAD/SSTORE ops to track storage change. +func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + stack := scope.Stack + fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) + + if !t.cfg.DisableStack { + // format stack + var a []string + for _, elem := range stack.Data() { + a = append(a, elem.Hex()) + } + b := fmt.Sprintf("[%v]", strings.Join(a, ",")) + fmt.Fprintf(t.out, "%10v |", b) + } + fmt.Fprintf(t.out, "%10v |", t.env.StateDB.GetRefund()) + fmt.Fprintln(t.out, "") + if err != nil { + fmt.Fprintf(t.out, "Error: %v\n", err) + } +} + +func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { + fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) +} + +func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) { + fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n", + output, gasUsed, err) +} + +func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +} + +func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} diff --git a/core/vm/logger_json.go b/eth/tracers/logger/logger_json.go similarity index 76% rename from core/vm/logger_json.go rename to eth/tracers/logger/logger_json.go index 364ce738a0..14d90c2dba 100644 --- a/core/vm/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package logger import ( "encoding/json" @@ -24,32 +24,33 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/vm" ) type JSONLogger struct { encoder *json.Encoder - cfg *LogConfig - env *EVM + cfg *Config + env *vm.EVM } // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects // into the provided stream. -func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { +func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { l := &JSONLogger{encoder: json.NewEncoder(writer), cfg: cfg} if l.cfg == nil { - l.cfg = &LogConfig{} + l.cfg = &Config{} } return l } -func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { l.env = env } -func (l *JSONLogger) CaptureFault(uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {} +func (l *JSONLogger) CaptureFault(uint64, vm.OpCode, uint64, uint64, *vm.ScopeContext, int, error) {} // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { +func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { memory := scope.Memory stack := scope.Stack @@ -67,7 +68,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope log.Memory = memory.Data() } if !l.cfg.DisableStack { - log.Stack = stack.data + log.Stack = stack.Data() } if l.cfg.EnableReturnData { log.ReturnData = rData @@ -90,7 +91,7 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg}) } -func (l *JSONLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} diff --git a/core/vm/logger_test.go b/eth/tracers/logger/logger_test.go similarity index 75% rename from core/vm/logger_test.go rename to eth/tracers/logger/logger_test.go index 7726c90bd5..205ee31120 100644 --- a/core/vm/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package vm +package logger import ( "math/big" @@ -22,8 +22,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" ) type dummyContractRef struct { @@ -46,24 +46,23 @@ type dummyStatedb struct { state.StateDB } -func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetRefund() uint64 { return 1337 } +func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} } +func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} func TestStoreCapture(t *testing.T) { var ( - env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{}) logger = NewStructLogger(nil) - contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) - scope = &ScopeContext{ - Memory: NewMemory(), - Stack: newstack(), - Contract: contract, - } + env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: logger}) + contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000) ) - scope.Stack.push(uint256.NewInt(1)) - scope.Stack.push(new(uint256.Int)) + contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil) - logger.CaptureState(0, SSTORE, 0, 0, scope, nil, 0, nil) + _, err := env.Interpreter().Run(contract, []byte{}, false) + if err != nil { + t.Fatal(err) + } if len(logger.storage[contract.Address()]) == 0 { t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()])) diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 915cab10d4..ce9289dd75 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/tests" ) @@ -95,7 +96,7 @@ func BenchmarkTransactionTrace(b *testing.B) { } _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false) // Create the tracer, the EVM environment and run it - tracer := vm.NewStructLogger(&vm.LogConfig{ + tracer := logger.NewStructLogger(&logger.Config{ Debug: false, //DisableStorage: true, //EnableMemory: false, diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index aa8e8767c3..f1b4a01991 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -40,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" @@ -1138,7 +1139,7 @@ type StructLogRes struct { } // FormatLogs formats EVM returned structured logs for json output -func FormatLogs(logs []vm.StructLog) []StructLogRes { +func FormatLogs(logs []logger.StructLog) []StructLogRes { formatted := make([]StructLogRes, len(logs)) for index, trace := range logs { formatted[index] = StructLogRes{ @@ -1425,9 +1426,9 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH precompiles := vm.ActivePrecompiles(b.ChainConfig().Rules(header.Number)) // Create an initial tracer - prevTracer := vm.NewAccessListTracer(nil, args.from(), to, precompiles) + prevTracer := logger.NewAccessListTracer(nil, args.from(), to, precompiles) if args.AccessList != nil { - prevTracer = vm.NewAccessListTracer(*args.AccessList, args.from(), to, precompiles) + prevTracer = logger.NewAccessListTracer(*args.AccessList, args.from(), to, precompiles) } for { // Retrieve the current access list to expand @@ -1453,7 +1454,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH } // Apply the transaction with the access list tracer - tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles) + tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true} vmenv, _, err := b.GetEVM(ctx, msg, statedb, header, &config) if err != nil { diff --git a/tests/state_test.go b/tests/state_test.go index 9554e75630..78ecda0409 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/logger" ) func TestState(t *testing.T) { @@ -115,7 +116,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { } buf := new(bytes.Buffer) w := bufio.NewWriter(buf) - tracer := vm.NewJSONLogger(&vm.LogConfig{}, w) + tracer := logger.NewJSONLogger(&logger.Config{}, w) config.Debug, config.Tracer = true, tracer err2 := test(config) if !reflect.DeepEqual(err, err2) {