cmd/evm: refactor handling output-files for `t8n` (#30854)
As part of trying to make the inputs and outputs of the evm subcommands more streamlined and aligned, this PR modifies how `evm t8n` manages output-files. Previously, we do a kind of wonky thing where between each transaction, we invoke a `getTracer` closure. In that closure, we create a new output-file, a tracer, and then make the tracer stream output to the file. We also fiddle a bit to ensure that the file becomes properly closed. It is a kind of hacky solution we have in place. This PR changes it, so that from the execution-pipeline point of view, we have just a regular tracer. No fiddling with re-setting it or closing files. That particular tracer, however, is a bit special: it takes care of creating new files per transaction (in the tx-start-hook) and closing (on tx-end-hook). Also instantiating the right type of underlying tracer, which can be a json-logger or a custom tracer. --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
parent
e25cedf16d
commit
2bf4a8ff73
|
@ -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"
|
||||||
|
@ -35,7 +33,6 @@ import (
|
||||||
"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/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
@ -130,9 +127,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) (*state.StateDB, *ExecutionResult, []byte, error) {
|
||||||
txIt txIterator, miningReward int64,
|
|
||||||
getTracerFn func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error)) (*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,23 +236,13 @@ 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
|
|
||||||
// middle of block execution, please improve it somehow.
|
|
||||||
if tracer != nil {
|
|
||||||
evm.SetTracer(tracer.Hooks)
|
|
||||||
}
|
|
||||||
statedb.SetTxContext(tx.Hash(), txIndex)
|
statedb.SetTxContext(tx.Hash(), txIndex)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
snapshot = statedb.Snapshot()
|
snapshot = statedb.Snapshot()
|
||||||
prevGas = gaspool.Gas()
|
prevGas = gaspool.Gas()
|
||||||
)
|
)
|
||||||
if tracer != nil && tracer.OnTxStart != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
|
||||||
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
}
|
}
|
||||||
// (ret []byte, usedGas uint64, failed bool, err error)
|
// (ret []byte, usedGas uint64, failed bool, err error)
|
||||||
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
|
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
|
||||||
|
@ -266,13 +251,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 evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil {
|
||||||
if tracer.OnTxEnd != nil {
|
evm.Config.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 +296,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 evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil {
|
||||||
if tracer.Hooks.OnTxEnd != nil {
|
evm.Config.Tracer.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 +443,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
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
// 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 (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"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/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fileWritingTracer wraps either a tracer or a logger. On tx start,
|
||||||
|
// it instantiates a tracer/logger, creates a new file to direct output to,
|
||||||
|
// and on tx end it closes the file.
|
||||||
|
type fileWritingTracer struct {
|
||||||
|
txIndex int // transaction counter
|
||||||
|
inner *tracing.Hooks // inner hooks
|
||||||
|
destination io.WriteCloser // the currently open file (if any)
|
||||||
|
baseDir string // baseDir to write output-files to
|
||||||
|
suffix string // suffix is the suffix to use when creating files
|
||||||
|
|
||||||
|
// for custom tracing
|
||||||
|
getResult func() (json.RawMessage, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
|
||||||
|
if l.destination != nil {
|
||||||
|
return l.destination.Write(p)
|
||||||
|
}
|
||||||
|
log.Warn("Tracer wrote to non-existing output")
|
||||||
|
// It is tempting to return an error here, however, the json encoder
|
||||||
|
// will no retry writing to an io.Writer once it has returned an error once.
|
||||||
|
// Therefore, we must squash the error.
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFileWriter creates a set of hooks which wraps inner hooks (typically a logger),
|
||||||
|
// and writes the output to a file, one file per transaction.
|
||||||
|
func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *tracing.Hooks {
|
||||||
|
t := &fileWritingTracer{
|
||||||
|
baseDir: baseDir,
|
||||||
|
suffix: "jsonl",
|
||||||
|
}
|
||||||
|
t.inner = innerFn(t) // instantiate the inner tracer
|
||||||
|
return t.hooks()
|
||||||
|
}
|
||||||
|
|
||||||
|
// newResultWriter creates a set of hooks wraps and invokes an underlying tracer,
|
||||||
|
// and writes the result (getResult-output) to file, one per transaction.
|
||||||
|
func newResultWriter(baseDir string, tracer *tracers.Tracer) *tracing.Hooks {
|
||||||
|
t := &fileWritingTracer{
|
||||||
|
baseDir: baseDir,
|
||||||
|
getResult: tracer.GetResult,
|
||||||
|
inner: tracer.Hooks,
|
||||||
|
suffix: "json",
|
||||||
|
}
|
||||||
|
return t.hooks()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnTxStart creates a new output-file specific for this transaction, and invokes
|
||||||
|
// the inner OnTxStart handler.
|
||||||
|
func (l *fileWritingTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
|
// Open a new file, or print a warning log if it's failed
|
||||||
|
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.%v", l.txIndex, tx.Hash().String(), l.suffix))
|
||||||
|
traceFile, err := os.Create(fname)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed creating trace-file", "err", err)
|
||||||
|
} else {
|
||||||
|
log.Info("Created tracing-file", "path", fname)
|
||||||
|
l.destination = traceFile
|
||||||
|
}
|
||||||
|
if l.inner != nil && l.inner.OnTxStart != nil {
|
||||||
|
l.inner.OnTxStart(env, tx, from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnTxEnd writes result (if getResult exist), closes any currently open output-file,
|
||||||
|
// and invokes the inner OnTxEnd handler.
|
||||||
|
func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
|
if l.inner != nil && l.inner.OnTxEnd != nil {
|
||||||
|
l.inner.OnTxEnd(receipt, err)
|
||||||
|
}
|
||||||
|
if l.getResult != nil && l.destination != nil {
|
||||||
|
if result, err := l.getResult(); result != nil {
|
||||||
|
json.NewEncoder(l.destination).Encode(result)
|
||||||
|
} else {
|
||||||
|
log.Warn("Error obtaining tracer result", "err", err)
|
||||||
|
}
|
||||||
|
l.destination.Close()
|
||||||
|
l.destination = nil
|
||||||
|
}
|
||||||
|
l.txIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *fileWritingTracer) hooks() *tracing.Hooks {
|
||||||
|
return &tracing.Hooks{
|
||||||
|
OnTxStart: l.OnTxStart,
|
||||||
|
OnTxEnd: l.OnTxEnd,
|
||||||
|
OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
if l.inner != nil && 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 != nil && 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 != nil && 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 != nil && l.inner.OnFault != nil {
|
||||||
|
l.inner.OnFault(pc, op, gas, cost, scope, depth, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OnSystemCallStart: func() {
|
||||||
|
if l.inner != nil && l.inner.OnSystemCallStart != nil {
|
||||||
|
l.inner.OnSystemCallStart()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OnSystemCallEnd: func() {
|
||||||
|
if l.inner != nil && l.inner.OnSystemCallEnd != nil {
|
||||||
|
l.inner.OnSystemCallEnd()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,12 +64,10 @@ func (r *result) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Transaction(ctx *cli.Context) error {
|
func Transaction(ctx *cli.Context) error {
|
||||||
var (
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
// We need to load the transactions. May be either in stdin input or in files.
|
// We need to load the transactions. May be either in stdin input or in files.
|
||||||
// Check if anything needs to be read from stdin
|
// Check if anything needs to be read from stdin
|
||||||
var (
|
var (
|
||||||
|
err error
|
||||||
txStr = ctx.String(InputTxsFlag.Name)
|
txStr = ctx.String(InputTxsFlag.Name)
|
||||||
inputData = &input{}
|
inputData = &input{}
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
|
@ -82,6 +80,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
// Set the chain id
|
// Set the chain id
|
||||||
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
|
||||||
|
|
||||||
var body hexutil.Bytes
|
var body hexutil.Bytes
|
||||||
if txStr == stdinSelector {
|
if txStr == stdinSelector {
|
||||||
decoder := json.NewDecoder(os.Stdin)
|
decoder := json.NewDecoder(os.Stdin)
|
||||||
|
@ -107,6 +106,7 @@ func Transaction(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signer := types.MakeSigner(chainConfig, new(big.Int), 0)
|
signer := types.MakeSigner(chainConfig, new(big.Int), 0)
|
||||||
|
|
||||||
// We now have the transactions in 'body', which is supposed to be an
|
// We now have the transactions in 'body', which is supposed to be an
|
||||||
// rlp list of transactions
|
// rlp list of transactions
|
||||||
it, err := rlp.NewListIterator([]byte(body))
|
it, err := rlp.NewListIterator([]byte(body))
|
||||||
|
|
|
@ -82,58 +82,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 +131,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 +150,34 @@ 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
|
||||||
|
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
|
||||||
|
config := json.RawMessage(ctx.String(TraceTracerConfigFlag.Name))
|
||||||
|
tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
|
||||||
|
nil, config, chainConfig)
|
||||||
|
if err != nil {
|
||||||
|
return NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %v", err))
|
||||||
|
}
|
||||||
|
vmConfig.Tracer = newResultWriter(baseDir, 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),
|
||||||
|
}
|
||||||
|
if ctx.Bool(TraceEnableCallFramesFlag.Name) {
|
||||||
|
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||||
|
return logger.NewJSONLoggerWithCallFrames(logConfig, out)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||||
|
return logger.NewJSONLogger(logConfig, out)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
// 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))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,8 @@ var (
|
||||||
}
|
}
|
||||||
TraceFormatFlag = &cli.StringFlag{
|
TraceFormatFlag = &cli.StringFlag{
|
||||||
Name: "trace.format",
|
Name: "trace.format",
|
||||||
Usage: "Trace output format to use (struct|json)",
|
Usage: "Trace output format to use (json|struct|md)",
|
||||||
Value: "struct",
|
Value: "json",
|
||||||
Category: traceCategory,
|
Category: traceCategory,
|
||||||
}
|
}
|
||||||
TraceDisableMemoryFlag = &cli.BoolFlag{
|
TraceDisableMemoryFlag = &cli.BoolFlag{
|
||||||
|
|
|
@ -606,18 +606,18 @@ func TestEvmRun(t *testing.T) {
|
||||||
wantStdout: "./testdata/evmrun/1.out.1.txt",
|
wantStdout: "./testdata/evmrun/1.out.1.txt",
|
||||||
wantStderr: "./testdata/evmrun/1.out.2.txt",
|
wantStderr: "./testdata/evmrun/1.out.2.txt",
|
||||||
},
|
},
|
||||||
{ // default tracing (struct)
|
{ // Struct tracing
|
||||||
input: []string{"run", "--trace", "0x6040"},
|
input: []string{"run", "--trace", "--trace.format=struct", "0x6040"},
|
||||||
wantStdout: "./testdata/evmrun/2.out.1.txt",
|
wantStdout: "./testdata/evmrun/2.out.1.txt",
|
||||||
wantStderr: "./testdata/evmrun/2.out.2.txt",
|
wantStderr: "./testdata/evmrun/2.out.2.txt",
|
||||||
},
|
},
|
||||||
{ // default tracing (struct), plus alloc-dump
|
{ // struct-tracing, plus alloc-dump
|
||||||
input: []string{"run", "--trace", "--dump", "0x6040"},
|
input: []string{"run", "--trace", "--trace.format=struct", "--dump", "0x6040"},
|
||||||
wantStdout: "./testdata/evmrun/3.out.1.txt",
|
wantStdout: "./testdata/evmrun/3.out.1.txt",
|
||||||
//wantStderr: "./testdata/evmrun/3.out.2.txt",
|
//wantStderr: "./testdata/evmrun/3.out.2.txt",
|
||||||
},
|
},
|
||||||
{ // json-tracing, plus alloc-dump
|
{ // json-tracing (default), plus alloc-dump
|
||||||
input: []string{"run", "--trace", "--trace.format=json", "--dump", "0x6040"},
|
input: []string{"run", "--trace", "--dump", "0x6040"},
|
||||||
wantStdout: "./testdata/evmrun/4.out.1.txt",
|
wantStdout: "./testdata/evmrun/4.out.1.txt",
|
||||||
//wantStderr: "./testdata/evmrun/4.out.2.txt",
|
//wantStderr: "./testdata/evmrun/4.out.2.txt",
|
||||||
},
|
},
|
||||||
|
@ -698,7 +698,10 @@ func TestEVMTracing(t *testing.T) {
|
||||||
"--input.env=./testdata/31/env.json", "--state.fork=Cancun",
|
"--input.env=./testdata/31/env.json", "--state.fork=Cancun",
|
||||||
"--trace",
|
"--trace",
|
||||||
},
|
},
|
||||||
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"},
|
//expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"},
|
||||||
|
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl",
|
||||||
|
"trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.jsonl",
|
||||||
|
"trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.jsonl"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
base: "./testdata/31",
|
base: "./testdata/31",
|
||||||
|
@ -706,14 +709,17 @@ func TestEVMTracing(t *testing.T) {
|
||||||
"--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json",
|
"--input.alloc=./testdata/31/alloc.json", "--input.txs=./testdata/31/txs.json",
|
||||||
"--input.env=./testdata/31/env.json", "--state.fork=Cancun",
|
"--input.env=./testdata/31/env.json", "--state.fork=Cancun",
|
||||||
"--trace.tracer", `
|
"--trace.tracer", `
|
||||||
{
|
{ count: 0,
|
||||||
result: function(){
|
result: function(){
|
||||||
return "hello world"
|
this.count = this.count + 1;
|
||||||
},
|
return "hello world " + this.count
|
||||||
fault: function(){}
|
},
|
||||||
|
fault: function(){}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"},
|
expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json",
|
||||||
|
"trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.json",
|
||||||
|
"trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.json"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
base: "./testdata/32",
|
base: "./testdata/32",
|
||||||
|
|
|
@ -1 +1,11 @@
|
||||||
This test does some EVM execution, and can be used to test the tracers and trace-outputs.
|
This test does some EVM execution, and can be used to test the tracers and trace-outputs.
|
||||||
|
This test should yield three output-traces, in separate files
|
||||||
|
|
||||||
|
For example:
|
||||||
|
```
|
||||||
|
[user@work evm]$ go run . t8n --input.alloc ./testdata/31/alloc.json --input.txs ./testdata/31/txs.json --input.env ./testdata/31/env.json --state.fork Cancun --output.basedir /tmp --trace
|
||||||
|
INFO [12-06|09:53:32.123] Created tracing-file path=/tmp/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl
|
||||||
|
INFO [12-06|09:53:32.124] Created tracing-file path=/tmp/trace-1-0x03a7b0a91e61a170d64ea94b8263641ef5a8bbdb10ac69f466083a6789c77fb8.jsonl
|
||||||
|
INFO [12-06|09:53:32.125] Created tracing-file path=/tmp/trace-2-0xd96e0ce6418ee3360e11d3c7b6886f5a9a08f7ef183da72c23bb3b2374530128.jsonl
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"hello world"
|
"hello world 1"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
"hello world 2"
|
|
@ -0,0 +1,6 @@
|
||||||
|
{"pc":0,"op":96,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":2,"op":96,"gas":"0x13495","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":4,"op":96,"gas":"0x13492","gasCost":"0x3","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":6,"op":96,"gas":"0x1348f","gasCost":"0x3","memSize":0,"stack":["0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":8,"op":0,"gas":"0x1348c","gasCost":"0x0","memSize":0,"stack":["0x40","0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"STOP"}
|
||||||
|
{"output":"","gasUsed":"0xc"}
|
|
@ -0,0 +1 @@
|
||||||
|
"hello world 3"
|
|
@ -0,0 +1,6 @@
|
||||||
|
{"pc":0,"op":96,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":2,"op":96,"gas":"0x13495","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":4,"op":96,"gas":"0x13492","gasCost":"0x3","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":6,"op":96,"gas":"0x1348f","gasCost":"0x3","memSize":0,"stack":["0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":8,"op":0,"gas":"0x1348c","gasCost":"0x0","memSize":0,"stack":["0x40","0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"STOP"}
|
||||||
|
{"output":"","gasUsed":"0xc"}
|
|
@ -10,5 +10,29 @@
|
||||||
"r" : "0x0",
|
"r" : "0x0",
|
||||||
"s" : "0x0",
|
"s" : "0x0",
|
||||||
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gas": "0x186a0",
|
||||||
|
"gasPrice": "0x600",
|
||||||
|
"input": "0x",
|
||||||
|
"nonce": "0x1",
|
||||||
|
"to": "0x1111111111111111111111111111111111111111",
|
||||||
|
"value": "0x1",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gas": "0x186a0",
|
||||||
|
"gasPrice": "0x600",
|
||||||
|
"input": "0x",
|
||||||
|
"nonce": "0x2",
|
||||||
|
"to": "0x1111111111111111111111111111111111111111",
|
||||||
|
"value": "0x1",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -133,11 +133,6 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
|
||||||
return evm
|
return evm
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTracer sets the tracer for following state transition.
|
|
||||||
func (evm *EVM) SetTracer(tracer *tracing.Hooks) {
|
|
||||||
evm.Config.Tracer = tracer
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPrecompiles sets the precompiled contracts for the EVM.
|
// SetPrecompiles sets the precompiled contracts for the EVM.
|
||||||
// This method is only used through RPC calls.
|
// This method is only used through RPC calls.
|
||||||
// It is not thread-safe.
|
// It is not thread-safe.
|
||||||
|
|
Loading…
Reference in New Issue