diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 2cdf559737b..8a8e3ca48c9 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -46,9 +46,9 @@ import (
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/event"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/turbo/services"
@@ -715,7 +715,7 @@ func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg
}
// Set infinite balance to the fake caller account.
from := statedb.GetOrNewStateObject(call.From)
- from.SetBalance(uint256.NewInt(0).SetAllOne(), evmtypes.BalanceChangeUnspecified)
+ from.SetBalance(uint256.NewInt(0).SetAllOne(), tracing.BalanceChangeUnspecified)
// Execute the call.
msg := callMsg{call}
diff --git a/cmd/devnet/devnet/node.go b/cmd/devnet/devnet/node.go
index b8aa1be220b..e697a081b63 100644
--- a/cmd/devnet/devnet/node.go
+++ b/cmd/devnet/devnet/node.go
@@ -139,7 +139,7 @@ func (n *devnetNode) EnableMetrics(int) {
// run configures, creates and serves an erigon node
func (n *devnetNode) run(ctx *cli.Context) error {
var logger log.Logger
- var tracer tracers.Tracer
+ var tracer *tracers.Tracer
var err error
var metricsMux *http.ServeMux
diff --git a/cmd/erigon/main.go b/cmd/erigon/main.go
index 081821d4663..b4a505c0969 100644
--- a/cmd/erigon/main.go
+++ b/cmd/erigon/main.go
@@ -43,7 +43,7 @@ func main() {
func runErigon(cliCtx *cli.Context) error {
var logger log.Logger
- var tracer tracers.Tracer
+ var tracer *tracers.Tracer
var err error
var metricsMux *http.ServeMux
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index 355ec9e2a91..8e520748110 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -29,8 +29,8 @@ import (
"github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/consensus/ethash"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
)
@@ -84,7 +84,7 @@ func MakePreState(chainRules *chain.Rules, tx kv.RwTx, accounts types.GenesisAll
statedb.SetCode(addr, a.Code)
statedb.SetNonce(addr, a.Nonce)
balance, _ := uint256.FromBig(a.Balance)
- statedb.SetBalance(addr, balance, evmtypes.BalanceIncreaseGenesisBalance)
+ statedb.SetBalance(addr, balance, tracing.BalanceIncreaseGenesisBalance)
for k, v := range a.Storage {
key := k
val := uint256.NewInt(0).SetBytes(v.Bytes())
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index 14401d0f94b..7c91ff484c2 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -30,6 +30,7 @@ import (
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/common/datadir"
"github.com/ledgerwatch/erigon/core/state/temporal"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/log/v3"
"github.com/urfave/cli/v2"
@@ -100,7 +101,7 @@ func Main(ctx *cli.Context) error {
err error
baseDir = ""
)
- var getTracer func(txIndex int, txHash libcommon.Hash) (vm.EVMLogger, error)
+ var getTracer func(txIndex int, txHash libcommon.Hash) (*tracing.Hooks, error)
// If user specified a basedir, make sure it exists
if ctx.IsSet(OutputBasedir.Name) {
@@ -127,7 +128,7 @@ func Main(ctx *cli.Context) error {
prevFile.Close()
}
}()
- getTracer = func(txIndex int, txHash libcommon.Hash) (vm.EVMLogger, error) {
+ getTracer = func(txIndex int, txHash libcommon.Hash) (*tracing.Hooks, error) {
if prevFile != nil {
prevFile.Close()
}
@@ -136,10 +137,10 @@ func Main(ctx *cli.Context) error {
return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err2))
}
prevFile = traceFile
- return trace_logger.NewJSONLogger(logConfig, traceFile), nil
+ return trace_logger.NewJSONLogger(logConfig, traceFile).Hooks, nil
}
} else {
- getTracer = func(txIndex int, txHash libcommon.Hash) (tracer vm.EVMLogger, err error) {
+ getTracer = func(txIndex int, txHash libcommon.Hash) (tracer *tracing.Hooks, err error) {
return nil, nil
}
}
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index 6a86f7781fd..2889e13d260 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -135,7 +135,7 @@ func runCmd(ctx *cli.Context) error {
}
var (
- tracer tracers.Tracer
+ tracer *tracers.Tracer
debugLogger *logger.StructLogger
statedb *state.IntraBlockState
chainConfig *chain.Config
@@ -147,7 +147,7 @@ func runCmd(ctx *cli.Context) error {
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
} else if ctx.Bool(DebugFlag.Name) {
debugLogger = logger.NewStructLogger(logconfig)
- tracer = debugLogger
+ tracer = debugLogger.Tracer()
} else {
debugLogger = logger.NewStructLogger(logconfig)
}
@@ -238,7 +238,7 @@ func runCmd(ctx *cli.Context) error {
Coinbase: genesisConfig.Coinbase,
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
EVMConfig: vm.Config{
- Tracer: tracer,
+ Tracer: tracer.Hooks,
Debug: ctx.Bool(DebugFlag.Name) || ctx.Bool(MachineFlag.Name),
},
}
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index 151da18661f..94a5a86c8d3 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -75,9 +75,9 @@ func stateTestCmd(ctx *cli.Context) error {
Debug: ctx.Bool(DebugFlag.Name) || ctx.Bool(MachineFlag.Name),
}
if machineFriendlyOutput {
- cfg.Tracer = logger.NewJSONLogger(config, os.Stderr)
+ cfg.Tracer = logger.NewJSONLogger(config, os.Stderr).Hooks
} else if ctx.Bool(DebugFlag.Name) {
- cfg.Tracer = logger.NewStructLogger(config)
+ cfg.Tracer = logger.NewStructLogger(config).Hooks()
}
if len(ctx.Args().First()) != 0 {
diff --git a/cmd/integration/commands/state_stages.go b/cmd/integration/commands/state_stages.go
index 5e342009b0b..f7f441e517c 100644
--- a/cmd/integration/commands/state_stages.go
+++ b/cmd/integration/commands/state_stages.go
@@ -248,8 +248,10 @@ func syncBySmallSteps(db kv.RwDB, miningConfig params.MiningConfig, ctx context.
stopAt = 1
}
+ var structLogger *logger.StructLogger
traceStart := func() {
- vmConfig.Tracer = logger.NewStructLogger(&logger.LogConfig{})
+ structLogger = logger.NewStructLogger(&logger.LogConfig{})
+ vmConfig.Tracer = structLogger.Hooks()
vmConfig.Debug = true
}
traceStop := func(id int) {
@@ -262,7 +264,7 @@ func syncBySmallSteps(db kv.RwDB, miningConfig params.MiningConfig, ctx context.
}
encoder := json.NewEncoder(w)
encoder.SetIndent(" ", " ")
- for _, l := range logger.FormatLogs(vmConfig.Tracer.(*logger.StructLogger).StructLogs()) {
+ for _, l := range logger.FormatLogs(structLogger.StructLogs()) {
if err2 := encoder.Encode(l); err2 != nil {
panic(err2)
}
diff --git a/cmd/state/commands/opcode_tracer.go b/cmd/state/commands/opcode_tracer.go
index 61ca8b2bbbb..a9ae193d750 100644
--- a/cmd/state/commands/opcode_tracer.go
+++ b/cmd/state/commands/opcode_tracer.go
@@ -6,7 +6,6 @@ import (
"encoding/gob"
"encoding/json"
"fmt"
- "math/big"
"os"
"os/signal"
"strconv"
@@ -29,10 +28,11 @@ import (
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/ethconfig"
+ "github.com/ledgerwatch/erigon/eth/tracers"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
"github.com/ledgerwatch/erigon/turbo/snapshotsync/freezeblocks"
)
@@ -115,7 +115,7 @@ type opcodeTracer struct {
saveBblocks bool
blockNumber uint64
depth int
- env *vm.EVM
+ env *tracing.VMContext
}
func NewOpcodeTracer(blockNum uint64, saveOpcodes bool, saveBblocks bool) *opcodeTracer {
@@ -164,13 +164,23 @@ type blockTxs struct {
Txs slicePtrTx
}
-func (ot *opcodeTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
+func (ot *opcodeTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: ot.OnTxStart,
+ OnEnter: ot.OnEnter,
+ OnExit: ot.OnExit,
+ OnFault: ot.OnFault,
+ OnOpcode: ot.OnOpcode,
+ },
+ }
+}
+
+func (ot *opcodeTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
ot.env = env
ot.depth = 0
}
-func (ot *opcodeTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
func (ot *opcodeTracer) captureStartOrEnter(from, to libcommon.Address, create bool, input []byte) {
//fmt.Fprint(ot.summary, ot.lastLine)
@@ -199,14 +209,9 @@ func (ot *opcodeTracer) captureStartOrEnter(from, to libcommon.Address, create b
ot.stack = append(ot.stack, &newTx)
}
-func (ot *opcodeTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- ot.depth = 0
- ot.captureStartOrEnter(from, to, create, input)
-}
-
-func (ot *opcodeTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- ot.depth++
- ot.captureStartOrEnter(from, to, create, input)
+func (ot *opcodeTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ ot.depth = depth
+ ot.captureStartOrEnter(from, to, vm.OpCode(typ) == vm.CREATE, input)
}
func (ot *opcodeTracer) captureEndOrExit(err error) {
@@ -241,18 +246,13 @@ func (ot *opcodeTracer) captureEndOrExit(err error) {
}
}
-func (ot *opcodeTracer) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
+func (ot *opcodeTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
ot.captureEndOrExit(err)
+ ot.depth = depth
}
-func (ot *opcodeTracer) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
- ot.captureEndOrExit(err)
- ot.depth--
-}
-
-func (ot *opcodeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, opDepth int, err error) {
+func (ot *opcodeTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, opDepth int, err error) {
//CaptureState sees the system as it is before the opcode is run. It seems to never get an error.
- contract := scope.Contract
//sanity check
if pc > uint64(MaxUint16) {
@@ -284,8 +284,8 @@ func (ot *opcodeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
currentEntry.TxHash = new(libcommon.Hash)
currentEntry.TxHash.SetBytes(currentTxHash.Bytes())
currentEntry.CodeHash = new(libcommon.Hash)
- currentEntry.CodeHash.SetBytes(contract.CodeHash.Bytes())
- currentEntry.CodeSize = len(contract.Code)
+ currentEntry.CodeHash.SetBytes(scope.CodeHash().Bytes())
+ currentEntry.CodeSize = len(scope.Code())
if ot.saveOpcodes {
currentEntry.Opcodes = make([]opcode, 0, 200)
}
@@ -313,7 +313,7 @@ func (ot *opcodeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
//sanity check
if currentEntry.OpcodeFault != "" {
panic(fmt.Sprintf("Running opcodes but fault is already set. txFault=%s, opFault=%v, op=%s",
- currentEntry.OpcodeFault, err, op.String()))
+ currentEntry.OpcodeFault, err, vm.OpCode(op).String()))
}
// if it is a Fault, check whether we already have a record of the opcode. If so, just add the flag to it
@@ -325,11 +325,11 @@ func (ot *opcodeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
faultAndRepeated := false
- if pc16 == currentEntry.lastPc16 && op == currentEntry.lastOp {
+ if pc16 == currentEntry.lastPc16 && vm.OpCode(op) == currentEntry.lastOp {
//it's a repeated opcode. We assume this only happens when it's a Fault.
if err == nil {
panic(fmt.Sprintf("Duplicate opcode with no fault. bn=%d txaddr=%s pc=%x op=%s",
- ot.blockNumber, currentEntry.TxAddr, pc, op.String()))
+ ot.blockNumber, currentEntry.TxAddr, pc, vm.OpCode(op).String()))
}
faultAndRepeated = true
//ot.fsumWriter.WriteString("Fault for EXISTING opcode\n")
@@ -341,7 +341,7 @@ func (ot *opcodeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
} else {
// it's a new opcode
if ot.saveOpcodes {
- newOpcode := opcode{pc16, op, errstr}
+ newOpcode := opcode{pc16, vm.OpCode(op), errstr}
currentEntry.Opcodes = append(currentEntry.Opcodes, newOpcode)
}
}
@@ -372,56 +372,25 @@ func (ot *opcodeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
//sanity check
// we're starting a bblock, so either we're in PC=0 or we have OP=JUMPDEST
- if pc16 != 0 && op.String() != "JUMPDEST" {
+ if pc16 != 0 && vm.OpCode(op).String() != "JUMPDEST" {
panic(fmt.Sprintf("Bad bblock? lastpc=%x, lastOp=%s; pc=%x, op=%s; bn=%d txaddr=%s tx=%d-%s",
- currentEntry.lastPc16, currentEntry.lastOp.String(), pc, op.String(), ot.blockNumber, currentEntry.TxAddr, currentEntry.Depth, currentEntry.TxHash.String()))
+ currentEntry.lastPc16, currentEntry.lastOp.String(), pc, vm.OpCode(op).String(), ot.blockNumber, currentEntry.TxAddr, currentEntry.Depth, currentEntry.TxHash.String()))
}
}
}
}
currentEntry.lastPc16 = pc16
- currentEntry.lastOp = op
+ currentEntry.lastOp = vm.OpCode(op)
}
-func (ot *opcodeTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, opDepth int, err error) {
+func (ot *opcodeTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, opDepth int, err error) {
// CaptureFault sees the system as it is after the fault happens
// CaptureState might have already recorded the opcode before it failed. Let's centralize the processing there.
- ot.CaptureState(pc, op, gas, cost, scope, nil, opDepth, err)
-
-}
-
-func (ot *opcodeTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain2.Config) {
-}
-
-func (ot *opcodeTracer) OnBlockEnd(err error) {
+ ot.OnOpcode(pc, op, gas, cost, scope, nil, opDepth, err)
}
-func (ot *opcodeTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (ot *opcodeTracer) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (ot *opcodeTracer) OnBeaconBlockRootEnd() {}
-
-func (ot *opcodeTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (ot *opcodeTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (ot *opcodeTracer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (ot *opcodeTracer) OnNonceChange(a libcommon.Address, prev, new uint64) {}
-
-func (ot *opcodeTracer) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (ot *opcodeTracer) OnStorageChange(a libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (ot *opcodeTracer) OnLog(log *types.Log) {}
-
// GetResult returns an empty json object.
func (ot *opcodeTracer) GetResult() (json.RawMessage, error) {
return json.RawMessage(`{}`), nil
@@ -474,7 +443,7 @@ func OpcodeTracer(genesis *types.Genesis, blockNum uint64, chaindata string, num
blockReader := freezeblocks.NewBlockReader(freezeblocks.NewRoSnapshots(ethconfig.BlocksFreezing{Enabled: false}, "", 0, log.New()), nil /* BorSnapshots */)
chainConfig := genesis.Config
- vmConfig := vm.Config{Tracer: ot, Debug: true}
+ vmConfig := vm.Config{Tracer: ot.Tracer().Hooks, Debug: true}
noOpWriter := state.NewNoopWriter()
diff --git a/cmd/state/exec3/calltracer_v3.go b/cmd/state/exec3/calltracer_v3.go
index b46f05a2263..8bee0355223 100644
--- a/cmd/state/exec3/calltracer_v3.go
+++ b/cmd/state/exec3/calltracer_v3.go
@@ -2,14 +2,11 @@ package exec3
import (
"encoding/json"
- "math/big"
"github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
- "github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
+ "github.com/ledgerwatch/erigon/core/tracing"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
type CallTracer struct {
@@ -20,68 +17,29 @@ type CallTracer struct {
func NewCallTracer() *CallTracer {
return &CallTracer{}
}
+
+func (ct *CallTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnEnter: ct.OnEnter,
+ },
+ }
+}
+
func (ct *CallTracer) Reset() {
ct.froms, ct.tos = nil, nil
}
+
func (ct *CallTracer) Froms() map[libcommon.Address]struct{} { return ct.froms }
func (ct *CallTracer) Tos() map[libcommon.Address]struct{} { return ct.tos }
-func (ct *CallTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {}
-
-func (ct *CallTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (ct *CallTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- if ct.froms == nil {
- ct.froms = map[libcommon.Address]struct{}{}
- ct.tos = map[libcommon.Address]struct{}{}
- }
- ct.froms[from], ct.tos[to] = struct{}{}, struct{}{}
-}
-func (ct *CallTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (ct *CallTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
if ct.froms == nil {
ct.froms = map[libcommon.Address]struct{}{}
ct.tos = map[libcommon.Address]struct{}{}
}
ct.froms[from], ct.tos[to] = struct{}{}, struct{}{}
}
-func (ct *CallTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
-}
-func (ct *CallTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-func (ct *CallTracer) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
-}
-func (ct *CallTracer) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
-}
-
-func (ct *CallTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (ct *CallTracer) OnBlockEnd(err error) {
-}
-
-func (ct *CallTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (ct *CallTracer) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (ct *CallTracer) OnBeaconBlockRootEnd() {}
-
-func (ct *CallTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (ct *CallTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (ct *CallTracer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (ct *CallTracer) OnNonceChange(a libcommon.Address, prev, new uint64) {}
-
-func (ct *CallTracer) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (ct *CallTracer) OnStorageChange(a libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (ct *CallTracer) OnLog(log *types.Log) {}
// GetResult returns an empty json object.
func (ct *CallTracer) GetResult() (json.RawMessage, error) {
diff --git a/cmd/state/exec3/state.go b/cmd/state/exec3/state.go
index b89e86974a3..b6d02e8e797 100644
--- a/cmd/state/exec3/state.go
+++ b/cmd/state/exec3/state.go
@@ -187,7 +187,7 @@ func (rw *Worker) RunTxTaskNoLock(txTask *exec22.TxTask) {
rw.taskGasPool.Reset(txTask.Tx.GetGas())
rw.callTracer.Reset()
- vmConfig := vm.Config{Debug: true, Tracer: rw.callTracer, SkipAnalysis: txTask.SkipAnalysis}
+ vmConfig := vm.Config{Debug: true, Tracer: rw.callTracer.Tracer().Hooks, SkipAnalysis: txTask.SkipAnalysis}
ibs.SetTxContext(txHash, txTask.BlockHash, txTask.TxIndex)
msg := txTask.TxAsMessage
diff --git a/consensus/aura/aura.go b/consensus/aura/aura.go
index c701b40d1b4..1ab82f4202e 100644
--- a/consensus/aura/aura.go
+++ b/consensus/aura/aura.go
@@ -35,8 +35,8 @@ import (
"github.com/ledgerwatch/erigon/consensus/clique"
"github.com/ledgerwatch/erigon/consensus/ethash"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/rpc"
)
@@ -632,7 +632,7 @@ func (c *AuRa) Prepare(chain consensus.ChainHeaderReader, header *types.Header,
}
func (c *AuRa) Initialize(config *chain.Config, chain consensus.ChainHeaderReader, header *types.Header,
- state *state.IntraBlockState, syscallCustom consensus.SysCallCustom, logger log.Logger, eLogger consensus.EngineLogger,
+ state *state.IntraBlockState, syscallCustom consensus.SysCallCustom, logger log.Logger, eLogger *tracing.Hooks,
) {
blockNum := header.Number.Uint64()
@@ -693,7 +693,7 @@ func (c *AuRa) applyRewards(header *types.Header, state *state.IntraBlockState,
return err
}
for _, r := range rewards {
- state.AddBalance(r.Beneficiary, &r.Amount, evmtypes.BalanceIncreaseRewardMineBlock)
+ state.AddBalance(r.Beneficiary, &r.Amount, tracing.BalanceIncreaseRewardMineBlock)
}
return nil
}
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index ef0b2f54dd5..9ddfee535b7 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -44,6 +44,7 @@ import (
"github.com/ledgerwatch/erigon/common/debug"
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/crypto"
@@ -367,7 +368,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header
}
func (c *Clique) Initialize(config *chain.Config, chain consensus.ChainHeaderReader, header *types.Header,
- state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger consensus.EngineLogger) {
+ state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger *tracing.Hooks) {
}
func (c *Clique) CalculateRewards(config *chain.Config, header *types.Header, uncles []*types.Header, syscall consensus.SystemCall,
diff --git a/consensus/consensus.go b/consensus/consensus.go
index 16e38509e92..df3a3ed910d 100644
--- a/consensus/consensus.go
+++ b/consensus/consensus.go
@@ -26,6 +26,7 @@ import (
"github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/rpc"
@@ -89,12 +90,6 @@ type Call func(contract libcommon.Address, data []byte) ([]byte, error)
// different semantics which could lead e.g. to different reward values.
type RewardKind uint16
-// EngineLogger interface for logging blockchain events
-type EngineLogger interface {
- OnBeaconBlockRootStart(root libcommon.Hash)
- OnBeaconBlockRootEnd()
-}
-
const (
// RewardAuthor - attributed to the block author.
RewardAuthor RewardKind = 0
@@ -155,7 +150,7 @@ type EngineWriter interface {
// Initialize runs any pre-transaction state modifications (e.g. epoch start)
Initialize(config *chain.Config, chain ChainHeaderReader, header *types.Header,
- state *state.IntraBlockState, syscall SysCallCustom, logger log.Logger, eLogger EngineLogger)
+ state *state.IntraBlockState, syscall SysCallCustom, logger log.Logger, eLogger *tracing.Hooks)
// Finalize runs any post-transaction state modifications (e.g. block rewards)
// but does not assemble the block.
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index a74f70808eb..c506cef01e7 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -37,8 +37,8 @@ import (
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
)
@@ -551,7 +551,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
}
func (ethash *Ethash) Initialize(config *chain.Config, chain consensus.ChainHeaderReader, header *types.Header,
- state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger consensus.EngineLogger) {
+ state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger *tracing.Hooks) {
if config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(header.Number) == 0 {
misc.ApplyDAOHardFork(state)
}
@@ -666,8 +666,8 @@ func accumulateRewards(config *chain.Config, state *state.IntraBlockState, heade
minerReward, uncleRewards := AccumulateRewards(config, header, uncles)
for i, uncle := range uncles {
if i < len(uncleRewards) {
- state.AddBalance(uncle.Coinbase, &uncleRewards[i], evmtypes.BalanceIncreaseRewardMineUncle)
+ state.AddBalance(uncle.Coinbase, &uncleRewards[i], tracing.BalanceIncreaseRewardMineUncle)
}
}
- state.AddBalance(header.Coinbase, &minerReward, evmtypes.BalanceIncreaseRewardMineBlock)
+ state.AddBalance(header.Coinbase, &minerReward, tracing.BalanceIncreaseRewardMineBlock)
}
diff --git a/consensus/merge/merge.go b/consensus/merge/merge.go
index f0c26f41790..ee0b6c5ef2f 100644
--- a/consensus/merge/merge.go
+++ b/consensus/merge/merge.go
@@ -14,8 +14,8 @@ import (
"github.com/ledgerwatch/erigon/consensus/aura"
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/log/v3"
@@ -146,11 +146,11 @@ func (s *Merge) Finalize(config *chain.Config, header *types.Header, state *stat
for _, r := range rewards {
switch r.Kind {
case consensus.RewardAuthor:
- state.AddBalance(r.Beneficiary, &r.Amount, evmtypes.BalanceIncreaseRewardMineBlock)
+ state.AddBalance(r.Beneficiary, &r.Amount, tracing.BalanceIncreaseRewardMineBlock)
case consensus.RewardUncle:
- state.AddBalance(r.Beneficiary, &r.Amount, evmtypes.BalanceIncreaseRewardMineUncle)
+ state.AddBalance(r.Beneficiary, &r.Amount, tracing.BalanceIncreaseRewardMineUncle)
default:
- state.AddBalance(r.Beneficiary, &r.Amount, evmtypes.BalanceChangeUnspecified)
+ state.AddBalance(r.Beneficiary, &r.Amount, tracing.BalanceChangeUnspecified)
}
}
@@ -162,7 +162,7 @@ func (s *Merge) Finalize(config *chain.Config, header *types.Header, state *stat
} else {
for _, w := range withdrawals {
amountInWei := new(uint256.Int).Mul(uint256.NewInt(w.Amount), uint256.NewInt(params.GWei))
- state.AddBalance(w.Address, amountInWei, evmtypes.BalanceIncreaseWithdrawal)
+ state.AddBalance(w.Address, amountInWei, tracing.BalanceIncreaseWithdrawal)
}
}
}
@@ -280,7 +280,7 @@ func (s *Merge) IsServiceTransaction(sender libcommon.Address, syscall consensus
}
func (s *Merge) Initialize(config *chain.Config, chain consensus.ChainHeaderReader, header *types.Header,
- state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger consensus.EngineLogger,
+ state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger *tracing.Hooks,
) {
if !misc.IsPoSHeader(header) {
s.eth1Engine.Initialize(config, chain, header, state, syscall, logger, eLogger)
@@ -288,7 +288,7 @@ func (s *Merge) Initialize(config *chain.Config, chain consensus.ChainHeaderRead
if chain.Config().IsCancun(header.Time) {
misc.ApplyBeaconRootEip4788(header.ParentBeaconBlockRoot, func(addr libcommon.Address, data []byte) ([]byte, error) {
return syscall(addr, data, state, header, false /* constCall */)
- }, eLogger)
+ })
}
}
diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go
index ce7bc945f37..5d20982ac10 100644
--- a/consensus/misc/dao.go
+++ b/consensus/misc/dao.go
@@ -25,8 +25,8 @@ import (
"github.com/ledgerwatch/erigon-lib/chain"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/params"
)
@@ -77,7 +77,7 @@ func ApplyDAOHardFork(statedb *state.IntraBlockState) {
// Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList() {
- statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), evmtypes.BalanceIncreaseDaoContract)
- statedb.SetBalance(addr, new(uint256.Int), evmtypes.BalanceDecreaseDaoAccount)
+ statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract)
+ statedb.SetBalance(addr, new(uint256.Int), tracing.BalanceDecreaseDaoAccount)
}
}
diff --git a/consensus/misc/eip4788.go b/consensus/misc/eip4788.go
index 9de2dd0d483..26293004b28 100644
--- a/consensus/misc/eip4788.go
+++ b/consensus/misc/eip4788.go
@@ -8,14 +8,7 @@ import (
"github.com/ledgerwatch/erigon/params"
)
-func ApplyBeaconRootEip4788(parentBeaconBlockRoot *libcommon.Hash, syscall consensus.SystemCall, eLogger consensus.EngineLogger) {
- if eLogger != nil {
- eLogger.OnBeaconBlockRootStart(*parentBeaconBlockRoot)
- defer func() {
- eLogger.OnBeaconBlockRootEnd()
- }()
- }
-
+func ApplyBeaconRootEip4788(parentBeaconBlockRoot *libcommon.Hash, syscall consensus.SystemCall) {
_, err := syscall(params.BeaconRootsAddress, parentBeaconBlockRoot.Bytes())
if err != nil {
log.Warn("Failed to call beacon roots contract", "err", err)
diff --git a/core/blockchain.go b/core/blockchain.go
index 13f294c66e6..b2c79552265 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -20,7 +20,6 @@ package core
import (
"encoding/json"
"fmt"
- "math/big"
"time"
"github.com/ledgerwatch/log/v3"
@@ -36,6 +35,7 @@ import (
"github.com/ledgerwatch/erigon/common/u256"
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
@@ -56,19 +56,6 @@ const (
SysCallGasLimit = uint64(30_000_000)
)
-// BlockchainLogger is used to collect traces during chain processing.
-// Please make a copy of the referenced types if you intend to retain them.
-type BlockchainLogger interface {
- vm.EVMLogger
- state.StateLogger
- consensus.EngineLogger
- // OnBlockStart is called before executing `block`.
- // `td` is the total difficulty prior to `block`.
- OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header, chainConfig *chain.Config)
- OnBlockEnd(err error)
- OnGenesisBlock(genesis *types.Block, alloc types.GenesisAlloc)
-}
-
type RejectedTx struct {
Index int `json:"index" gencodec:"required"`
Err string `json:"error" gencodec:"required"`
@@ -96,24 +83,14 @@ func ExecuteBlockEphemerally(
blockHashFunc func(n uint64) libcommon.Hash,
engine consensus.Engine, block *types.Block,
stateReader state.StateReader, stateWriter state.WriterWithChangeSets,
- chainReader consensus.ChainReader, getTracer func(txIndex int, txHash libcommon.Hash) (vm.EVMLogger, error),
+ chainReader consensus.ChainReader, getTracer func(txIndex int, txHash libcommon.Hash) (*tracing.Hooks, error),
logger log.Logger,
) (res *EphemeralExecResult, executeBlockErr error) {
- var bcLogger BlockchainLogger
- if vmConfig.Tracer != nil {
- l, ok := vmConfig.Tracer.(BlockchainLogger)
- if ok {
- bcLogger = l
- } else {
- log.Warn("only extended tracers are supported for live mode")
- }
- }
-
defer blockExecutionTimer.ObserveDuration(time.Now())
block.Uncles()
ibs := state.New(stateReader)
- ibs.SetLogger(bcLogger)
+ ibs.SetLogger(vmConfig.Tracer)
header := block.Header()
usedGas := new(uint64)
@@ -127,15 +104,22 @@ func ExecuteBlockEphemerally(
receipts types.Receipts
)
- if bcLogger != nil {
+ if vmConfig.Tracer != nil && vmConfig.Tracer.OnBlockStart != nil {
td := chainReader.GetTd(block.ParentHash(), block.NumberU64()-1)
- bcLogger.OnBlockStart(block, td, chainReader.CurrentFinalizedHeader(), chainReader.CurrentSafeHeader(), chainConfig)
+ vmConfig.Tracer.OnBlockStart(tracing.BlockEvent{
+ Block: block,
+ TD: td,
+ Finalized: chainReader.CurrentFinalizedHeader(),
+ Safe: chainReader.CurrentSafeHeader(),
+ })
+ }
+ if vmConfig.Tracer != nil && vmConfig.Tracer.OnBlockEnd != nil {
defer func() {
- bcLogger.OnBlockEnd(executeBlockErr)
+ vmConfig.Tracer.OnBlockEnd(executeBlockErr)
}()
}
- if err := InitializeBlockExecution(engine, chainReader, block.Header(), chainConfig, ibs, logger, bcLogger); err != nil {
+ if err := InitializeBlockExecution(engine, chainReader, block.Header(), chainConfig, ibs, logger, vmConfig.Tracer); err != nil {
return nil, err
}
@@ -144,23 +128,24 @@ func ExecuteBlockEphemerally(
noop := state.NewNoopWriter()
for i, tx := range block.Transactions() {
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
- writeTrace := false
+ // writeTrace := false
if vmConfig.Debug && vmConfig.Tracer == nil {
tracer, err := getTracer(i, tx.Hash())
if err != nil {
return nil, fmt.Errorf("could not obtain tracer: %w", err)
}
vmConfig.Tracer = tracer
- writeTrace = true
+ // writeTrace = true
}
receipt, _, err := ApplyTransaction(chainConfig, blockHashFunc, engine, nil, gp, ibs, noop, header, tx, usedGas, usedBlobGas, *vmConfig)
- if writeTrace {
- if ftracer, ok := vmConfig.Tracer.(vm.FlushableTracer); ok {
- ftracer.Flush(tx)
- }
-
- vmConfig.Tracer = nil
- }
+ // Todo: check how to implement flushable tracer
+ // if writeTrace {
+ // if ftracer, ok := vmConfig.Tracer.(vm.FlushableTracer); ok {
+ // ftracer.Flush(tx)
+ // }
+
+ // vmConfig.Tracer = nil
+ // }
if err != nil {
if !vmConfig.StatelessExec {
return nil, fmt.Errorf("could not apply tx %d from block %d [%v]: %w", i, block.NumberU64(), tx.Hash().Hex(), err)
@@ -379,7 +364,7 @@ func FinalizeBlockExecution(
}
func InitializeBlockExecution(engine consensus.Engine, chain consensus.ChainHeaderReader, header *types.Header,
- cc *chain.Config, ibs *state.IntraBlockState, logger log.Logger, bcLogger BlockchainLogger,
+ cc *chain.Config, ibs *state.IntraBlockState, logger log.Logger, bcLogger *tracing.Hooks,
) error {
engine.Initialize(cc, chain, header, ibs, func(contract libcommon.Address, data []byte, ibState *state.IntraBlockState, header *types.Header, constCall bool) ([]byte, error) {
return SysCallContract(contract, data, cc, ibState, header, engine, constCall)
diff --git a/core/evm.go b/core/evm.go
index 2c9a56af1cc..43e64c73719 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -27,6 +27,7 @@ import (
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/consensus/merge"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
)
@@ -127,9 +128,9 @@ func CanTransfer(db evmtypes.IntraBlockState, addr libcommon.Address, amount *ui
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db evmtypes.IntraBlockState, sender, recipient libcommon.Address, amount *uint256.Int, bailout bool) {
if !bailout {
- db.SubBalance(sender, amount, evmtypes.BalanceChangeTransfer)
+ db.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
}
- db.AddBalance(recipient, amount, evmtypes.BalanceChangeTransfer)
+ db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
}
// BorTransfer transfer in Bor
@@ -139,9 +140,9 @@ func BorTransfer(db evmtypes.IntraBlockState, sender, recipient libcommon.Addres
input2 := db.GetBalance(recipient).Clone()
if !bailout {
- db.SubBalance(sender, amount, evmtypes.BalanceChangeTransfer)
+ db.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
}
- db.AddBalance(recipient, amount, evmtypes.BalanceChangeTransfer)
+ db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
// get outputs after
output1 := db.GetBalance(sender).Clone()
diff --git a/core/genesis_write.go b/core/genesis_write.go
index 438573383e6..0601fad8ce9 100644
--- a/core/genesis_write.go
+++ b/core/genesis_write.go
@@ -45,8 +45,8 @@ import (
"github.com/ledgerwatch/erigon/consensus/merge"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/params"
@@ -66,11 +66,11 @@ import (
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
//
// The returned chain configuration is never nil.
-func CommitGenesisBlock(db kv.RwDB, genesis *types.Genesis, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) (*chain.Config, *types.Block, error) {
+func CommitGenesisBlock(db kv.RwDB, genesis *types.Genesis, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) (*chain.Config, *types.Block, error) {
return CommitGenesisBlockWithOverride(db, genesis, nil, tmpDir, logger, bcLogger)
}
-func CommitGenesisBlockWithOverride(db kv.RwDB, genesis *types.Genesis, overrideCancunTime *big.Int, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) (*chain.Config, *types.Block, error) {
+func CommitGenesisBlockWithOverride(db kv.RwDB, genesis *types.Genesis, overrideCancunTime *big.Int, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) (*chain.Config, *types.Block, error) {
tx, err := db.BeginRw(context.Background())
if err != nil {
return nil, nil, err
@@ -87,7 +87,7 @@ func CommitGenesisBlockWithOverride(db kv.RwDB, genesis *types.Genesis, override
return c, b, nil
}
-func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrideCancunTime *big.Int, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) (*chain.Config, *types.Block, error) {
+func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrideCancunTime *big.Int, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) (*chain.Config, *types.Block, error) {
var storedBlock *types.Block
if genesis != nil && genesis.Config == nil {
return params.AllProtocolChanges, nil, types.ErrGenesisNoConfig
@@ -181,7 +181,7 @@ func WriteGenesisBlock(tx kv.RwTx, genesis *types.Genesis, overrideCancunTime *b
return newCfg, storedBlock, nil
}
-func WriteGenesisState(g *types.Genesis, tx kv.RwTx, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) (*types.Block, *state.IntraBlockState, error) {
+func WriteGenesisState(g *types.Genesis, tx kv.RwTx, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) (*types.Block, *state.IntraBlockState, error) {
block, statedb, err := GenesisToBlock(g, tmpDir, logger, bcLogger)
if err != nil {
return nil, nil, err
@@ -230,7 +230,7 @@ func WriteGenesisState(g *types.Genesis, tx kv.RwTx, tmpDir string, logger log.L
}
return block, statedb, nil
}
-func MustCommitGenesis(g *types.Genesis, db kv.RwDB, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) *types.Block {
+func MustCommitGenesis(g *types.Genesis, db kv.RwDB, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) *types.Block {
tx, err := db.BeginRw(context.Background())
if err != nil {
panic(err)
@@ -249,7 +249,7 @@ func MustCommitGenesis(g *types.Genesis, db kv.RwDB, tmpDir string, logger log.L
// Write writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
-func write(tx kv.RwTx, g *types.Genesis, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) (*types.Block, *state.IntraBlockState, error) {
+func write(tx kv.RwTx, g *types.Genesis, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) (*types.Block, *state.IntraBlockState, error) {
block, statedb, err2 := WriteGenesisState(g, tx, tmpDir, logger, bcLogger)
if err2 != nil {
return block, statedb, err2
@@ -325,7 +325,7 @@ type GenAccount struct {
Balance *big.Int
}
-func GenesisWithAccounts(db kv.RwDB, accs []GenAccount, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) *types.Block {
+func GenesisWithAccounts(db kv.RwDB, accs []GenAccount, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) *types.Block {
g := types.Genesis{Config: params.TestChainConfig}
allocs := make(map[libcommon.Address]types.GenesisAccount)
for _, acc := range accs {
@@ -494,7 +494,7 @@ func DeveloperGenesisBlock(period uint64, faucet libcommon.Address) *types.Genes
// ToBlock creates the genesis block and writes state of a genesis specification
// to the given database (or discards it if nil).
-func GenesisToBlock(g *types.Genesis, tmpDir string, logger log.Logger, bcLogger BlockchainLogger) (*types.Block, *state.IntraBlockState, error) {
+func GenesisToBlock(g *types.Genesis, tmpDir string, logger log.Logger, bcLogger *tracing.Hooks) (*types.Block, *state.IntraBlockState, error) {
_ = g.Alloc //nil-check
head := &types.Header{
@@ -595,7 +595,7 @@ func GenesisToBlock(g *types.Genesis, tmpDir string, logger log.Logger, bcLogger
}
// This is not actually logged via tracer because OnGenesisBlock
// already captures the allocations.
- statedb.AddBalance(addr, balance, evmtypes.BalanceIncreaseGenesisBalance)
+ statedb.AddBalance(addr, balance, tracing.BalanceIncreaseGenesisBalance)
statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce)
for key, value := range account.Storage {
diff --git a/core/state/intra_block_state.go b/core/state/intra_block_state.go
index 415039574fc..3d69a161672 100644
--- a/core/state/intra_block_state.go
+++ b/core/state/intra_block_state.go
@@ -27,9 +27,9 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
types2 "github.com/ledgerwatch/erigon-lib/types"
"github.com/ledgerwatch/erigon/common/u256"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/turbo/trie"
)
@@ -39,20 +39,6 @@ type revision struct {
journalIndex int
}
-// StateLogger is used to collect state update traces from EVM transaction
-// execution.
-// The following hooks are invoked post execution. I.e. looking up state
-// after the hook should reflect the new value.
-// Note that reference types are actual VM data structures; make copies
-// if you need to retain them beyond the current call.
-type StateLogger interface {
- OnBalanceChange(addr libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason)
- OnNonceChange(addr libcommon.Address, prev, new uint64)
- OnCodeChange(addr libcommon.Address, prevCodeHash libcommon.Hash, prevCode []byte, codeHash libcommon.Hash, code []byte)
- OnStorageChange(addr libcommon.Address, slot *libcommon.Hash, prev, new uint256.Int)
- OnLog(log *types.Log)
-}
-
// SystemAddress - sender address for internal state updates.
var SystemAddress = libcommon.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
@@ -103,7 +89,7 @@ type IntraBlockState struct {
validRevisions []revision
nextRevisionID int
trace bool
- logger StateLogger
+ logger *tracing.Hooks
balanceInc map[libcommon.Address]*BalanceIncrease // Map of balance increases (without first reading the account)
}
@@ -123,7 +109,7 @@ func New(stateReader StateReader) *IntraBlockState {
}
// SetLogger sets the logger for account update hooks.
-func (sdb *IntraBlockState) SetLogger(l StateLogger) {
+func (sdb *IntraBlockState) SetLogger(l *tracing.Hooks) {
sdb.logger = l
}
@@ -168,7 +154,7 @@ func (sdb *IntraBlockState) AddLog(log2 *types.Log) {
log2.BlockHash = sdb.bhash
log2.TxIndex = uint(sdb.txIndex)
log2.Index = sdb.logSize
- if sdb.logger != nil {
+ if sdb.logger != nil && sdb.logger.OnLog != nil {
sdb.logger.OnLog(log2)
}
sdb.logs[sdb.thash] = append(sdb.logs[sdb.thash], log2)
@@ -324,7 +310,7 @@ func (sdb *IntraBlockState) HasSelfdestructed(addr libcommon.Address) bool {
// AddBalance adds amount to the account associated with addr.
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
-func (sdb *IntraBlockState) AddBalance(addr libcommon.Address, amount *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (sdb *IntraBlockState) AddBalance(addr libcommon.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) {
if sdb.trace {
fmt.Printf("AddBalance %x, %d\n", addr, amount)
}
@@ -357,7 +343,7 @@ func (sdb *IntraBlockState) AddBalance(addr libcommon.Address, amount *uint256.I
// SubBalance subtracts amount from the account associated with addr.
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
-func (sdb *IntraBlockState) SubBalance(addr libcommon.Address, amount *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (sdb *IntraBlockState) SubBalance(addr libcommon.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) {
if sdb.trace {
fmt.Printf("SubBalance %x, %d\n", addr, amount)
}
@@ -369,7 +355,7 @@ func (sdb *IntraBlockState) SubBalance(addr libcommon.Address, amount *uint256.I
}
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
-func (sdb *IntraBlockState) SetBalance(addr libcommon.Address, amount *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (sdb *IntraBlockState) SetBalance(addr libcommon.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) {
stateObject := sdb.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalance(amount, reason)
@@ -444,8 +430,8 @@ func (sdb *IntraBlockState) Selfdestruct(addr libcommon.Address) bool {
prevbalance: prevBalance,
})
- if sdb.logger != nil && !prevBalance.IsZero() {
- sdb.logger.OnBalanceChange(addr, &prevBalance, uint256.NewInt(0), evmtypes.BalanceDecreaseSelfdestruct)
+ if sdb.logger != nil && sdb.logger.OnBalanceChange != nil && !prevBalance.IsZero() {
+ sdb.logger.OnBalanceChange(addr, &prevBalance, uint256.NewInt(0), tracing.BalanceDecreaseSelfdestruct)
}
stateObject.markSelfdestructed()
@@ -639,12 +625,12 @@ func (sdb *IntraBlockState) GetRefund() uint64 {
return sdb.refund
}
-func updateAccount(EIP161Enabled bool, isAura bool, stateWriter StateWriter, addr libcommon.Address, stateObject *stateObject, isDirty bool, logger StateLogger) error {
+func updateAccount(EIP161Enabled bool, isAura bool, stateWriter StateWriter, addr libcommon.Address, stateObject *stateObject, isDirty bool, logger *tracing.Hooks) error {
emptyRemoval := EIP161Enabled && stateObject.empty() && (!isAura || addr != SystemAddress)
if stateObject.selfdestructed || (isDirty && emptyRemoval) {
// If ether was sent to account post-selfdestruct it is burnt.
- if logger != nil && !stateObject.Balance().IsZero() && stateObject.selfdestructed {
- logger.OnBalanceChange(stateObject.address, stateObject.Balance(), uint256.NewInt(0), evmtypes.BalanceDecreaseSelfdestructBurn)
+ if logger != nil && logger.OnBalanceChange != nil && !stateObject.Balance().IsZero() && stateObject.selfdestructed {
+ logger.OnBalanceChange(stateObject.address, stateObject.Balance(), uint256.NewInt(0), tracing.BalanceDecreaseSelfdestructBurn)
}
if err := stateWriter.DeleteAccount(addr, &stateObject.original); err != nil {
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 8398f023fe1..fb2a302f5a6 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -25,8 +25,8 @@ import (
"github.com/holiman/uint256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types/accounts"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/turbo/trie"
@@ -225,7 +225,7 @@ func (so *stateObject) SetState(key *libcommon.Hash, value uint256.Int) {
key: *key,
prevalue: prev,
})
- if so.db.logger != nil {
+ if so.db.logger != nil && so.db.logger.OnStorageChange != nil {
so.db.logger.OnStorageChange(so.address, key, prev, value)
}
so.setState(key, value)
@@ -273,7 +273,7 @@ func (so *stateObject) printTrie() {
// AddBalance adds amount to so's balance.
// It is used to add funds to the destination account of a transfer.
-func (so *stateObject) AddBalance(amount *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (so *stateObject) AddBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
// EIP161: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect.
if amount.IsZero() {
@@ -289,19 +289,19 @@ func (so *stateObject) AddBalance(amount *uint256.Int, reason evmtypes.BalanceCh
// SubBalance removes amount from so's balance.
// It is used to remove funds from the origin account of a transfer.
-func (so *stateObject) SubBalance(amount *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (so *stateObject) SubBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
if amount.IsZero() {
return
}
so.SetBalance(new(uint256.Int).Sub(so.Balance(), amount), reason)
}
-func (so *stateObject) SetBalance(amount *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (so *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
so.db.journal.append(balanceChange{
account: &so.address,
prev: so.data.Balance,
})
- if so.db.logger != nil {
+ if so.db.logger != nil && so.db.logger.OnBalanceChange != nil {
so.db.logger.OnBalanceChange(so.address, so.Balance(), amount, reason)
}
so.setBalance(amount)
@@ -351,7 +351,7 @@ func (so *stateObject) SetCode(codeHash libcommon.Hash, code []byte) {
prevhash: so.data.CodeHash,
prevcode: prevcode,
})
- if so.db.logger != nil {
+ if so.db.logger != nil && so.db.logger.OnCodeChange != nil {
so.db.logger.OnCodeChange(so.address, so.data.CodeHash, prevcode, codeHash, code)
}
so.setCode(codeHash, code)
@@ -368,7 +368,7 @@ func (so *stateObject) SetNonce(nonce uint64) {
account: &so.address,
prev: so.data.Nonce,
})
- if so.db.logger != nil {
+ if so.db.logger != nil && so.db.logger.OnNonceChange != nil {
so.db.logger.OnNonceChange(so.address, so.data.Nonce, nonce)
}
so.setNonce(nonce)
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 961614c6717..4be4b62b90a 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -29,8 +29,8 @@ import (
"github.com/ledgerwatch/erigon-lib/kv/memdb"
checker "gopkg.in/check.v1"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types/accounts"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
)
@@ -53,7 +53,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02}))
- obj3.SetBalance(uint256.NewInt(44), evmtypes.BalanceChangeUnspecified)
+ obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified)
// write some of them to the trie
err := s.w.UpdateAccountData(obj1.address, &obj1.data, new(accounts.Account))
@@ -227,7 +227,7 @@ func TestSnapshot2(t *testing.T) {
// db, trie are already non-empty values
so0 := state.getStateObject(stateobjaddr0)
- so0.SetBalance(uint256.NewInt(42), evmtypes.BalanceChangeUnspecified)
+ so0.SetBalance(uint256.NewInt(42), tracing.BalanceChangeUnspecified)
so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
so0.selfdestructed = false
@@ -247,7 +247,7 @@ func TestSnapshot2(t *testing.T) {
// and one with deleted == true
so1 := state.getStateObject(stateobjaddr1)
- so1.SetBalance(uint256.NewInt(52), evmtypes.BalanceChangeUnspecified)
+ so1.SetBalance(uint256.NewInt(52), tracing.BalanceChangeUnspecified)
so1.SetNonce(53)
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
so1.selfdestructed = true
@@ -338,7 +338,7 @@ func TestDump(t *testing.T) {
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj2.setIncarnation(1)
obj3 := state.GetOrNewStateObject(toAddr([]byte{0x02}))
- obj3.SetBalance(uint256.NewInt(44), evmtypes.BalanceChangeUnspecified)
+ obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified)
// write some of them to the trie
err := w.UpdateAccountData(obj1.address, &obj1.data, new(accounts.Account))
diff --git a/core/state_processor.go b/core/state_processor.go
index 9d0edcdddc0..47e80ab994a 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -22,6 +22,7 @@ import (
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
@@ -39,12 +40,6 @@ func applyTransaction(config *chain.Config, engine consensus.EngineReader, gp *G
receipt *types.Receipt
err error
)
- if evm.Config().Tracer != nil {
- evm.Config().Tracer.CaptureTxStart(evm, tx)
- defer func() {
- evm.Config().Tracer.CaptureTxEnd(receipt, err)
- }()
- }
rules := evm.ChainRules()
msg, err := tx.AsMessage(*types.MakeSigner(config, header.Number.Uint64(), header.Time), header.BaseFee, rules)
@@ -53,6 +48,25 @@ func applyTransaction(config *chain.Config, engine consensus.EngineReader, gp *G
}
msg.SetCheckNonce(!cfg.StatelessExec)
+ if evm.Config().Tracer != nil {
+ if evm.Config().Tracer != nil && evm.Config().Tracer.OnTxStart != nil {
+ evm.Config().Tracer.OnTxStart(&tracing.VMContext{
+ ChainConfig: evm.ChainConfig(),
+ IntraBlockState: ibs,
+ BlockNumber: evm.Context.BlockNumber,
+ Time: evm.Context.Time,
+ Coinbase: evm.Context.Coinbase,
+ Random: evm.Context.PrevRanDao,
+ TxHash: evm.TxHash,
+ }, tx, msg.From())
+ }
+ if evm.Config().Tracer.OnTxEnd != nil {
+ defer func() {
+ evm.Config().Tracer.OnTxEnd(receipt, err)
+ }()
+ }
+ }
+
if msg.FeeCap().IsZero() && engine != nil {
// Only zero-gas transactions may be service ones
syscall := func(contract libcommon.Address, data []byte) ([]byte, error) {
diff --git a/core/state_transition.go b/core/state_transition.go
index 4bf28d1184f..80717e94817 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -27,6 +27,7 @@ import (
cmath "github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/common/u256"
"github.com/ledgerwatch/erigon/consensus/misc"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
@@ -249,8 +250,8 @@ func (st *StateTransition) buyGas(gasBailout bool) error {
}
}
- if st.evm.Config().Tracer != nil {
- st.evm.Config().Tracer.OnGasChange(0, st.msg.Gas(), vm.GasChangeTxInitialBalance)
+ if st.evm.Config().Tracer != nil && st.evm.Config().Tracer.OnGasChange != nil {
+ st.evm.Config().Tracer.OnGasChange(0, st.msg.Gas(), tracing.GasChangeTxInitialBalance)
}
st.gas += st.msg.Gas()
@@ -258,8 +259,8 @@ func (st *StateTransition) buyGas(gasBailout bool) error {
if subBalance {
// CS TODO: cross check
- st.state.SubBalance(st.msg.From(), gasVal, evmtypes.BalanceDecreaseGasBuy)
- st.state.SubBalance(st.msg.From(), blobGasVal, evmtypes.BalanceDecreaseGasBuy)
+ st.state.SubBalance(st.msg.From(), gasVal, tracing.BalanceDecreaseGasBuy)
+ st.state.SubBalance(st.msg.From(), blobGasVal, tracing.BalanceDecreaseGasBuy)
}
return nil
}
@@ -384,8 +385,8 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas)
}
- if t := st.evm.Config().Tracer; t != nil {
- t.OnGasChange(st.gas, st.gas-gas, vm.GasChangeTxIntrinsicGas)
+ if t := st.evm.Config().Tracer; t != nil && t.OnGasChange != nil {
+ t.OnGasChange(st.gas, st.gas-gas, tracing.GasChangeTxIntrinsicGas)
}
st.gas -= gas
@@ -441,12 +442,12 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
}
amount := new(uint256.Int).SetUint64(st.gasUsed())
amount.Mul(amount, effectiveTip) // gasUsed * effectiveTip = how much goes to the block producer (miner, validator)
- st.state.AddBalance(coinbase, amount, evmtypes.BalanceIncreaseRewardTransactionFee)
+ st.state.AddBalance(coinbase, amount, tracing.BalanceIncreaseRewardTransactionFee)
if !msg.IsFree() && rules.IsLondon {
burntContractAddress := st.evm.ChainConfig().GetBurntContract(st.evm.Context.BlockNumber)
if burntContractAddress != nil {
burnAmount := new(uint256.Int).Mul(new(uint256.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee)
- st.state.AddBalance(*burntContractAddress, burnAmount, evmtypes.BalanceChangeUnspecified)
+ st.state.AddBalance(*burntContractAddress, burnAmount, tracing.BalanceChangeUnspecified)
}
}
if st.isBor {
@@ -482,17 +483,17 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
refund = st.state.GetRefund()
}
- if st.evm.Config().Tracer != nil && refund > 0 {
- st.evm.Config().Tracer.OnGasChange(st.gas, st.gas+refund, vm.GasChangeTxRefunds)
+ if st.evm.Config().Tracer != nil && st.evm.Config().Tracer.OnGasChange != nil && refund > 0 {
+ st.evm.Config().Tracer.OnGasChange(st.gas, st.gas+refund, tracing.GasChangeTxRefunds)
}
st.gas += refund
// Return ETH for remaining gas, exchanged at the original rate.
remaining := new(uint256.Int).Mul(new(uint256.Int).SetUint64(st.gas), st.gasPrice)
- st.state.AddBalance(st.msg.From(), remaining, evmtypes.BalanceIncreaseGasReturn)
+ st.state.AddBalance(st.msg.From(), remaining, tracing.BalanceIncreaseGasReturn)
- if st.evm.Config().Tracer != nil && st.gas > 0 {
- st.evm.Config().Tracer.OnGasChange(st.gas, 0, vm.GasChangeTxLeftOverReturned)
+ if st.evm.Config().Tracer != nil && st.evm.Config().Tracer.OnGasChange != nil && st.gas > 0 {
+ st.evm.Config().Tracer.OnGasChange(st.gas, 0, tracing.GasChangeTxLeftOverReturned)
}
// Also return remaining gas to the block gas counter so it is
diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go
new file mode 100644
index 00000000000..684233544be
--- /dev/null
+++ b/core/tracing/hooks.go
@@ -0,0 +1,281 @@
+// Copyright 2024 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 tracing
+
+import (
+ "math/big"
+
+ libcommon "github.com/ledgerwatch/erigon-lib/common"
+
+ "github.com/ledgerwatch/erigon-lib/chain"
+ "github.com/ledgerwatch/erigon/core/types"
+
+ "github.com/holiman/uint256"
+)
+
+// OpContext provides the context at which the opcode is being
+// executed in, including the memory, stack and various contract-level information.
+type OpContext interface {
+ MemoryData() []byte
+ StackData() []uint256.Int
+ Caller() libcommon.Address
+ Address() libcommon.Address
+ CallValue() *uint256.Int
+ CallInput() []byte
+ Code() []byte
+ CodeHash() libcommon.Hash
+}
+
+// IntraBlockState gives tracers access to the whole state.
+type IntraBlockState interface {
+ GetBalance(libcommon.Address) *uint256.Int
+ GetNonce(libcommon.Address) uint64
+ GetCode(libcommon.Address) []byte
+ GetState(addr libcommon.Address, key *libcommon.Hash, value *uint256.Int)
+ Exist(libcommon.Address) bool
+ GetRefund() uint64
+}
+
+// VMContext provides the context for the EVM execution.
+type VMContext struct {
+ Coinbase libcommon.Address
+ BlockNumber uint64
+ Time uint64
+ Random *libcommon.Hash
+ // Effective tx gas price
+ GasPrice *uint256.Int
+ ChainConfig *chain.Config
+ IntraBlockState IntraBlockState
+
+ TxHash libcommon.Hash
+}
+
+// BlockEvent is emitted upon tracing an incoming block.
+// It contains the block as well as consensus related information.
+type BlockEvent struct {
+ Block *types.Block
+ TD *big.Int
+ Finalized *types.Header
+ Safe *types.Header
+}
+
+type (
+ /*
+ - VM events -
+ */
+
+ // TxStartHook is called before the execution of a transaction starts.
+ // Call simulations don't come with a valid signature. `from` field
+ // to be used for address of the caller.
+ TxStartHook = func(vm *VMContext, tx types.Transaction, from libcommon.Address)
+
+ // TxEndHook is called after the execution of a transaction ends.
+ TxEndHook = func(receipt *types.Receipt, err error)
+
+ // EnterHook is invoked when the processing of a message starts.
+ EnterHook = func(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte)
+
+ // ExitHook is invoked when the processing of a message ends.
+ // `revert` is true when there was an error during the execution.
+ // Exceptionally, before the homestead hardfork a contract creation that
+ // ran out of gas when attempting to persist the code to database did not
+ // count as a call failure and did not cause a revert of the call. This will
+ // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`.
+ ExitHook = func(depth int, output []byte, gasUsed uint64, err error, reverted bool)
+
+ // OpcodeHook is invoked just prior to the execution of an opcode.
+ OpcodeHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, rData []byte, depth int, err error)
+
+ // FaultHook is invoked when an error occurs during the execution of an opcode.
+ FaultHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, depth int, err error)
+
+ // GasChangeHook is invoked when the gas changes.
+ GasChangeHook = func(old, new uint64, reason GasChangeReason)
+
+ /*
+ - Chain events -
+ */
+
+ // BlockchainInitHook is called when the blockchain is initialized.
+ BlockchainInitHook = func(chainConfig *chain.Config)
+
+ // BlockStartHook is called before executing `block`.
+ // `td` is the total difficulty prior to `block`.
+ BlockStartHook = func(event BlockEvent)
+
+ // BlockEndHook is called after executing a block.
+ BlockEndHook = func(err error)
+
+ // SkippedBlockHook indicates a block was skipped during processing
+ // due to it being known previously. This can happen e.g. when recovering
+ // from a crash.
+ SkippedBlockHook = func(event BlockEvent)
+
+ // GenesisBlockHook is called when the genesis block is being processed.
+ GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc)
+
+ /*
+ - State events -
+ */
+
+ // BalanceChangeHook is called when the balance of an account changes.
+ BalanceChangeHook = func(addr libcommon.Address, prev, new *uint256.Int, reason BalanceChangeReason)
+
+ // NonceChangeHook is called when the nonce of an account changes.
+ NonceChangeHook = func(addr libcommon.Address, prev, new uint64)
+
+ // CodeChangeHook is called when the code of an account changes.
+ CodeChangeHook = func(addr libcommon.Address, prevCodeHash libcommon.Hash, prevCode []byte, codeHash libcommon.Hash, code []byte)
+
+ // StorageChangeHook is called when the storage of an account changes.
+ StorageChangeHook = func(addr libcommon.Address, slot *libcommon.Hash, prev, new uint256.Int)
+
+ // LogHook is called when a log is emitted.
+ LogHook = func(log *types.Log)
+)
+
+type Hooks struct {
+ // VM events
+ OnTxStart TxStartHook
+ OnTxEnd TxEndHook
+ OnEnter EnterHook
+ OnExit ExitHook
+ OnOpcode OpcodeHook
+ OnFault FaultHook
+ OnGasChange GasChangeHook
+ // Chain events
+ OnBlockchainInit BlockchainInitHook
+ OnBlockStart BlockStartHook
+ OnBlockEnd BlockEndHook
+ OnSkippedBlock SkippedBlockHook
+ OnGenesisBlock GenesisBlockHook
+ // State events
+ OnBalanceChange BalanceChangeHook
+ OnNonceChange NonceChangeHook
+ OnCodeChange CodeChangeHook
+ OnStorageChange StorageChangeHook
+ OnLog LogHook
+}
+
+// BalanceChangeReason is used to indicate the reason for a balance change, useful
+// for tracing and reporting.
+type BalanceChangeReason byte
+
+const (
+ BalanceChangeUnspecified BalanceChangeReason = 0
+
+ // Issuance
+ // BalanceIncreaseRewardMineUncle is a reward for mining an uncle block.
+ BalanceIncreaseRewardMineUncle BalanceChangeReason = 1
+ // BalanceIncreaseRewardMineBlock is a reward for mining a block.
+ BalanceIncreaseRewardMineBlock BalanceChangeReason = 2
+ // BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain.
+ BalanceIncreaseWithdrawal BalanceChangeReason = 3
+ // BalanceIncreaseGenesisBalance is ether allocated at the genesis block.
+ BalanceIncreaseGenesisBalance BalanceChangeReason = 4
+
+ // Transaction fees
+ // BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance.
+ BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5
+ // BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction.
+ // Part of this gas will be burnt as per EIP-1559 rules.
+ BalanceDecreaseGasBuy BalanceChangeReason = 6
+ // BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution.
+ BalanceIncreaseGasReturn BalanceChangeReason = 7
+
+ // DAO fork
+ // BalanceIncreaseDaoContract is ether sent to the DAO refund contract.
+ BalanceIncreaseDaoContract BalanceChangeReason = 8
+ // BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract.
+ BalanceDecreaseDaoAccount BalanceChangeReason = 9
+
+ // BalanceChangeTransfer is ether transferred via a call.
+ // it is a decrease for the sender and an increase for the recipient.
+ BalanceChangeTransfer BalanceChangeReason = 10
+ // BalanceChangeTouchAccount is a transfer of zero value. It is only there to
+ // touch-create an account.
+ BalanceChangeTouchAccount BalanceChangeReason = 11
+
+ // BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account.
+ BalanceIncreaseSelfdestruct BalanceChangeReason = 12
+ // BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct.
+ BalanceDecreaseSelfdestruct BalanceChangeReason = 13
+ // BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed
+ // account within the same tx (captured at end of tx).
+ // Note it doesn't account for a self-destruct which appoints itself as recipient.
+ BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14
+)
+
+// GasChangeReason is used to indicate the reason for a gas change, useful
+// for tracing and reporting.
+//
+// There is essentially two types of gas changes, those that can be emitted once per transaction
+// and those that can be emitted on a call basis, so possibly multiple times per transaction.
+//
+// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted
+// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis.
+type GasChangeReason byte
+
+const (
+ GasChangeUnspecified GasChangeReason = 0
+
+ // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
+ // one such gas change per transaction.
+ GasChangeTxInitialBalance GasChangeReason = 1
+ // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
+ // always exactly one of those per transaction.
+ GasChangeTxIntrinsicGas GasChangeReason = 2
+ // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared)
+ // this generates an increase in gas. There is at most one of such gas change per transaction.
+ GasChangeTxRefunds GasChangeReason = 3
+ // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned
+ // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas
+ // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller.
+ // There is at most one of such gas change per transaction.
+ GasChangeTxLeftOverReturned GasChangeReason = 4
+
+ // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
+ // one such gas change per call.
+ GasChangeCallInitialBalance GasChangeReason = 5
+ // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always
+ // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even
+ // will be emitted.
+ GasChangeCallLeftOverReturned GasChangeReason = 6
+ // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it
+ // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child.
+ // If there was no gas left to be refunded, no such even will be emitted.
+ GasChangeCallLeftOverRefunded GasChangeReason = 7
+ // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE.
+ GasChangeCallContractCreation GasChangeReason = 8
+ // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2.
+ GasChangeCallContractCreation2 GasChangeReason = 9
+ // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage.
+ GasChangeCallCodeStorage GasChangeReason = 10
+ // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was
+ // performed can be check by `OnOpcode` handling.
+ GasChangeCallOpCode GasChangeReason = 11
+ // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution.
+ GasChangeCallPrecompiledContract GasChangeReason = 12
+ // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules.
+ GasChangeCallStorageColdAccess GasChangeReason = 13
+ // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
+ GasChangeCallFailedExecution GasChangeReason = 14
+
+ // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
+ // it will be "manually" tracked by a direct emit of the gas change event.
+ GasChangeIgnored GasChangeReason = 0xFF
+)
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 9f9ed821657..2af9a15631f 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -19,6 +19,7 @@ package vm
import (
"github.com/holiman/uint256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
)
// ContractRef is a reference to the contract's backing object
@@ -166,17 +167,28 @@ func (c *Contract) Caller() libcommon.Address {
}
// UseGas attempts the use gas and subtracts it and returns true on success
-func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) (ok bool) {
+func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
if c.Gas < gas {
return false
}
- if logger != nil && reason != GasChangeIgnored {
+ if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
}
c.Gas -= gas
return true
}
+// RefundGas refunds gas to the contract
+func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
+ if gas == 0 {
+ return
+ }
+ if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
+ logger.OnGasChange(c.Gas, c.Gas+gas, reason)
+ }
+ c.Gas += gas
+}
+
// Address returns the contracts address
func (c *Contract) Address() libcommon.Address {
return c.self
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 60fd4832c9e..6f65f067288 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -31,6 +31,7 @@ import (
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/math"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/crypto/bls12381"
"github.com/ledgerwatch/erigon/crypto/bn256"
@@ -192,14 +193,14 @@ func ActivePrecompiles(rules *chain.Rules) []libcommon.Address {
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
-func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger EVMLogger,
+func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks,
) (ret []byte, remainingGas uint64, err error) {
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas
}
- if logger != nil {
- logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract)
+ if logger != nil && logger.OnGasChange != nil {
+ logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
}
suppliedGas -= gasCost
output, err := p.Run(input)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 0ba75ef8492..c4c3907ec85 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -26,6 +26,7 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/common/u256"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/params"
@@ -182,9 +183,9 @@ func (evm *EVM) call(typ OpCode, caller ContractRef, addr libcommon.Address, inp
// DELEGATECALL inherits value from parent call
v = parent.value
}
- evm.captureBegin(depth == 0, typ, caller.Address(), addr, isPrecompile, input, gas, v, code)
+ evm.captureBegin(depth, typ, caller.Address(), addr, isPrecompile, input, gas, v, code)
defer func(startGas uint64) {
- evm.captureEnd(depth == 0, typ, startGas, leftOverGas, ret, err)
+ evm.captureEnd(depth, typ, startGas, leftOverGas, ret, err)
}(gas)
}
@@ -220,7 +221,7 @@ func (evm *EVM) call(typ OpCode, caller ContractRef, addr libcommon.Address, inp
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios
- evm.intraBlockState.AddBalance(addr, u256.Num0, evmtypes.BalanceChangeTouchAccount)
+ evm.intraBlockState.AddBalance(addr, u256.Num0, tracing.BalanceChangeTouchAccount)
}
// It is allowed to call precompiles, even via delegatecall
@@ -260,8 +261,8 @@ func (evm *EVM) call(typ OpCode, caller ContractRef, addr libcommon.Address, inp
if err != nil || evm.config.RestoreState {
evm.intraBlockState.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
- if evm.Config().Tracer != nil {
- evm.Config().Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
+ if evm.config.Tracer != nil && evm.config.Tracer.OnGasChange != nil {
+ evm.Config().Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
gas = 0
}
@@ -325,9 +326,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
depth := evm.interpreter.Depth()
if evm.Config().Tracer != nil {
- evm.captureBegin(depth == 0, typ, caller.Address(), address, false, codeAndHash.code, gas, value, nil)
+ evm.captureBegin(depth, typ, caller.Address(), address, false, codeAndHash.code, gas, value, nil)
defer func(startGas uint64) {
- evm.captureEnd(depth == 0, typ, startGas, leftOverGas, ret, err)
+ evm.captureEnd(depth, typ, startGas, leftOverGas, ret, err)
}(gas)
}
@@ -358,8 +359,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contractHash := evm.intraBlockState.GetCodeHash(address)
if evm.intraBlockState.GetNonce(address) != 0 || (contractHash != (libcommon.Hash{}) && contractHash != emptyCodeHash) {
err = ErrContractAddressCollision
- if evm.Config().Tracer != nil {
- evm.Config().Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
+ if evm.config.Tracer != nil && evm.config.Tracer.OnGasChange != nil {
+ evm.Config().Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
return nil, libcommon.Address{}, 0, err
}
@@ -401,7 +402,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// by the error checking condition below.
if err == nil {
createDataGas := uint64(len(ret)) * params.CreateDataGas
- if contract.UseGas(createDataGas, evm.Config().Tracer, GasChangeCallCodeStorage) {
+ if contract.UseGas(createDataGas, evm.Config().Tracer, tracing.GasChangeCallCodeStorage) {
evm.intraBlockState.SetCode(address, ret)
} else if evm.chainRules.IsHomestead {
err = ErrCodeStoreOutOfGas
@@ -414,7 +415,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.intraBlockState.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
- contract.UseGas(contract.Gas, evm.Config().Tracer, GasChangeCallFailedExecution)
+ contract.UseGas(contract.Gas, evm.Config().Tracer, tracing.GasChangeCallFailedExecution)
}
}
@@ -466,23 +467,22 @@ func (evm *EVM) IntraBlockState() evmtypes.IntraBlockState {
return evm.intraBlockState
}
-func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, startGas uint64, value *uint256.Int, code []byte) {
+func (evm *EVM) captureBegin(depth int, typ OpCode, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, startGas uint64, value *uint256.Int, code []byte) {
tracer := evm.Config().Tracer
- if isRoot {
- tracer.CaptureStart(from, to, precompile, typ == CREATE || typ == CREATE2, input, startGas, value, code)
- } else {
- tracer.CaptureEnter(typ, from, to, precompile, typ == CREATE || typ == CREATE2, input, startGas, value, code)
+ if tracer.OnEnter != nil {
+ tracer.OnEnter(depth, byte(typ), from, to, precompile, input, startGas, value, code)
+ }
+ if tracer.OnGasChange != nil {
+ tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
}
-
- tracer.OnGasChange(0, startGas, GasChangeCallInitialBalance)
}
-func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) {
+func (evm *EVM) captureEnd(depth int, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) {
tracer := evm.Config().Tracer
- if leftOverGas != 0 {
- tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
+ if leftOverGas != 0 && tracer.OnGasChange != nil {
+ tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
}
var reverted bool
@@ -493,9 +493,21 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas
reverted = false
}
- if isRoot {
- tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
- } else {
- tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
+ if tracer.OnExit != nil {
+ tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
+ }
+}
+
+// GetVMContext provides context about the block being executed as well as state
+// to the tracers.
+func (evm *EVM) GetVMContext() *tracing.VMContext {
+ return &tracing.VMContext{
+ Coinbase: evm.Context.Coinbase,
+ BlockNumber: evm.Context.BlockNumber,
+ Time: evm.Context.Time,
+ Random: evm.Context.PrevRanDao,
+ GasPrice: evm.TxContext.GasPrice,
+ ChainConfig: evm.ChainConfig(),
+ IntraBlockState: evm.IntraBlockState(),
}
}
diff --git a/core/vm/evmtypes/evmtypes.go b/core/vm/evmtypes/evmtypes.go
index 6f0e6fcc89e..a03be566ad9 100644
--- a/core/vm/evmtypes/evmtypes.go
+++ b/core/vm/evmtypes/evmtypes.go
@@ -9,6 +9,7 @@ import (
"github.com/ledgerwatch/erigon-lib/common"
types2 "github.com/ledgerwatch/erigon-lib/types"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
)
@@ -55,60 +56,12 @@ type (
GetHashFunc func(uint64) common.Hash
)
-// BalanceChangeReason is used to indicate the reason for a balance change, useful
-// for tracing and reporting.
-type BalanceChangeReason byte
-
-const (
- BalanceChangeUnspecified BalanceChangeReason = 0
- // Issuance
- // BalanceIncreaseRewardMineUncle is a reward for mining an uncle block.
- BalanceIncreaseRewardMineUncle BalanceChangeReason = 1
- // BalanceIncreaseRewardMineBlock is a reward for mining a block.
- BalanceIncreaseRewardMineBlock BalanceChangeReason = 2
- // BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain.
- BalanceIncreaseWithdrawal BalanceChangeReason = 3
- // BalanceIncreaseGenesisBalance is ether allocated at the genesis block.
- BalanceIncreaseGenesisBalance BalanceChangeReason = 4
-
- // Transaction fees
- // BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance.
- BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5
- // BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction.
- // Part of this gas will be burnt as per EIP-1559 rules.
- BalanceDecreaseGasBuy BalanceChangeReason = 6
- // BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution.
- BalanceIncreaseGasReturn BalanceChangeReason = 7
-
- // DAO fork
- // BalanceIncreaseDaoContract is ether sent to the DAO refund contract.
- BalanceIncreaseDaoContract BalanceChangeReason = 8
- // BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract.
- BalanceDecreaseDaoAccount BalanceChangeReason = 9
-
- // BalanceChangeTransfer is ether transfered via a call.
- // it is a decrease for the sender and an increase for the recipient.
- BalanceChangeTransfer BalanceChangeReason = 10
- // BalanceChangeTouchAccount is a transfer of zero value. It is only there to
- // touch-create an account.
- BalanceChangeTouchAccount BalanceChangeReason = 11
-
- // BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account.
- BalanceIncreaseSelfdestruct BalanceChangeReason = 12
- // BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct.
- BalanceDecreaseSelfdestruct BalanceChangeReason = 13
- // BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed
- // account within the same tx (captured at end of tx).
- // Note it doesn't account for a self-destruct which appoints itself as recipient.
- BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14
-)
-
// IntraBlockState is an EVM database for full state querying.
type IntraBlockState interface {
CreateAccount(common.Address, bool)
- SubBalance(common.Address, *uint256.Int, BalanceChangeReason)
- AddBalance(common.Address, *uint256.Int, BalanceChangeReason)
+ SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason)
+ AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason)
GetBalance(common.Address) *uint256.Int
GetNonce(common.Address) uint64
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 9a22dff97db..c48414744bb 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -26,8 +26,8 @@ import (
"github.com/ledgerwatch/log/v3"
"github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/params"
)
@@ -279,9 +279,6 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
panic(err)
}
- if interpreter.evm.Config().Tracer != nil {
- interpreter.evm.Config().Tracer.CaptureKeccakPreimage(libcommon.BytesToHash(interpreter.hasherBuf[:]), data)
- }
size.SetBytes(interpreter.hasherBuf[:])
return nil, nil
}
@@ -655,7 +652,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// reuse size int for stackvalue
stackvalue := size
- scope.Contract.UseGas(gas, interpreter.evm.Config().Tracer, GasChangeCallContractCreation)
+ scope.Contract.UseGas(gas, interpreter.evm.Config().Tracer, tracing.GasChangeCallContractCreation)
res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value)
@@ -671,10 +668,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
stackvalue.SetBytes(addr.Bytes())
}
- if interpreter.evm.Config().Tracer != nil && returnGas > 0 {
- interpreter.evm.Config().Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
- }
- scope.Contract.Gas += returnGas
+ scope.Contract.RefundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded)
if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer
@@ -698,7 +692,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
// Apply EIP150
gas -= gas / 64
- scope.Contract.UseGas(gas, interpreter.evm.Config().Tracer, GasChangeCallContractCreation2)
+ scope.Contract.UseGas(gas, interpreter.evm.Config().Tracer, tracing.GasChangeCallContractCreation2)
// reuse size int for stackvalue
stackValue := size
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, &endowment, &salt)
@@ -711,11 +705,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
}
scope.Stack.Push(&stackValue)
- if interpreter.evm.Config().Tracer != nil && returnGas > 0 {
- interpreter.evm.Config().Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
- }
-
- scope.Contract.Gas += returnGas
+ scope.Contract.RefundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded)
if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer
@@ -757,11 +747,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- if interpreter.evm.Config().Tracer != nil && returnGas > 0 {
- interpreter.evm.Config().Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
- }
-
- scope.Contract.Gas += returnGas
+ scope.Contract.RefundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@@ -795,11 +781,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- if interpreter.evm.Config().Tracer != nil && returnGas > 0 {
- interpreter.evm.Config().Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
- }
-
- scope.Contract.Gas += returnGas
+ scope.Contract.RefundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@@ -829,11 +811,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- if interpreter.evm.Config().Tracer != nil && returnGas > 0 {
- interpreter.evm.Config().Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
- }
-
- scope.Contract.Gas += returnGas
+ scope.Contract.RefundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@@ -863,11 +841,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- if interpreter.evm.Config().Tracer != nil && returnGas > 0 {
- interpreter.evm.Config().Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
- }
-
- scope.Contract.Gas += returnGas
+ scope.Contract.RefundGas(returnGas, interpreter.evm.config.Tracer, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@@ -902,11 +876,15 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
callerAddr := scope.Contract.Address()
beneficiaryAddr := libcommon.Address(beneficiary.Bytes20())
balance := *interpreter.evm.IntraBlockState().GetBalance(callerAddr)
- interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, &balance, evmtypes.BalanceIncreaseSelfdestruct)
+ interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, &balance, tracing.BalanceIncreaseSelfdestruct)
interpreter.evm.IntraBlockState().Selfdestruct(callerAddr)
if interpreter.evm.Config().Tracer != nil {
- interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, callerAddr, beneficiaryAddr, false /* precompile */, false /* create */, []byte{}, 0, &balance, nil /* code */)
- interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil, false)
+ if interpreter.evm.Config().Tracer.OnEnter != nil {
+ interpreter.evm.Config().Tracer.OnEnter(interpreter.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), false, []byte{}, 0, &balance, nil)
+ }
+ if interpreter.evm.Config().Tracer.OnExit != nil {
+ interpreter.evm.Config().Tracer.OnExit(interpreter.depth, []byte{}, 0, nil, false)
+ }
}
return nil, errStopToken
}
@@ -919,12 +897,16 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
callerAddr := scope.Contract.Address()
beneficiaryAddr := libcommon.Address(beneficiary.Bytes20())
balance := *interpreter.evm.IntraBlockState().GetBalance(callerAddr)
- interpreter.evm.IntraBlockState().SubBalance(callerAddr, &balance, evmtypes.BalanceDecreaseSelfdestruct)
- interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, &balance, evmtypes.BalanceIncreaseSelfdestruct)
+ interpreter.evm.IntraBlockState().SubBalance(callerAddr, &balance, tracing.BalanceDecreaseSelfdestruct)
+ interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, &balance, tracing.BalanceIncreaseSelfdestruct)
interpreter.evm.IntraBlockState().Selfdestruct6780(callerAddr)
if interpreter.evm.Config().Tracer != nil {
- interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, callerAddr, beneficiaryAddr, false /* precompile */, false /* create */, []byte{}, 0, &balance, nil /* code */)
- interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil, false)
+ if interpreter.cfg.Tracer.OnEnter != nil {
+ interpreter.cfg.Tracer.OnEnter(interpreter.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), false, []byte{}, 0, &balance, nil)
+ }
+ if interpreter.cfg.Tracer.OnExit != nil {
+ interpreter.cfg.Tracer.OnExit(interpreter.depth, []byte{}, 0, nil, false)
+ }
}
return nil, errStopToken
}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index be294bf5af0..ac409915f1e 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -20,26 +20,29 @@ import (
"hash"
"sync"
+ "github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/chain"
+ "github.com/ledgerwatch/erigon-lib/common"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/math"
"github.com/ledgerwatch/log/v3"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/vm/stack"
)
// Config are the configuration options for the Interpreter
type Config struct {
- Debug bool // Enables debugging
- Tracer EVMLogger // Opcode logger
- NoRecursion bool // Disables call, callcode, delegate call and create
- NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
- SkipAnalysis bool // Whether we can skip jumpdest analysis based on the checked history
- TraceJumpDest bool // Print transaction hashes where jumpdest analysis was useful
- NoReceipts bool // Do not calculate receipts
- ReadOnly bool // Do no perform any block finalisation
- StatelessExec bool // true is certain conditions (like state trie root hash matching) need to be relaxed for stateless EVM execution
- RestoreState bool // Revert all changes made to the state (useful for constant system calls)
+ Debug bool // Enables debugging
+ Tracer *tracing.Hooks
+ NoRecursion bool // Disables call, callcode, delegate call and create
+ NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
+ SkipAnalysis bool // Whether we can skip jumpdest analysis based on the checked history
+ TraceJumpDest bool // Print transaction hashes where jumpdest analysis was useful
+ NoReceipts bool // Do not calculate receipts
+ ReadOnly bool // Do no perform any block finalisation
+ StatelessExec bool // true is certain conditions (like state trie root hash matching) need to be relaxed for stateless EVM execution
+ RestoreState bool // Revert all changes made to the state (useful for constant system calls)
ExtraEips []int // Additional EIPS that are to be enabled
}
@@ -80,6 +83,53 @@ type ScopeContext struct {
Contract *Contract
}
+// MemoryData returns the underlying memory slice. Callers must not modify the contents
+// of the returned data.
+func (ctx *ScopeContext) MemoryData() []byte {
+ if ctx.Memory == nil {
+ return nil
+ }
+ return ctx.Memory.Data()
+}
+
+// MemoryData returns the stack data. Callers must not modify the contents
+// of the returned data.
+func (ctx *ScopeContext) StackData() []uint256.Int {
+ if ctx.Stack == nil {
+ return nil
+ }
+ return ctx.Stack.Data
+}
+
+// Caller returns the current caller.
+func (ctx *ScopeContext) Caller() common.Address {
+ return ctx.Contract.Caller()
+}
+
+// Address returns the address where this scope of execution is taking place.
+func (ctx *ScopeContext) Address() common.Address {
+ return ctx.Contract.Address()
+}
+
+// CallValue returns the value supplied with this call.
+func (ctx *ScopeContext) CallValue() *uint256.Int {
+ return ctx.Contract.Value()
+}
+
+// CallInput returns the input/calldata with this call. Callers must not modify
+// the contents of the returned data.
+func (ctx *ScopeContext) CallInput() []byte {
+ return ctx.Contract.Input
+}
+
+func (ctx *ScopeContext) Code() []byte {
+ return ctx.Contract.Code
+}
+
+func (ctx *ScopeContext) CodeHash() libcommon.Hash {
+ return ctx.Contract.CodeHash
+}
+
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
@@ -226,10 +276,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
defer func() {
// first: capture data/memory/state/depth/etc... then clenup them
if in.cfg.Tracer != nil && err != nil {
- if !logged {
- in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err)) //nolint:errcheck
- } else {
- in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.depth, VMErrorFromErr(err))
+ if !logged && in.evm.config.Tracer.OnOpcode != nil {
+ in.evm.config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err))
+ }
+ if logged && in.evm.config.Tracer.OnFault != nil {
+ in.evm.config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.depth, VMErrorFromErr(err))
}
}
// this function must execute _after_: the `CaptureState` needs the stacks before
@@ -266,7 +317,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
- if !contract.UseGas(cost, in.cfg.Tracer, GasChangeIgnored) {
+ if !contract.UseGas(cost, in.cfg.Tracer, tracing.GasChangeIgnored) {
return nil, ErrOutOfGas
}
if operation.dynamicGas != nil {
@@ -292,21 +343,27 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, locStack, mem, memorySize)
cost += dynamicCost // for tracing
- if err != nil || !contract.UseGas(dynamicCost, in.cfg.Tracer, GasChangeIgnored) {
+ if err != nil || !contract.UseGas(dynamicCost, in.cfg.Tracer, tracing.GasChangeIgnored) {
return nil, ErrOutOfGas
}
// Do tracing before memory expansion
if in.cfg.Tracer != nil {
- in.cfg.Tracer.CaptureState(_pc, op, gasCopy, cost, callContext, in.returnData, in.depth, err) //nolint:errcheck
- logged = true
+ if in.evm.config.Tracer.OnOpcode != nil {
+ in.evm.config.Tracer.OnOpcode(_pc, byte(op), gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err))
+ logged = true
+ }
}
if memorySize > 0 {
mem.Resize(memorySize)
}
} else if in.cfg.Tracer != nil {
- in.cfg.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode)
- in.cfg.Tracer.CaptureState(_pc, op, gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err)) //nolint:errcheck
- logged = true
+ if in.evm.config.Tracer.OnGasChange != nil {
+ in.evm.config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
+ }
+ if in.evm.config.Tracer.OnOpcode != nil {
+ in.evm.config.Tracer.OnOpcode(_pc, byte(op), gasCopy, cost, callContext, in.returnData, in.depth, VMErrorFromErr(err))
+ logged = true
+ }
}
// execute the operation
res, err = operation.execute(pc, in, callContext)
diff --git a/core/vm/logger.go b/core/vm/logger.go
deleted file mode 100644
index b27825b5ac5..00000000000
--- a/core/vm/logger.go
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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 vm
-
-import (
- "github.com/holiman/uint256"
- libcommon "github.com/ledgerwatch/erigon-lib/common"
-
- "github.com/ledgerwatch/erigon/core/types"
-)
-
-// 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.
-// Note that reference types are actual VM data structures; make copies
-// if you need to retain them beyond the current call.
-type EVMLogger interface {
- // Transaction level
- CaptureTxStart(evm *EVM, tx types.Transaction)
- CaptureTxEnd(receipt *types.Receipt, err error)
- // Top call frame
- CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte)
- // CaptureEnd is invoked when the processing of the top call ends.
- // See docs for `CaptureExit` for info on the `reverted` parameter.
- CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool)
- // Rest of the frames
- CaptureEnter(typ OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte)
- // CaptureExit is invoked when the processing of a message ends.
- // `revert` is true when there was an error during the execution.
- // Exceptionally, before the homestead hardfork a contract creation that
- // ran out of gas when attempting to persist the code to database did not
- // count as a call failure and did not cause a revert of the call. This will
- // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`.
- CaptureExit(output []byte, gasUsed uint64, err error, reverted bool)
- // Opcode level
- CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
- CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
- CaptureKeccakPreimage(hash libcommon.Hash, data []byte)
- // Misc
- OnGasChange(old, new uint64, reason GasChangeReason)
-}
-
-// GasChangeReason is used to indicate the reason for a gas change, useful
-// for tracing and reporting.
-//
-// There is essentially two types of gas changes, those that can be emitted once per transaction
-// and those that can be emitted on a call basis, so possibly multiple times per transaction.
-//
-// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted
-// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis.
-type GasChangeReason byte
-
-const (
- GasChangeUnspecified GasChangeReason = iota
-
- // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
- // one such gas change per transaction.
- GasChangeTxInitialBalance
- // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
- // always exactly one of those per transaction.
- GasChangeTxIntrinsicGas
- // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared)
- // this generates an increase in gas. There is at most one of such gas change per transaction.
- GasChangeTxRefunds
- // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned
- // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas
- // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller.
- // There is at most one of such gas change per transaction.
- GasChangeTxLeftOverReturned
-
- // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
- // one such gas change per call.
- GasChangeCallInitialBalance
- // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always
- // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even
- // will be emitted.
- GasChangeCallLeftOverReturned
- // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it
- // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child.
- // If there was no gas left to be refunded, no such even will be emitted.
- GasChangeCallLeftOverRefunded
- // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE.
- GasChangeCallContractCreation
- // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2.
- GasChangeCallContractCreation2
- // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage.
- GasChangeCallCodeStorage
- // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was
- // performed can be check by `CaptureState` handling.
- GasChangeCallOpCode
- // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution.
- GasChangeCallPrecompiledContract
- // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules.
- GasChangeCallStorageColdAccess
- // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
- GasChangeCallFailedExecution
-
- // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
- // it will be "manually" tracked by a direct emit of the gas change event.
- GasChangeIgnored GasChangeReason = 0xFF
-)
-
-// FlushableTracer is a Tracer extension whose accumulated traces has to be
-// flushed once the tracing is completed.
-type FlushableTracer interface {
- EVMLogger
- Flush(tx types.Transaction)
-}
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
index c80016783a2..678891adda9 100644
--- a/core/vm/operations_acl.go
+++ b/core/vm/operations_acl.go
@@ -23,6 +23,7 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/math"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/vm/stack"
"github.com/ledgerwatch/erigon/params"
)
@@ -163,7 +164,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
if addrMod {
// Charge the remaining difference here already, to correctly calculate available
// gas for call
- if !contract.UseGas(coldCost, evm.Config().Tracer, GasChangeCallStorageColdAccess) {
+ if !contract.UseGas(coldCost, evm.Config().Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 892af8b9c26..ee221c5b237 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -136,7 +136,7 @@ func Execute(code, input []byte, cfg *Config, bn uint64) ([]byte, *state.IntraBl
rules = vmenv.ChainRules()
)
if cfg.EVMConfig.Tracer != nil {
- cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTransaction(0, address, cfg.Value, cfg.GasLimit, cfg.GasPrice, input))
+ cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTransaction(0, address, cfg.Value, cfg.GasLimit, cfg.GasPrice, input), cfg.Origin)
}
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
cfg.State.CreateAccount(address, true)
@@ -183,7 +183,7 @@ func Create(input []byte, cfg *Config, blockNr uint64) ([]byte, libcommon.Addres
rules = vmenv.ChainRules()
)
if cfg.EVMConfig.Tracer != nil {
- cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewContractCreation(0, cfg.Value, cfg.GasLimit, cfg.GasPrice, input))
+ cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewContractCreation(0, cfg.Value, cfg.GasLimit, cfg.GasPrice, input), cfg.Origin)
}
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
@@ -212,7 +212,7 @@ func Call(address libcommon.Address, input []byte, cfg *Config) ([]byte, uint64,
statedb := cfg.State
rules := vmenv.ChainRules()
if cfg.EVMConfig.Tracer != nil {
- cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTransaction(0, address, cfg.Value, cfg.GasLimit, cfg.GasPrice, input))
+ cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTransaction(0, address, cfg.Value, cfg.GasLimit, cfg.GasPrice, input), cfg.Origin)
}
statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 1e326eea237..f4d8ffb801f 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -525,7 +525,7 @@ func TestEip2929Cases(t *testing.T) {
Execute(code, nil, &Config{
EVMConfig: vm.Config{
Debug: true,
- Tracer: logger.NewMarkdownLogger(nil, os.Stdout),
+ Tracer: logger.NewMarkdownLogger(nil, os.Stdout).Hooks(),
ExtraEips: []int{2929},
},
}, 0)
diff --git a/eth/backend.go b/eth/backend.go
index 55726c14bab..d8027683707 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -220,7 +220,7 @@ const blockBufferSize = 128
// New creates a new Ethereum object (including the
// initialisation of the common Ethereum object)
-func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger log.Logger, tracer tracers.Tracer) (*Ethereum, error) {
+func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger log.Logger, tracer *tracers.Tracer) (*Ethereum, error) {
config.Snapshot.Enabled = config.Sync.UseSnapshots
if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(libcommon.Big0) <= 0 {
logger.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice)
@@ -287,7 +287,7 @@ func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger
genesisSpec = nil
}
var genesisErr error
- chainConfig, genesis, genesisErr = core.WriteGenesisBlock(tx, genesisSpec, config.OverrideCancunTime, tmpdir, logger, tracer)
+ chainConfig, genesis, genesisErr = core.WriteGenesisBlock(tx, genesisSpec, config.OverrideCancunTime, tmpdir, logger, tracer.Hooks)
if _, ok := genesisErr.(*chain.ConfigCompatError); genesisErr != nil && !ok {
return genesisErr
}
diff --git a/eth/calltracer/calltracer.go b/eth/calltracer/calltracer.go
index 4104f3c454b..ad98d6a02f1 100644
--- a/eth/calltracer/calltracer.go
+++ b/eth/calltracer/calltracer.go
@@ -2,22 +2,23 @@ package calltracer
import (
"encoding/binary"
- "math/big"
"sort"
"github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/length"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
type CallTracer struct {
+ t *tracers.Tracer
+
froms map[libcommon.Address]struct{}
tos map[libcommon.Address]bool // address -> isCreated
}
@@ -29,9 +30,13 @@ func NewCallTracer() *CallTracer {
}
}
-func (ct *CallTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {}
-
-func (ct *CallTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
+func (ct *CallTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnEnter: ct.OnEnter,
+ },
+ }
+}
// CaptureStart and CaptureEnter also capture SELFDESTRUCT opcode invocations
func (ct *CallTracer) captureStartOrEnter(from, to libcommon.Address, create bool, code []byte) {
@@ -49,49 +54,10 @@ func (ct *CallTracer) captureStartOrEnter(from, to libcommon.Address, create boo
}
}
-func (ct *CallTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (ct *CallTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ create := vm.OpCode(typ) == vm.CREATE
ct.captureStartOrEnter(from, to, create, code)
}
-func (ct *CallTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- ct.captureStartOrEnter(from, to, create, code)
-}
-func (ct *CallTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
-}
-func (ct *CallTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-func (ct *CallTracer) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
-}
-
-func (ct *CallTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (ct *CallTracer) OnBlockEnd(err error) {}
-
-func (ct *CallTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {}
-
-func (ct *CallTracer) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (ct *CallTracer) OnBeaconBlockRootEnd() {}
-
-func (ct *CallTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (ct *CallTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (ct *CallTracer) OnBalanceChange(addr libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (ct *CallTracer) OnNonceChange(addr libcommon.Address, prev, new uint64) {}
-
-func (ct *CallTracer) OnCodeChange(addr libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (ct *CallTracer) OnStorageChange(addr libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (ct *CallTracer) OnLog(log *types.Log) {}
-
-func (ct *CallTracer) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
-}
func (ct *CallTracer) WriteToDb(tx kv.StatelessWriteTx, block *types.Block, vmConfig vm.Config) error {
ct.tos[block.Coinbase()] = false
diff --git a/eth/stagedsync/stage_execute.go b/eth/stagedsync/stage_execute.go
index c63182c5c92..12b43eb8b0f 100644
--- a/eth/stagedsync/stage_execute.go
+++ b/eth/stagedsync/stage_execute.go
@@ -36,6 +36,7 @@ import (
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/core/vm"
@@ -158,14 +159,14 @@ func executeBlock(
return h
}
- getTracer := func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) {
- return tracelogger.NewStructLogger(&tracelogger.LogConfig{}), nil
+ getTracer := func(txIndex int, txHash common.Hash) (*tracing.Hooks, error) {
+ return tracelogger.NewStructLogger(&tracelogger.LogConfig{}).Hooks(), nil
}
callTracer := calltracer.NewCallTracer()
vmConfig.Debug = true
if vmConfig.Tracer == nil {
- vmConfig.Tracer = callTracer
+ vmConfig.Tracer = callTracer.Tracer().Hooks
}
var receipts types.Receipts
diff --git a/eth/tracers/api.go b/eth/tracers/config/api.go
similarity index 97%
rename from eth/tracers/api.go
rename to eth/tracers/config/api.go
index 9890821192a..9e3e0b2a654 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/config/api.go
@@ -1,4 +1,4 @@
-package tracers
+package config
import (
"encoding/json"
diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go
index 35dfa8d9c19..792f3d835cc 100644
--- a/eth/tracers/internal/tracetest/calltrace_test.go
+++ b/eth/tracers/internal/tracetest/calltrace_test.go
@@ -161,18 +161,18 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- statedb.SetLogger(tracer)
- evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+ statedb.SetLogger(tracer.Hooks)
+ evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer.Hooks})
msg, err := tx.AsMessage(*signer, test.Genesis.BaseFee, rules)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
- tracer.CaptureTxStart(evm, tx)
+ tracer.OnTxStart(evm.GetVMContext(), tx, msg.From())
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.GetGas()).AddBlobGas(tx.GetBlobGas()), true /* refunds */, false /* gasBailout */)
if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err)
// Retrieve the trace result and compare against the expected.
res, err := tracer.GetResult()
if err != nil {
@@ -272,7 +272,7 @@ func benchTracer(b *testing.B, tracerName string, test *callTracerTest) {
if err != nil {
b.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+ evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer.Hooks})
snap := statedb.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.GetGas()).AddBlobGas(tx.GetBlobGas()))
if _, err = st.TransitionDb(true /* refunds */, false /* gasBailout */); err != nil {
@@ -346,18 +346,18 @@ func TestZeroValueToNotExitCall(t *testing.T) {
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- statedb.SetLogger(tracer)
- evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
- tracer.CaptureTxStart(evm, tx)
+ statedb.SetLogger(tracer.Hooks)
+ evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer.Hooks})
msg, err := tx.AsMessage(*signer, nil, rules)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
+ tracer.OnTxStart(evm.GetVMContext(), tx, msg.From())
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.GetGas()).AddBlobGas(tx.GetBlobGas()), true /* refunds */, false /* gasBailout */)
if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err)
// Retrieve the trace result and compare against the etalon
res, err := tracer.GetResult()
if err != nil {
diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go
index f052192c171..510ace78106 100644
--- a/eth/tracers/internal/tracetest/prestate_test.go
+++ b/eth/tracers/internal/tracetest/prestate_test.go
@@ -126,18 +126,18 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- statedb.SetLogger(tracer)
- evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+ statedb.SetLogger(tracer.Hooks)
+ evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer.Hooks})
msg, err := tx.AsMessage(*signer, nil, rules) // BaseFee is set to nil and not to contet.BaseFee, to match the output to go-ethereum tests
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
- tracer.CaptureTxStart(evm, tx)
+ tracer.OnTxStart(evm.GetVMContext(), tx, msg.From())
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.GetGas()), true, false)
if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
// Retrieve the trace result and compare against the expected
res, err := tracer.GetResult()
if err != nil {
diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go
index 8ab2df831af..800f01e028b 100644
--- a/eth/tracers/js/goja.go
+++ b/eth/tracers/js/goja.go
@@ -28,10 +28,9 @@ import (
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
- "github.com/ledgerwatch/erigon/core/vm/stack"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/eth/tracers"
jsassets "github.com/ledgerwatch/erigon/eth/tracers/js/internal/tracers"
@@ -98,7 +97,7 @@ type jsTracer struct {
tracers.NoopTracer
vm *goja.Runtime
- env *vm.EVM
+ env *tracing.VMContext
toBig toBigFn // Converts a hex string into a JS bigint
toBuf toBufFn // Converts a []byte into a JS buffer
fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte
@@ -134,7 +133,7 @@ type jsTracer struct {
// The methods `result` and `fault` are required to be present.
// The methods `step`, `enter`, and `exit` are optional, but note that
// `enter` and `exit` always go together.
-func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
if c, ok := assetTracers[code]; ok {
code = c
}
@@ -210,26 +209,37 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer
t.frameValue = t.frame.setupObject()
t.frameResultValue = t.frameResult.setupObject()
t.logValue = t.log.setupObject()
- return t, nil
-}
-
-// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnTxEnd: t.OnTxEnd,
+ OnEnter: t.OnEnter,
+ OnExit: t.OnExit,
+ OnOpcode: t.OnOpcode,
+ OnFault: t.OnFault,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }, nil
+}
+
+// OnTxStart implements the Tracer interface and is invoked at the beginning of
// transaction processing.
-func (t *jsTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
+func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
t.env = env
// Need statedb access for db object
- db := &dbObj{ibs: env.IntraBlockState(), vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
+ db := &dbObj{ibs: env.IntraBlockState, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
t.dbValue = db.setupObject()
// Update list of precompiles based on current block
- rules := env.ChainRules()
+ rules := env.ChainConfig.Rules(env.BlockNumber, env.Time)
t.activePrecompiles = vm.ActivePrecompiles(rules)
- t.ctx["block"] = t.vm.ToValue(t.env.Context.BlockNumber)
- t.ctx["gasPrice"] = t.vm.ToValue(t.env.TxContext.GasPrice.ToBig())
+ t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber)
+ t.ctx["gasPrice"] = t.vm.ToValue(t.env.GasPrice.ToBig())
}
-// CaptureTxEnd implements the Tracer interface and is invoked at the end of
+// OnTxEnd implements the Tracer interface and is invoked at the end of
// transaction processing.
-func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
+func (t *jsTracer) OnTxEnd(receipt *types.Receipt, err error) {
if err != nil {
// Don't override vm error
if _, ok := t.ctx["error"]; !ok {
@@ -240,8 +250,11 @@ func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
t.ctx["gasUsed"] = t.vm.ToValue(receipt.GasUsed)
}
-// CaptureStart implements the Tracer interface to initialize the tracing operation.
-func (t *jsTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+// onStart implements the Tracer interface to initialize the tracing operation.
+func (t *jsTracer) onStart(from libcommon.Address, to libcommon.Address, create bool, input []byte, gas uint64, value *uint256.Int) {
+ if t.err != nil {
+ return
+ }
if create {
t.ctx["type"] = t.vm.ToValue("CREATE")
} else {
@@ -259,8 +272,8 @@ func (t *jsTracer) CaptureStart(from libcommon.Address, to libcommon.Address, pr
t.ctx["value"] = valueBig
}
-// CaptureState implements the Tracer interface to trace a single step of VM execution.
-func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+// OnOpcode implements the Tracer interface to trace a single step of VM execution.
+func (t *jsTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
if !t.traceStep {
return
}
@@ -269,14 +282,14 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
}
log := t.log
- log.op.op = op
- log.memory.memory = scope.Memory
- log.stack.stack = scope.Stack
- log.contract.contract = scope.Contract
+ log.op.op = vm.OpCode(op)
+ log.memory.memory = scope.MemoryData()
+ log.stack.stack = scope.StackData()
+ log.contract.scope = scope
log.pc = pc
log.gas = gas
log.cost = cost
- log.refund = t.env.IntraBlockState().GetRefund()
+ log.refund = t.env.IntraBlockState.GetRefund()
log.depth = depth
log.err = err
if _, err := t.step(t.obj, t.logValue, t.dbValue); err != nil {
@@ -284,28 +297,28 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
}
}
-// CaptureFault implements the Tracer interface to trace an execution fault
-func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+// OnFault implements the Tracer interface to trace an execution fault
+func (t *jsTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
if t.err != nil {
return
}
- // Other log fields have been already set as part of the last CaptureState.
+ // Other log fields have been already set as part of the last OnFault.
t.log.err = err
if _, err := t.fault(t.obj, t.logValue, t.dbValue); err != nil {
t.onError("fault", err)
}
}
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
+// onEnd is called after the call finishes to finalize the tracing.
+func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool) {
t.ctx["output"] = t.vm.ToValue(output)
if err != nil {
t.ctx["error"] = t.vm.ToValue(err.Error())
}
}
-// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *jsTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *jsTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
if !t.traceFrame {
return
}
@@ -313,7 +326,12 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcom
return
}
- t.frame.typ = typ.String()
+ if depth == 0 {
+ t.onStart(from, to, vm.OpCode(typ) == vm.CREATE, input, gas, value)
+ return
+ }
+
+ t.frame.typ = vm.OpCode(typ).String()
t.frame.from = from
t.frame.to = to
t.frame.input = libcommon.CopyBytes(input)
@@ -328,13 +346,22 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcom
}
}
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// OnExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
-func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
+func (t *jsTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if !t.traceFrame {
return
}
+ if t.err != nil {
+ return
+ }
+
+ if depth == 0 {
+ t.onEnd(output, gasUsed, err, reverted)
+ return
+ }
+
t.frameResult.gasUsed = uint(gasUsed)
t.frameResult.output = libcommon.CopyBytes(output)
t.frameResult.err = err
@@ -368,9 +395,6 @@ func (t *jsTracer) Stop(err error) {
// execution.
func (t *jsTracer) onError(context string, err error) {
t.err = wrapError(context, err)
- // `env` is set on CaptureStart which comes before any JS execution.
- // So it should be non-nil.
- t.env.Cancel()
}
func wrapError(context string, err error) error {
@@ -549,7 +573,7 @@ func (o *opObj) setupObject() *goja.Object {
}
type memoryObj struct {
- memory *vm.Memory
+ memory []byte
vm *goja.Runtime
toBig toBigFn
toBuf toBufFn
@@ -577,14 +601,10 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
if end < begin || begin < 0 {
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
}
- mlen := mo.memory.Len()
- if end-int64(mlen) > memoryPadLimit {
- return nil, fmt.Errorf("tracer reached limit for padding memory slice: end %d, memorySize %d", end, mlen)
+ slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin)
+ if err != nil {
+ return nil, err
}
- slice := make([]byte, end-begin)
- end = min(end, int64(mo.memory.Len()))
- ptr := mo.memory.GetPtr(begin, end-begin)
- copy(slice, ptr)
return slice, nil
}
@@ -604,14 +624,14 @@ func (mo *memoryObj) GetUint(addr int64) goja.Value {
// getUint returns the 32 bytes at the specified address interpreted as a uint.
func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
- if mo.memory.Len() < int(addr)+32 || addr < 0 {
- return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
+ if len(mo.memory) < int(addr)+32 || addr < 0 {
+ return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", len(mo.memory), addr, 32)
}
- return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil
+ return new(big.Int).SetBytes(tracers.MemoryPtr(mo.memory, addr, 32)), nil
}
func (mo *memoryObj) Length() int {
- return mo.memory.Len()
+ return len(mo.memory)
}
func (m *memoryObj) setupObject() *goja.Object {
@@ -623,7 +643,7 @@ func (m *memoryObj) setupObject() *goja.Object {
}
type stackObj struct {
- stack *stack.Stack
+ stack []uint256.Int
vm *goja.Runtime
toBig toBigFn
}
@@ -644,14 +664,14 @@ func (s *stackObj) Peek(idx int) goja.Value {
// peek returns the nth-from-the-top element of the stack.
func (s *stackObj) peek(idx int) (*big.Int, error) {
- if len(s.stack.Data) <= idx || idx < 0 {
- return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data), idx)
+ if len(s.stack) <= idx || idx < 0 {
+ return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack), idx)
}
- return s.stack.Back(idx).ToBig(), nil
+ return tracers.StackBack(s.stack, idx).ToBig(), nil
}
func (s *stackObj) Length() int {
- return len(s.stack.Data)
+ return len(s.stack)
}
func (s *stackObj) setupObject() *goja.Object {
@@ -662,7 +682,7 @@ func (s *stackObj) setupObject() *goja.Object {
}
type dbObj struct {
- ibs evmtypes.IntraBlockState
+ ibs tracing.IntraBlockState
vm *goja.Runtime
toBig toBigFn
toBuf toBufFn
@@ -755,14 +775,14 @@ func (do *dbObj) setupObject() *goja.Object {
}
type contractObj struct {
- contract *vm.Contract
- vm *goja.Runtime
- toBig toBigFn
- toBuf toBufFn
+ scope tracing.OpContext
+ vm *goja.Runtime
+ toBig toBigFn
+ toBuf toBufFn
}
func (co *contractObj) GetCaller() goja.Value {
- caller := co.contract.Caller().Bytes()
+ caller := co.scope.Caller().Bytes()
res, err := co.toBuf(co.vm, caller)
if err != nil {
co.vm.Interrupt(err)
@@ -772,7 +792,7 @@ func (co *contractObj) GetCaller() goja.Value {
}
func (co *contractObj) GetAddress() goja.Value {
- addr := co.contract.Address().Bytes()
+ addr := co.scope.Address().Bytes()
res, err := co.toBuf(co.vm, addr)
if err != nil {
co.vm.Interrupt(err)
@@ -782,7 +802,7 @@ func (co *contractObj) GetAddress() goja.Value {
}
func (co *contractObj) GetValue() goja.Value {
- value := co.contract.Value()
+ value := co.scope.CallValue()
res, err := co.toBig(co.vm, value.String())
if err != nil {
co.vm.Interrupt(err)
@@ -792,7 +812,7 @@ func (co *contractObj) GetValue() goja.Value {
}
func (co *contractObj) GetInput() goja.Value {
- input := libcommon.CopyBytes(co.contract.Input)
+ input := libcommon.CopyBytes(co.scope.CallInput())
res, err := co.toBuf(co.vm, input)
if err != nil {
co.vm.Interrupt(err)
diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go
index 5485a815f8d..40e77ff9a08 100644
--- a/eth/tracers/js/tracer_test.go
+++ b/eth/tracers/js/tracer_test.go
@@ -65,9 +65,9 @@ func testCtx() *vmContext {
return &vmContext{blockCtx: evmtypes.BlockContext{BlockNumber: 1}, txCtx: evmtypes.TxContext{GasPrice: uint256.NewInt(100000)}}
}
-func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *chain.Config, contractCode []byte) (json.RawMessage, error) {
+func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *chain.Config, contractCode []byte) (json.RawMessage, error) {
var (
- env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer})
+ env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer.Hooks})
gasLimit uint64 = 31000
startGas uint64 = 10000
value = uint256.NewInt(0)
@@ -78,12 +78,12 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *chain.Config, c
contract.Code = contractCode
}
- tracer.CaptureTxStart(env, types.NewTransaction(0, libcommon.Address{}, nil, gasLimit, nil, nil))
- tracer.CaptureStart(contract.Caller(), contract.Address(), false /* precompile */, false /* create */, []byte{}, startGas, value, []byte{} /* code */)
+ tracer.OnTxStart(env.GetVMContext(), types.NewTransaction(0, libcommon.Address{}, nil, gasLimit, nil, nil), contract.Caller())
+ tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), false, []byte{}, startGas, value, contractCode)
ret, err := env.Interpreter().Run(contract, []byte{}, false)
- tracer.CaptureEnd(ret, startGas-contract.Gas, err, true)
+ tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
// Rest gas assumes no refund
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)
if err != nil {
return nil, err
}
@@ -185,16 +185,16 @@ func TestHaltBetweenSteps(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- env := vm.NewEVM(evmtypes.BlockContext{BlockNumber: 1}, evmtypes.TxContext{GasPrice: uint256.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ env := vm.NewEVM(evmtypes.BlockContext{BlockNumber: 1}, evmtypes.TxContext{GasPrice: uint256.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer.Hooks})
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, libcommon.Address{}, uint256.NewInt(0), 0, false /* skipAnalysis */),
}
- tracer.CaptureTxStart(env, types.NewTransaction(0, libcommon.Address{}, new(uint256.Int), 0, new(uint256.Int), nil))
- tracer.CaptureStart(libcommon.Address{}, libcommon.Address{}, false /* precompile */, false /* create */, []byte{}, 0, uint256.NewInt(0), []byte{} /* code */)
- tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
+ tracer.OnTxStart(env.GetVMContext(), types.NewTransaction(0, libcommon.Address{}, new(uint256.Int), 0, new(uint256.Int), nil), libcommon.Address{})
+ tracer.OnEnter(0, byte(vm.CALL), libcommon.Address{}, libcommon.Address{}, false, []byte{}, 0, uint256.NewInt(0), []byte{})
+ tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
timeout := errors.New("stahp")
tracer.Stop(timeout)
- tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
+ tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
if _, err := tracer.GetResult(); !strings.Contains(err.Error(), timeout.Error()) {
t.Errorf("Expected timeout error, got %v", err)
@@ -210,10 +210,10 @@ func TestNoStepExec(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- env := vm.NewEVM(evmtypes.BlockContext{BlockNumber: 1}, evmtypes.TxContext{GasPrice: uint256.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
- tracer.CaptureTxStart(env, types.NewTransaction(0, libcommon.Address{}, new(uint256.Int), 0, new(uint256.Int), nil))
- tracer.CaptureStart(libcommon.Address{}, libcommon.Address{}, false /* precompile */, false /* create */, []byte{}, 1000, uint256.NewInt(0), []byte{} /* code */)
- tracer.CaptureEnd(nil, 0, nil, false)
+ env := vm.NewEVM(evmtypes.BlockContext{BlockNumber: 1}, evmtypes.TxContext{GasPrice: uint256.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer.Hooks})
+ tracer.OnTxStart(env.GetVMContext(), types.NewTransaction(0, libcommon.Address{}, new(uint256.Int), 0, new(uint256.Int), nil), libcommon.Address{})
+ tracer.OnEnter(0, byte(vm.CALL), libcommon.Address{}, libcommon.Address{}, false, []byte{}, 1000, uint256.NewInt(0), []byte{})
+ tracer.OnExit(0, nil, 0, nil, false)
ret, err := tracer.GetResult()
if err != nil {
t.Fatal(err)
@@ -282,8 +282,8 @@ func TestEnterExit(t *testing.T) {
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, libcommon.Address{}, uint256.NewInt(0), 0, false /* skipAnalysis */),
}
- tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), false, false, []byte{}, 1000, new(uint256.Int), []byte{})
- tracer.CaptureExit([]byte{}, 400, nil, false)
+ tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), false, []byte{}, 1000, new(uint256.Int), []byte{})
+ tracer.OnExit(1, []byte{}, 400, nil, false)
have, err := tracer.GetResult()
if err != nil {
diff --git a/eth/tracers/live/printer.go b/eth/tracers/live/printer.go
index 8a914e7bec9..2a850bc3669 100644
--- a/eth/tracers/live/printer.go
+++ b/eth/tracers/live/printer.go
@@ -9,9 +9,8 @@ import (
"github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/tracers"
)
@@ -21,55 +20,58 @@ func init() {
type Printer struct{}
-func newPrinter(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
- return &Printer{}, nil
+func newPrinter(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
+ t := &Printer{}
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnTxEnd: t.OnTxEnd,
+ OnEnter: t.OnEnter,
+ OnExit: t.OnExit,
+ OnOpcode: t.OnOpcode,
+ OnFault: t.OnFault,
+ OnGasChange: t.OnGasChange,
+ OnBalanceChange: t.OnBalanceChange,
+ OnNonceChange: t.OnNonceChange,
+ OnCodeChange: t.OnCodeChange,
+ OnStorageChange: t.OnStorageChange,
+ OnLog: t.OnLog,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }, nil
}
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (p *Printer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- fmt.Printf("CaptureStart: from=%v, to=%v, precompile=%v, create=%v, input=%s, gas=%v, value=%v, code=%v\n", from, to, precompile, create, hexutility.Bytes(input), gas, value, hexutility.Bytes(code))
+// OnExit is called after the call finishes to finalize the tracing.
+func (p *Printer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ fmt.Printf("OnExit: output=%s, gasUsed=%v, err=%v\n", hexutility.Bytes(output), gasUsed, err)
}
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (p *Printer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
- fmt.Printf("CaptureEnd: output=%s, gasUsed=%v, err=%v\n", hexutility.Bytes(output), gasUsed, err)
+// OnOpcode implements the EVMLogger interface to trace a single step of VM execution.
+func (p *Printer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ fmt.Printf("OnOpcode: pc=%v, op=%v, gas=%v, cost=%v, scope=%v, rData=%v, depth=%v, err=%v\n", pc, op, gas, cost, scope, rData, depth, err)
}
-// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
-func (p *Printer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
- //fmt.Printf("CaptureState: pc=%v, op=%v, gas=%v, cost=%v, scope=%v, rData=%v, depth=%v, err=%v\n", pc, op, gas, cost, scope, rData, depth, err)
+// OnFault implements the EVMLogger interface to trace an execution fault.
+func (p *Printer) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpContext, depth int, err error) {
+ fmt.Printf("OnFault: pc=%v, op=%v, gas=%v, cost=%v, depth=%v, err=%v\n", pc, op, gas, cost, depth, err)
}
-// CaptureFault implements the EVMLogger interface to trace an execution fault.
-func (p *Printer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
- fmt.Printf("CaptureFault: pc=%v, op=%v, gas=%v, cost=%v, depth=%v, err=%v\n", pc, op, gas, cost, depth, err)
+func (p *Printer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ fmt.Printf("CaptureEnter: depth=%v, typ=%v from=%v, to=%v, input=%s, gas=%v, value=%v\n", depth, typ, from, to, hexutility.Bytes(input), gas, value)
}
-// CaptureKeccakPreimage is called during the KECCAK256 opcode.
-func (p *Printer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (p *Printer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- fmt.Printf("CaptureEnter: typ=%v, from=%v, to=%v, precompile=%v, create=%v, input=%s, gas=%v, value=%v, code=%v\n", typ, from, to, precompile, create, hexutility.Bytes(input), gas, value, hexutility.Bytes(code))
-}
-
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
-// execute any code.
-func (p *Printer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
- fmt.Printf("CaptureExit: output=%s, gasUsed=%v, err=%v\n", hexutility.Bytes(output), gasUsed, err)
-}
-
-func (p *Printer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
+func (p *Printer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
buf, err := json.Marshal(tx)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
- fmt.Printf("CaptureTxStart: tx=%s\n", buf)
+ fmt.Printf("OnTxStart: tx=%s\n", buf)
}
-func (p *Printer) CaptureTxEnd(receipt *types.Receipt, err error) {
+func (p *Printer) OnTxEnd(receipt *types.Receipt, err error) {
if err != nil {
fmt.Printf("CaptureTxEnd err: %v\n", err)
return
@@ -98,15 +100,7 @@ func (p *Printer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
fmt.Printf("OnGenesisBlock: b=%v, allocLength=%d\n", b.NumberU64(), len(alloc))
}
-func (p *Printer) OnBeaconBlockRootStart(root libcommon.Hash) {
- fmt.Printf("OnBeaconBlockRootStart: b=%v", root)
-}
-
-func (p *Printer) OnBeaconBlockRootEnd() {
- fmt.Printf("OnBeaconBlockRootEnd:")
-}
-
-func (p *Printer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (p *Printer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason tracing.BalanceChangeReason) {
fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new)
}
@@ -131,7 +125,7 @@ func (p *Printer) OnLog(l *types.Log) {
fmt.Printf("OnLog: l=%s\n", buf)
}
-func (p *Printer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
+func (p *Printer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
fmt.Printf("OnGasChange: old=%v, new=%v, diff=%v\n", old, new, new-old)
}
diff --git a/eth/tracers/live/tracer.go b/eth/tracers/live/tracer.go
index f51277341ec..1b9979e31e6 100644
--- a/eth/tracers/live/tracer.go
+++ b/eth/tracers/live/tracer.go
@@ -13,7 +13,7 @@ func init() {
}
// ctorFn is the constructor signature of a native tracer.
-type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
+type ctorFn = func(*tracers.Context, json.RawMessage) (*tracers.Tracer, error)
// ctors is a map of package-local tracer constructors.
var ctors map[string]ctorFn
@@ -27,7 +27,7 @@ func register(name string, ctor ctorFn) {
}
// lookup returns a tracer, if one can be matched to the given name.
-func lookup(name string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func lookup(name string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
if ctors == nil {
ctors = make(map[string]ctorFn)
}
diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go
index bad04e1a17b..21b4f910d1e 100644
--- a/eth/tracers/logger/access_list_tracer.go
+++ b/eth/tracers/logger/access_list_tracer.go
@@ -18,18 +18,14 @@ package logger
import (
"encoding/json"
- "math/big"
"sort"
- "github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
types2 "github.com/ledgerwatch/erigon-lib/types"
- "github.com/ledgerwatch/erigon/core/types"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
- "github.com/ledgerwatch/erigon/crypto"
)
// accessList is an accumulator for the set of accounts and storage slots an EVM
@@ -174,113 +170,33 @@ func NewAccessListTracer(acl types2.AccessList, exclude map[libcommon.Address]st
}
}
-func (a *AccessListTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {}
-
-func (a *AccessListTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (a *AccessListTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-func (a *AccessListTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (a *AccessListTracer) Hooks() *tracing.Hooks {
+ return &tracing.Hooks{
+ OnOpcode: a.OnOpcode,
+ }
}
-// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
-func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
- stack := scope.Stack
- contract := scope.Contract
- caller := contract.Address()
-
- stackData := stack.Data
+// OnOpcode captures all opcodes that touch storage or addresses and adds them to the accesslist.
+func (a *AccessListTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ stackData := scope.StackData()
stackLen := len(stackData)
+ op := vm.OpCode(opcode)
if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 {
- addr := contract.Address()
slot := libcommon.Hash(stackData[stackLen-1].Bytes32())
- if _, ok := a.excl[addr]; !ok {
- a.list.addSlot(addr, slot)
- if _, ok := a.createdContracts[addr]; !ok {
- a.usedBeforeCreation[addr] = struct{}{}
- }
- }
+ a.list.addSlot(scope.Address(), slot)
}
if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 {
addr := libcommon.Address(stackData[stackLen-1].Bytes20())
if _, ok := a.excl[addr]; !ok {
a.list.addAddress(addr)
- if _, ok := a.createdContracts[addr]; !ok {
- a.usedBeforeCreation[addr] = struct{}{}
- }
}
}
if (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE) && stackLen >= 5 {
addr := libcommon.Address(stackData[stackLen-2].Bytes20())
if _, ok := a.excl[addr]; !ok {
a.list.addAddress(addr)
- if _, ok := a.createdContracts[addr]; !ok {
- a.usedBeforeCreation[addr] = struct{}{}
- }
}
}
- if op == vm.CREATE {
- // contract address for CREATE can only be generated with state
- if a.state != nil {
- nonce := a.state.GetNonce(caller)
- addr := crypto.CreateAddress(caller, nonce)
- if _, ok := a.excl[addr]; !ok {
- a.createdContracts[addr] = struct{}{}
- }
- }
- }
- if op == vm.CREATE2 && stackLen >= 4 {
- offset := stackData[stackLen-2]
- size := stackData[stackLen-3]
- init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
- inithash := crypto.Keccak256(init)
- salt := stackData[stackLen-4]
- addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash)
- if _, ok := a.excl[addr]; !ok {
- a.createdContracts[addr] = struct{}{}
- }
- }
-
-}
-
-func (a *AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-
-func (a *AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
-}
-
-func (a *AccessListTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (a *AccessListTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (a *AccessListTracer) OnBlockEnd(err error) {
-}
-
-func (a *AccessListTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (a *AccessListTracer) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (a *AccessListTracer) OnBeaconBlockRootEnd() {}
-
-func (a *AccessListTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (a *AccessListTracer) OnBalanceChange(addr libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (a *AccessListTracer) OnNonceChange(addr libcommon.Address, prev, new uint64) {}
-
-func (a *AccessListTracer) OnCodeChange(addr libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (a *AccessListTracer) OnStorageChange(addr libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (a *AccessListTracer) OnLog(log *types.Log) {}
-
-func (a *AccessListTracer) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
}
// GetResult returns an empty json object.
diff --git a/eth/tracers/logger/json_stream.go b/eth/tracers/logger/json_stream.go
index 0376972750b..0408e8fdcda 100644
--- a/eth/tracers/logger/json_stream.go
+++ b/eth/tracers/logger/json_stream.go
@@ -4,18 +4,17 @@ import (
"context"
"encoding/hex"
"encoding/json"
- "math/big"
"sort"
"github.com/holiman/uint256"
jsoniter "github.com/json-iterator/go"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
// JsonStreamLogger is an EVM state logger and implements Tracer.
@@ -35,7 +34,7 @@ type JsonStreamLogger struct {
logs []StructLog
output []byte //nolint
err error //nolint
- env *vm.EVM
+ env *tracing.VMContext
}
// NewStructLogger returns a new logger
@@ -52,25 +51,33 @@ func NewJsonStreamLogger(cfg *LogConfig, ctx context.Context, stream *jsoniter.S
return logger
}
-func (l *JsonStreamLogger) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
- l.env = env
+func (l *JsonStreamLogger) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: l.OnTxStart,
+ OnOpcode: l.OnOpcode,
+ },
+ }
}
-func (l *JsonStreamLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (l *JsonStreamLogger) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (l *JsonStreamLogger) Hooks() *tracing.Hooks {
+ return &tracing.Hooks{
+ OnTxStart: l.OnTxStart,
+ OnOpcode: l.OnOpcode,
+ }
}
-func (l *JsonStreamLogger) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (l *JsonStreamLogger) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
+ l.env = env
}
-// CaptureState logs a new structured log message and pushes it out to the environment
+// OnOpcode 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 *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
- contract := scope.Contract
- memory := scope.Memory
- stack := scope.Stack
+// OnOpcode also tracks SLOAD/SSTORE ops to track storage change.
+func (l *JsonStreamLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ contractAddr := scope.Address()
+ memory := scope.MemoryData()
+ stack := scope.StackData()
select {
case <-l.ctx.Done():
@@ -90,26 +97,26 @@ func (l *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
if !l.cfg.DisableStorage {
// initialise new changed values storage container for this contract
// if not present.
- if l.storage[contract.Address()] == nil {
- l.storage[contract.Address()] = make(Storage)
+ if l.storage[contractAddr] == nil {
+ l.storage[contractAddr] = make(Storage)
}
// capture SLOAD opcodes and record the read entry in the local storage
- if op == vm.SLOAD && stack.Len() >= 1 {
+ if vm.OpCode(op) == vm.SLOAD && len(stack) >= 1 {
var (
- address = libcommon.Hash(stack.Data[stack.Len()-1].Bytes32())
+ address = libcommon.Hash(stack[len(stack)-1].Bytes32())
value uint256.Int
)
- l.env.IntraBlockState().GetState(contract.Address(), &address, &value)
- l.storage[contract.Address()][address] = value.Bytes32()
+ l.env.IntraBlockState.GetState(contractAddr, &address, &value)
+ l.storage[contractAddr][address] = value.Bytes32()
outputStorage = true
}
// capture SSTORE opcodes and record the written entry in the local storage.
- if op == vm.SSTORE && stack.Len() >= 2 {
+ if vm.OpCode(op) == vm.SSTORE && len(stack) >= 2 {
var (
- value = libcommon.Hash(stack.Data[stack.Len()-2].Bytes32())
- address = libcommon.Hash(stack.Data[stack.Len()-1].Bytes32())
+ value = libcommon.Hash(stack[len(stack)-2].Bytes32())
+ address = libcommon.Hash(stack[len(stack)-1].Bytes32())
)
- l.storage[contract.Address()][address] = value
+ l.storage[contractAddr][address] = value
outputStorage = true
}
}
@@ -119,7 +126,7 @@ func (l *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
l.stream.WriteUint64(pc)
l.stream.WriteMore()
l.stream.WriteObjectField("op")
- l.stream.WriteString(op.String())
+ l.stream.WriteString(vm.OpCode(op).String())
l.stream.WriteMore()
l.stream.WriteObjectField("gas")
l.stream.WriteUint64(gas)
@@ -140,7 +147,7 @@ func (l *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
l.stream.WriteMore()
l.stream.WriteObjectField("stack")
l.stream.WriteArrayStart()
- for i, stackValue := range stack.Data {
+ for i, stackValue := range stack {
if i > 0 {
l.stream.WriteMore()
}
@@ -149,7 +156,7 @@ func (l *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
l.stream.WriteArrayEnd()
}
if !l.cfg.DisableMemory {
- memData := memory.Data()
+ memData := memory
l.stream.WriteMore()
l.stream.WriteObjectField("memory")
l.stream.WriteArrayStart()
@@ -170,7 +177,7 @@ func (l *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
if l.locations != nil {
l.locations = l.locations[:0]
}
- s := l.storage[contract.Address()]
+ s := l.storage[contractAddr]
for loc := range s {
l.locations = append(l.locations, loc)
}
@@ -191,47 +198,6 @@ func (l *JsonStreamLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
_ = l.stream.Flush()
}
-// CaptureFault implements the Tracer interface to trace an execution fault
-// while running an opcode.
-func (l *JsonStreamLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-
-func (l *JsonStreamLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
-}
-
-func (l *JsonStreamLogger) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (l *JsonStreamLogger) OnBlockEnd(err error) {
-}
-
-func (l *JsonStreamLogger) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (l *JsonStreamLogger) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (l *JsonStreamLogger) OnBeaconBlockRootEnd() {}
-
-func (l *JsonStreamLogger) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (l *JsonStreamLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (l *JsonStreamLogger) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (l *JsonStreamLogger) OnNonceChange(a libcommon.Address, prev, new uint64) {}
-
-func (l *JsonStreamLogger) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (l *JsonStreamLogger) OnStorageChange(a libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (l *JsonStreamLogger) OnLog(log *types.Log) {}
-
-func (l *JsonStreamLogger) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
-}
-
// GetResult returns an empty json object.
func (l *JsonStreamLogger) GetResult() (json.RawMessage, error) {
return json.RawMessage(`{}`), nil
diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go
index 6fbfcbe2570..2bed66b6581 100644
--- a/eth/tracers/logger/logger.go
+++ b/eth/tracers/logger/logger.go
@@ -17,9 +17,10 @@ import (
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon/common/math"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
var ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
@@ -117,7 +118,9 @@ type StructLogger struct {
logs []StructLog
output []byte
err error
- env *vm.EVM
+ env *tracing.VMContext
+
+ usedGas uint64
}
// NewStructLogger returns a new logger
@@ -131,26 +134,30 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
return logger
}
-func (l *StructLogger) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
- l.env = env
-}
-
-func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (l *StructLogger) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (l *StructLogger) Hooks() *tracing.Hooks {
+ return &tracing.Hooks{
+ OnTxStart: l.OnTxStart,
+ OnTxEnd: l.OnTxEnd,
+ OnExit: l.OnExit,
+ OnOpcode: l.OnOpcode,
+ }
}
-// CaptureEnter implements the Tracer interface to initialize the tracing operation for an internal call.
-func (l *StructLogger) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (l *StructLogger) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: l.Hooks(),
+ GetResult: l.GetResult,
+ Stop: l.Stop,
+ }
}
-// CaptureState logs a new structured log message and pushes it out to the environment
+// OnOpcode 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
+// OnOpcode also tracks SLOAD/SSTORE ops to track storage change.
+func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ op := vm.OpCode(opcode)
+ memory := scope.MemoryData()
+ stack := scope.StackData()
// check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
@@ -160,43 +167,46 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
// Copy a snapshot of the current memory state to a new buffer
var mem []byte
if !l.cfg.DisableMemory {
- mem = make([]byte, len(memory.Data()))
- copy(mem, memory.Data())
+ mem = make([]byte, len(memory))
+ copy(mem, memory)
}
// Copy a snapshot of the current stack state to a new buffer
var stck []*big.Int
if !l.cfg.DisableStack {
- stck = make([]*big.Int, len(stack.Data))
- for i, item := range stack.Data {
+ stck = make([]*big.Int, len(stack))
+ for i, item := range stack {
stck[i] = new(big.Int).Set(item.ToBig())
}
}
+
+ contractAddr := scope.Address()
+ stackLen := len(stack)
// Copy a snapshot of the current storage to a new container
var storage Storage
if !l.cfg.DisableStorage {
// initialise new changed values storage container for this contract
// if not present.
- if l.storage[contract.Address()] == nil {
- l.storage[contract.Address()] = make(Storage)
+ if l.storage[contractAddr] == nil {
+ l.storage[contractAddr] = make(Storage)
}
// capture SLOAD opcodes and record the read entry in the local storage
- if op == vm.SLOAD && stack.Len() >= 1 {
+ if op == vm.SLOAD && stackLen >= 1 {
var (
- address = libcommon.Hash(stack.Data[stack.Len()-1].Bytes32())
+ address = libcommon.Hash(stack[stackLen-1].Bytes32())
value uint256.Int
)
- l.env.IntraBlockState().GetState(contract.Address(), &address, &value)
- l.storage[contract.Address()][address] = value.Bytes32()
+ l.env.IntraBlockState.GetState(contractAddr, &address, &value)
+ l.storage[contractAddr][address] = value.Bytes32()
}
// capture SSTORE opcodes and record the written entry in the local storage.
- if op == vm.SSTORE && stack.Len() >= 2 {
+ if op == vm.SSTORE && stackLen >= 2 {
var (
- value = libcommon.Hash(stack.Data[stack.Len()-2].Bytes32())
- address = libcommon.Hash(stack.Data[stack.Len()-1].Bytes32())
+ value = libcommon.Hash(stack[stackLen-2].Bytes32())
+ address = libcommon.Hash(stack[stackLen-1].Bytes32())
)
- l.storage[contract.Address()][address] = value
+ l.storage[contractAddr][address] = value
}
- storage = l.storage[contract.Address()].Copy()
+ storage = l.storage[contractAddr].Copy()
}
var rdata []byte
if !l.cfg.DisableReturnData {
@@ -204,17 +214,16 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
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.IntraBlockState().GetRefund(), err}
+ log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.IntraBlockState.GetRefund(), err}
l.logs = append(l.logs, log)
}
-// CaptureFault implements the Tracer 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) {
-}
+// OnExit is called after the call finishes to finalize the tracing.
+func (l *StructLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ if depth != 0 {
+ return
+ }
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (l *StructLogger) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
l.output = output
l.err = err
if l.cfg.Debug {
@@ -225,38 +234,19 @@ func (l *StructLogger) CaptureEnd(output []byte, usedGas uint64, err error, reve
}
}
-func (l *StructLogger) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (l *StructLogger) OnBlockEnd(err error) {
-}
-
-func (l *StructLogger) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (l *StructLogger) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (l *StructLogger) OnBeaconBlockRootEnd() {}
-
-func (l *StructLogger) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (l *StructLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (l *StructLogger) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (l *StructLogger) OnNonceChange(a libcommon.Address, prev, new uint64) {}
-
-func (l *StructLogger) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (l *StructLogger) OnStorageChange(a libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
+func (l *StructLogger) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
+ l.env = env
}
-func (l *StructLogger) OnLog(log *types.Log) {}
-
-// CaptureExit is called after the internal call finishes to finalize the tracing.
-func (l *StructLogger) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
+func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) {
+ if err != nil {
+ // Don't override vm error
+ if l.err == nil {
+ l.err = err
+ }
+ return
+ }
+ l.usedGas = receipt.GasUsed
}
// GetResult returns an empty json object.
@@ -379,7 +369,7 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
type mdLogger struct {
out io.Writer
cfg *LogConfig
- env *vm.EVM
+ env *tracing.VMContext
}
// NewMarkdownLogger creates a logger which outputs information in a format adapted
@@ -392,11 +382,19 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
return l
}
-func (t *mdLogger) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
- t.env = env
+func (t *mdLogger) Hooks() *tracing.Hooks {
+ return &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnEnter: t.OnEnter,
+ OnExit: t.OnExit,
+ OnOpcode: t.OnOpcode,
+ OnFault: t.OnFault,
+ }
}
-func (t *mdLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
+func (t *mdLogger) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
+ t.env = env
+}
func (t *mdLogger) captureStartOrEnter(from, to libcommon.Address, create bool, input []byte, gas uint64, value *uint256.Int) {
if !create {
@@ -415,82 +413,46 @@ func (t *mdLogger) captureStartOrEnter(from, to libcommon.Address, create bool,
`)
}
-func (t *mdLogger) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) { //nolint:interfacer
+func (t *mdLogger) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ if depth != 0 {
+ return
+ }
+ create := vm.OpCode(typ) == vm.CREATE
t.captureStartOrEnter(from, to, create, input, gas, value)
}
-func (t *mdLogger) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) { //nolint:interfacer
- t.captureStartOrEnter(from, to, create, input, gas, value)
+func (t *mdLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ if depth == 0 {
+ fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
+ output, gasUsed, err)
+ }
}
-func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
- stack := scope.Stack
-
+// OnOpcode also tracks SLOAD/SSTORE ops to track storage change.
+func (t *mdLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ stack := scope.StackData()
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
if !t.cfg.DisableStack {
// format stack
var a []string
- for _, elem := range stack.Data {
+ for _, elem := range stack {
a = append(a, elem.String())
}
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
fmt.Fprintf(t.out, "%10v |", b)
}
- fmt.Fprintf(t.out, "%10v |", t.env.IntraBlockState().GetRefund())
+ fmt.Fprintf(t.out, "%10v |", t.env.IntraBlockState.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) {
+func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}
-func (t *mdLogger) captureEndOrExit(output []byte, usedGas uint64, err error) {
- fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
- output, usedGas, err)
-}
-
-func (t *mdLogger) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
- t.captureEndOrExit(output, usedGas, err)
-}
-
-func (t *mdLogger) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (t *mdLogger) OnBlockEnd(err error) {
-}
-
-func (t *mdLogger) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (t *mdLogger) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (t *mdLogger) OnBeaconBlockRootEnd() {}
-
-func (t *mdLogger) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (t *mdLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (t *mdLogger) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (t *mdLogger) OnNonceChange(a libcommon.Address, prev, new uint64) {}
-
-func (t *mdLogger) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (t *mdLogger) OnStorageChange(a libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (t *mdLogger) OnLog(log *types.Log) {}
-
-func (t *mdLogger) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
- t.captureEndOrExit(output, usedGas, err)
-}
-
// GetResult returns an empty json object.
func (t *mdLogger) GetResult() (json.RawMessage, error) {
return json.RawMessage(`{}`), nil
diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go
index 7065172b126..5d6d36e19e5 100644
--- a/eth/tracers/logger/logger_json.go
+++ b/eth/tracers/logger/logger_json.go
@@ -21,68 +21,66 @@ import (
"io"
"math/big"
- "github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/math"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
type JSONLogger struct {
encoder *json.Encoder
cfg *LogConfig
- env *vm.EVM
+ env *tracing.VMContext
}
// 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 *LogConfig, writer io.Writer) *tracers.Tracer {
l := &JSONLogger{json.NewEncoder(writer), cfg, nil}
if l.cfg == nil {
l.cfg = &LogConfig{}
}
- return l
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: l.OnTxStart,
+ OnExit: l.OnExit,
+ OnOpcode: l.OnOpcode,
+ OnFault: l.OnFault,
+ },
+ }
}
-func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
+func (l *JSONLogger) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
l.env = env
}
-func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (l *JSONLogger) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-// CaptureState outputs state information on the logger.
-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
+// OnOpcode outputs state information on the logger.
+func (l *JSONLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ memory := scope.MemoryData()
+ stack := scope.StackData()
log := StructLog{
Pc: pc,
- Op: op,
+ Op: vm.OpCode(op),
Gas: gas,
GasCost: cost,
- MemorySize: memory.Len(),
+ MemorySize: len(memory),
Storage: nil,
Depth: depth,
- RefundCounter: l.env.IntraBlockState().GetRefund(),
+ RefundCounter: l.env.IntraBlockState.GetRefund(),
Err: err,
}
if !l.cfg.DisableMemory {
- log.Memory = memory.Data()
+ log.Memory = memory
}
if !l.cfg.DisableStack {
//TODO(@holiman) improve this
- logstack := make([]*big.Int, len(stack.Data))
- for i, item := range stack.Data {
+ logstack := make([]*big.Int, len(stack))
+ for i, item := range stack {
logstack[i] = item.ToBig()
}
log.Stack = logstack
@@ -91,11 +89,15 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
}
// CaptureFault outputs state information on the logger.
-func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+func (l *JSONLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) {
+ l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err)
}
// CaptureEnd is triggered at end of execution.
-func (l *JSONLogger) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
+func (l *JSONLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ if depth > 0 {
+ return
+ }
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
@@ -105,40 +107,7 @@ func (l *JSONLogger) CaptureEnd(output []byte, usedGas uint64, err error, revert
if err != nil {
errMsg = err.Error()
}
- _ = l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(usedGas), errMsg})
-}
-
-func (l *JSONLogger) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (l *JSONLogger) OnBlockEnd(err error) {
-}
-
-func (l *JSONLogger) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (l *JSONLogger) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (l *JSONLogger) OnBeaconBlockRootEnd() {}
-
-func (l *JSONLogger) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (l *JSONLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (l *JSONLogger) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (l *JSONLogger) OnNonceChange(a libcommon.Address, prev, new uint64) {}
-
-func (l *JSONLogger) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (l *JSONLogger) OnStorageChange(a libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (l *JSONLogger) OnLog(log *types.Log) {}
-
-func (l *JSONLogger) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
+ _ = l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
}
// GetResult returns an empty json object.
diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go
index c382cfdb8b3..77a32ac5876 100644
--- a/eth/tracers/logger/logger_test.go
+++ b/eth/tracers/logger/logger_test.go
@@ -56,8 +56,8 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func TestStoreCapture(t *testing.T) {
var (
- env = vm.NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{})
logger = NewStructLogger(nil)
+ env = vm.NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()})
mem = vm.NewMemory()
stack = stack.New()
contract = vm.NewContract(&dummyContractRef{}, libcommon.Address{}, new(uint256.Int), 0, false /* skipAnalysis */)
@@ -65,9 +65,8 @@ func TestStoreCapture(t *testing.T) {
stack.Push(uint256.NewInt(1))
stack.Push(uint256.NewInt(0))
var index libcommon.Hash
- logger.CaptureTxStart(env, nil)
- logger.CaptureStart(libcommon.Address{}, libcommon.Address{}, false, false, nil, 0, nil, nil)
- logger.CaptureState(0, vm.SSTORE, 0, 0, &vm.ScopeContext{
+ logger.OnTxStart(env.GetVMContext(), nil, libcommon.Address{})
+ logger.OnOpcode(0, byte(vm.SSTORE), 0, 0, &vm.ScopeContext{
Memory: mem,
Stack: stack,
Contract: contract,
diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go
index 157989c5d3c..54c5240f1bd 100644
--- a/eth/tracers/native/4byte.go
+++ b/eth/tracers/native/4byte.go
@@ -25,6 +25,7 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/tracers"
@@ -53,16 +54,23 @@ type fourByteTracer struct {
ids map[string]int // ids aggregates the 4byte ids found
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
- activePrecompiles []libcommon.Address // Updated on CaptureStart based on given rules
+ activePrecompiles []libcommon.Address // Updated on tx start based on given rules
}
// newFourByteTracer returns a native go tracer which collects
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
-func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
+func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) {
t := &fourByteTracer{
ids: make(map[string]int),
}
- return t, nil
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnEnter: t.OnEnter,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }, nil
}
// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
@@ -81,22 +89,13 @@ func (t *fourByteTracer) store(id []byte, size int) {
t.ids[key] += 1
}
-func (t *fourByteTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
- rules := env.ChainRules()
+func (t *fourByteTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
+ rules := env.ChainConfig.Rules(env.BlockNumber, env.Time)
t.activePrecompiles = vm.ActivePrecompiles(rules)
}
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (t *fourByteTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- // Save the outer calldata also
- if len(input) >= 4 {
- t.store(input[0:4], len(input)-4)
- }
-}
-
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from libcommon.Address, to libcommon.Address, precompile, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- // Skip if tracing was interrupted
+func (t *fourByteTracer) OnEnter(depth int, opcode byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) { // Skip if tracing was interrupted
if atomic.LoadUint32(&t.interrupt) > 0 {
return
}
@@ -104,6 +103,7 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from libcommon.Address, to l
return
}
// primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT
+ op := vm.OpCode(opcode)
if op != vm.DELEGATECALL && op != vm.STATICCALL &&
op != vm.CALL && op != vm.CALLCODE {
return
diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go
index 6d7318e3e45..bf0dc0d5821 100644
--- a/eth/tracers/native/call.go
+++ b/eth/tracers/native/call.go
@@ -23,12 +23,12 @@ import (
"sync/atomic"
"github.com/holiman/uint256"
-
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutil"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon/accounts/abi"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/tracers"
@@ -124,7 +124,7 @@ type callTracerConfig struct {
// newCallTracer returns a native go tracer which tracks
// call frames of a tx, and implements vm.EVMLogger.
-func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
var config callTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
@@ -133,39 +133,24 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
}
// First callframe contains tx context info
// and is populated on start and end.
- return &callTracer{callstack: make([]callFrame, 1), config: config}, nil
-}
-
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (t *callTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- t.callstack[0] = callFrame{
- Type: vm.CALL,
- From: from,
- To: to,
- Input: libcommon.CopyBytes(input),
- Gas: t.gasLimit, // gas has intrinsicGas already subtracted
- }
- if value != nil {
- t.callstack[0].Value = value.ToBig()
- }
- if create {
- t.callstack[0].Type = vm.CREATE
- }
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
- t.callstack[0].processOutput(output, err, reverted)
-}
-
-// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
-func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+ t := &callTracer{callstack: make([]callFrame, 1), config: config}
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnTxEnd: t.OnTxEnd,
+ OnEnter: t.OnEnter,
+ OnExit: t.OnExit,
+ OnLog: t.OnLog,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }, nil
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *callTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- t.depth++
- if t.config.OnlyTopCall {
+func (t *callTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ t.depth = depth
+ if t.config.OnlyTopCall && depth > 0 {
return
}
// Skip if tracing was interrupted
@@ -174,7 +159,7 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libc
}
toCopy := to
call := callFrame{
- Type: typ,
+ Type: vm.OpCode(typ),
From: from,
To: toCopy,
Input: libcommon.CopyBytes(input),
@@ -186,11 +171,19 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libc
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
-func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
- t.depth--
+
+func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ if depth == 0 {
+ t.captureEnd(output, gasUsed, err, reverted)
+ return
+ }
+
+ t.depth = depth - 1
+
if t.config.OnlyTopCall {
return
}
+
size := len(t.callstack)
if size <= 1 {
return
@@ -205,11 +198,18 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, rever
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
}
-func (t *callTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
+func (t *callTracer) captureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
+ if len(t.callstack) != 1 {
+ return
+ }
+ t.callstack[0].processOutput(output, err, reverted)
+}
+
+func (t *callTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
t.gasLimit = tx.GetGas()
}
-func (t *callTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
+func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) {
// Error happened during tx validation.
if err != nil {
return
diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go
index 50a27d7f531..84ad42bdfe3 100644
--- a/eth/tracers/native/mux.go
+++ b/eth/tracers/native/mux.go
@@ -18,15 +18,12 @@ package native
import (
"encoding/json"
- "math/big"
"github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/tracers"
)
@@ -38,18 +35,18 @@ func init() {
// runs multiple tracers in one go.
type muxTracer struct {
names []string
- tracers []tracers.Tracer
+ tracers []*tracers.Tracer
}
// newMuxTracer returns a new mux tracer.
-func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
var config map[string]json.RawMessage
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, err
}
}
- objects := make([]tracers.Tracer, 0, len(config))
+ objects := make([]*tracers.Tracer, 0, len(config))
names := make([]string, 0, len(config))
for k, v := range config {
t, err := tracers.New(k, ctx, v)
@@ -60,81 +57,80 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, er
names = append(names, k)
}
- return &muxTracer{names: names, tracers: objects}, nil
-}
-
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (t *muxTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- for _, t := range t.tracers {
- t.CaptureStart(from, to, precompile, create, input, gas, value, code)
- }
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
- for _, t := range t.tracers {
- t.CaptureEnd(output, gasUsed, err, reverted)
- }
-}
-
-// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
-func (t *muxTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
- for _, t := range t.tracers {
- t.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
- }
-}
-
-// CaptureFault implements the EVMLogger interface to trace an execution fault.
-func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
- for _, t := range t.tracers {
- t.CaptureFault(pc, op, gas, cost, scope, depth, err)
- }
-}
-
-// CaptureKeccakPreimage is called during the KECCAK256 opcode.
-func (t *muxTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {
- for _, t := range t.tracers {
- t.CaptureKeccakPreimage(hash, data)
+ t := &muxTracer{names: names, tracers: objects}
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnTxEnd: t.OnTxEnd,
+ OnEnter: t.OnEnter,
+ OnExit: t.OnExit,
+ OnOpcode: t.OnOpcode,
+ OnFault: t.OnFault,
+ OnGasChange: t.OnGasChange,
+ OnBalanceChange: t.OnBalanceChange,
+ OnNonceChange: t.OnNonceChange,
+ OnCodeChange: t.OnCodeChange,
+ OnStorageChange: t.OnStorageChange,
+ OnLog: t.OnLog,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }, nil
+}
+
+func (t *muxTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ for _, t := range t.tracers {
+ if t.OnOpcode != nil {
+ t.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
+ }
}
}
-// CaptureGasConsumed is called when gas is consumed.
-func (t *muxTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
+func (t *muxTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
for _, t := range t.tracers {
- t.OnGasChange(old, new, reason)
+ if t.OnFault != nil {
+ t.OnFault(pc, op, gas, cost, scope, depth, err)
+ }
}
}
-// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *muxTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
for _, t := range t.tracers {
- t.CaptureEnter(typ, from, to, precompile, create, input, gas, value, code)
+ if t.OnGasChange != nil {
+ t.OnGasChange(old, new, reason)
+ }
}
}
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
-// execute any code.
-func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
+func (t *muxTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
for _, t := range t.tracers {
- t.CaptureExit(output, gasUsed, err, reverted)
+ if t.OnEnter != nil {
+ t.OnEnter(depth, typ, from, to, precompile, input, gas, value, code)
+ }
}
}
-func (t *muxTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
+func (t *muxTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
for _, t := range t.tracers {
- t.CaptureTxStart(env, tx)
+ if t.OnExit != nil {
+ t.OnExit(depth, output, gasUsed, err, reverted)
+ }
}
}
-func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
+func (t *muxTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
for _, t := range t.tracers {
- t.CaptureTxEnd(receipt, err)
+ if t.OnTxStart != nil {
+ t.OnTxStart(env, tx, from)
+ }
}
}
-func (t *muxTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
+func (t *muxTracer) OnTxEnd(receipt *types.Receipt, err error) {
for _, t := range t.tracers {
- t.OnBlockStart(b, td, finalized, safe, chainConfig)
+ if t.OnTxEnd != nil {
+ t.OnTxEnd(receipt, err)
+ }
}
}
@@ -150,39 +146,35 @@ func (t *muxTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
}
}
-func (t *muxTracer) OnBeaconBlockRootStart(root libcommon.Hash) {
- for _, t := range t.tracers {
- t.OnBeaconBlockRootStart(root)
- }
-}
-
-func (t *muxTracer) OnBeaconBlockRootEnd() {
+func (t *muxTracer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason tracing.BalanceChangeReason) {
for _, t := range t.tracers {
- t.OnBeaconBlockRootEnd()
- }
-}
-
-func (t *muxTracer) OnBalanceChange(addr libcommon.Address, prev *uint256.Int, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
- for _, t := range t.tracers {
- t.OnBalanceChange(addr, prev, new, reason)
+ if t.OnBalanceChange != nil {
+ t.OnBalanceChange(a, prev, new, reason)
+ }
}
}
func (t *muxTracer) OnNonceChange(a libcommon.Address, prev, new uint64) {
for _, t := range t.tracers {
- t.OnNonceChange(a, prev, new)
+ if t.OnNonceChange != nil {
+ t.OnNonceChange(a, prev, new)
+ }
}
}
func (t *muxTracer) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
for _, t := range t.tracers {
- t.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
+ if t.OnCodeChange != nil {
+ t.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
+ }
}
}
func (t *muxTracer) OnStorageChange(addr libcommon.Address, slot *libcommon.Hash, prev uint256.Int, new uint256.Int) {
for _, t := range t.tracers {
- t.OnStorageChange(addr, slot, prev, new)
+ if t.OnStorageChange != nil {
+ t.OnStorageChange(addr, slot, prev, new)
+ }
}
}
@@ -205,7 +197,9 @@ func (t *muxTracer) GetResult() (json.RawMessage, error) {
func (t *muxTracer) OnLog(log *types.Log) {
for _, t := range t.tracers {
- t.OnLog(log)
+ if t.OnLog != nil {
+ t.OnLog(log)
+ }
}
}
diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go
index 8b980b0bf28..f109fc4182b 100644
--- a/eth/tracers/native/prestate.go
+++ b/eth/tracers/native/prestate.go
@@ -30,6 +30,7 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/crypto"
@@ -61,8 +62,7 @@ type accountMarshaling struct {
}
type prestateTracer struct {
- tracers.NoopTracer
- env *vm.EVM
+ env *tracing.VMContext
pre state
post state
create bool
@@ -79,47 +79,37 @@ type prestateTracerConfig struct {
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
}
-func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
var config prestateTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, err
}
}
- return &prestateTracer{
+ t := &prestateTracer{
pre: state{},
post: state{},
config: config,
created: make(map[libcommon.Address]bool),
deleted: make(map[libcommon.Address]bool),
- }, nil
-}
-
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (t *prestateTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
- if t.config.DiffMode {
- return
- }
-
- if t.create {
- // Keep existing account prior to contract creation at that address
- if s := t.pre[t.to]; s != nil && !s.exists() {
- // Exclude newly created contract.
- delete(t.pre, t.to)
- }
}
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnTxEnd: t.OnTxEnd,
+ OnOpcode: t.OnOpcode,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }, nil
}
-// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
-func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
- stack := scope.Stack
- stackData := stack.Data
+// OnOpcode implements the EVMLogger interface to trace a single step of VM execution.
+func (t *prestateTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ op := vm.OpCode(opcode)
+ stackData := scope.StackData()
stackLen := len(stackData)
- caller := scope.Contract.Address()
+ caller := scope.Address()
switch {
case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
slot := libcommon.Hash(stackData[stackLen-1].Bytes32())
@@ -134,14 +124,18 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
addr := libcommon.Address(stackData[stackLen-2].Bytes20())
t.lookupAccount(addr)
case op == vm.CREATE:
- nonce := t.env.IntraBlockState().GetNonce(caller)
+ nonce := t.env.IntraBlockState.GetNonce(caller)
addr := crypto.CreateAddress(caller, nonce)
t.lookupAccount(addr)
t.created[addr] = true
case stackLen >= 4 && op == vm.CREATE2:
offset := stackData[stackLen-2]
size := stackData[stackLen-3]
- init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ init, err := tracers.GetMemoryCopyPadded(scope.MemoryData(), int64(offset.Uint64()), int64(size.Uint64()))
+ if err != nil {
+ t.Stop(fmt.Errorf("failed to copy CREATE2 in prestate tracer input err: %s", err))
+ return
+ }
inithash := crypto.Keccak256(init)
salt := stackData[stackLen-4]
addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash)
@@ -150,10 +144,10 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
}
}
-func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
+func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
t.env = env
- signer := types.MakeSigner(env.ChainConfig(), env.Context.BlockNumber, env.Context.Time)
+ signer := types.MakeSigner(env.ChainConfig, env.BlockNumber, env.Time)
from, err := tx.Sender(*signer)
if err != nil {
t.Stop(fmt.Errorf("could not recover sender address: %v", err))
@@ -161,7 +155,7 @@ func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
}
if tx.GetTo() == nil {
t.create = true
- t.to = crypto.CreateAddress(from, env.IntraBlockState().GetNonce(from))
+ t.to = crypto.CreateAddress(from, env.IntraBlockState.GetNonce(from))
} else {
t.to = *tx.GetTo()
t.create = false
@@ -169,14 +163,14 @@ func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {
t.lookupAccount(from)
t.lookupAccount(t.to)
- t.lookupAccount(env.Context.Coinbase)
+ t.lookupAccount(env.Coinbase)
if t.create && t.config.DiffMode {
t.created[t.to] = true
}
}
-func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
+func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) {
if !t.config.DiffMode {
return
}
@@ -192,9 +186,9 @@ func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
}
modified := false
postAccount := &account{Storage: make(map[libcommon.Hash]libcommon.Hash)}
- newBalance := t.env.IntraBlockState().GetBalance(addr).ToBig()
- newNonce := t.env.IntraBlockState().GetNonce(addr)
- newCode := t.env.IntraBlockState().GetCode(addr)
+ newBalance := t.env.IntraBlockState.GetBalance(addr).ToBig()
+ newNonce := t.env.IntraBlockState.GetNonce(addr)
+ newCode := t.env.IntraBlockState.GetCode(addr)
if newBalance.Cmp(t.pre[addr].Balance) != 0 {
modified = true
@@ -216,7 +210,7 @@ func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
}
var newVal uint256.Int
- t.env.IntraBlockState().GetState(addr, &key, &newVal)
+ t.env.IntraBlockState.GetState(addr, &key, &newVal)
if new(uint256.Int).SetBytes(val[:]).Eq(&newVal) {
// Omit unchanged slots
delete(t.pre[addr].Storage, key)
@@ -277,9 +271,9 @@ func (t *prestateTracer) lookupAccount(addr libcommon.Address) {
}
t.pre[addr] = &account{
- Balance: t.env.IntraBlockState().GetBalance(addr).ToBig(),
- Nonce: t.env.IntraBlockState().GetNonce(addr),
- Code: t.env.IntraBlockState().GetCode(addr),
+ Balance: t.env.IntraBlockState.GetBalance(addr).ToBig(),
+ Nonce: t.env.IntraBlockState.GetNonce(addr),
+ Code: t.env.IntraBlockState.GetCode(addr),
Storage: make(map[libcommon.Hash]libcommon.Hash),
}
}
@@ -292,6 +286,6 @@ func (t *prestateTracer) lookupStorage(addr libcommon.Address, key libcommon.Has
return
}
var val uint256.Int
- t.env.IntraBlockState().GetState(addr, &key, &val)
+ t.env.IntraBlockState.GetState(addr, &key, &val)
t.pre[addr].Storage[key] = val.Bytes32()
}
diff --git a/eth/tracers/native/tracer.go b/eth/tracers/native/tracer.go
index bdcc31394ec..29bf07eebf4 100644
--- a/eth/tracers/native/tracer.go
+++ b/eth/tracers/native/tracer.go
@@ -43,7 +43,7 @@ func init() {
}
// ctorFn is the constructor signature of a native tracer.
-type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
+type ctorFn = func(*tracers.Context, json.RawMessage) (*tracers.Tracer, error)
/*
ctors is a map of package-local tracer constructors.
@@ -68,7 +68,7 @@ func register(name string, ctor ctorFn) {
}
// lookup returns a tracer, if one can be matched to the given name.
-func lookup(name string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func lookup(name string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
if ctors == nil {
ctors = make(map[string]ctorFn)
}
diff --git a/eth/tracers/noop.go b/eth/tracers/noop.go
index 68d29737bc9..84fa3ba81cc 100644
--- a/eth/tracers/noop.go
+++ b/eth/tracers/noop.go
@@ -18,15 +18,13 @@ package tracers
import (
"encoding/json"
- "math/big"
"github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
+ "github.com/ledgerwatch/erigon-lib/common"
libcommon "github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
)
func init() {
@@ -38,59 +36,48 @@ func init() {
type NoopTracer struct{}
// newNoopTracer returns a new noop tracer.
-func newNoopTracer(ctx *Context, _ json.RawMessage) (Tracer, error) {
- return &NoopTracer{}, nil
+func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) {
+ t := &NoopTracer{}
+ return &Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: t.OnTxStart,
+ OnTxEnd: t.OnTxEnd,
+ OnEnter: t.OnEnter,
+ OnExit: t.OnExit,
+ OnOpcode: t.OnOpcode,
+ OnFault: t.OnFault,
+ OnGasChange: t.OnGasChange,
+ OnBalanceChange: t.OnBalanceChange,
+ OnNonceChange: t.OnNonceChange,
+ OnCodeChange: t.OnCodeChange,
+ OnStorageChange: t.OnStorageChange,
+ OnLog: t.OnLog,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }, nil
}
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (t *NoopTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (t *NoopTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
}
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
+func (t *NoopTracer) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpContext, depth int, err error) {
}
-// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
-func (t *NoopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
-}
-
-// CaptureFault implements the EVMLogger interface to trace an execution fault.
-func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
-}
-
-// CaptureKeccakPreimage is called during the KECCAK256 opcode.
-func (t *NoopTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-// OnGasChange is called when gas is either consumed or refunded.
-func (t *NoopTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-// CaptureExit is called when EVM exits a scope, even if the scope didn't
-// execute any code.
-func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
-}
+func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {}
-func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {}
-
-func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (*NoopTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
+func (t *NoopTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
}
-func (*NoopTracer) OnBlockEnd(err error) {
+func (t *NoopTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
}
-func (*NoopTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
+func (*NoopTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from common.Address) {
}
-func (*NoopTracer) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (*NoopTracer) OnBeaconBlockRootEnd() {}
+func (*NoopTracer) OnTxEnd(receipt *types.Receipt, err error) {}
-func (*NoopTracer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
+func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *uint256.Int, reason tracing.BalanceChangeReason) {
}
func (*NoopTracer) OnNonceChange(a libcommon.Address, prev, new uint64) {}
diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go
index 89a39636979..53d885e2f4e 100644
--- a/eth/tracers/tracers.go
+++ b/eth/tracers/tracers.go
@@ -21,8 +21,9 @@ import (
"encoding/json"
"errors"
+ "github.com/ledgerwatch/erigon/core/tracing"
+
libcommon "github.com/ledgerwatch/erigon-lib/common"
- "github.com/ledgerwatch/erigon/core"
)
// Context contains some contextual infos for a transaction execution that is not
@@ -35,14 +36,14 @@ type Context struct {
// Tracer interface extends vm.EVMLogger and additionally
// allows collecting the tracing result.
-type Tracer interface {
- core.BlockchainLogger
- GetResult() (json.RawMessage, error)
+type Tracer struct {
+ *tracing.Hooks
+ GetResult func() (json.RawMessage, error)
// Stop terminates execution of the tracer at the first opportune moment.
- Stop(err error)
+ Stop func(err error)
}
-type lookupFunc func(string, *Context, json.RawMessage) (Tracer, error)
+type lookupFunc func(string, *Context, json.RawMessage) (*Tracer, error)
var (
lookups []lookupFunc
@@ -62,7 +63,7 @@ func RegisterLookup(wildcard bool, lookup lookupFunc) {
// New returns a new instance of a tracer, by iterating through the
// registered lookups.
-func New(code string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
+func New(code string, ctx *Context, cfg json.RawMessage) (*Tracer, error) {
for _, lookup := range lookups {
if tracer, err := lookup(code, ctx, cfg); err == nil {
return tracer, nil
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index cc3e29146cf..6cdc704c936 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -20,10 +20,11 @@ import (
"crypto/ecdsa"
"crypto/rand"
"encoding/json"
- "github.com/ledgerwatch/erigon-lib/common/hexutil"
"math/big"
"testing"
+ "github.com/ledgerwatch/erigon-lib/common/hexutil"
+
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/types"
@@ -109,20 +110,20 @@ func TestPrestateTracerCreate2(t *testing.T) {
if err != nil {
t.Fatalf("failed to prestate tracer: %v", err)
}
- evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer.Hooks})
msg, err := txn.AsMessage(*signer, nil, rules)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
- tracer.CaptureTxStart(evm, txn)
+ tracer.OnTxStart(evm.GetVMContext(), txn, msg.From())
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(txn.GetGas()).AddBlobGas(txn.GetBlobGas()))
exeRes, err := st.TransitionDb(false, false)
if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: exeRes.UsedGas}, nil)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: exeRes.UsedGas}, nil)
// Retrieve the trace result and compare against the etalon
res, err := tracer.GetResult()
if err != nil {
diff --git a/eth/tracers/util.go b/eth/tracers/util.go
new file mode 100644
index 00000000000..b115df34925
--- /dev/null
+++ b/eth/tracers/util.go
@@ -0,0 +1,80 @@
+// Copyright 2023 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 tracers
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/holiman/uint256"
+)
+
+const (
+ memoryPadLimit = 1024 * 1024
+)
+
+// GetMemoryCopyPadded returns offset + size as a new slice.
+// It zero-pads the slice if it extends beyond memory bounds.
+func GetMemoryCopyPadded(m []byte, offset, size int64) ([]byte, error) {
+ if offset < 0 || size < 0 {
+ return nil, errors.New("offset or size must not be negative")
+ }
+ length := int64(len(m))
+ if offset+size < length { // slice fully inside memory
+ return memoryCopy(m, offset, size), nil
+ }
+ paddingNeeded := offset + size - length
+ if paddingNeeded > memoryPadLimit {
+ return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded)
+ }
+ cpy := make([]byte, size)
+ if overlap := length - offset; overlap > 0 {
+ copy(cpy, MemoryPtr(m, offset, overlap))
+ }
+ return cpy, nil
+}
+
+func memoryCopy(m []byte, offset, size int64) (cpy []byte) {
+ if size == 0 {
+ return nil
+ }
+
+ if len(m) > int(offset) {
+ cpy = make([]byte, size)
+ copy(cpy, m[offset:offset+size])
+
+ return
+ }
+
+ return
+}
+
+func MemoryPtr(m []byte, offset, size int64) []byte {
+ if size == 0 {
+ return nil
+ }
+
+ if len(m) > int(offset) {
+ return m[offset : offset+size]
+ }
+
+ return nil
+}
+
+// Back returns the n'th item in stack
+func StackBack(st []uint256.Int, n int) *uint256.Int {
+ return &st[len(st)-n-1]
+}
diff --git a/polygon/bor/bor.go b/polygon/bor/bor.go
index 43e7be821a9..30e7660ff51 100644
--- a/polygon/bor/bor.go
+++ b/polygon/bor/bor.go
@@ -31,6 +31,7 @@ import (
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/crypto"
@@ -1093,7 +1094,7 @@ func (c *Bor) GenerateSeal(chain consensus.ChainHeaderReader, currnt, parent *ty
}
func (c *Bor) Initialize(config *chain.Config, chain consensus.ChainHeaderReader, header *types.Header,
- state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger consensus.EngineLogger) {
+ state *state.IntraBlockState, syscall consensus.SysCallCustom, logger log.Logger, eLogger *tracing.Hooks) {
}
// Authorize injects a private key into the consensus engine to mint new blocks
diff --git a/polygon/tracer/bor_state_sync_txn_tracer.go b/polygon/tracer/bor_state_sync_txn_tracer.go
index a70ff5ae4ae..bdaf72c8632 100644
--- a/polygon/tracer/bor_state_sync_txn_tracer.go
+++ b/polygon/tracer/bor_state_sync_txn_tracer.go
@@ -2,29 +2,43 @@ package tracer
import (
"encoding/json"
- "math/big"
"github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
- "github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/tracers"
)
func NewBorStateSyncTxnTracer(
- tracer vm.EVMLogger,
+ tracer *tracers.Tracer,
stateSyncEventsCount int,
stateReceiverContractAddress libcommon.Address,
-) tracers.Tracer {
- return &borStateSyncTxnTracer{
- EVMLogger: tracer,
+) *tracers.Tracer {
+ l := &borStateSyncTxnTracer{
+ Tracer: tracer,
stateSyncEventsCount: stateSyncEventsCount,
stateReceiverContractAddress: stateReceiverContractAddress,
}
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnTxStart: l.OnTxStart,
+ OnTxEnd: l.OnTxEnd,
+ OnEnter: l.OnEnter,
+ OnExit: l.OnExit,
+ OnOpcode: l.OnOpcode,
+ OnFault: l.OnFault,
+ OnGasChange: l.OnGasChange,
+ OnBalanceChange: l.OnBalanceChange,
+ OnNonceChange: l.OnNonceChange,
+ OnCodeChange: l.OnCodeChange,
+ OnStorageChange: l.OnStorageChange,
+ OnLog: l.OnLog,
+ },
+ GetResult: l.GetResult,
+ Stop: l.Stop,
+ }
}
// borStateSyncTxnTracer is a special tracer which is used only for tracing bor state sync transactions. Bor state sync
@@ -37,44 +51,25 @@ func NewBorStateSyncTxnTracer(
// state sync events at end of each sprint these are synthetically executed as if they were sub-calls of the
// state sync events bor transaction.
type borStateSyncTxnTracer struct {
- vm.EVMLogger
+ Tracer *tracers.Tracer
captureStartCalledOnce bool
stateSyncEventsCount int
stateReceiverContractAddress libcommon.Address
}
-func (bsstt *borStateSyncTxnTracer) CaptureTxStart(evm *vm.EVM, tx types.Transaction) {
- bsstt.EVMLogger.CaptureTxStart(evm, tx)
-}
-
-func (bsstt *borStateSyncTxnTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
- bsstt.EVMLogger.CaptureTxEnd(receipt, err)
+func (bsstt *borStateSyncTxnTracer) OnTxStart(env *tracing.VMContext, tx types.Transaction, from libcommon.Address) {
+ if bsstt.Tracer.OnTxStart != nil {
+ bsstt.Tracer.OnTxStart(env, tx, from)
+ }
}
-func (bsstt *borStateSyncTxnTracer) CaptureStart(
- from libcommon.Address,
- to libcommon.Address,
- precompile bool,
- create bool,
- input []byte,
- gas uint64,
- value *uint256.Int,
- code []byte,
-) {
- if !bsstt.captureStartCalledOnce {
- // first event execution started
- // perform a CaptureStart for the synthetic state sync transaction
- from := state.SystemAddress
- to := bsstt.stateReceiverContractAddress
- bsstt.EVMLogger.CaptureStart(from, to, false, false, nil, 0, uint256.NewInt(0), nil)
- bsstt.captureStartCalledOnce = true
+func (bsstt *borStateSyncTxnTracer) OnTxEnd(receipt *types.Receipt, err error) {
+ if bsstt.Tracer.OnTxEnd != nil {
+ bsstt.Tracer.OnTxEnd(receipt, err)
}
-
- // trick the tracer to think it is a CaptureEnter
- bsstt.EVMLogger.CaptureEnter(vm.CALL, from, to, precompile, create, input, gas, value, code)
}
-func (bsstt *borStateSyncTxnTracer) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
+func (bsstt *borStateSyncTxnTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if bsstt.stateSyncEventsCount == 0 {
// guard against unexpected use
panic("unexpected extra call to borStateSyncTxnTracer.CaptureEnd")
@@ -83,153 +78,96 @@ func (bsstt *borStateSyncTxnTracer) CaptureEnd(output []byte, usedGas uint64, er
// finished executing 1 event
bsstt.stateSyncEventsCount--
- // trick tracer to think it is a CaptureExit
- bsstt.EVMLogger.CaptureExit(output, usedGas, err, reverted)
-
- if bsstt.stateSyncEventsCount == 0 {
- // reached last event
- // perform a CaptureEnd for the synthetic state sync transaction
- bsstt.EVMLogger.CaptureEnd(nil, 0, nil, reverted)
- }
-}
-
-func (bsstt *borStateSyncTxnTracer) CaptureState(
- pc uint64,
- op vm.OpCode,
- gas uint64,
- cost uint64,
- scope *vm.ScopeContext,
- rData []byte,
- depth int,
- err error,
-) {
- // trick tracer to think it is 1 level deeper
- bsstt.EVMLogger.CaptureState(pc, op, gas, cost, scope, rData, depth+1, err)
-}
-
-func (bsstt *borStateSyncTxnTracer) CaptureFault(
- pc uint64,
- op vm.OpCode,
- gas uint64,
- cost uint64,
- scope *vm.ScopeContext,
- depth int,
- err error,
-) {
- // trick tracer to think it is 1 level deeper
- bsstt.EVMLogger.CaptureFault(pc, op, gas, cost, scope, depth+1, err)
+ if bsstt.Tracer.OnExit != nil {
+ // trick tracer to think it is a CaptureExit
+ bsstt.Tracer.OnExit(depth, output, gasUsed, err, reverted)
+ }
}
-func (bsstt *borStateSyncTxnTracer) GetResult() (json.RawMessage, error) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- return tracer.GetResult()
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.GetResult called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ if bsstt.Tracer.OnEnter != nil {
+ bsstt.Tracer.OnEnter(depth, typ, from, to, precompile, input, gas, value, code)
}
}
-func (bsstt *borStateSyncTxnTracer) Stop(err error) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.Stop(err)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.Stop called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ if bsstt.Tracer.OnOpcode != nil {
+ // trick tracer to think it is 1 level deeper
+ bsstt.Tracer.OnOpcode(pc, op, gas, cost, scope, rData, depth+1, err)
}
}
-// CaptureKeccakPreimage is called during the KECCAK256 opcode.
-func (bsstt *borStateSyncTxnTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.CaptureKeccakPreimage(hash, data)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.CaptureKeccakPreimage called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
+ if bsstt.Tracer.OnFault != nil {
+ // trick tracer to think it is 1 level deeper
+ bsstt.Tracer.OnFault(pc, op, gas, cost, scope, depth+1, err)
}
}
-// OnGasChange is called when gas is either consumed or refunded.
-func (bsstt *borStateSyncTxnTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnGasChange(old, new, reason)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnGasChange called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) GetResult() (json.RawMessage, error) {
+ if bsstt.Tracer.GetResult != nil {
+ return bsstt.Tracer.GetResult()
}
+ return nil, nil
}
-func (bsstt *borStateSyncTxnTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnBlockStart(b, td, finalized, safe, chainConfig)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnBlockStart called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) Stop(err error) {
+ if bsstt.Tracer.Stop != nil {
+ bsstt.Tracer.Stop(err)
}
}
-func (bsstt *borStateSyncTxnTracer) OnBlockEnd(err error) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnBlockEnd(err)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnBlockEnd called on a wrapped tracer which does not support it")
+// OnGasChange is called when gas is either consumed or refunded.
+func (bsstt *borStateSyncTxnTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
+ if bsstt.Tracer.OnGasChange != nil {
+ bsstt.Tracer.OnGasChange(old, new, reason)
}
}
-func (bsstt *borStateSyncTxnTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnGenesisBlock(b, alloc)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnGenesisBlock called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) OnBlockStart(event tracing.BlockEvent) {
+ if bsstt.Tracer.OnBlockStart != nil {
+ bsstt.Tracer.OnBlockStart(event)
}
}
-func (bsstt *borStateSyncTxnTracer) OnBeaconBlockRootStart(root libcommon.Hash) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnBeaconBlockRootStart(root)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnBeaconBlockRootStart called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) OnBlockEnd(err error) {
+ if bsstt.Tracer.OnBlockEnd != nil {
+ bsstt.Tracer.OnBlockEnd(err)
}
}
-func (bsstt *borStateSyncTxnTracer) OnBeaconBlockRootEnd() {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnBeaconBlockRootEnd()
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnBeaconBlockRootEnd called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
+ if bsstt.Tracer.OnGenesisBlock != nil {
+ bsstt.Tracer.OnGenesisBlock(b, alloc)
}
}
-func (bsstt *borStateSyncTxnTracer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnBalanceChange(a, prev, new, reason)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnBalanceChange called on a wrapped tracer which does not support it")
+func (bsstt *borStateSyncTxnTracer) OnBalanceChange(a libcommon.Address, prev, new *uint256.Int, reason tracing.BalanceChangeReason) {
+ if bsstt.Tracer.OnBalanceChange != nil {
+ bsstt.Tracer.OnBalanceChange(a, prev, new, reason)
}
}
func (bsstt *borStateSyncTxnTracer) OnNonceChange(a libcommon.Address, prev, new uint64) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnNonceChange(a, prev, new)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnNonceChange called on a wrapped tracer which does not support it")
+ if bsstt.Tracer.OnNonceChange != nil {
+ bsstt.Tracer.OnNonceChange(a, prev, new)
}
}
func (bsstt *borStateSyncTxnTracer) OnCodeChange(a libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnCodeChange called on a wrapped tracer which does not support it")
+ if bsstt.Tracer.OnCodeChange != nil {
+ bsstt.Tracer.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
}
}
func (bsstt *borStateSyncTxnTracer) OnStorageChange(a libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnStorageChange(a, k, prev, new)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnStorageChange called on a wrapped tracer which does not support it")
+ if bsstt.Tracer.OnStorageChange != nil {
+ bsstt.Tracer.OnStorageChange(a, k, prev, new)
}
}
func (bsstt *borStateSyncTxnTracer) OnLog(log *types.Log) {
- if tracer, ok := bsstt.EVMLogger.(tracers.Tracer); ok {
- tracer.OnLog(log)
- } else {
- panic("unexpected usage - borStateSyncTxnTracer.OnLog called on a wrapped tracer which does not support it")
+ if bsstt.Tracer.OnLog != nil {
+ bsstt.Tracer.OnLog(log)
}
}
diff --git a/polygon/tracer/trace_bor_state_sync_txn.go b/polygon/tracer/trace_bor_state_sync_txn.go
index eb9d8855a36..6d60cfda369 100644
--- a/polygon/tracer/trace_bor_state_sync_txn.go
+++ b/polygon/tracer/trace_bor_state_sync_txn.go
@@ -17,6 +17,7 @@ import (
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/tracers"
+ tracerConfig "github.com/ledgerwatch/erigon/eth/tracers/config"
"github.com/ledgerwatch/erigon/polygon/bor/borcfg"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/turbo/services"
@@ -27,7 +28,7 @@ func TraceBorStateSyncTxnDebugAPI(
ctx context.Context,
dbTx kv.Tx,
chainConfig *chain.Config,
- traceConfig *tracers.TraceConfig,
+ traceConfig *tracerConfig.TraceConfig,
ibs *state.IntraBlockState,
blockReader services.FullBlockReader,
blockHash libcommon.Hash,
@@ -74,6 +75,7 @@ func TraceBorStateSyncTxnTraceAPI(
blockHash libcommon.Hash,
blockNum uint64,
blockTime uint64,
+ tracer *tracers.Tracer,
) (*core.ExecutionResult, error) {
stateSyncEvents, err := blockReader.EventsByBlock(ctx, dbTx, blockHash, blockNum)
if err != nil {
@@ -82,7 +84,7 @@ func TraceBorStateSyncTxnTraceAPI(
stateReceiverContract := libcommon.HexToAddress(chainConfig.Bor.(*borcfg.BorConfig).StateReceiverContract)
if vmConfig.Tracer != nil {
- vmConfig.Tracer = NewBorStateSyncTxnTracer(vmConfig.Tracer, len(stateSyncEvents), stateReceiverContract)
+ vmConfig.Tracer = NewBorStateSyncTxnTracer(tracer, len(stateSyncEvents), stateReceiverContract).Hooks
}
txCtx := initStateSyncTxContext(blockNum, blockHash)
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 1480bf508d1..af84a1a58dc 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -39,9 +39,9 @@ import (
"github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/rlp"
@@ -318,7 +318,7 @@ func MakePreState(rules *chain.Rules, tx kv.RwTx, accounts types.GenesisAlloc, b
if a.Balance != nil {
balance, _ = uint256.FromBig(a.Balance)
}
- statedb.SetBalance(addr, balance, evmtypes.BalanceChangeUnspecified)
+ statedb.SetBalance(addr, balance, tracing.BalanceChangeUnspecified)
for k, v := range a.Storage {
key := k
val := uint256.NewInt(0).SetBytes(v.Bytes())
diff --git a/turbo/adapter/ethapi/api.go b/turbo/adapter/ethapi/api.go
index a640a8f767d..5ced3af752f 100644
--- a/turbo/adapter/ethapi/api.go
+++ b/turbo/adapter/ethapi/api.go
@@ -19,9 +19,10 @@ package ethapi
import (
"errors"
"fmt"
- "github.com/ledgerwatch/erigon-lib/common/hexutil"
"math/big"
+ "github.com/ledgerwatch/erigon-lib/common/hexutil"
+
"github.com/holiman/uint256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
diff --git a/turbo/adapter/ethapi/state_overrides.go b/turbo/adapter/ethapi/state_overrides.go
index 23226188489..19c9467058e 100644
--- a/turbo/adapter/ethapi/state_overrides.go
+++ b/turbo/adapter/ethapi/state_overrides.go
@@ -8,7 +8,7 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/core/state"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
+ "github.com/ledgerwatch/erigon/core/tracing"
)
type StateOverrides map[libcommon.Address]Account
@@ -30,7 +30,7 @@ func (overrides *StateOverrides) Override(state *state.IntraBlockState) error {
if overflow {
return fmt.Errorf("account.Balance higher than 2^256-1")
}
- state.SetBalance(addr, balance, evmtypes.BalanceChangeUnspecified)
+ state.SetBalance(addr, balance, tracing.BalanceChangeUnspecified)
}
if account.State != nil && account.StateDiff != nil {
return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
diff --git a/turbo/app/init_cmd.go b/turbo/app/init_cmd.go
index ce33eeb2c65..5c1ec2552a5 100644
--- a/turbo/app/init_cmd.go
+++ b/turbo/app/init_cmd.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"os"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/eth/tracers"
"github.com/ledgerwatch/erigon/turbo/debug"
@@ -37,7 +38,7 @@ It expects the genesis file as argument.`,
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
func initGenesis(cliCtx *cli.Context) error {
var logger log.Logger
- var tracer tracers.Tracer
+ var tracer *tracers.Tracer
var err error
if logger, tracer, _, err = debug.Setup(cliCtx, true /* rootLogger */); err != nil {
return err
@@ -67,7 +68,11 @@ func initGenesis(cliCtx *cli.Context) error {
if err != nil {
utils.Fatalf("Failed to open database: %v", err)
}
- _, hash, err := core.CommitGenesisBlock(chaindb, genesis, "", logger, tracer)
+ var tracingHooks *tracing.Hooks
+ if tracer != nil {
+ tracingHooks = tracer.Hooks
+ }
+ _, hash, err := core.CommitGenesisBlock(chaindb, genesis, "", logger, tracingHooks)
if err != nil {
utils.Fatalf("Failed to write genesis block: %v", err)
}
diff --git a/turbo/app/snapshots_cmd.go b/turbo/app/snapshots_cmd.go
index dc3c56c5c18..d0c1aa73653 100644
--- a/turbo/app/snapshots_cmd.go
+++ b/turbo/app/snapshots_cmd.go
@@ -651,7 +651,7 @@ func doRetireCommand(cliCtx *cli.Context) error {
func doUploaderCommand(cliCtx *cli.Context) error {
var logger log.Logger
- var tracer tracers.Tracer
+ var tracer *tracers.Tracer
var err error
var metricsMux *http.ServeMux
diff --git a/turbo/debug/flags.go b/turbo/debug/flags.go
index 5ab263eec63..619bc120200 100644
--- a/turbo/debug/flags.go
+++ b/turbo/debug/flags.go
@@ -179,7 +179,7 @@ func SetupCobra(cmd *cobra.Command, filePrefix string) log.Logger {
// Setup initializes profiling and logging based on the CLI flags.
// It should be called as early as possible in the program.
-func Setup(ctx *cli.Context, rootLogger bool) (log.Logger, tracers.Tracer, *http.ServeMux, error) {
+func Setup(ctx *cli.Context, rootLogger bool) (log.Logger, *tracers.Tracer, *http.ServeMux, error) {
// ensure we've read in config file details before setting up metrics etc.
if err := SetFlagsFromConfigFile(ctx); err != nil {
log.Warn("failed setting config flags from yaml/toml file", "err", err)
diff --git a/turbo/jsonrpc/debug_api.go b/turbo/jsonrpc/debug_api.go
index 81aab4fc903..7565fadde96 100644
--- a/turbo/jsonrpc/debug_api.go
+++ b/turbo/jsonrpc/debug_api.go
@@ -18,7 +18,7 @@ import (
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
- "github.com/ledgerwatch/erigon/eth/tracers"
+ tracerConfig "github.com/ledgerwatch/erigon/eth/tracers/config"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
@@ -32,13 +32,13 @@ const AccountRangeMaxResults = 256
// PrivateDebugAPI Exposed RPC endpoints for debugging use
type PrivateDebugAPI interface {
StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutility.Bytes, maxResult int) (StorageRangeResult, error)
- TraceTransaction(ctx context.Context, hash common.Hash, config *tracers.TraceConfig, stream *jsoniter.Stream) error
- TraceBlockByHash(ctx context.Context, hash common.Hash, config *tracers.TraceConfig, stream *jsoniter.Stream) error
- TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *tracers.TraceConfig, stream *jsoniter.Stream) error
+ TraceTransaction(ctx context.Context, hash common.Hash, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error
+ TraceBlockByHash(ctx context.Context, hash common.Hash, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error
+ TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error
AccountRange(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, start []byte, maxResults int, nocode, nostorage bool) (state.IteratorDump, error)
GetModifiedAccountsByNumber(ctx context.Context, startNum rpc.BlockNumber, endNum *rpc.BlockNumber) ([]common.Address, error)
GetModifiedAccountsByHash(_ context.Context, startHash common.Hash, endHash *common.Hash) ([]common.Address, error)
- TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *tracers.TraceConfig, stream *jsoniter.Stream) error
+ TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error
AccountAt(ctx context.Context, blockHash common.Hash, txIndex uint64, account common.Address) (*AccountResult, error)
GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutility.Bytes, error)
GetRawBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutility.Bytes, error)
diff --git a/turbo/jsonrpc/debug_api_test.go b/turbo/jsonrpc/debug_api_test.go
index a67440281bf..08e0763b73d 100644
--- a/turbo/jsonrpc/debug_api_test.go
+++ b/turbo/jsonrpc/debug_api_test.go
@@ -15,7 +15,7 @@ import (
"github.com/ledgerwatch/erigon-lib/kv/order"
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/rpcdaemontest"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/eth/tracers"
+ tracerConfig "github.com/ledgerwatch/erigon/eth/tracers/config"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/rpc/rpccfg"
"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
@@ -65,7 +65,7 @@ func TestTraceBlockByNumber(t *testing.T) {
if err != nil {
t.Errorf("traceBlock %s: %v", tt.txHash, err)
}
- err = api.TraceBlockByNumber(m.Ctx, rpc.BlockNumber(tx.BlockNumber.ToInt().Uint64()), &tracers.TraceConfig{}, stream)
+ err = api.TraceBlockByNumber(m.Ctx, rpc.BlockNumber(tx.BlockNumber.ToInt().Uint64()), &tracerConfig.TraceConfig{}, stream)
if err != nil {
t.Errorf("traceBlock %s: %v", tt.txHash, err)
}
@@ -82,7 +82,7 @@ func TestTraceBlockByNumber(t *testing.T) {
}
var buf bytes.Buffer
stream := jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 4096)
- err := api.TraceBlockByNumber(m.Ctx, rpc.LatestBlockNumber, &tracers.TraceConfig{}, stream)
+ err := api.TraceBlockByNumber(m.Ctx, rpc.LatestBlockNumber, &tracerConfig.TraceConfig{}, stream)
if err != nil {
t.Errorf("traceBlock %v: %v", rpc.LatestBlockNumber, err)
}
@@ -110,7 +110,7 @@ func TestTraceBlockByHash(t *testing.T) {
if err != nil {
t.Errorf("traceBlock %s: %v", tt.txHash, err)
}
- err = api.TraceBlockByHash(m.Ctx, *tx.BlockHash, &tracers.TraceConfig{}, stream)
+ err = api.TraceBlockByHash(m.Ctx, *tx.BlockHash, &tracerConfig.TraceConfig{}, stream)
if err != nil {
t.Errorf("traceBlock %s: %v", tt.txHash, err)
}
@@ -133,7 +133,7 @@ func TestTraceTransaction(t *testing.T) {
for _, tt := range debugTraceTransactionTests {
var buf bytes.Buffer
stream := jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 4096)
- err := api.TraceTransaction(m.Ctx, common.HexToHash(tt.txHash), &tracers.TraceConfig{}, stream)
+ err := api.TraceTransaction(m.Ctx, common.HexToHash(tt.txHash), &tracerConfig.TraceConfig{}, stream)
if err != nil {
t.Errorf("traceTransaction %s: %v", tt.txHash, err)
}
@@ -163,7 +163,7 @@ func TestTraceTransactionNoRefund(t *testing.T) {
var buf bytes.Buffer
stream := jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 4096)
var norefunds = true
- err := api.TraceTransaction(m.Ctx, common.HexToHash(tt.txHash), &tracers.TraceConfig{NoRefunds: &norefunds}, stream)
+ err := api.TraceTransaction(m.Ctx, common.HexToHash(tt.txHash), &tracerConfig.TraceConfig{NoRefunds: &norefunds}, stream)
if err != nil {
t.Errorf("traceTransaction %s: %v", tt.txHash, err)
}
diff --git a/turbo/jsonrpc/eth_call.go b/turbo/jsonrpc/eth_call.go
index 84871efc594..8f87a781aa4 100644
--- a/turbo/jsonrpc/eth_call.go
+++ b/turbo/jsonrpc/eth_call.go
@@ -548,7 +548,7 @@ func (api *APIImpl) CreateAccessList(ctx context.Context, args ethapi2.CallArgs,
// Apply the transaction with the access list tracer
tracer := logger.NewAccessListTracer(accessList, excl, state)
- config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true}
+ config := vm.Config{Tracer: tracer.Hooks(), Debug: true, NoBaseFee: true}
blockCtx := transactions.NewEVMBlockContext(engine, header, bNrOrHash.RequireCanonical, tx, api._blockReader)
txCtx := core.NewEVMTxContext(msg)
diff --git a/turbo/jsonrpc/eth_receipts.go b/turbo/jsonrpc/eth_receipts.go
index e0f14b790f0..140290ca302 100644
--- a/turbo/jsonrpc/eth_receipts.go
+++ b/turbo/jsonrpc/eth_receipts.go
@@ -523,7 +523,7 @@ func txnExecutor(tx kv.TemporalTx, chainConfig *chain.Config, engine consensus.E
ibs: state.New(stateReader),
}
if tracer != nil {
- ie.vmConfig = &vm.Config{Debug: true, Tracer: tracer}
+ ie.vmConfig = &vm.Config{Debug: true, Tracer: tracer.Tracer().Hooks}
}
return ie
}
diff --git a/turbo/jsonrpc/gen_traces_test.go b/turbo/jsonrpc/gen_traces_test.go
index a5f4c7bdc7a..32ff7fcf673 100644
--- a/turbo/jsonrpc/gen_traces_test.go
+++ b/turbo/jsonrpc/gen_traces_test.go
@@ -13,7 +13,7 @@ import (
"github.com/ledgerwatch/erigon-lib/kv/kvcache"
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/cli/httpcfg"
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/rpcdaemontest"
- "github.com/ledgerwatch/erigon/eth/tracers"
+ tracerConfig "github.com/ledgerwatch/erigon/eth/tracers/config"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/rpc/rpccfg"
@@ -35,7 +35,7 @@ func TestGeneratedDebugApi(t *testing.T) {
var buf bytes.Buffer
stream := jsoniter.NewStream(jsoniter.ConfigDefault, &buf, 4096)
callTracer := "callTracer"
- err := api.TraceBlockByNumber(context.Background(), rpc.BlockNumber(1), &tracers.TraceConfig{Tracer: &callTracer}, stream)
+ err := api.TraceBlockByNumber(context.Background(), rpc.BlockNumber(1), &tracerConfig.TraceConfig{Tracer: &callTracer}, stream)
if err != nil {
t.Errorf("debug_traceBlock %d: %v", 0, err)
}
diff --git a/turbo/jsonrpc/otterscan_api.go b/turbo/jsonrpc/otterscan_api.go
index 4e294b521ed..54bd5e84c6d 100644
--- a/turbo/jsonrpc/otterscan_api.go
+++ b/turbo/jsonrpc/otterscan_api.go
@@ -116,7 +116,7 @@ func (api *OtterscanAPIImpl) getTransactionByHash(ctx context.Context, tx kv.Tx,
return txn, block, blockHash, blockNum, txnIndex, nil
}
-func (api *OtterscanAPIImpl) runTracer(ctx context.Context, tx kv.Tx, hash common.Hash, tracer tracers.Tracer) (*core.ExecutionResult, error) {
+func (api *OtterscanAPIImpl) runTracer(ctx context.Context, tx kv.Tx, hash common.Hash, tracer *tracers.Tracer) (*core.ExecutionResult, error) {
txn, block, _, _, txIndex, err := api.getTransactionByHash(ctx, tx, hash)
if err != nil {
return nil, err
@@ -136,26 +136,26 @@ func (api *OtterscanAPIImpl) runTracer(ctx context.Context, tx kv.Tx, hash commo
return nil, err
}
- ibs.SetLogger(tracer)
+ ibs.SetLogger(tracer.Hooks)
var vmConfig vm.Config
if tracer == nil {
vmConfig = vm.Config{}
} else {
- vmConfig = vm.Config{Debug: true, Tracer: tracer}
+ vmConfig = vm.Config{Debug: true, Tracer: tracer.Hooks}
}
vmenv := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vmConfig)
- tracer.CaptureTxStart(vmenv, txn)
+ tracer.OnTxStart(vmenv.GetVMContext(), txn, msg.From())
result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()).AddBlobGas(msg.BlobGas()), true, false /* gasBailout */)
if err != nil {
if tracer != nil {
- tracer.CaptureTxEnd(nil, err)
+ tracer.OnTxEnd(nil, err)
}
return nil, fmt.Errorf("tracing failed: %v", err)
}
if tracer != nil {
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: result.UsedGas}, nil)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: result.UsedGas}, nil)
}
return result, nil
}
@@ -168,7 +168,7 @@ func (api *OtterscanAPIImpl) GetInternalOperations(ctx context.Context, hash com
defer tx.Rollback()
tracer := NewOperationsTracer(ctx)
- if _, err := api.runTracer(ctx, tx, hash, tracer); err != nil {
+ if _, err := api.runTracer(ctx, tx, hash, tracer.Tracer()); err != nil {
return nil, err
}
diff --git a/turbo/jsonrpc/otterscan_default_tracer.go b/turbo/jsonrpc/otterscan_default_tracer.go
index d1c0842c4c1..a03f7f73bd6 100644
--- a/turbo/jsonrpc/otterscan_default_tracer.go
+++ b/turbo/jsonrpc/otterscan_default_tracer.go
@@ -1,79 +1,7 @@
package jsonrpc
-import (
- "encoding/json"
- "math/big"
-
- "github.com/holiman/uint256"
- "github.com/ledgerwatch/erigon-lib/chain"
- "github.com/ledgerwatch/erigon-lib/common"
- libcommon "github.com/ledgerwatch/erigon-lib/common"
-
- "github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
-)
-
// Helper implementation of vm.Tracer; since the interface is big and most
// custom tracers implement just a few of the methods, this is a base struct
// to avoid lots of empty boilerplate code
type DefaultTracer struct {
}
-
-func (a *DefaultTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {}
-
-func (a *DefaultTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (a *DefaultTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-func (t *DefaultTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-func (t *DefaultTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
-}
-
-func (t *DefaultTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-
-func (t *DefaultTracer) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
-}
-
-func (t *DefaultTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (t *DefaultTracer) OnBlockEnd(err error) {
-}
-
-func (t *DefaultTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (t *DefaultTracer) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (t *DefaultTracer) OnBeaconBlockRootEnd() {}
-
-func (t *DefaultTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (t *DefaultTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (t *DefaultTracer) OnBalanceChange(addr libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (t *DefaultTracer) OnNonceChange(addr libcommon.Address, prev, new uint64) {}
-
-func (t *DefaultTracer) OnCodeChange(addr libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (t *DefaultTracer) OnStorageChange(addr libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (t *DefaultTracer) OnLog(log *types.Log) {}
-
-func (t *DefaultTracer) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
-}
-
-func (t *DefaultTracer) GetResult() (json.RawMessage, error) {
- return json.RawMessage{}, nil
-}
-
-func (t *DefaultTracer) Stop(err error) {}
diff --git a/turbo/jsonrpc/otterscan_generic_tracer.go b/turbo/jsonrpc/otterscan_generic_tracer.go
index 676e6ee9b2a..acb5c1ccb84 100644
--- a/turbo/jsonrpc/otterscan_generic_tracer.go
+++ b/turbo/jsonrpc/otterscan_generic_tracer.go
@@ -18,7 +18,7 @@ import (
)
type GenericTracer interface {
- tracers.Tracer
+ Tracer() *tracers.Tracer
SetTransaction(tx types.Transaction)
Found() bool
}
@@ -64,7 +64,7 @@ func (api *OtterscanAPIImpl) genericTracer(dbtx kv.Tx, ctx context.Context, bloc
cachedWriter := state.NewCachedWriter(noop, stateCache)
ibs := state.New(cachedReader)
- ibs.SetLogger(tracer)
+ ibs.SetLogger(tracer.Tracer().Hooks)
getHeader := func(hash common.Hash, number uint64) *types.Header {
h, e := api._blockReader.Header(ctx, dbtx, hash, number)
@@ -99,20 +99,20 @@ func (api *OtterscanAPIImpl) genericTracer(dbtx kv.Tx, ctx context.Context, bloc
BlockContext := core.NewEVMBlockContext(header, core.GetHashFn(header, getHeader), engine, nil)
TxContext := core.NewEVMTxContext(msg)
- vmenv := vm.NewEVM(BlockContext, TxContext, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer})
+ vmenv := vm.NewEVM(BlockContext, TxContext, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer.Tracer().Hooks})
if tracer != nil {
- tracer.CaptureTxStart(vmenv, tx)
+ tracer.Tracer().OnTxStart(vmenv.GetVMContext(), tx, msg.From())
}
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.GetGas()).AddBlobGas(tx.GetBlobGas()), true /* refunds */, false /* gasBailout */)
if err != nil {
if tracer != nil {
- tracer.CaptureTxEnd(nil, err)
+ tracer.Tracer().OnTxEnd(nil, err)
}
return err
}
if tracer != nil {
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: res.UsedGas}, nil)
+ tracer.Tracer().OnTxEnd(&types.Receipt{GasUsed: res.UsedGas}, nil)
}
_ = ibs.FinalizeTx(rules, cachedWriter)
diff --git a/turbo/jsonrpc/otterscan_search_trace.go b/turbo/jsonrpc/otterscan_search_trace.go
index fcf1b0f8a89..cd69c243a96 100644
--- a/turbo/jsonrpc/otterscan_search_trace.go
+++ b/turbo/jsonrpc/otterscan_search_trace.go
@@ -89,18 +89,18 @@ func (api *OtterscanAPIImpl) traceBlock(dbtx kv.Tx, ctx context.Context, blockNu
msg, _ := tx.AsMessage(*signer, header.BaseFee, rules)
tracer := NewTouchTracer(searchAddr)
- ibs.SetLogger(tracer)
+ ibs.SetLogger(tracer.Tracer().Hooks)
BlockContext := core.NewEVMBlockContext(header, core.GetHashFn(header, getHeader), engine, nil)
TxContext := core.NewEVMTxContext(msg)
- vmenv := vm.NewEVM(BlockContext, TxContext, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer})
- tracer.CaptureTxStart(vmenv, tx)
+ vmenv := vm.NewEVM(BlockContext, TxContext, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer.Tracer().Hooks})
+ tracer.Tracer().OnTxStart(vmenv.GetVMContext(), tx, msg.From())
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.GetGas()).AddBlobGas(tx.GetBlobGas()), true /* refunds */, false /* gasBailout */)
if err != nil {
- tracer.CaptureTxEnd(nil, err)
+ tracer.Tracer().OnTxEnd(nil, err)
return false, nil, err
}
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: res.UsedGas}, nil)
+ tracer.Tracer().OnTxEnd(&types.Receipt{GasUsed: res.UsedGas}, nil)
_ = ibs.FinalizeTx(rules, cachedWriter)
if tracer.Found {
diff --git a/turbo/jsonrpc/otterscan_trace_contract_creator.go b/turbo/jsonrpc/otterscan_trace_contract_creator.go
index cc9ca4057d1..f57c0d23bcb 100644
--- a/turbo/jsonrpc/otterscan_trace_contract_creator.go
+++ b/turbo/jsonrpc/otterscan_trace_contract_creator.go
@@ -6,8 +6,10 @@ import (
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
type CreateTracer struct {
@@ -27,6 +29,14 @@ func NewCreateTracer(ctx context.Context, target common.Address) *CreateTracer {
}
}
+func (t *CreateTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnEnter: t.OnEnter,
+ },
+ }
+}
+
func (t *CreateTracer) SetTransaction(tx types.Transaction) {
t.Tx = tx
}
@@ -50,10 +60,6 @@ func (t *CreateTracer) captureStartOrEnter(from, to common.Address, create bool)
t.Creator = from
}
-func (t *CreateTracer) CaptureStart(from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- t.captureStartOrEnter(from, to, create)
-}
-
-func (t *CreateTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- t.captureStartOrEnter(from, to, create)
+func (t *CreateTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ t.captureStartOrEnter(from, to, vm.OpCode(typ) == vm.CREATE)
}
diff --git a/turbo/jsonrpc/otterscan_trace_operations.go b/turbo/jsonrpc/otterscan_trace_operations.go
index 2d032b903d0..3dd95cf3129 100644
--- a/turbo/jsonrpc/otterscan_trace_operations.go
+++ b/turbo/jsonrpc/otterscan_trace_operations.go
@@ -2,12 +2,16 @@ package jsonrpc
import (
"context"
+ "encoding/json"
+
"github.com/ledgerwatch/erigon-lib/common/hexutil"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/common"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
type OperationType int
@@ -27,7 +31,6 @@ type InternalOperation struct {
}
type OperationsTracer struct {
- DefaultTracer
ctx context.Context
Results []*InternalOperation
}
@@ -39,18 +42,34 @@ func NewOperationsTracer(ctx context.Context) *OperationsTracer {
}
}
-func (t *OperationsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- if typ == vm.CALL && value.Uint64() != 0 {
+func (t *OperationsTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnEnter: t.OnEnter,
+ },
+ GetResult: t.GetResult,
+ Stop: t.Stop,
+ }
+}
+
+func (t *OperationsTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ if vm.OpCode(typ) == vm.CALL && value.Uint64() != 0 {
t.Results = append(t.Results, &InternalOperation{OP_TRANSFER, from, to, (*hexutil.Big)(value.ToBig())})
return
}
- if typ == vm.CREATE {
+ if vm.OpCode(typ) == vm.CREATE {
t.Results = append(t.Results, &InternalOperation{OP_CREATE, from, to, (*hexutil.Big)(value.ToBig())})
}
- if typ == vm.CREATE2 {
+ if vm.OpCode(typ) == vm.CREATE2 {
t.Results = append(t.Results, &InternalOperation{OP_CREATE2, from, to, (*hexutil.Big)(value.ToBig())})
}
- if typ == vm.SELFDESTRUCT {
+ if vm.OpCode(typ) == vm.SELFDESTRUCT {
t.Results = append(t.Results, &InternalOperation{OP_SELF_DESTRUCT, from, to, (*hexutil.Big)(value.ToBig())})
}
}
+
+func (t *OperationsTracer) GetResult() (json.RawMessage, error) {
+ return json.RawMessage{}, nil
+}
+
+func (t *OperationsTracer) Stop(err error) {}
diff --git a/turbo/jsonrpc/otterscan_trace_touch.go b/turbo/jsonrpc/otterscan_trace_touch.go
index 03b05629a17..32a09415156 100644
--- a/turbo/jsonrpc/otterscan_trace_touch.go
+++ b/turbo/jsonrpc/otterscan_trace_touch.go
@@ -6,7 +6,8 @@ import (
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/common"
- "github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/core/tracing"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
type TouchTracer struct {
@@ -21,16 +22,20 @@ func NewTouchTracer(searchAddr common.Address) *TouchTracer {
}
}
+func (t *TouchTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnEnter: t.OnEnter,
+ },
+ }
+}
+
func (t *TouchTracer) captureStartOrEnter(from, to common.Address) {
if !t.Found && (bytes.Equal(t.searchAddr.Bytes(), from.Bytes()) || bytes.Equal(t.searchAddr.Bytes(), to.Bytes())) {
t.Found = true
}
}
-func (t *TouchTracer) CaptureStart(from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- t.captureStartOrEnter(from, to)
-}
-
-func (t *TouchTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (t *TouchTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
t.captureStartOrEnter(from, to)
}
diff --git a/turbo/jsonrpc/otterscan_trace_transaction.go b/turbo/jsonrpc/otterscan_trace_transaction.go
index cc8c95fc0ba..1909143f441 100644
--- a/turbo/jsonrpc/otterscan_trace_transaction.go
+++ b/turbo/jsonrpc/otterscan_trace_transaction.go
@@ -11,7 +11,9 @@ import (
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/eth/tracers"
)
func (api *OtterscanAPIImpl) TraceTransaction(ctx context.Context, hash common.Hash) ([]*TraceEntry, error) {
@@ -22,7 +24,7 @@ func (api *OtterscanAPIImpl) TraceTransaction(ctx context.Context, hash common.H
defer tx.Rollback()
tracer := NewTransactionTracer(ctx)
- if _, err := api.runTracer(ctx, tx, hash, tracer); err != nil {
+ if _, err := api.runTracer(ctx, tx, hash, tracer.Tracer()); err != nil {
return nil, err
}
@@ -52,6 +54,15 @@ func NewTransactionTracer(ctx context.Context) *TransactionTracer {
}
}
+func (t *TransactionTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnEnter: t.OnEnter,
+ OnExit: t.OnExit,
+ },
+ }
+}
+
func (t *TransactionTracer) captureStartOrEnter(typ vm.OpCode, from, to common.Address, precompile bool, input []byte, value *uint256.Int) {
if precompile {
return
@@ -99,11 +110,11 @@ func (t *TransactionTracer) CaptureStart(from common.Address, to common.Address,
t.captureStartOrEnter(vm.CALL, from, to, precompile, input, value)
}
-func (t *TransactionTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- t.depth++
- t.captureStartOrEnter(typ, from, to, precompile, input, value)
+func (t *TransactionTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ t.depth = depth
+ t.captureStartOrEnter(vm.OpCode(typ), from, to, precompile, input, value)
}
-func (t *TransactionTracer) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
- t.depth--
+func (t *TransactionTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ t.depth = depth
}
diff --git a/turbo/jsonrpc/trace_adhoc.go b/turbo/jsonrpc/trace_adhoc.go
index 2ff3c2c45a5..b7fa5a5253d 100644
--- a/turbo/jsonrpc/trace_adhoc.go
+++ b/turbo/jsonrpc/trace_adhoc.go
@@ -6,13 +6,11 @@ import (
"encoding/json"
"fmt"
"math"
- "math/big"
"strings"
"github.com/holiman/uint256"
"github.com/ledgerwatch/log/v3"
- "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutil"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
@@ -21,10 +19,10 @@ import (
math2 "github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/types/accounts"
"github.com/ledgerwatch/erigon/core/vm"
- "github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/tracers"
ptracer "github.com/ledgerwatch/erigon/polygon/tracer"
"github.com/ledgerwatch/erigon/rpc"
@@ -301,9 +299,17 @@ type OeTracer struct {
idx []string // Prefix for the "idx" inside operations, for easier navigation
}
-func (ot *OeTracer) CaptureTxStart(env *vm.EVM, tx types.Transaction) {}
-
-func (ot *OeTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
+func (ot *OeTracer) Tracer() *tracers.Tracer {
+ return &tracers.Tracer{
+ Hooks: &tracing.Hooks{
+ OnEnter: ot.OnEnter,
+ OnExit: ot.OnExit,
+ OnOpcode: ot.OnOpcode,
+ },
+ GetResult: ot.GetResult,
+ Stop: ot.Stop,
+ }
+}
func (ot *OeTracer) captureStartOrEnter(deep bool, typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
//fmt.Printf("captureStartOrEnter deep %t, typ %s, from %x, to %x, create %t, input %x, gas %d, value %d, precompile %t\n", deep, typ.String(), from, to, create, input, gas, value, precompile)
@@ -411,12 +417,8 @@ func (ot *OeTracer) captureStartOrEnter(deep bool, typ vm.OpCode, from libcommon
ot.traceStack = append(ot.traceStack, trace)
}
-func (ot *OeTracer) CaptureStart(from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- ot.captureStartOrEnter(false /* deep */, vm.CALL, from, to, precompile, create, input, gas, value, code)
-}
-
-func (ot *OeTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- ot.captureStartOrEnter(true /* deep */, typ, from, to, precompile, create, input, gas, value, code)
+func (ot *OeTracer) OnEnter(depth int, typ byte, from libcommon.Address, to libcommon.Address, precompile bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ ot.captureStartOrEnter(depth != 0 /* deep */, vm.OpCode(typ), from, to, precompile, vm.OpCode(typ) == vm.CREATE, input, gas, value, code)
}
func (ot *OeTracer) captureEndOrExit(deep bool, output []byte, usedGas uint64, err error) {
@@ -488,17 +490,13 @@ func (ot *OeTracer) captureEndOrExit(deep bool, output []byte, usedGas uint64, e
}
}
-func (ot *OeTracer) CaptureEnd(output []byte, usedGas uint64, err error, reverted bool) {
- ot.captureEndOrExit(false /* deep */, output, usedGas, err)
-}
-
-func (ot *OeTracer) CaptureExit(output []byte, usedGas uint64, err error, reverted bool) {
- ot.captureEndOrExit(true /* deep */, output, usedGas, err)
+func (ot *OeTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
+ ot.captureEndOrExit(depth != 0 /* deep */, output, gasUsed, err)
}
-func (ot *OeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, opDepth int, err error) {
- memory := scope.Memory
- st := scope.Stack
+func (ot *OeTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
+ memory := scope.MemoryData()
+ st := scope.StackData()
if ot.r.VmTrace != nil {
var vmTrace *VmTrace
@@ -527,8 +525,8 @@ func (ot *OeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scop
showStack = 1
}
for i := showStack - 1; i >= 0; i-- {
- if st.Len() > i {
- ot.lastVmOp.Ex.Push = append(ot.lastVmOp.Ex.Push, st.Back(i).String())
+ if len(st) > i {
+ ot.lastVmOp.Ex.Push = append(ot.lastVmOp.Ex.Push, tracers.StackBack(st, i).String())
}
}
// Set the "mem" of the last operation
@@ -538,7 +536,8 @@ func (ot *OeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scop
setMem = true
}
if setMem && ot.lastMemLen > 0 {
- cpy := memory.GetCopy(int64(ot.lastMemOff), int64(ot.lastMemLen))
+ // TODO: error handling
+ cpy, _ := tracers.GetMemoryCopyPadded(memory, int64(ot.lastMemOff), int64(ot.lastMemLen))
if len(cpy) == 0 {
cpy = make([]byte, ot.lastMemLen)
}
@@ -547,13 +546,13 @@ func (ot *OeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scop
}
if ot.lastOffStack != nil {
ot.lastOffStack.Ex.Used = int(gas)
- if st.Len() > 0 {
- ot.lastOffStack.Ex.Push = []string{st.Back(0).String()}
+ if len(st) > 0 {
+ ot.lastOffStack.Ex.Push = []string{tracers.StackBack(st, 0).String()}
} else {
ot.lastOffStack.Ex.Push = []string{}
}
if ot.lastMemLen > 0 && memory != nil {
- cpy := memory.GetCopy(int64(ot.lastMemOff), int64(ot.lastMemLen))
+ cpy, _ := tracers.GetMemoryCopyPadded(memory, int64(ot.lastMemOff), int64(ot.lastMemLen))
if len(cpy) == 0 {
cpy = make([]byte, ot.lastMemLen)
}
@@ -561,7 +560,7 @@ func (ot *OeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scop
}
ot.lastOffStack = nil
}
- if ot.lastOp == vm.STOP && op == vm.STOP && len(ot.vmOpStack) == 0 {
+ if ot.lastOp == vm.STOP && vm.OpCode(op) == vm.STOP && len(ot.vmOpStack) == 0 {
// Looks like OE is "optimising away" the second STOP
return
}
@@ -575,52 +574,52 @@ func (ot *OeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scop
}
ot.lastVmOp.Idx = fmt.Sprintf("%s%d", sb.String(), len(vmTrace.Ops)-1)
}
- ot.lastOp = op
+ ot.lastOp = vm.OpCode(op)
ot.lastVmOp.Cost = int(cost)
ot.lastVmOp.Pc = int(pc)
ot.lastVmOp.Ex.Push = []string{}
ot.lastVmOp.Ex.Used = int(gas) - int(cost)
if !ot.compat {
- ot.lastVmOp.Op = op.String()
+ ot.lastVmOp.Op = vm.OpCode(op).String()
}
- switch op {
+ switch vm.OpCode(op) {
case vm.MSTORE, vm.MLOAD:
- if st.Len() > 0 {
- ot.lastMemOff = st.Back(0).Uint64()
+ if len(st) > 0 {
+ ot.lastMemOff = tracers.StackBack(st, 0).Uint64()
ot.lastMemLen = 32
}
case vm.MSTORE8:
- if st.Len() > 0 {
- ot.lastMemOff = st.Back(0).Uint64()
+ if len(st) > 0 {
+ ot.lastMemOff = tracers.StackBack(st, 0).Uint64()
ot.lastMemLen = 1
}
case vm.RETURNDATACOPY, vm.CALLDATACOPY, vm.CODECOPY:
- if st.Len() > 2 {
- ot.lastMemOff = st.Back(0).Uint64()
- ot.lastMemLen = st.Back(2).Uint64()
+ if len(st) > 2 {
+ ot.lastMemOff = tracers.StackBack(st, 0).Uint64()
+ ot.lastMemLen = tracers.StackBack(st, 2).Uint64()
}
case vm.EXTCODECOPY:
- if st.Len() > 3 {
- ot.lastMemOff = st.Back(1).Uint64()
- ot.lastMemLen = st.Back(3).Uint64()
+ if len(st) > 3 {
+ ot.lastMemOff = tracers.StackBack(st, 1).Uint64()
+ ot.lastMemLen = tracers.StackBack(st, 3).Uint64()
}
case vm.STATICCALL, vm.DELEGATECALL:
- if st.Len() > 5 {
- ot.memOffStack = append(ot.memOffStack, st.Back(4).Uint64())
- ot.memLenStack = append(ot.memLenStack, st.Back(5).Uint64())
+ if len(st) > 5 {
+ ot.memOffStack = append(ot.memOffStack, tracers.StackBack(st, 4).Uint64())
+ ot.memLenStack = append(ot.memLenStack, tracers.StackBack(st, 5).Uint64())
}
case vm.CALL, vm.CALLCODE:
- if st.Len() > 6 {
- ot.memOffStack = append(ot.memOffStack, st.Back(5).Uint64())
- ot.memLenStack = append(ot.memLenStack, st.Back(6).Uint64())
+ if len(st) > 6 {
+ ot.memOffStack = append(ot.memOffStack, tracers.StackBack(st, 5).Uint64())
+ ot.memLenStack = append(ot.memLenStack, tracers.StackBack(st, 6).Uint64())
}
case vm.CREATE, vm.CREATE2, vm.SELFDESTRUCT:
// Effectively disable memory output
ot.memOffStack = append(ot.memOffStack, 0)
ot.memLenStack = append(ot.memLenStack, 0)
case vm.SSTORE:
- if st.Len() > 1 {
- ot.lastVmOp.Ex.Store = &VmTraceStore{Key: st.Back(0).String(), Val: st.Back(1).String()}
+ if len(st) > 1 {
+ ot.lastVmOp.Ex.Store = &VmTraceStore{Key: tracers.StackBack(st, 0).String(), Val: tracers.StackBack(st, 1).String()}
}
}
if ot.lastVmOp.Ex.Used < 0 {
@@ -629,39 +628,6 @@ func (ot *OeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scop
}
}
-func (ot *OeTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, opDepth int, err error) {
-}
-
-func (ot *OeTracer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, chainConfig *chain.Config) {
-}
-
-func (ot *OeTracer) OnBlockEnd(err error) {
-}
-
-func (ot *OeTracer) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
-}
-
-func (ot *OeTracer) OnBeaconBlockRootStart(root libcommon.Hash) {}
-
-func (ot *OeTracer) OnBeaconBlockRootEnd() {}
-
-func (ot *OeTracer) CaptureKeccakPreimage(hash libcommon.Hash, data []byte) {}
-
-func (ot *OeTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (ot *OeTracer) OnBalanceChange(addr libcommon.Address, prev, new *uint256.Int, reason evmtypes.BalanceChangeReason) {
-}
-
-func (ot *OeTracer) OnNonceChange(addr libcommon.Address, prev, new uint64) {}
-
-func (aot *OeTracer) OnCodeChange(addr libcommon.Address, prevCodeHash libcommon.Hash, prev []byte, codeHash libcommon.Hash, code []byte) {
-}
-
-func (ot *OeTracer) OnStorageChange(addr libcommon.Address, k *libcommon.Hash, prev, new uint256.Int) {
-}
-
-func (ot *OeTracer) OnLog(log *types.Log) {}
-
func (ot *OeTracer) GetResult() (json.RawMessage, error) {
return json.RawMessage{}, nil
}
@@ -1078,7 +1044,7 @@ func (api *TraceAPIImpl) Call(ctx context.Context, args TraceCallParam, traceTyp
blockCtx.GasLimit = math.MaxUint64
blockCtx.MaxGasLimit = true
- evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Debug: traceTypeTrace, Tracer: &ot})
+ evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Debug: traceTypeTrace, Tracer: ot.Tracer().Hooks})
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
@@ -1090,15 +1056,15 @@ func (api *TraceAPIImpl) Call(ctx context.Context, args TraceCallParam, traceTyp
gp := new(core.GasPool).AddGas(msg.Gas()).AddBlobGas(msg.BlobGas())
var execResult *core.ExecutionResult
ibs.SetTxContext(libcommon.Hash{}, libcommon.Hash{}, 0)
- ibs.SetLogger(&ot)
+ ibs.SetLogger(ot.Tracer().Hooks)
- ot.CaptureTxStart(evm, txn)
+ ot.Tracer().OnTxStart(evm.GetVMContext(), txn, msg.From())
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, true /* gasBailout */)
if err != nil {
- ot.CaptureTxEnd(nil, err)
+ ot.Tracer().OnTxEnd(nil, err)
return nil, err
}
- ot.CaptureTxEnd(&types.Receipt{GasUsed: execResult.UsedGas}, nil)
+ ot.Tracer().OnTxEnd(&types.Receipt{GasUsed: execResult.UsedGas}, nil)
traceResult.Output = libcommon.CopyBytes(execResult.ReturnData)
if traceTypeStateDiff {
sdMap := make(map[libcommon.Address]*StateDiffAccount)
@@ -1290,7 +1256,7 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx kv.Tx, txns []type
traceResult := &TraceCallResult{Trace: []*ParityTrace{}, TransactionHash: args.txHash}
vmConfig := vm.Config{}
- var tracer tracers.Tracer
+ var tracer *tracers.Tracer
if (traceTypeTrace && (txIndexNeeded == -1 || txIndex == txIndexNeeded)) || traceTypeVmTrace {
var ot OeTracer
ot.compat = api.compatibility
@@ -1303,8 +1269,8 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx kv.Tx, txns []type
traceResult.VmTrace = &VmTrace{Ops: []*VmTraceOp{}}
}
vmConfig.Debug = true
- vmConfig.Tracer = &ot
- tracer = &ot
+ vmConfig.Tracer = ot.Tracer().Hooks
+ tracer = ot.Tracer()
}
blockCtx := transactions.NewEVMBlockContext(engine, header, parentNrOrHash.RequireCanonical, dbtx, api._blockReader)
@@ -1350,6 +1316,7 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx kv.Tx, txns []type
header.Hash(),
header.Number.Uint64(),
header.Time,
+ tracer,
)
} else {
if args.txHash != nil {
@@ -1358,24 +1325,24 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx kv.Tx, txns []type
ibs.SetTxContext(libcommon.Hash{}, header.Hash(), txIndex)
}
- ibs.SetLogger(tracer)
+ ibs.SetLogger(tracer.Hooks)
txCtx := core.NewEVMTxContext(msg)
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vmConfig)
gp := new(core.GasPool).AddGas(msg.Gas()).AddBlobGas(msg.BlobGas())
if tracer != nil {
- tracer.CaptureTxStart(evm, txns[txIndex])
+ tracer.OnTxStart(evm.GetVMContext(), txns[txIndex], msg.From())
}
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, gasBailout /* gasBailout */)
}
if err != nil {
if tracer != nil {
- tracer.CaptureTxEnd(nil, err)
+ tracer.OnTxEnd(nil, err)
}
return nil, nil, fmt.Errorf("first run for txIndex %d error: %w", txIndex, err)
}
if tracer != nil {
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: execResult.UsedGas}, nil)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: execResult.UsedGas}, nil)
}
chainRules := chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Time)
diff --git a/turbo/jsonrpc/trace_filtering.go b/turbo/jsonrpc/trace_filtering.go
index edc1f39b941..b1c25decd11 100644
--- a/turbo/jsonrpc/trace_filtering.go
+++ b/turbo/jsonrpc/trace_filtering.go
@@ -772,7 +772,7 @@ func (api *TraceAPIImpl) filterV3(ctx context.Context, dbtx kv.TemporalTx, fromB
ot.idx = []string{fmt.Sprintf("%d-", txIndex)}
ot.traceAddr = []int{}
vmConfig.Debug = true
- vmConfig.Tracer = &ot
+ vmConfig.Tracer = ot.Tracer().Hooks
ibs := state.New(cachedReader)
blockCtx := transactions.NewEVMBlockContext(engine, lastHeader, true /* requireCanonical */, dbtx, api._blockReader)
@@ -781,13 +781,13 @@ func (api *TraceAPIImpl) filterV3(ctx context.Context, dbtx kv.TemporalTx, fromB
gp := new(core.GasPool).AddGas(msg.Gas()).AddBlobGas(msg.BlobGas())
ibs.SetTxContext(txHash, lastBlockHash, txIndex)
- ibs.SetLogger(&ot)
+ ibs.SetLogger(ot.Tracer().Hooks)
- ot.CaptureTxStart(evm, txn)
+ ot.Tracer().OnTxStart(evm.GetVMContext(), txn, msg.From())
var execResult *core.ExecutionResult
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, false /* gasBailout */)
if err != nil {
- ot.CaptureTxEnd(nil, err)
+ ot.Tracer().OnTxEnd(nil, err)
if first {
first = false
} else {
@@ -798,7 +798,7 @@ func (api *TraceAPIImpl) filterV3(ctx context.Context, dbtx kv.TemporalTx, fromB
stream.WriteObjectEnd()
continue
}
- ot.CaptureTxEnd(&types.Receipt{GasUsed: execResult.UsedGas}, nil)
+ ot.Tracer().OnTxEnd(&types.Receipt{GasUsed: execResult.UsedGas}, nil)
traceResult.Output = common.Copy(execResult.ReturnData)
if err = ibs.FinalizeTx(evm.ChainRules(), noop); err != nil {
if first {
diff --git a/turbo/jsonrpc/tracing.go b/turbo/jsonrpc/tracing.go
index 0be54df48a2..969c0d52732 100644
--- a/turbo/jsonrpc/tracing.go
+++ b/turbo/jsonrpc/tracing.go
@@ -18,7 +18,7 @@ import (
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
- "github.com/ledgerwatch/erigon/eth/tracers"
+ tracerConfig "github.com/ledgerwatch/erigon/eth/tracers/config"
polygontracer "github.com/ledgerwatch/erigon/polygon/tracer"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
@@ -27,16 +27,16 @@ import (
)
// TraceBlockByNumber implements debug_traceBlockByNumber. Returns Geth style block traces.
-func (api *PrivateDebugAPIImpl) TraceBlockByNumber(ctx context.Context, blockNum rpc.BlockNumber, config *tracers.TraceConfig, stream *jsoniter.Stream) error {
+func (api *PrivateDebugAPIImpl) TraceBlockByNumber(ctx context.Context, blockNum rpc.BlockNumber, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error {
return api.traceBlock(ctx, rpc.BlockNumberOrHashWithNumber(blockNum), config, stream)
}
// TraceBlockByHash implements debug_traceBlockByHash. Returns Geth style block traces.
-func (api *PrivateDebugAPIImpl) TraceBlockByHash(ctx context.Context, hash common.Hash, config *tracers.TraceConfig, stream *jsoniter.Stream) error {
+func (api *PrivateDebugAPIImpl) TraceBlockByHash(ctx context.Context, hash common.Hash, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error {
return api.traceBlock(ctx, rpc.BlockNumberOrHashWithHash(hash, true), config, stream)
}
-func (api *PrivateDebugAPIImpl) traceBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, config *tracers.TraceConfig, stream *jsoniter.Stream) error {
+func (api *PrivateDebugAPIImpl) traceBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error {
tx, err := api.db.BeginRo(ctx)
if err != nil {
stream.WriteNil()
@@ -79,7 +79,7 @@ func (api *PrivateDebugAPIImpl) traceBlock(ctx context.Context, blockNrOrHash rp
}
if config == nil {
- config = &tracers.TraceConfig{}
+ config = &tracerConfig.TraceConfig{}
}
if config.BorTraceEnabled == nil {
@@ -204,7 +204,7 @@ func (api *PrivateDebugAPIImpl) traceBlock(ctx context.Context, blockNrOrHash rp
}
// TraceTransaction implements debug_traceTransaction. Returns Geth style transaction traces.
-func (api *PrivateDebugAPIImpl) TraceTransaction(ctx context.Context, hash common.Hash, config *tracers.TraceConfig, stream *jsoniter.Stream) error {
+func (api *PrivateDebugAPIImpl) TraceTransaction(ctx context.Context, hash common.Hash, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error {
tx, err := api.db.BeginRo(ctx)
if err != nil {
stream.WriteNil()
@@ -309,7 +309,7 @@ func (api *PrivateDebugAPIImpl) TraceTransaction(ctx context.Context, hash commo
return transactions.TraceTx(ctx, txn, msg, blockCtx, txCtx, ibs, config, chainConfig, stream, api.evmCallTimeout)
}
-func (api *PrivateDebugAPIImpl) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *tracers.TraceConfig, stream *jsoniter.Stream) error {
+func (api *PrivateDebugAPIImpl) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error {
dbtx, err := api.db.BeginRo(ctx)
if err != nil {
return fmt.Errorf("create ro transaction: %v", err)
@@ -380,7 +380,7 @@ func (api *PrivateDebugAPIImpl) TraceCall(ctx context.Context, args ethapi.CallA
return transactions.TraceTx(ctx, transaction, msg, blockCtx, txCtx, ibs, config, chainConfig, stream, api.evmCallTimeout)
}
-func (api *PrivateDebugAPIImpl) TraceCallMany(ctx context.Context, bundles []Bundle, simulateContext StateContext, config *tracers.TraceConfig, stream *jsoniter.Stream) error {
+func (api *PrivateDebugAPIImpl) TraceCallMany(ctx context.Context, bundles []Bundle, simulateContext StateContext, config *tracerConfig.TraceConfig, stream *jsoniter.Stream) error {
var (
hash common.Hash
replayTransactions types.Transactions
@@ -392,7 +392,7 @@ func (api *PrivateDebugAPIImpl) TraceCallMany(ctx context.Context, bundles []Bun
)
if config == nil {
- config = &tracers.TraceConfig{}
+ config = &tracerConfig.TraceConfig{}
}
overrideBlockHash = make(map[uint64]common.Hash)
diff --git a/turbo/node/node.go b/turbo/node/node.go
index 8f0d620f147..b2230db1b31 100644
--- a/turbo/node/node.go
+++ b/turbo/node/node.go
@@ -115,7 +115,7 @@ func New(
nodeConfig *nodecfg.Config,
ethConfig *ethconfig.Config,
logger log.Logger,
- tracer tracers.Tracer,
+ tracer *tracers.Tracer,
) (*ErigonNode, error) {
//prepareBuckets(optionalParams.CustomBuckets)
node, err := node.New(ctx, nodeConfig, logger)
diff --git a/turbo/stages/stageloop.go b/turbo/stages/stageloop.go
index 32714a5c525..116fc0201e4 100644
--- a/turbo/stages/stageloop.go
+++ b/turbo/stages/stageloop.go
@@ -28,6 +28,7 @@ import (
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/rawdb/blockio"
+ "github.com/ledgerwatch/erigon/core/tracing"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/ethconfig"
@@ -477,8 +478,12 @@ func NewDefaultStages(ctx context.Context,
recents *lru.ARCCache[libcommon.Hash, *bor.Snapshot],
signatures *lru.ARCCache[libcommon.Hash, libcommon.Address],
logger log.Logger,
- tracer tracers.Tracer,
+ tracer *tracers.Tracer,
) []*stagedsync.Stage {
+ var tracingHooks *tracing.Hooks
+ if tracer != nil {
+ tracingHooks = tracer.Hooks
+ }
dirs := cfg.Dirs
blockWriter := blockio.NewBlockWriter(cfg.HistoryV3)
@@ -523,7 +528,7 @@ func NewDefaultStages(ctx context.Context,
nil,
controlServer.ChainConfig,
controlServer.Engine,
- &vm.Config{Tracer: tracer},
+ &vm.Config{Tracer: tracingHooks},
notifications.Accumulator,
cfg.StateStream,
/*stateStream=*/ false,
@@ -559,9 +564,13 @@ func NewPipelineStages(ctx context.Context,
silkworm *silkworm.Silkworm,
forkValidator *engine_helpers.ForkValidator,
logger log.Logger,
- tracer tracers.Tracer,
+ tracer *tracers.Tracer,
checkStateRoot bool,
) []*stagedsync.Stage {
+ var tracingHooks *tracing.Hooks
+ if tracer != nil {
+ tracingHooks = tracer.Hooks
+ }
dirs := cfg.Dirs
blockWriter := blockio.NewBlockWriter(cfg.HistoryV3)
@@ -598,7 +607,7 @@ func NewPipelineStages(ctx context.Context,
nil,
controlServer.ChainConfig,
controlServer.Engine,
- &vm.Config{Tracer: tracer},
+ &vm.Config{Tracer: tracingHooks},
notifications.Accumulator,
cfg.StateStream,
/*stateStream=*/ false,
@@ -634,7 +643,7 @@ func NewPipelineStages(ctx context.Context,
nil,
controlServer.ChainConfig,
controlServer.Engine,
- &vm.Config{Tracer: tracer},
+ &vm.Config{Tracer: tracingHooks},
notifications.Accumulator,
cfg.StateStream,
/*stateStream=*/ false,
@@ -660,7 +669,11 @@ func NewPipelineStages(ctx context.Context,
func NewInMemoryExecution(ctx context.Context, db kv.RwDB, cfg *ethconfig.Config, controlServer *sentry_multi_client.MultiClient,
dirs datadir.Dirs, notifications *shards.Notifications, blockReader services.FullBlockReader, blockWriter *blockio.BlockWriter, agg *state.AggregatorV3,
- silkworm *silkworm.Silkworm, logger log.Logger, tracer tracers.Tracer) *stagedsync.Sync {
+ silkworm *silkworm.Silkworm, logger log.Logger, tracer *tracers.Tracer) *stagedsync.Sync {
+ var tracingHooks *tracing.Hooks
+ if tracer != nil {
+ tracingHooks = tracer.Hooks
+ }
return stagedsync.New(
cfg.Sync,
stagedsync.StateStages(ctx,
@@ -675,7 +688,7 @@ func NewInMemoryExecution(ctx context.Context, db kv.RwDB, cfg *ethconfig.Config
nil,
controlServer.ChainConfig,
controlServer.Engine,
- &vm.Config{Tracer: tracer},
+ &vm.Config{Tracer: tracingHooks},
notifications.Accumulator,
cfg.StateStream,
true,
diff --git a/turbo/tracing/tracing.go b/turbo/tracing/tracing.go
index 68c89f35e1b..dcd5cc93951 100644
--- a/turbo/tracing/tracing.go
+++ b/turbo/tracing/tracing.go
@@ -9,7 +9,7 @@ import (
// SetupTracerCtx performs the tracing setup according to the parameters
// containted in the given urfave context.
-func SetupTracerCtx(ctx *cli.Context) (tracers.Tracer, error) {
+func SetupTracerCtx(ctx *cli.Context) (*tracers.Tracer, error) {
tracerName := ctx.String(TracerFlag.Name)
if tracerName == "" {
return nil, nil
diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go
index afb5d027a42..5c7bf1deba2 100644
--- a/turbo/transactions/tracing.go
+++ b/turbo/transactions/tracing.go
@@ -22,6 +22,7 @@ import (
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/stagedsync"
"github.com/ledgerwatch/erigon/eth/tracers"
+ tracerConfig "github.com/ledgerwatch/erigon/eth/tracers/config"
"github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
"github.com/ledgerwatch/erigon/turbo/services"
@@ -132,7 +133,7 @@ func TraceTx(
blockCtx evmtypes.BlockContext,
txCtx evmtypes.TxContext,
ibs *state.IntraBlockState,
- config *tracers.TraceConfig,
+ config *tracerConfig.TraceConfig,
chainConfig *chain.Config,
stream *jsoniter.Stream,
callTimeout time.Duration,
@@ -147,12 +148,12 @@ func TraceTx(
execCb := func(evm *vm.EVM, refunds bool) (*core.ExecutionResult, error) {
gp := new(core.GasPool).AddGas(message.Gas()).AddBlobGas(message.BlobGas())
- tracer.CaptureTxStart(evm, tx)
+ tracer.OnTxStart(evm.GetVMContext(), tx, message.From())
result, err := core.ApplyMessage(evm, message, gp, refunds, false /* gasBailout */)
if err != nil {
- tracer.CaptureTxEnd(nil, err)
+ tracer.OnTxEnd(nil, err)
} else {
- tracer.CaptureTxEnd(&types.Receipt{GasUsed: result.UsedGas}, nil)
+ tracer.OnTxEnd(&types.Receipt{GasUsed: result.UsedGas}, nil)
}
return result, err
}
@@ -162,11 +163,11 @@ func TraceTx(
func AssembleTracer(
ctx context.Context,
- config *tracers.TraceConfig,
+ config *tracerConfig.TraceConfig,
txHash libcommon.Hash,
stream *jsoniter.Stream,
callTimeout time.Duration,
-) (tracers.Tracer, bool, context.CancelFunc, error) {
+) (*tracers.Tracer, bool, context.CancelFunc, error) {
// Assemble the structured logger or the JavaScript tracer
switch {
case config != nil && config.Tracer != nil:
@@ -199,9 +200,9 @@ func AssembleTracer(
return tracer, false, cancel, nil
case config == nil:
- return logger.NewJsonStreamLogger(nil, ctx, stream), true, func() {}, nil
+ return logger.NewJsonStreamLogger(nil, ctx, stream).Tracer(), true, func() {}, nil
default:
- return logger.NewJsonStreamLogger(config.LogConfig, ctx, stream), true, func() {}, nil
+ return logger.NewJsonStreamLogger(config.LogConfig, ctx, stream).Tracer(), true, func() {}, nil
}
}
@@ -209,16 +210,16 @@ func ExecuteTraceTx(
blockCtx evmtypes.BlockContext,
txCtx evmtypes.TxContext,
ibs *state.IntraBlockState,
- config *tracers.TraceConfig,
+ config *tracerConfig.TraceConfig,
chainConfig *chain.Config,
stream *jsoniter.Stream,
- tracer tracers.Tracer,
+ tracer *tracers.Tracer,
streaming bool,
execCb func(evm *vm.EVM, refunds bool) (*core.ExecutionResult, error),
) error {
// Run the transaction with tracing enabled.
- evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer})
- ibs.SetLogger(tracer)
+ evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Debug: true, Tracer: tracer.Hooks})
+ ibs.SetLogger(tracer.Hooks)
var refunds = true
if config != nil && config.NoRefunds != nil && *config.NoRefunds {
refunds = false
@@ -260,7 +261,7 @@ func ExecuteTraceTx(
stream.WriteString(returnVal)
stream.WriteObjectEnd()
} else {
- r, err := tracer.(tracers.Tracer).GetResult()
+ r, err := tracer.GetResult()
if err != nil {
stream.WriteNil()
return err