mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
cmd/evm: refactor file-output-handling for evm t8n
This commit is contained in:
parent
75f847390f
commit
fa923e38eb
3 changed files with 193 additions and 85 deletions
|
|
@ -17,9 +17,7 @@
|
|||
package t8ntool
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -132,7 +130,7 @@ type rejectedTx struct {
|
|||
// Apply applies a set of transactions to a pre-state
|
||||
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
txIt txIterator, miningReward int64,
|
||||
getTracerFn func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) {
|
||||
tracer *tracers.Tracer) (*state.StateDB, *ExecutionResult, []byte, error) {
|
||||
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
|
||||
// required blockhashes
|
||||
var hashError error
|
||||
|
|
@ -241,10 +239,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
continue
|
||||
}
|
||||
}
|
||||
tracer, traceOutput, err := getTracerFn(txIndex, tx.Hash(), chainConfig)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// TODO (rjl493456442) it's a bit weird to reset the tracer in the
|
||||
// middle of block execution, please improve it somehow.
|
||||
if tracer != nil {
|
||||
|
|
@ -266,13 +260,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||
gaspool.SetGas(prevGas)
|
||||
if tracer != nil {
|
||||
if tracer.OnTxEnd != nil {
|
||||
tracer.OnTxEnd(nil, err)
|
||||
}
|
||||
if err := writeTraceResult(tracer, traceOutput); err != nil {
|
||||
log.Warn("Error writing tracer output", "err", err)
|
||||
}
|
||||
if tracer != nil && tracer.OnTxEnd != nil {
|
||||
tracer.OnTxEnd(nil, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
|
@ -316,13 +305,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
//receipt.BlockNumber
|
||||
receipt.TransactionIndex = uint(txIndex)
|
||||
receipts = append(receipts, receipt)
|
||||
if tracer != nil {
|
||||
if tracer.Hooks.OnTxEnd != nil {
|
||||
tracer.Hooks.OnTxEnd(receipt, nil)
|
||||
}
|
||||
if err = writeTraceResult(tracer, traceOutput); err != nil {
|
||||
log.Warn("Error writing tracer output", "err", err)
|
||||
}
|
||||
if tracer != nil && tracer.Hooks.OnTxEnd != nil {
|
||||
tracer.Hooks.OnTxEnd(receipt, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -468,16 +452,3 @@ func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime
|
|||
}
|
||||
return ethash.CalcDifficulty(config, currentTime, parent)
|
||||
}
|
||||
|
||||
func writeTraceResult(tracer *tracers.Tracer, f io.WriteCloser) error {
|
||||
defer f.Close()
|
||||
result, err := tracer.GetResult()
|
||||
if err != nil || result == nil {
|
||||
return err
|
||||
}
|
||||
err = json.NewEncoder(f).Encode(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
171
cmd/evm/internal/t8ntool/file_tracer.go
Normal file
171
cmd/evm/internal/t8ntool/file_tracer.go
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
// Copyright 2024 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package t8ntool
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// fileWritingTracer is a tracer which wraps either a different tracer,
|
||||
// or a logger. On tx start, it creates a new file to direct output to,
|
||||
// and on tx end it closes the file.
|
||||
type fileWritingTracer struct {
|
||||
txIndex int
|
||||
inner *tracing.Hooks
|
||||
destination io.WriteCloser
|
||||
baseDir string
|
||||
|
||||
// for json-tracing
|
||||
logConfig *logger.Config
|
||||
callFrames bool
|
||||
|
||||
// for custom tracing
|
||||
tracerName string
|
||||
tracerConf json.RawMessage
|
||||
chainConfig *params.ChainConfig
|
||||
getResult func() (json.RawMessage, error)
|
||||
}
|
||||
|
||||
func newFileWritingTracer(baseDir string, logConfig *logger.Config, callFrames bool) *fileWritingTracer {
|
||||
return &fileWritingTracer{
|
||||
baseDir: baseDir,
|
||||
logConfig: logConfig,
|
||||
callFrames: callFrames,
|
||||
}
|
||||
}
|
||||
|
||||
func newFileWritingCustomTracer(baseDir, tracerName string, traceConfig json.RawMessage, chainConfig *params.ChainConfig) *fileWritingTracer {
|
||||
return &fileWritingTracer{
|
||||
baseDir: baseDir,
|
||||
tracerName: tracerName,
|
||||
chainConfig: chainConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// OnTxStartJSONL is the OnTxStart-handler for jsonl logger.
|
||||
func (l *fileWritingTracer) OnTxStartJSONL(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||
// Open a new file,
|
||||
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.jsonl", l.txIndex, tx.Hash().String()))
|
||||
traceFile, err := os.Create(fname)
|
||||
if err != nil {
|
||||
log.Warn("Failed creating trace-file", "err", err)
|
||||
}
|
||||
log.Debug("Created tracing-file", "path", fname)
|
||||
l.destination = traceFile
|
||||
if !l.callFrames {
|
||||
l.inner = logger.NewJSONLogger(l.logConfig, traceFile)
|
||||
} else {
|
||||
l.inner = logger.NewJSONLoggerWithCallFrames(l.logConfig, traceFile)
|
||||
}
|
||||
if l.inner.OnTxStart != nil {
|
||||
l.inner.OnTxStart(env, tx, from)
|
||||
}
|
||||
}
|
||||
|
||||
// OnTxStartJSONL is the OnTxStart-handler for custom tracer.
|
||||
func (l *fileWritingTracer) OnTxStartJSON(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||
// Open a new file,
|
||||
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.json", l.txIndex, tx.Hash().String()))
|
||||
traceFile, err := os.Create(fname)
|
||||
if err != nil {
|
||||
log.Warn("Failed creating trace-file", "err", err)
|
||||
}
|
||||
fmt.Printf("Created tracing-file %v\n", fname)
|
||||
log.Info("Created tracing-file", "path", fname)
|
||||
l.destination = traceFile
|
||||
inner, err := tracers.DefaultDirectory.New(l.tracerName, nil, l.tracerConf, l.chainConfig)
|
||||
if err != nil {
|
||||
log.Warn("Failed instantiating tracer", "err", err)
|
||||
return
|
||||
}
|
||||
l.getResult = inner.GetResult
|
||||
l.inner = inner.Hooks
|
||||
if l.inner.OnTxStart != nil {
|
||||
l.inner.OnTxStart(env, tx, from)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||
if l.inner.OnTxEnd != nil {
|
||||
l.inner.OnTxEnd(receipt, err)
|
||||
}
|
||||
if l.getResult != nil {
|
||||
if result, err := l.getResult(); result != nil {
|
||||
json.NewEncoder(l.destination).Encode(result)
|
||||
} else {
|
||||
log.Warn("Error obtaining tracer result", "err", err)
|
||||
}
|
||||
}
|
||||
if l.destination != nil { // Close old file
|
||||
l.destination.Close()
|
||||
l.destination = nil
|
||||
}
|
||||
l.txIndex++
|
||||
}
|
||||
|
||||
func (l *fileWritingTracer) Tracer() *tracers.Tracer {
|
||||
hooks := &tracing.Hooks{
|
||||
OnTxStart: l.OnTxStartJSONL,
|
||||
OnTxEnd: l.OnTxEnd,
|
||||
OnSystemCallStart: func() {
|
||||
if l.inner.OnSystemCallStart != nil {
|
||||
l.inner.OnSystemCallStart()
|
||||
}
|
||||
},
|
||||
OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
if l.inner.OnEnter != nil {
|
||||
l.inner.OnEnter(depth, typ, from, to, input, gas, value)
|
||||
}
|
||||
},
|
||||
OnExit: func(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
if l.inner.OnExit != nil {
|
||||
l.inner.OnExit(depth, output, gasUsed, err, reverted)
|
||||
}
|
||||
},
|
||||
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||
if l.inner.OnOpcode != nil {
|
||||
l.inner.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
|
||||
}
|
||||
},
|
||||
OnFault: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||
if l.inner.OnFault != nil {
|
||||
l.inner.OnFault(pc, op, gas, cost, scope, depth, err)
|
||||
}
|
||||
},
|
||||
}
|
||||
if len(l.tracerName) > 0 { // a custom tracer
|
||||
hooks.OnTxStart = l.OnTxStartJSON
|
||||
}
|
||||
return &tracers.Tracer{
|
||||
Hooks: hooks,
|
||||
GetResult: func() (json.RawMessage, error) { return nil, nil },
|
||||
Stop: func(err error) {},
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -29,7 +28,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
|
|
@ -82,58 +80,10 @@ type input struct {
|
|||
}
|
||||
|
||||
func Transition(ctx *cli.Context) error {
|
||||
var getTracer = func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
baseDir, err := createBasedir(ctx)
|
||||
if err != nil {
|
||||
return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
|
||||
}
|
||||
|
||||
if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
|
||||
// Configure the EVM logger
|
||||
logConfig := &logger.Config{
|
||||
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
||||
EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name),
|
||||
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
||||
}
|
||||
getTracer = func(txIndex int, txHash common.Hash, _ *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) {
|
||||
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
|
||||
if err != nil {
|
||||
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
|
||||
}
|
||||
var l *tracing.Hooks
|
||||
if ctx.Bool(TraceEnableCallFramesFlag.Name) {
|
||||
l = logger.NewJSONLoggerWithCallFrames(logConfig, traceFile)
|
||||
} else {
|
||||
l = logger.NewJSONLogger(logConfig, traceFile)
|
||||
}
|
||||
tracer := &tracers.Tracer{
|
||||
Hooks: l,
|
||||
// jsonLogger streams out result to file.
|
||||
GetResult: func() (json.RawMessage, error) { return nil, nil },
|
||||
Stop: func(err error) {},
|
||||
}
|
||||
return tracer, traceFile, nil
|
||||
}
|
||||
} else if ctx.IsSet(TraceTracerFlag.Name) {
|
||||
var config json.RawMessage
|
||||
if ctx.IsSet(TraceTracerConfigFlag.Name) {
|
||||
config = []byte(ctx.String(TraceTracerConfigFlag.Name))
|
||||
}
|
||||
getTracer = func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) {
|
||||
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String())))
|
||||
if err != nil {
|
||||
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
|
||||
}
|
||||
tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config, chainConfig)
|
||||
if err != nil {
|
||||
return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
|
||||
}
|
||||
return tracer, traceFile, nil
|
||||
}
|
||||
}
|
||||
// We need to load three things: alloc, env and transactions. May be either in
|
||||
// stdin input or in files.
|
||||
// Check if anything needs to be read from stdin
|
||||
|
|
@ -179,6 +129,7 @@ func Transition(ctx *cli.Context) error {
|
|||
chainConfig = cConf
|
||||
vmConfig.ExtraEips = extraEips
|
||||
}
|
||||
|
||||
// Set the chain id
|
||||
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
||||
|
||||
|
|
@ -197,8 +148,23 @@ func Transition(ctx *cli.Context) error {
|
|||
if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Configure tracer
|
||||
var tracer *tracers.Tracer
|
||||
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
|
||||
config := json.RawMessage(ctx.String(TraceTracerConfigFlag.Name))
|
||||
tracer = newFileWritingCustomTracer(baseDir, ctx.String(TraceTracerFlag.Name), config, chainConfig).Tracer()
|
||||
} else if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
|
||||
logConfig := &logger.Config{
|
||||
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
||||
EnableMemory: ctx.Bool(TraceEnableMemoryFlag.Name),
|
||||
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
||||
}
|
||||
tracer = newFileWritingTracer(baseDir, logConfig, ctx.Bool(TraceEnableCallFramesFlag.Name)).Tracer()
|
||||
}
|
||||
|
||||
// Run the test and aggregate the result
|
||||
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name), getTracer)
|
||||
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name), tracer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue