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
|
package t8ntool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -132,7 +130,7 @@ type rejectedTx struct {
|
||||||
// Apply applies a set of transactions to a pre-state
|
// Apply applies a set of transactions to a pre-state
|
||||||
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
txIt txIterator, miningReward int64,
|
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
|
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
|
||||||
// required blockhashes
|
// required blockhashes
|
||||||
var hashError error
|
var hashError error
|
||||||
|
|
@ -241,10 +239,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
continue
|
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
|
// TODO (rjl493456442) it's a bit weird to reset the tracer in the
|
||||||
// middle of block execution, please improve it somehow.
|
// middle of block execution, please improve it somehow.
|
||||||
if tracer != nil {
|
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)
|
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||||
gaspool.SetGas(prevGas)
|
gaspool.SetGas(prevGas)
|
||||||
if tracer != nil {
|
if tracer != nil && tracer.OnTxEnd != nil {
|
||||||
if tracer.OnTxEnd != nil {
|
tracer.OnTxEnd(nil, err)
|
||||||
tracer.OnTxEnd(nil, err)
|
|
||||||
}
|
|
||||||
if err := writeTraceResult(tracer, traceOutput); err != nil {
|
|
||||||
log.Warn("Error writing tracer output", "err", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -316,13 +305,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
//receipt.BlockNumber
|
//receipt.BlockNumber
|
||||||
receipt.TransactionIndex = uint(txIndex)
|
receipt.TransactionIndex = uint(txIndex)
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
if tracer != nil {
|
if tracer != nil && tracer.Hooks.OnTxEnd != nil {
|
||||||
if tracer.Hooks.OnTxEnd != nil {
|
tracer.Hooks.OnTxEnd(receipt, nil)
|
||||||
tracer.Hooks.OnTxEnd(receipt, nil)
|
|
||||||
}
|
|
||||||
if err = writeTraceResult(tracer, traceOutput); err != nil {
|
|
||||||
log.Warn("Error writing tracer output", "err", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -468,16 +452,3 @@ func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime
|
||||||
}
|
}
|
||||||
return ethash.CalcDifficulty(config, currentTime, parent)
|
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"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -29,7 +28,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
|
@ -82,58 +80,10 @@ type input struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Transition(ctx *cli.Context) error {
|
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)
|
baseDir, err := createBasedir(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
|
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
|
// We need to load three things: alloc, env and transactions. May be either in
|
||||||
// stdin input or in files.
|
// stdin input or in files.
|
||||||
// Check if anything needs to be read from stdin
|
// Check if anything needs to be read from stdin
|
||||||
|
|
@ -179,6 +129,7 @@ func Transition(ctx *cli.Context) error {
|
||||||
chainConfig = cConf
|
chainConfig = cConf
|
||||||
vmConfig.ExtraEips = extraEips
|
vmConfig.ExtraEips = extraEips
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the chain id
|
// Set the chain id
|
||||||
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
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 {
|
if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil {
|
||||||
return err
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue