diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index 8bb1913928c..99cb74e9a4d 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -44,6 +44,7 @@ import (
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/crypto"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/tests"
@@ -109,7 +110,7 @@ func Main(ctx *cli.Context) error {
}
if ctx.Bool(TraceFlag.Name) {
// Configure the EVM logger
- logConfig := &vm.LogConfig{
+ logConfig := &logger.LogConfig{
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
@@ -131,7 +132,7 @@ func Main(ctx *cli.Context) error {
return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err2))
}
prevFile = traceFile
- return vm.NewJSONLogger(logConfig, traceFile), nil
+ return logger.NewJSONLogger(logConfig, traceFile), nil
}
} else {
getTracer = func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error) {
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index a65493984df..fe9726d6854 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -42,6 +42,7 @@ import (
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/runtime"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/params"
)
@@ -117,7 +118,7 @@ func runCmd(ctx *cli.Context) error {
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
//glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
//log.Root().SetHandler(glogger)
- logconfig := &vm.LogConfig{
+ logconfig := &logger.LogConfig{
DisableMemory: ctx.Bool(DisableMemoryFlag.Name),
DisableStack: ctx.Bool(DisableStackFlag.Name),
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
@@ -127,7 +128,7 @@ func runCmd(ctx *cli.Context) error {
var (
tracer vm.EVMLogger
- debugLogger *vm.StructLogger
+ debugLogger *logger.StructLogger
statedb *state.IntraBlockState
chainConfig *params.ChainConfig
sender = common.BytesToAddress([]byte("sender"))
@@ -135,12 +136,12 @@ func runCmd(ctx *cli.Context) error {
genesisConfig *core.Genesis
)
if ctx.Bool(MachineFlag.Name) {
- tracer = vm.NewJSONLogger(logconfig, os.Stdout)
+ tracer = logger.NewJSONLogger(logconfig, os.Stdout)
} else if ctx.Bool(DebugFlag.Name) {
- debugLogger = vm.NewStructLogger(logconfig)
+ debugLogger = logger.NewStructLogger(logconfig)
tracer = debugLogger
} else {
- debugLogger = vm.NewStructLogger(logconfig)
+ debugLogger = logger.NewStructLogger(logconfig)
}
db := memdb.New()
if ctx.String(GenesisFlag.Name) != "" {
@@ -317,13 +318,13 @@ func runCmd(ctx *cli.Context) error {
if printErr != nil {
log.Warn("Failed to print to stderr", "err", printErr)
}
- vm.WriteTrace(os.Stderr, debugLogger.StructLogs())
+ logger.WriteTrace(os.Stderr, debugLogger.StructLogs())
}
_, printErr := fmt.Fprintln(os.Stderr, "#### LOGS ####")
if printErr != nil {
log.Warn("Failed to print to stderr", "err", printErr)
}
- vm.WriteLogs(os.Stderr, statedb.Logs())
+ logger.WriteLogs(os.Stderr, statedb.Logs())
}
if bench || ctx.Bool(StatDumpFlag.Name) {
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index 46e5d889a5f..36fcfb638e4 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -30,6 +30,7 @@ import (
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/tests"
"github.com/ledgerwatch/erigon/turbo/trie"
)
@@ -60,7 +61,7 @@ func stateTestCmd(ctx *cli.Context) error {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StderrHandler))
// Configure the EVM logger
- config := &vm.LogConfig{
+ config := &logger.LogConfig{
DisableMemory: ctx.Bool(DisableMemoryFlag.Name),
DisableStack: ctx.Bool(DisableStackFlag.Name),
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
@@ -68,18 +69,18 @@ func stateTestCmd(ctx *cli.Context) error {
}
var (
tracer vm.EVMLogger
- debugger *vm.StructLogger
+ debugger *logger.StructLogger
)
switch {
case ctx.Bool(MachineFlag.Name):
- tracer = vm.NewJSONLogger(config, os.Stderr)
+ tracer = logger.NewJSONLogger(config, os.Stderr)
case ctx.Bool(DebugFlag.Name):
- debugger = vm.NewStructLogger(config)
+ debugger = logger.NewStructLogger(config)
tracer = debugger
default:
- debugger = vm.NewStructLogger(config)
+ debugger = logger.NewStructLogger(config)
}
// Load the test content from the input file
src, err := os.ReadFile(ctx.Args().First())
@@ -106,7 +107,7 @@ func aggregateResultsFromStateTests(
ctx *cli.Context,
stateTests map[string]tests.StateTest,
tracer vm.EVMLogger,
- debugger *vm.StructLogger,
+ debugger *logger.StructLogger,
) ([]StatetestResult, error) {
// Iterate over all the stateTests, run them and aggregate the results
cfg := vm.Config{
@@ -175,7 +176,7 @@ func aggregateResultsFromStateTests(
if printErr != nil {
log.Warn("Failed to write to stderr", "err", printErr)
}
- vm.WriteTrace(os.Stderr, debugger.StructLogs())
+ logger.WriteTrace(os.Stderr, debugger.StructLogs())
}
}
}
diff --git a/cmd/integration/commands/state_stages.go b/cmd/integration/commands/state_stages.go
index d55d7ab8686..95cb514b0d8 100644
--- a/cmd/integration/commands/state_stages.go
+++ b/cmd/integration/commands/state_stages.go
@@ -28,11 +28,11 @@ import (
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/eth/integrity"
"github.com/ledgerwatch/erigon/eth/stagedsync"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/node/nodecfg"
"github.com/ledgerwatch/erigon/params"
erigoncli "github.com/ledgerwatch/erigon/turbo/cli"
@@ -218,7 +218,7 @@ func syncBySmallSteps(db kv.RwDB, miningConfig params.MiningConfig, ctx context.
}
traceStart := func() {
- vmConfig.Tracer = vm.NewStructLogger(&vm.LogConfig{})
+ vmConfig.Tracer = logger.NewStructLogger(&logger.LogConfig{})
vmConfig.Debug = true
}
traceStop := func(id int) {
@@ -231,7 +231,7 @@ func syncBySmallSteps(db kv.RwDB, miningConfig params.MiningConfig, ctx context.
}
encoder := json.NewEncoder(w)
encoder.SetIndent(" ", " ")
- for _, l := range vm.FormatLogs(vmConfig.Tracer.(*vm.StructLogger).StructLogs()) {
+ for _, l := range logger.FormatLogs(vmConfig.Tracer.(*logger.StructLogger).StructLogs()) {
if err2 := encoder.Encode(l); err2 != nil {
panic(err2)
}
diff --git a/cmd/rpcdaemon/commands/otterscan_default_tracer.go b/cmd/rpcdaemon/commands/otterscan_default_tracer.go
index 4d73ad823d2..20984296d98 100644
--- a/cmd/rpcdaemon/commands/otterscan_default_tracer.go
+++ b/cmd/rpcdaemon/commands/otterscan_default_tracer.go
@@ -33,14 +33,3 @@ func (t *DefaultTracer) CaptureEnd(output []byte, usedGas uint64, err error) {
func (t *DefaultTracer) CaptureExit(output []byte, usedGas uint64, err error) {
}
-
-func (t *DefaultTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (t *DefaultTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (t *DefaultTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
diff --git a/cmd/rpcdaemon/commands/otterscan_trace_operations.go b/cmd/rpcdaemon/commands/otterscan_trace_operations.go
index f8483fe68ee..2fb956be626 100644
--- a/cmd/rpcdaemon/commands/otterscan_trace_operations.go
+++ b/cmd/rpcdaemon/commands/otterscan_trace_operations.go
@@ -49,8 +49,7 @@ func (t *OperationsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to c
if typ == vm.CREATE2 {
t.Results = append(t.Results, &InternalOperation{OP_CREATE2, from, to, (*hexutil.Big)(value.ToBig())})
}
-}
-
-func (l *OperationsTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
- l.Results = append(l.Results, &InternalOperation{OP_SELF_DESTRUCT, from, to, (*hexutil.Big)(value.ToBig())})
+ if typ == vm.SELFDESTRUCT {
+ t.Results = append(t.Results, &InternalOperation{OP_SELF_DESTRUCT, from, to, (*hexutil.Big)(value.ToBig())})
+ }
}
diff --git a/cmd/rpcdaemon/commands/otterscan_trace_transaction.go b/cmd/rpcdaemon/commands/otterscan_trace_transaction.go
index b8778055e72..9d268cbd110 100644
--- a/cmd/rpcdaemon/commands/otterscan_trace_transaction.go
+++ b/cmd/rpcdaemon/commands/otterscan_trace_transaction.go
@@ -83,6 +83,11 @@ func (t *TransactionTracer) captureStartOrEnter(typ vm.OpCode, from, to common.A
t.Results = append(t.Results, &TraceEntry{"CREATE2", t.depth, from, to, (*hexutil.Big)(value.ToBig()), inputCopy})
return
}
+
+ if typ == vm.SELFDESTRUCT {
+ last := t.Results[len(t.Results)-1]
+ t.Results = append(t.Results, &TraceEntry{"SELFDESTRUCT", last.Depth + 1, from, to, (*hexutil.Big)(value.ToBig()), nil})
+ }
}
func (t *TransactionTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
@@ -98,8 +103,3 @@ func (t *TransactionTracer) CaptureEnter(typ vm.OpCode, from common.Address, to
func (t *TransactionTracer) CaptureExit(output []byte, usedGas uint64, err error) {
t.depth--
}
-
-func (l *TransactionTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
- last := l.Results[len(l.Results)-1]
- l.Results = append(l.Results, &TraceEntry{"SELFDESTRUCT", last.Depth + 1, from, to, (*hexutil.Big)(value.ToBig()), nil})
-}
diff --git a/cmd/rpcdaemon/commands/trace_adhoc.go b/cmd/rpcdaemon/commands/trace_adhoc.go
index 28852c8ac84..3b56f6a8e44 100644
--- a/cmd/rpcdaemon/commands/trace_adhoc.go
+++ b/cmd/rpcdaemon/commands/trace_adhoc.go
@@ -313,6 +313,21 @@ func (ot *OeTracer) captureStartOrEnter(deep bool, typ vm.OpCode, from common.Ad
action.Init = common.CopyBytes(input)
action.Value.ToInt().Set(value.ToBig())
trace.Action = &action
+ } else if typ == vm.SELFDESTRUCT {
+ trace := &ParityTrace{}
+ trace.Type = SUICIDE
+ action := &SuicideTraceAction{}
+ action.Address = from
+ action.RefundAddress = to
+ action.Balance.ToInt().Set(value.ToBig())
+ trace.Action = action
+ topTrace := ot.traceStack[len(ot.traceStack)-1]
+ traceIdx := topTrace.Subtraces
+ ot.traceAddr = append(ot.traceAddr, traceIdx)
+ topTrace.Subtraces++
+ trace.TraceAddress = make([]int, len(ot.traceAddr))
+ copy(trace.TraceAddress, ot.traceAddr)
+ ot.traceAddr = ot.traceAddr[:len(ot.traceAddr)-1]
} else {
action := CallTraceAction{}
switch typ {
@@ -568,31 +583,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) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
- trace := &ParityTrace{}
- trace.Type = SUICIDE
- action := &SuicideTraceAction{}
- action.Address = from
- action.RefundAddress = to
- action.Balance.ToInt().Set(value.ToBig())
- trace.Action = action
- topTrace := ot.traceStack[len(ot.traceStack)-1]
- traceIdx := topTrace.Subtraces
- ot.traceAddr = append(ot.traceAddr, traceIdx)
- topTrace.Subtraces++
- trace.TraceAddress = make([]int, len(ot.traceAddr))
- copy(trace.TraceAddress, ot.traceAddr)
- ot.traceAddr = ot.traceAddr[:len(ot.traceAddr)-1]
- ot.r.Trace = append(ot.r.Trace, trace)
-}
-
-func (ot *OeTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-func (ot *OeTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
// Implements core/state/StateWriter to provide state diffs
type StateDiff struct {
sdMap map[common.Address]*StateDiffAccount
diff --git a/cmd/state/commands/opcode_tracer.go b/cmd/state/commands/opcode_tracer.go
index ccb8bb40753..4b2c9233a28 100644
--- a/cmd/state/commands/opcode_tracer.go
+++ b/cmd/state/commands/opcode_tracer.go
@@ -384,15 +384,6 @@ func (ot *opcodeTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64,
}
-func (ot *opcodeTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-func (ot *opcodeTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-func (ot *opcodeTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
type segPrefix struct {
BlockNum uint64
NumTxs uint
@@ -580,7 +571,6 @@ func OpcodeTracer(genesis *core.Genesis, blockNum uint64, chaindata string, numB
dbstate := state.NewPlainState(historyTx, block.NumberU64(), systemcontracts.SystemContractCodeLookup[chainConfig.ChainName])
intraBlockState := state.New(dbstate)
- intraBlockState.SetTracer(ot)
getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(historyTx, hash, number) }
receipts, err1 := runBlock(ethash.NewFullFaker(), intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, block, vmConfig, false)
diff --git a/cmd/state/exec3/calltracer22.go b/cmd/state/exec3/calltracer22.go
index 37f4fd9a27a..0368773bc3f 100644
--- a/cmd/state/exec3/calltracer22.go
+++ b/cmd/state/exec3/calltracer22.go
@@ -40,13 +40,3 @@ func (ct *CallTracer) CaptureEnd(output []byte, usedGas uint64, err error) {
}
func (ct *CallTracer) CaptureExit(output []byte, usedGas uint64, err error) {
}
-func (ct *CallTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
- ct.froms[from] = struct{}{}
- ct.tos[to] = struct{}{}
-}
-func (ct *CallTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-func (ct *CallTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
diff --git a/core/state/intra_block_state.go b/core/state/intra_block_state.go
index e61fad8dc02..81becada430 100644
--- a/core/state/intra_block_state.go
+++ b/core/state/intra_block_state.go
@@ -38,11 +38,6 @@ type revision struct {
journalIndex int
}
-type StateTracer interface {
- CaptureAccountRead(account common.Address) error
- CaptureAccountWrite(account common.Address) error
-}
-
// SystemAddress - sender address for internal state updates.
var SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
@@ -86,7 +81,6 @@ type IntraBlockState struct {
journal *journal
validRevisions []revision
nextRevisionID int
- tracer StateTracer
trace bool
accessList *accessList
balanceInc map[common.Address]*BalanceIncrease // Map of balance increases (without first reading the account)
@@ -106,10 +100,6 @@ func New(stateReader StateReader) *IntraBlockState {
}
}
-func (sdb *IntraBlockState) SetTracer(tracer StateTracer) {
- sdb.tracer = tracer
-}
-
func (sdb *IntraBlockState) SetTrace(trace bool) {
sdb.trace = trace
}
@@ -194,12 +184,6 @@ func (sdb *IntraBlockState) SubRefund(gas uint64) {
// Exist reports whether the given account address exists in the state.
// Notably this also returns true for suicided accounts.
func (sdb *IntraBlockState) Exist(addr common.Address) bool {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- }
s := sdb.getStateObject(addr)
return s != nil && !s.deleted
}
@@ -207,12 +191,6 @@ func (sdb *IntraBlockState) Exist(addr common.Address) bool {
// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
func (sdb *IntraBlockState) Empty(addr common.Address) bool {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- }
so := sdb.getStateObject(addr)
return so == nil || so.deleted || so.empty()
}
@@ -220,12 +198,6 @@ func (sdb *IntraBlockState) Empty(addr common.Address) bool {
// GetBalance retrieves the balance from the given address or 0 if object not found
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) GetBalance(addr common.Address) *uint256.Int {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- }
stateObject := sdb.getStateObject(addr)
if stateObject != nil && !stateObject.deleted {
return stateObject.Balance()
@@ -235,12 +207,6 @@ func (sdb *IntraBlockState) GetBalance(addr common.Address) *uint256.Int {
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) GetNonce(addr common.Address) uint64 {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- }
stateObject := sdb.getStateObject(addr)
if stateObject != nil && !stateObject.deleted {
return stateObject.Nonce()
@@ -256,12 +222,6 @@ func (sdb *IntraBlockState) TxIndex() int {
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) GetCode(addr common.Address) []byte {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- }
stateObject := sdb.getStateObject(addr)
if stateObject != nil && !stateObject.deleted {
if sdb.trace {
@@ -277,12 +237,6 @@ func (sdb *IntraBlockState) GetCode(addr common.Address) []byte {
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) GetCodeSize(addr common.Address) int {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- }
stateObject := sdb.getStateObject(addr)
if stateObject == nil || stateObject.deleted {
return 0
@@ -299,12 +253,6 @@ func (sdb *IntraBlockState) GetCodeSize(addr common.Address) int {
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) GetCodeHash(addr common.Address) common.Hash {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- }
stateObject := sdb.getStateObject(addr)
if stateObject == nil || stateObject.deleted {
return common.Hash{}
@@ -334,7 +282,7 @@ func (sdb *IntraBlockState) GetCommittedState(addr common.Address, key *common.H
}
}
-func (sdb *IntraBlockState) HasSuicided(addr common.Address) bool {
+func (sdb *IntraBlockState) HasSelfdestructed(addr common.Address) bool {
stateObject := sdb.getStateObject(addr)
if stateObject == nil {
return false
@@ -345,7 +293,7 @@ func (sdb *IntraBlockState) HasSuicided(addr common.Address) bool {
if stateObject.created {
return false
}
- return stateObject.suicided
+ return stateObject.selfdestructed
}
/*
@@ -358,12 +306,6 @@ func (sdb *IntraBlockState) AddBalance(addr common.Address, amount *uint256.Int)
if sdb.trace {
fmt.Printf("AddBalance %x, %d\n", addr, amount)
}
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountWrite(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountWrite err", err)
- }
- }
// If this account has not been read, add to the balance increment map
_, needAccount := sdb.stateObjects[addr]
if !needAccount && addr == ripemd && amount.IsZero() {
@@ -394,13 +336,6 @@ func (sdb *IntraBlockState) SubBalance(addr common.Address, amount *uint256.Int)
if sdb.trace {
fmt.Printf("SubBalance %x, %d\n", addr, amount)
}
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountWrite(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountWrite err", err)
- }
-
- }
stateObject := sdb.GetOrNewStateObject(addr)
if stateObject != nil {
@@ -410,13 +345,6 @@ func (sdb *IntraBlockState) SubBalance(addr common.Address, amount *uint256.Int)
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) SetBalance(addr common.Address, amount *uint256.Int) {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountWrite(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountWrite err", err)
- }
- }
-
stateObject := sdb.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalance(amount)
@@ -425,13 +353,6 @@ func (sdb *IntraBlockState) SetBalance(addr common.Address, amount *uint256.Int)
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) SetNonce(addr common.Address, nonce uint64) {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountWrite(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountWrite err", err)
- }
- }
-
stateObject := sdb.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetNonce(nonce)
@@ -441,13 +362,6 @@ func (sdb *IntraBlockState) SetNonce(addr common.Address, nonce uint64) {
// DESCRIBED: docs/programmers_guide/guide.md#code-hash
// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
func (sdb *IntraBlockState) SetCode(addr common.Address, code []byte) {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountWrite(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountWrite err", err)
- }
- }
-
stateObject := sdb.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetCode(crypto.Keccak256Hash(code), code)
@@ -487,32 +401,22 @@ func (sdb *IntraBlockState) GetIncarnation(addr common.Address) uint64 {
return 0
}
-// Suicide marks the given account as suicided.
+// Selfdestruct marks the given account as suicided.
// This clears the account balance.
//
// The account's state object is still available until the state is committed,
// getStateObject will return a non-nil account after Suicide.
-func (sdb *IntraBlockState) Suicide(addr common.Address) bool {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountRead err", err)
- }
- err = sdb.tracer.CaptureAccountWrite(addr)
- if sdb.trace {
- fmt.Println("CaptureAccountWrite err", err)
- }
- }
+func (sdb *IntraBlockState) Selfdestruct(addr common.Address) bool {
stateObject := sdb.getStateObject(addr)
if stateObject == nil || stateObject.deleted {
return false
}
- sdb.journal.append(suicideChange{
+ sdb.journal.append(selfdestructChange{
account: &addr,
- prev: stateObject.suicided,
+ prev: stateObject.selfdestructed,
prevbalance: *stateObject.Balance(),
})
- stateObject.markSuicided()
+ stateObject.markSelfdestructed()
stateObject.created = false
stateObject.data.Balance.Clear()
@@ -602,22 +506,10 @@ func (sdb *IntraBlockState) createObject(addr common.Address, previous *stateObj
//
// Carrying over the balance ensures that Ether doesn't disappear.
func (sdb *IntraBlockState) CreateAccount(addr common.Address, contractCreation bool) {
- if sdb.tracer != nil {
- err := sdb.tracer.CaptureAccountRead(addr)
- if sdb.trace && err != nil {
- log.Error("error while CaptureAccountRead", "err", err)
- }
-
- err = sdb.tracer.CaptureAccountWrite(addr)
- if sdb.trace && err != nil {
- log.Error("error while CaptureAccountWrite", "err", err)
- }
- }
-
var prevInc uint64
previous := sdb.getStateObject(addr)
if contractCreation {
- if previous != nil && previous.suicided {
+ if previous != nil && previous.selfdestructed {
prevInc = previous.data.Incarnation
} else {
inc, err := sdb.stateReader.ReadAccountIncarnation(addr)
@@ -640,7 +532,7 @@ func (sdb *IntraBlockState) CreateAccount(addr common.Address, contractCreation
newObj.created = true
newObj.data.Incarnation = prevInc + 1
} else {
- newObj.suicided = false
+ newObj.selfdestructed = false
}
}
@@ -675,13 +567,13 @@ func (sdb *IntraBlockState) GetRefund() uint64 {
func updateAccount(EIP161Enabled bool, isAura bool, stateWriter StateWriter, addr common.Address, stateObject *stateObject, isDirty bool) error {
emptyRemoval := EIP161Enabled && stateObject.empty() && (!isAura || addr != SystemAddress)
- if stateObject.suicided || (isDirty && emptyRemoval) {
+ if stateObject.selfdestructed || (isDirty && emptyRemoval) {
if err := stateWriter.DeleteAccount(addr, &stateObject.original); err != nil {
return err
}
stateObject.deleted = true
}
- if isDirty && (stateObject.created || !stateObject.suicided) && !emptyRemoval {
+ if isDirty && (stateObject.created || !stateObject.selfdestructed) && !emptyRemoval {
stateObject.deleted = false
// Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode {
@@ -706,10 +598,10 @@ func updateAccount(EIP161Enabled bool, isAura bool, stateWriter StateWriter, add
func printAccount(EIP161Enabled bool, addr common.Address, stateObject *stateObject, isDirty bool) {
emptyRemoval := EIP161Enabled && stateObject.empty()
- if stateObject.suicided || (isDirty && emptyRemoval) {
+ if stateObject.selfdestructed || (isDirty && emptyRemoval) {
fmt.Printf("delete: %x\n", addr)
}
- if isDirty && (stateObject.created || !stateObject.suicided) && !emptyRemoval {
+ if isDirty && (stateObject.created || !stateObject.selfdestructed) && !emptyRemoval {
// Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode {
fmt.Printf("UpdateCode: %x,%x\n", addr, stateObject.CodeHash())
diff --git a/core/state/journal.go b/core/state/journal.go
index d5183dbbc55..61ad9e44238 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -105,9 +105,9 @@ type (
account *common.Address
prev *stateObject
}
- suicideChange struct {
+ selfdestructChange struct {
account *common.Address
- prev bool // whether account had already suicided
+ prev bool // whether account had already selfdestructed
prevbalance uint256.Int
}
@@ -180,15 +180,15 @@ func (ch resetObjectChange) dirtied() *common.Address {
return nil
}
-func (ch suicideChange) revert(s *IntraBlockState) {
+func (ch selfdestructChange) revert(s *IntraBlockState) {
obj := s.getStateObject(*ch.account)
if obj != nil {
- obj.suicided = ch.prev
+ obj.selfdestructed = ch.prev
obj.setBalance(&ch.prevbalance)
}
}
-func (ch suicideChange) dirtied() *common.Address {
+func (ch selfdestructChange) dirtied() *common.Address {
return ch.account
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index ffdcde3e770..638e2bcd176 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -82,12 +82,12 @@ type stateObject struct {
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
// Cache flags.
- // When an object is marked suicided it will be delete from the trie
+ // When an object is marked selfdestructed it will be delete from the trie
// during the "update" phase of the state transition.
- dirtyCode bool // true if the code was updated
- suicided bool
- deleted bool // true if account was deleted during the lifetime of this object
- created bool // true if this object represents a newly created contract
+ dirtyCode bool // true if the code was updated
+ selfdestructed bool
+ deleted bool // true if account was deleted during the lifetime of this object
+ created bool // true if this object represents a newly created contract
}
// empty returns whether the account is considered empty.
@@ -132,8 +132,8 @@ func (so *stateObject) setError(err error) {
}
}
-func (so *stateObject) markSuicided() {
- so.suicided = true
+func (so *stateObject) markSelfdestructed() {
+ so.selfdestructed = true
}
func (so *stateObject) touch() {
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 361eaf1c6de..48b403ca0ed 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -222,7 +222,7 @@ func TestSnapshot2(t *testing.T) {
so0.SetBalance(uint256.NewInt(42))
so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
- so0.suicided = false
+ so0.selfdestructed = false
so0.deleted = false
state.setStateObject(stateobjaddr0, so0)
@@ -242,7 +242,7 @@ func TestSnapshot2(t *testing.T) {
so1.SetBalance(uint256.NewInt(52))
so1.SetNonce(53)
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
- so1.suicided = true
+ so1.selfdestructed = true
so1.deleted = true
state.setStateObject(stateobjaddr1, so1)
diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go
deleted file mode 100644
index 77fbe217a40..00000000000
--- a/core/vm/access_list_tracer.go
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2021 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// 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"
- "github.com/ledgerwatch/erigon/common"
- "github.com/ledgerwatch/erigon/core/types"
-)
-
-// accessList is an accumulator for the set of accounts and storage slots an EVM
-// contract execution touches.
-type accessList map[common.Address]accessListSlots
-
-// accessListSlots is an accumulator for the set of storage slots within a single
-// contract that an EVM contract execution touches.
-type accessListSlots map[common.Hash]struct{}
-
-// newAccessList creates a new accessList.
-func newAccessList() accessList {
- return make(map[common.Address]accessListSlots)
-}
-
-// addAddress adds an address to the accesslist.
-func (al accessList) addAddress(address common.Address) {
- // Set address if not previously present
- if _, present := al[address]; !present {
- al[address] = make(map[common.Hash]struct{})
- }
-}
-
-// addSlot adds a storage slot to the accesslist.
-func (al accessList) addSlot(address common.Address, slot common.Hash) {
- // Set address if not previously present
- al.addAddress(address)
-
- // Set the slot on the surely existent storage set
- al[address][slot] = struct{}{}
-}
-
-// equal checks if the content of the current access list is the same as the
-// content of the other one.
-func (al accessList) equal(other accessList) bool {
- // Cross reference the accounts first
- if len(al) != len(other) {
- return false
- }
- for addr := range al {
- if _, ok := other[addr]; !ok {
- return false
- }
- }
- for addr := range other {
- if _, ok := al[addr]; !ok {
- return false
- }
- }
- // Accounts match, cross reference the storage slots too
- for addr, slots := range al {
- otherslots := other[addr]
-
- if len(slots) != len(otherslots) {
- return false
- }
- for hash := range slots {
- if _, ok := otherslots[hash]; !ok {
- return false
- }
- }
- for hash := range otherslots {
- if _, ok := slots[hash]; !ok {
- return false
- }
- }
- }
- return true
-}
-
-// accesslist converts the accesslist to a types.AccessList.
-func (al accessList) accessList() types.AccessList {
- acl := make(types.AccessList, 0, len(al))
- for addr, slots := range al {
- tuple := types.AccessTuple{Address: addr}
- for slot := range slots {
- tuple.StorageKeys = append(tuple.StorageKeys, slot)
- }
- acl = append(acl, tuple)
- }
- return acl
-}
-
-var _ EVMLogger = (*AccessListTracer)(nil)
-
-// AccessListTracer is a tracer that accumulates touched accounts and storage
-// slots into an internal set.
-type AccessListTracer struct {
- excl map[common.Address]struct{} // Set of account to exclude from the list
- list accessList // Set of accounts and storage slots touched
-}
-
-func (a *AccessListTracer) CaptureAccountWrite(account common.Address) error {
- panic("implement me")
-}
-
-func (*AccessListTracer) CaptureTxStart(gasLimit uint64) {}
-
-func (*AccessListTracer) CaptureTxEnd(restGas uint64) {}
-
-// NewAccessListTracer creates a new tracer that can generate AccessLists.
-// An optional AccessList can be specified to occupy slots and addresses in
-// the resulting accesslist.
-func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer {
- excl := map[common.Address]struct{}{
- from: {}, to: {},
- }
- for _, addr := range precompiles {
- excl[addr] = struct{}{}
- }
- list := newAccessList()
- for _, al := range acl {
- if _, ok := excl[al.Address]; !ok {
- list.addAddress(al.Address)
- }
- for _, slot := range al.StorageKeys {
- list.addSlot(al.Address, slot)
- }
- }
- return &AccessListTracer{
- excl: excl,
- list: list,
- }
-}
-
-// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
-func (a *AccessListTracer) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
- stack := scope.Stack
- if (op == SLOAD || op == SSTORE) && stack.Len() >= 1 {
- slot := common.Hash(stack.Data[stack.Len()-1].Bytes32())
- a.list.addSlot(scope.Contract.Address(), slot)
- }
- if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stack.Len() >= 1 {
- addr := common.Address(stack.Data[stack.Len()-1].Bytes20())
- if _, ok := a.excl[addr]; !ok {
- a.list.addAddress(addr)
- }
- }
- if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stack.Len() >= 5 {
- addr := common.Address(stack.Data[stack.Len()-2].Bytes20())
- if _, ok := a.excl[addr]; !ok {
- a.list.addAddress(addr)
- }
- }
-}
-
-func (*AccessListTracer) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
-}
-
-func (*AccessListTracer) CaptureEnd(output []byte, usedGas uint64, err error) {
-}
-
-func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- panic("implement me")
-}
-
-func (a *AccessListTracer) CaptureEnter(typ OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- panic("implement me")
-}
-
-func (*AccessListTracer) CaptureExit(output []byte, usedGas uint64, err error) {
-}
-
-func (a *AccessListTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (a *AccessListTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-// AccessList returns the current accesslist maintained by the tracer.
-func (a *AccessListTracer) AccessList() types.AccessList {
- return a.list.accessList()
-}
-
-// Equal returns if the content of two access list traces are equal.
-func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
- return a.list.equal(other.list)
-}
diff --git a/core/vm/evmtypes/evmtypes.go b/core/vm/evmtypes/evmtypes.go
index 0a92625b743..30c005152e9 100644
--- a/core/vm/evmtypes/evmtypes.go
+++ b/core/vm/evmtypes/evmtypes.go
@@ -73,8 +73,8 @@ type IntraBlockState interface {
GetState(address common.Address, slot *common.Hash, outValue *uint256.Int)
SetState(common.Address, *common.Hash, uint256.Int)
- Suicide(common.Address) bool
- HasSuicided(common.Address) bool
+ Selfdestruct(common.Address) bool
+ HasSelfdestructed(common.Address) bool
// Exist reports whether the given account exists in state.
// Notably this should also return true for suicided accounts.
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index c3f96101d8b..a749f17a112 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -490,7 +490,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memo
}
}
- if !evm.IntraBlockState().HasSuicided(contract.Address()) {
+ if !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
}
return gas, nil
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index e2d966a7e5e..27836e6129b 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -864,7 +864,7 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
return nil, errStopToken
}
-func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
@@ -877,10 +877,9 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, callerAddr, beneficiaryAddr, false /* precompile */, false /* create */, []byte{}, 0, balance, nil /* code */)
interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
}
- interpreter.evm.Config().Tracer.CaptureSelfDestruct(callerAddr, beneficiaryAddr, balance)
}
interpreter.evm.IntraBlockState().AddBalance(beneficiaryAddr, balance)
- interpreter.evm.IntraBlockState().Suicide(callerAddr)
+ interpreter.evm.IntraBlockState().Selfdestruct(callerAddr)
return nil, errStopToken
}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 1ffbf729198..bf36e10395e 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -1179,7 +1179,7 @@ func newFrontierInstructionSet() JumpTable {
memorySize: memoryReturn,
},
SELFDESTRUCT: {
- execute: opSuicide,
+ execute: opSelfdestruct,
dynamicGas: gasSelfdestruct,
numPop: 1,
numPush: 0,
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 1268192a52a..e9b0ba2f1b6 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -17,92 +17,11 @@
package vm
import (
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "math/big"
- "os"
- "strings"
-
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon/common"
- "github.com/ledgerwatch/erigon/common/hexutil"
- "github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/params"
)
-var ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
-
-// Storage represents a contract's storage.
-type Storage map[common.Hash]common.Hash
-
-// Copy duplicates the current storage.
-func (s Storage) Copy() Storage {
- cpy := make(Storage)
- for key, value := range s {
- cpy[key] = value
- }
- return cpy
-}
-
-// LogConfig are the configuration options for structured logger the EVM
-type LogConfig struct {
- DisableMemory bool // disable memory capture
- DisableStack bool // disable stack capture
- DisableStorage bool // disable storage capture
- DisableReturnData bool // disable return data capture
- Debug bool // print output during capture end
- Limit int // maximum length of output, but zero means unlimited
- // Chain overrides, can be used to execute a trace using future fork rules
- Overrides *params.ChainConfig `json:"overrides,omitempty"`
-}
-
-//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
-
-// StructLog is emitted to the EVM each cycle and lists information about the current internal state
-// prior to the execution of the statement.
-type StructLog struct {
- Pc uint64 `json:"pc"`
- Op OpCode `json:"op"`
- Gas uint64 `json:"gas"`
- GasCost uint64 `json:"gasCost"`
- Memory []byte `json:"memory"`
- MemorySize int `json:"memSize"`
- Stack []*big.Int `json:"stack"`
- ReturnData []byte `json:"returnData"`
- Storage map[common.Hash]common.Hash `json:"-"`
- Depth int `json:"depth"`
- RefundCounter uint64 `json:"refund"`
- Err error `json:"-"`
-}
-
-// overrides for gencodec
-type structLogMarshaling struct {
- Stack []*math.HexOrDecimal256
- Gas math.HexOrDecimal64
- GasCost math.HexOrDecimal64
- Memory hexutil.Bytes
- ReturnData hexutil.Bytes
- OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
- ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON
-}
-
-// OpName formats the operand name in a human-readable format.
-func (s *StructLog) OpName() string {
- return s.Op.String()
-}
-
-// ErrorString formats the log's error as a string.
-func (s *StructLog) ErrorString() string {
- if s.Err != nil {
- return s.Err.Error()
- }
- return ""
-}
-
// EVMLogger is used to collect execution traces from an EVM transaction
// execution. CaptureState is called for each step of the VM with the
// current VM state.
@@ -121,10 +40,6 @@ type EVMLogger interface {
// 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)
-
- CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int)
- CaptureAccountRead(account common.Address) error
- CaptureAccountWrite(account common.Address) error
}
// FlushableTracer is a Tracer extension whose accumulated traces has to be
@@ -133,355 +48,3 @@ type FlushableTracer interface {
EVMLogger
Flush(tx types.Transaction)
}
-
-// StructLogRes stores a structured log emitted by the EVM while replaying a
-// transaction in debug mode
-type StructLogRes struct {
- Pc uint64 `json:"pc"`
- Op string `json:"op"`
- Gas uint64 `json:"gas"`
- GasCost uint64 `json:"gasCost"`
- Depth int `json:"depth"`
- Error error `json:"error,omitempty"`
- Stack *[]string `json:"stack,omitempty"`
- Memory *[]string `json:"memory,omitempty"`
- Storage *map[string]string `json:"storage,omitempty"`
-}
-
-// StructLogger is an EVM state logger and implements Tracer.
-//
-// StructLogger can capture state based on the given Log configuration and also keeps
-// a track record of modified storage which is used in reporting snapshots of the
-// contract their storage.
-type StructLogger struct {
- cfg LogConfig
-
- storage map[common.Address]Storage
- logs []StructLog
- output []byte
- err error
- env *EVM
-}
-
-// NewStructLogger returns a new logger
-func NewStructLogger(cfg *LogConfig) *StructLogger {
- logger := &StructLogger{
- storage: make(map[common.Address]Storage),
- }
- if cfg != nil {
- logger.cfg = *cfg
- }
- return logger
-}
-
-func (l *StructLogger) CaptureTxStart(gasLimit uint64) {}
-
-func (l *StructLogger) CaptureTxEnd(restGas uint64) {}
-
-// CaptureStart implements the Tracer interface to initialize the tracing operation.
-func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- l.env = env
-}
-
-// CaptureEnter implements the Tracer interface to initialize the tracing operation for an internal call.
-func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-// CaptureState logs a new structured log message and pushes it out to the environment
-//
-// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
-func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
- memory := scope.Memory
- stack := scope.Stack
- contract := scope.Contract
-
- // check if already accumulated the specified number of logs
- if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
- return
- }
-
- // Copy a snapshot of the current memory state to a new buffer
- var mem []byte
- if !l.cfg.DisableMemory {
- mem = make([]byte, len(memory.Data()))
- copy(mem, memory.Data())
- }
- // 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[i] = new(big.Int).Set(item.ToBig())
- }
- }
- // 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)
- }
- // capture SLOAD opcodes and record the read entry in the local storage
- if op == SLOAD && stack.Len() >= 1 {
- var (
- address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
- value uint256.Int
- )
- l.env.IntraBlockState().GetState(contract.Address(), &address, &value)
- l.storage[contract.Address()][address] = value.Bytes32()
- }
- // capture SSTORE opcodes and record the written entry in the local storage.
- if op == SSTORE && stack.Len() >= 2 {
- var (
- value = common.Hash(stack.Data[stack.Len()-2].Bytes32())
- address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
- )
- l.storage[contract.Address()][address] = value
- }
- storage = l.storage[contract.Address()].Copy()
- }
- var rdata []byte
- if !l.cfg.DisableReturnData {
- rdata = make([]byte, len(rData))
- copy(rdata, rData)
- }
- // create a new snapshot of the EVM.
- log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.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 OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (l *StructLogger) CaptureEnd(output []byte, usedGas uint64, err error) {
- l.output = output
- l.err = err
- if l.cfg.Debug {
- fmt.Printf("0x%x\n", output)
- if err != nil {
- fmt.Printf(" error: %v\n", err)
- }
- }
-}
-
-// CaptureExit is called after the internal call finishes to finalize the tracing.
-func (l *StructLogger) CaptureExit(output []byte, usedGas uint64, err error) {
-}
-
-func (l *StructLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (l *StructLogger) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (l *StructLogger) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
-// StructLogs returns the captured log entries.
-func (l *StructLogger) StructLogs() []StructLog { return l.logs }
-
-// Error returns the VM error captured by the trace.
-func (l *StructLogger) Error() error { return l.err }
-
-// Output returns the VM return value captured by the trace.
-func (l *StructLogger) Output() []byte { return l.output }
-
-func (l *StructLogger) Flush(tx types.Transaction) {
- w, err1 := os.Create(fmt.Sprintf("txtrace_%x.txt", tx.Hash()))
- if err1 != nil {
- panic(err1)
- }
- encoder := json.NewEncoder(w)
- logs := FormatLogs(l.StructLogs())
- if err2 := encoder.Encode(logs); err2 != nil {
- panic(err2)
- }
- if err2 := w.Close(); err2 != nil {
- panic(err2)
- }
-}
-
-// FormatLogs formats EVM returned structured logs for json output
-func FormatLogs(logs []StructLog) []StructLogRes {
- formatted := make([]StructLogRes, len(logs))
- for index, trace := range logs {
- formatted[index] = StructLogRes{
- Pc: trace.Pc,
- Op: trace.Op.String(),
- Gas: trace.Gas,
- GasCost: trace.GasCost,
- Depth: trace.Depth,
- Error: trace.Err,
- }
- if trace.Stack != nil {
- stack := make([]string, len(trace.Stack))
- for i, stackValue := range trace.Stack {
- stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
- }
- formatted[index].Stack = &stack
- }
- if trace.Memory != nil {
- memory := make([]string, 0, (len(trace.Memory)+31)/32)
- for i := 0; i+32 <= len(trace.Memory); i += 32 {
- memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
- }
- formatted[index].Memory = &memory
- }
- if trace.Storage != nil {
- storage := make(map[string]string)
- for i, storageValue := range trace.Storage {
- storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
- }
- formatted[index].Storage = &storage
- }
- }
- return formatted
-}
-
-// WriteTrace writes a formatted trace to the given writer
-func WriteTrace(writer io.Writer, logs []StructLog) {
- for _, log := range logs {
- fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
- if log.Err != nil {
- fmt.Fprintf(writer, " ERROR: %v", log.Err)
- }
- fmt.Fprintln(writer)
-
- if len(log.Stack) > 0 {
- fmt.Fprintln(writer, "Stack:")
- for i := len(log.Stack) - 1; i >= 0; i-- {
- fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
- }
- }
- if len(log.Memory) > 0 {
- fmt.Fprintln(writer, "Memory:")
- fmt.Fprint(writer, hex.Dump(log.Memory))
- }
- if len(log.Storage) > 0 {
- fmt.Fprintln(writer, "Storage:")
- for h, item := range log.Storage {
- fmt.Fprintf(writer, "%x: %x\n", h, item)
- }
- }
- if len(log.ReturnData) > 0 {
- fmt.Fprintln(writer, "ReturnData:")
- fmt.Fprint(writer, hex.Dump(log.ReturnData))
- }
- fmt.Fprintln(writer)
- }
-}
-
-// WriteLogs writes vm logs in a readable format to the given writer
-func WriteLogs(writer io.Writer, logs []*types.Log) {
- for _, log := range logs {
- fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
-
- for i, topic := range log.Topics {
- fmt.Fprintf(writer, "%08d %x\n", i, topic)
- }
-
- fmt.Fprint(writer, hex.Dump(log.Data))
- fmt.Fprintln(writer)
- }
-}
-
-type mdLogger struct {
- out io.Writer
- cfg *LogConfig
- env *EVM
-}
-
-// NewMarkdownLogger creates a logger which outputs information in a format adapted
-// for human readability, and is also a valid markdown table
-func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
- l := &mdLogger{writer, cfg, nil}
- if l.cfg == nil {
- l.cfg = &LogConfig{}
- }
- return l
-}
-
-func (t *mdLogger) CaptureTxStart(gasLimit uint64) {}
-
-func (t *mdLogger) CaptureTxEnd(restGas uint64) {}
-
-func (t *mdLogger) captureStartOrEnter(from, to common.Address, create bool, input []byte, gas uint64, value *uint256.Int) {
- if !create {
- fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
- from.String(), to.String(),
- input, gas, value)
- } else {
- fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
- from.String(), to.String(),
- input, gas, value)
- }
-
- fmt.Fprintf(t.out, `
-| Pc | Op | Cost | Stack | RStack | Refund |
-|-------|-------------|------|-----------|-----------|---------|
-`)
-}
-
-func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) { //nolint:interfacer
- t.env = env
- t.captureStartOrEnter(from, to, create, input, gas, value)
-}
-
-func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.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) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
- stack := scope.Stack
-
- fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
-
- if !t.cfg.DisableStack {
- // format stack
- var a []string
- for _, elem := range stack.Data {
- a = append(a, fmt.Sprintf("%v", 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.Fprintln(t.out, "")
- if err != nil {
- fmt.Fprintf(t.out, "Error: %v\n", err)
- }
-}
-
-func (t *mdLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
- fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
-}
-
-func (t *mdLogger) 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) {
- t.captureEndOrExit(output, usedGas, err)
-}
-
-func (t *mdLogger) CaptureExit(output []byte, usedGas uint64, err error) {
- t.captureEndOrExit(output, usedGas, err)
-}
-
-func (t *mdLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (t *mdLogger) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (t *mdLogger) CaptureAccountWrite(account common.Address) error {
- return nil
-}
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
index 4b54daa4a8a..3cc0daa1172 100644
--- a/core/vm/operations_acl.go
+++ b/core/vm/operations_acl.go
@@ -241,7 +241,7 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
if evm.IntraBlockState().Empty(address) && !evm.IntraBlockState().GetBalance(contract.Address()).IsZero() {
gas += params.CreateBySelfdestructGas
}
- if refundsEnabled && !evm.IntraBlockState().HasSuicided(contract.Address()) {
+ if refundsEnabled && !evm.IntraBlockState().HasSelfdestructed(contract.Address()) {
evm.IntraBlockState().AddRefund(params.SelfdestructRefundGas)
}
return gas, nil
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 2cf6f888d8d..bd77de38967 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -32,6 +32,7 @@ import (
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/params"
)
@@ -507,7 +508,7 @@ func TestEip2929Cases(t *testing.T) {
Execute(code, nil, &Config{
EVMConfig: vm.Config{
Debug: true,
- Tracer: vm.NewMarkdownLogger(nil, os.Stdout),
+ Tracer: logger.NewMarkdownLogger(nil, os.Stdout),
ExtraEips: []int{2929},
},
}, 0)
diff --git a/eth/calltracer/calltracer.go b/eth/calltracer/calltracer.go
index f139b28a197..b2fe9d9f879 100644
--- a/eth/calltracer/calltracer.go
+++ b/eth/calltracer/calltracer.go
@@ -27,6 +27,7 @@ func NewCallTracer() *CallTracer {
func (ct *CallTracer) CaptureTxStart(gasLimit uint64) {}
func (ct *CallTracer) CaptureTxEnd(restGas uint64) {}
+// CaptureStart and CaptureEnter also capture SELFDESTRUCT opcode invocations
func (ct *CallTracer) captureStartOrEnter(from, to common.Address, create bool, code []byte) {
ct.froms[from] = struct{}{}
@@ -56,16 +57,6 @@ func (ct *CallTracer) CaptureEnd(output []byte, usedGas uint64, err error) {
}
func (ct *CallTracer) CaptureExit(output []byte, usedGas uint64, err error) {
}
-func (ct *CallTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
- ct.froms[from] = struct{}{}
- ct.tos[to] = false
-}
-func (ct *CallTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-func (ct *CallTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
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 78fb2a155eb..540362c574e 100644
--- a/eth/stagedsync/stage_execute.go
+++ b/eth/stagedsync/stage_execute.go
@@ -34,6 +34,7 @@ import (
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/eth/ethconfig/estimate"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/ethdb"
"github.com/ledgerwatch/erigon/ethdb/olddb"
"github.com/ledgerwatch/erigon/ethdb/prune"
@@ -152,7 +153,7 @@ func executeBlock(
}
getTracer := func(txIndex int, txHash ecom.Hash) (vm.EVMLogger, error) {
- return vm.NewStructLogger(&vm.LogConfig{}), nil
+ return logger.NewStructLogger(&logger.LogConfig{}), nil
}
callTracer := calltracer.NewCallTracer()
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index c1854e36db0..bb3b80be069 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -1,13 +1,13 @@
package tracers
import (
- "github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
)
// TraceConfig holds extra parameters to trace functions.
type TraceConfig struct {
- *vm.LogConfig
+ *logger.LogConfig
Tracer *string
Timeout *string
Reexec *uint64
diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go
index 1b31f7a6cbe..86af2a2bda3 100644
--- a/eth/tracers/js/goja.go
+++ b/eth/tracers/js/goja.go
@@ -513,16 +513,6 @@ func (t *jsTracer) setTypeConverters() error {
return nil
}
-func (t *jsTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (t *jsTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
-func (t *jsTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {}
-
type opObj struct {
vm *goja.Runtime
op vm.OpCode
diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go
index 0d485a82cb0..347990a1f43 100644
--- a/eth/tracers/logger/access_list_tracer.go
+++ b/eth/tracers/logger/access_list_tracer.go
@@ -177,14 +177,6 @@ func (*AccessListTracer) CaptureEnd(output []byte, usedGas uint64, err error) {
}
func (*AccessListTracer) CaptureExit(output []byte, usedGas uint64, err error) {
}
-func (*AccessListTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-func (*AccessListTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-func (*AccessListTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
// AccessList returns the current accesslist maintained by the tracer.
func (a *AccessListTracer) AccessList() types.AccessList {
diff --git a/core/vm/gen_structlog.go b/eth/tracers/logger/gen_structlog.go
similarity index 94%
rename from core/vm/gen_structlog.go
rename to eth/tracers/logger/gen_structlog.go
index adaf717d00e..ee370b0f254 100644
--- a/core/vm/gen_structlog.go
+++ b/eth/tracers/logger/gen_structlog.go
@@ -1,6 +1,4 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package vm
+package logger
import (
"encoding/json"
@@ -9,6 +7,7 @@ import (
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
"github.com/ledgerwatch/erigon/common/math"
+ "github.com/ledgerwatch/erigon/core/vm"
)
var _ = (*structLogMarshaling)(nil)
@@ -17,7 +16,7 @@ var _ = (*structLogMarshaling)(nil)
func (s StructLog) MarshalJSON() ([]byte, error) {
type StructLog struct {
Pc uint64 `json:"pc"`
- Op OpCode `json:"op"`
+ Op vm.OpCode `json:"op"`
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
@@ -59,7 +58,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
func (s *StructLog) UnmarshalJSON(input []byte) error {
type StructLog struct {
Pc *uint64 `json:"pc"`
- Op *OpCode `json:"op"`
+ Op *vm.OpCode `json:"op"`
Gas *math.HexOrDecimal64 `json:"gas"`
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory"`
diff --git a/eth/tracers/logger/json_stream.go b/eth/tracers/logger/json_stream.go
new file mode 100644
index 00000000000..ee4eeab7de2
--- /dev/null
+++ b/eth/tracers/logger/json_stream.go
@@ -0,0 +1,197 @@
+package logger
+
+import (
+ "context"
+ "encoding/hex"
+ "sort"
+
+ "github.com/holiman/uint256"
+ jsoniter "github.com/json-iterator/go"
+ "github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/core/vm"
+)
+
+// JsonStreamLogger is an EVM state logger and implements Tracer.
+//
+// JsonStreamLogger can capture state based on the given Log configuration and also keeps
+// a track record of modified storage which is used in reporting snapshots of the
+// contract their storage.
+type JsonStreamLogger struct {
+ ctx context.Context
+ cfg LogConfig
+ stream *jsoniter.Stream
+ hexEncodeBuf [128]byte
+ firstCapture bool
+
+ locations common.Hashes // For sorting
+ storage map[common.Address]Storage
+ logs []StructLog
+ output []byte //nolint
+ err error //nolint
+ env *vm.EVM
+}
+
+// NewStructLogger returns a new logger
+func NewJsonStreamLogger(cfg *LogConfig, ctx context.Context, stream *jsoniter.Stream) *JsonStreamLogger {
+ logger := &JsonStreamLogger{
+ ctx: ctx,
+ stream: stream,
+ storage: make(map[common.Address]Storage),
+ firstCapture: true,
+ }
+ if cfg != nil {
+ logger.cfg = *cfg
+ }
+ return logger
+}
+
+func (l *JsonStreamLogger) CaptureTxStart(gasLimit uint64) {}
+
+func (l *JsonStreamLogger) CaptureTxEnd(restGas uint64) {}
+
+// CaptureStart implements the Tracer interface to initialize the tracing operation.
+func (l *JsonStreamLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ l.env = env
+}
+
+func (l *JsonStreamLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+}
+
+// CaptureState logs a new structured log message and pushes it out to the environment
+//
+// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
+func (l *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
+
+ select {
+ case <-l.ctx.Done():
+ return
+ default:
+ }
+ // check if already accumulated the specified number of logs
+ if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
+ return
+ }
+ if !l.firstCapture {
+ l.stream.WriteMore()
+ } else {
+ l.firstCapture = false
+ }
+ var outputStorage bool
+ 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)
+ }
+ // capture SLOAD opcodes and record the read entry in the local storage
+ if op == vm.SLOAD && stack.Len() >= 1 {
+ var (
+ address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
+ value uint256.Int
+ )
+ l.env.IntraBlockState().GetState(contract.Address(), &address, &value)
+ l.storage[contract.Address()][address] = value.Bytes32()
+ outputStorage = true
+ }
+ // capture SSTORE opcodes and record the written entry in the local storage.
+ if op == vm.SSTORE && stack.Len() >= 2 {
+ var (
+ value = common.Hash(stack.Data[stack.Len()-2].Bytes32())
+ address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
+ )
+ l.storage[contract.Address()][address] = value
+ outputStorage = true
+ }
+ }
+ // create a new snapshot of the EVM.
+ l.stream.WriteObjectStart()
+ l.stream.WriteObjectField("pc")
+ l.stream.WriteUint64(pc)
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("op")
+ l.stream.WriteString(op.String())
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("gas")
+ l.stream.WriteUint64(gas)
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("gasCost")
+ l.stream.WriteUint64(cost)
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("depth")
+ l.stream.WriteInt(depth)
+ if err != nil {
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("error")
+ l.stream.WriteObjectStart()
+ l.stream.WriteObjectEnd()
+ //l.stream.WriteString(err.Error())
+ }
+ if !l.cfg.DisableStack {
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("stack")
+ l.stream.WriteArrayStart()
+ for i, stackValue := range stack.Data {
+ if i > 0 {
+ l.stream.WriteMore()
+ }
+ l.stream.WriteString(stackValue.String())
+ }
+ l.stream.WriteArrayEnd()
+ }
+ if !l.cfg.DisableMemory {
+ memData := memory.Data()
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("memory")
+ l.stream.WriteArrayStart()
+ for i := 0; i+32 <= len(memData); i += 32 {
+ if i > 0 {
+ l.stream.WriteMore()
+ }
+ l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], memData[i:i+32])]))
+ }
+ l.stream.WriteArrayEnd()
+ }
+ if outputStorage {
+ l.stream.WriteMore()
+ l.stream.WriteObjectField("storage")
+ l.stream.WriteObjectStart()
+ first := true
+ // Sort storage by locations for easier comparison with geth
+ if l.locations != nil {
+ l.locations = l.locations[:0]
+ }
+ s := l.storage[contract.Address()]
+ for loc := range s {
+ l.locations = append(l.locations, loc)
+ }
+ sort.Sort(l.locations)
+ for _, loc := range l.locations {
+ value := s[loc]
+ if first {
+ first = false
+ } else {
+ l.stream.WriteMore()
+ }
+ l.stream.WriteObjectField(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], loc[:])]))
+ l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], value[:])]))
+ }
+ l.stream.WriteObjectEnd()
+ }
+ l.stream.WriteObjectEnd()
+ _ = 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) {
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (l *JsonStreamLogger) CaptureEnd(output []byte, usedGas uint64, err error) {
+}
+
+func (l *JsonStreamLogger) CaptureExit(output []byte, usedGas uint64, err error) {
+}
diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go
new file mode 100644
index 00000000000..67f77128c19
--- /dev/null
+++ b/eth/tracers/logger/logger.go
@@ -0,0 +1,419 @@
+package logger
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+ "os"
+ "strings"
+
+ "github.com/holiman/uint256"
+ "github.com/ledgerwatch/erigon/common"
+ "github.com/ledgerwatch/erigon/common/hexutil"
+ "github.com/ledgerwatch/erigon/common/math"
+ "github.com/ledgerwatch/erigon/core/types"
+ "github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/params"
+)
+
+var ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
+
+// Storage represents a contract's storage.
+type Storage map[common.Hash]common.Hash
+
+// Copy duplicates the current storage.
+func (s Storage) Copy() Storage {
+ cpy := make(Storage)
+ for key, value := range s {
+ cpy[key] = value
+ }
+ return cpy
+}
+
+// LogConfig are the configuration options for structured logger the EVM
+type LogConfig struct {
+ DisableMemory bool // disable memory capture
+ DisableStack bool // disable stack capture
+ DisableStorage bool // disable storage capture
+ DisableReturnData bool // disable return data capture
+ Debug bool // print output during capture end
+ Limit int // maximum length of output, but zero means unlimited
+ // Chain overrides, can be used to execute a trace using future fork rules
+ Overrides *params.ChainConfig `json:"overrides,omitempty"`
+}
+
+//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
+
+// StructLog is emitted to the EVM each cycle and lists information about the current internal state
+// prior to the execution of the statement.
+type StructLog struct {
+ Pc uint64 `json:"pc"`
+ Op vm.OpCode `json:"op"`
+ Gas uint64 `json:"gas"`
+ GasCost uint64 `json:"gasCost"`
+ Memory []byte `json:"memory"`
+ MemorySize int `json:"memSize"`
+ Stack []*big.Int `json:"stack"`
+ ReturnData []byte `json:"returnData"`
+ Storage map[common.Hash]common.Hash `json:"-"`
+ Depth int `json:"depth"`
+ RefundCounter uint64 `json:"refund"`
+ Err error `json:"-"`
+}
+
+// overrides for gencodec
+type structLogMarshaling struct {
+ Stack []*math.HexOrDecimal256
+ Gas math.HexOrDecimal64
+ GasCost math.HexOrDecimal64
+ Memory hexutil.Bytes
+ ReturnData hexutil.Bytes
+ OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
+ ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON
+}
+
+// OpName formats the operand name in a human-readable format.
+func (s *StructLog) OpName() string {
+ return s.Op.String()
+}
+
+// ErrorString formats the log's error as a string.
+func (s *StructLog) ErrorString() string {
+ if s.Err != nil {
+ return s.Err.Error()
+ }
+ return ""
+}
+
+// StructLogRes stores a structured log emitted by the EVM while replaying a
+// transaction in debug mode
+type StructLogRes struct {
+ Pc uint64 `json:"pc"`
+ Op string `json:"op"`
+ Gas uint64 `json:"gas"`
+ GasCost uint64 `json:"gasCost"`
+ Depth int `json:"depth"`
+ Error error `json:"error,omitempty"`
+ Stack *[]string `json:"stack,omitempty"`
+ Memory *[]string `json:"memory,omitempty"`
+ Storage *map[string]string `json:"storage,omitempty"`
+}
+
+// StructLogger is an EVM state logger and implements Tracer.
+//
+// StructLogger can capture state based on the given Log configuration and also keeps
+// a track record of modified storage which is used in reporting snapshots of the
+// contract their storage.
+type StructLogger struct {
+ cfg LogConfig
+
+ storage map[common.Address]Storage
+ logs []StructLog
+ output []byte
+ err error
+ env *vm.EVM
+}
+
+// NewStructLogger returns a new logger
+func NewStructLogger(cfg *LogConfig) *StructLogger {
+ logger := &StructLogger{
+ storage: make(map[common.Address]Storage),
+ }
+ if cfg != nil {
+ logger.cfg = *cfg
+ }
+ return logger
+}
+
+func (l *StructLogger) CaptureTxStart(gasLimit uint64) {}
+
+func (l *StructLogger) CaptureTxEnd(restGas uint64) {}
+
+// CaptureStart implements the Tracer interface to initialize the tracing operation.
+func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+ l.env = env
+}
+
+// CaptureEnter implements the Tracer interface to initialize the tracing operation for an internal call.
+func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+}
+
+// CaptureState logs a new structured log message and pushes it out to the environment
+//
+// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
+func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+ memory := scope.Memory
+ stack := scope.Stack
+ contract := scope.Contract
+
+ // check if already accumulated the specified number of logs
+ if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
+ return
+ }
+
+ // Copy a snapshot of the current memory state to a new buffer
+ var mem []byte
+ if !l.cfg.DisableMemory {
+ mem = make([]byte, len(memory.Data()))
+ copy(mem, memory.Data())
+ }
+ // 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[i] = new(big.Int).Set(item.ToBig())
+ }
+ }
+ // 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)
+ }
+ // capture SLOAD opcodes and record the read entry in the local storage
+ if op == vm.SLOAD && stack.Len() >= 1 {
+ var (
+ address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
+ value uint256.Int
+ )
+ l.env.IntraBlockState().GetState(contract.Address(), &address, &value)
+ l.storage[contract.Address()][address] = value.Bytes32()
+ }
+ // capture SSTORE opcodes and record the written entry in the local storage.
+ if op == vm.SSTORE && stack.Len() >= 2 {
+ var (
+ value = common.Hash(stack.Data[stack.Len()-2].Bytes32())
+ address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
+ )
+ l.storage[contract.Address()][address] = value
+ }
+ storage = l.storage[contract.Address()].Copy()
+ }
+ var rdata []byte
+ if !l.cfg.DisableReturnData {
+ rdata = make([]byte, len(rData))
+ copy(rdata, rData)
+ }
+ // create a new snapshot of the EVM.
+ log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.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) {
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (l *StructLogger) CaptureEnd(output []byte, usedGas uint64, err error) {
+ l.output = output
+ l.err = err
+ if l.cfg.Debug {
+ fmt.Printf("0x%x\n", output)
+ if err != nil {
+ fmt.Printf(" error: %v\n", err)
+ }
+ }
+}
+
+// CaptureExit is called after the internal call finishes to finalize the tracing.
+func (l *StructLogger) CaptureExit(output []byte, usedGas uint64, err error) {
+}
+
+// StructLogs returns the captured log entries.
+func (l *StructLogger) StructLogs() []StructLog { return l.logs }
+
+// Error returns the VM error captured by the trace.
+func (l *StructLogger) Error() error { return l.err }
+
+// Output returns the VM return value captured by the trace.
+func (l *StructLogger) Output() []byte { return l.output }
+
+func (l *StructLogger) Flush(tx types.Transaction) {
+ w, err1 := os.Create(fmt.Sprintf("txtrace_%x.txt", tx.Hash()))
+ if err1 != nil {
+ panic(err1)
+ }
+ encoder := json.NewEncoder(w)
+ logs := FormatLogs(l.StructLogs())
+ if err2 := encoder.Encode(logs); err2 != nil {
+ panic(err2)
+ }
+ if err2 := w.Close(); err2 != nil {
+ panic(err2)
+ }
+}
+
+// FormatLogs formats EVM returned structured logs for json output
+func FormatLogs(logs []StructLog) []StructLogRes {
+ formatted := make([]StructLogRes, len(logs))
+ for index, trace := range logs {
+ formatted[index] = StructLogRes{
+ Pc: trace.Pc,
+ Op: trace.Op.String(),
+ Gas: trace.Gas,
+ GasCost: trace.GasCost,
+ Depth: trace.Depth,
+ Error: trace.Err,
+ }
+ if trace.Stack != nil {
+ stack := make([]string, len(trace.Stack))
+ for i, stackValue := range trace.Stack {
+ stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
+ }
+ formatted[index].Stack = &stack
+ }
+ if trace.Memory != nil {
+ memory := make([]string, 0, (len(trace.Memory)+31)/32)
+ for i := 0; i+32 <= len(trace.Memory); i += 32 {
+ memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
+ }
+ formatted[index].Memory = &memory
+ }
+ if trace.Storage != nil {
+ storage := make(map[string]string)
+ for i, storageValue := range trace.Storage {
+ storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
+ }
+ formatted[index].Storage = &storage
+ }
+ }
+ return formatted
+}
+
+// WriteTrace writes a formatted trace to the given writer
+func WriteTrace(writer io.Writer, logs []StructLog) {
+ for _, log := range logs {
+ fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost)
+ if log.Err != nil {
+ fmt.Fprintf(writer, " ERROR: %v", log.Err)
+ }
+ fmt.Fprintln(writer)
+
+ if len(log.Stack) > 0 {
+ fmt.Fprintln(writer, "Stack:")
+ for i := len(log.Stack) - 1; i >= 0; i-- {
+ fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
+ }
+ }
+ if len(log.Memory) > 0 {
+ fmt.Fprintln(writer, "Memory:")
+ fmt.Fprint(writer, hex.Dump(log.Memory))
+ }
+ if len(log.Storage) > 0 {
+ fmt.Fprintln(writer, "Storage:")
+ for h, item := range log.Storage {
+ fmt.Fprintf(writer, "%x: %x\n", h, item)
+ }
+ }
+ if len(log.ReturnData) > 0 {
+ fmt.Fprintln(writer, "ReturnData:")
+ fmt.Fprint(writer, hex.Dump(log.ReturnData))
+ }
+ fmt.Fprintln(writer)
+ }
+}
+
+// WriteLogs writes vm logs in a readable format to the given writer
+func WriteLogs(writer io.Writer, logs []*types.Log) {
+ for _, log := range logs {
+ fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
+
+ for i, topic := range log.Topics {
+ fmt.Fprintf(writer, "%08d %x\n", i, topic)
+ }
+
+ fmt.Fprint(writer, hex.Dump(log.Data))
+ fmt.Fprintln(writer)
+ }
+}
+
+type mdLogger struct {
+ out io.Writer
+ cfg *LogConfig
+ env *vm.EVM
+}
+
+// NewMarkdownLogger creates a logger which outputs information in a format adapted
+// for human readability, and is also a valid markdown table
+func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
+ l := &mdLogger{writer, cfg, nil}
+ if l.cfg == nil {
+ l.cfg = &LogConfig{}
+ }
+ return l
+}
+
+func (t *mdLogger) CaptureTxStart(gasLimit uint64) {}
+
+func (t *mdLogger) CaptureTxEnd(restGas uint64) {}
+
+func (t *mdLogger) captureStartOrEnter(from, to common.Address, create bool, input []byte, gas uint64, value *uint256.Int) {
+ if !create {
+ fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
+ from.String(), to.String(),
+ input, gas, value)
+ } else {
+ fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
+ from.String(), to.String(),
+ input, gas, value)
+ }
+
+ fmt.Fprintf(t.out, `
+| Pc | Op | Cost | Stack | RStack | Refund |
+|-------|-------------|------|-----------|-----------|---------|
+`)
+}
+
+func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) { //nolint:interfacer
+ t.env = env
+ t.captureStartOrEnter(from, to, create, input, gas, value)
+}
+
+func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.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) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+ stack := scope.Stack
+
+ fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
+
+ if !t.cfg.DisableStack {
+ // format stack
+ var a []string
+ for _, elem := range stack.Data {
+ a = append(a, fmt.Sprintf("%v", 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.Fprintln(t.out, "")
+ if err != nil {
+ fmt.Fprintf(t.out, "Error: %v\n", err)
+ }
+}
+
+func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+ fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
+}
+
+func (t *mdLogger) 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) {
+ t.captureEndOrExit(output, usedGas, err)
+}
+
+func (t *mdLogger) CaptureExit(output []byte, usedGas uint64, err error) {
+ t.captureEndOrExit(output, usedGas, err)
+}
diff --git a/core/vm/logger_json.go b/eth/tracers/logger/logger_json.go
similarity index 74%
rename from core/vm/logger_json.go
rename to eth/tracers/logger/logger_json.go
index 3ebc04988a7..9beb87da1e1 100644
--- a/core/vm/logger_json.go
+++ b/eth/tracers/logger/logger_json.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package vm
+package logger
import (
"encoding/json"
@@ -24,12 +24,13 @@ import (
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/math"
+ "github.com/ledgerwatch/erigon/core/vm"
)
type JSONLogger struct {
encoder *json.Encoder
cfg *LogConfig
- env *EVM
+ env *vm.EVM
}
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
@@ -46,15 +47,15 @@ func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {}
func (l *JSONLogger) CaptureTxEnd(restGas uint64) {}
-func (l *JSONLogger) CaptureStart(env *EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (l *JSONLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
l.env = env
}
-func (l *JSONLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
+func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.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 OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
+func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
memory := scope.Memory
stack := scope.Stack
@@ -84,7 +85,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope
}
// CaptureFault outputs state information on the logger.
-func (l *JSONLogger) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
+func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}
// CaptureEnd is triggered at end of execution.
@@ -103,14 +104,3 @@ func (l *JSONLogger) CaptureEnd(output []byte, usedGas uint64, err error) {
func (l *JSONLogger) CaptureExit(output []byte, usedGas uint64, err error) {
}
-
-func (l *JSONLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (l *JSONLogger) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (l *JSONLogger) CaptureAccountWrite(account common.Address) error {
- return nil
-}
diff --git a/core/vm/logger_test.go b/eth/tracers/logger/logger_test.go
similarity index 57%
rename from core/vm/logger_test.go
rename to eth/tracers/logger/logger_test.go
index 9ad7a0613b4..02a0627279f 100644
--- a/core/vm/logger_test.go
+++ b/eth/tracers/logger/logger_test.go
@@ -14,13 +14,15 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package vm
+package logger
import (
"math/big"
"testing"
"github.com/holiman/uint256"
+ "github.com/ledgerwatch/erigon/core/state"
+ "github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/common"
@@ -28,19 +30,42 @@ import (
"github.com/ledgerwatch/erigon/params"
)
+type dummyContractRef struct {
+ calledForEach bool
+}
+
+func (dummyContractRef) ReturnGas(*big.Int) {}
+func (dummyContractRef) Address() common.Address { return common.Address{} }
+func (dummyContractRef) Value() *big.Int { return new(big.Int) }
+func (dummyContractRef) SetCode(common.Hash, []byte) {}
+func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
+ d.calledForEach = true
+}
+func (d *dummyContractRef) SubBalance(amount *big.Int) {}
+func (d *dummyContractRef) AddBalance(amount *big.Int) {}
+func (d *dummyContractRef) SetBalance(*big.Int) {}
+func (d *dummyContractRef) SetNonce(uint64) {}
+func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
+
+type dummyStatedb struct {
+ state.IntraBlockState
+}
+
+func (*dummyStatedb) GetRefund() uint64 { return 1337 }
+
func TestStoreCapture(t *testing.T) {
var (
- env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{})
+ env = vm.NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{})
logger = NewStructLogger(nil)
- mem = NewMemory()
+ mem = vm.NewMemory()
stack = stack.New()
- contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 0, false /* skipAnalysis */)
+ contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 0, false /* skipAnalysis */)
)
stack.Push(uint256.NewInt(1))
stack.Push(uint256.NewInt(0))
var index common.Hash
logger.CaptureStart(env, common.Address{}, common.Address{}, false, false, nil, 0, nil, nil)
- logger.CaptureState(0, SSTORE, 0, 0, &ScopeContext{
+ logger.CaptureState(0, 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 e62cc5e9954..98a32efdd14 100644
--- a/eth/tracers/native/4byte.go
+++ b/eth/tracers/native/4byte.go
@@ -111,17 +111,6 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to comm
t.store(input[0:4], len(input)-4)
}
-func (t *fourByteTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (t *fourByteTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (t *fourByteTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
// GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`).
func (t *fourByteTracer) GetResult() (json.RawMessage, error) {
diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go
index 63284dbd686..458e8deb554 100644
--- a/eth/tracers/native/call.go
+++ b/eth/tracers/native/call.go
@@ -239,17 +239,6 @@ func (t *callTracer) CaptureTxEnd(restGas uint64) {
}
}
-func (t *callTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (t *callTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (t *callTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
// GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`).
func (t *callTracer) GetResult() (json.RawMessage, error) {
diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go
index 77ca6c5d57f..76695514ed3 100644
--- a/eth/tracers/native/mux.go
+++ b/eth/tracers/native/mux.go
@@ -113,17 +113,6 @@ func (t *muxTracer) CaptureTxEnd(restGas uint64) {
}
}
-func (t *muxTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (t *muxTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (t *muxTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
// GetResult returns an empty json object.
func (t *muxTracer) GetResult() (json.RawMessage, error) {
resObject := make(map[string]json.RawMessage)
diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go
index 59e52530675..ca7c5d7f79a 100644
--- a/eth/tracers/native/noop.go
+++ b/eth/tracers/native/noop.go
@@ -67,17 +67,6 @@ func (*noopTracer) CaptureTxStart(gasLimit uint64) {}
func (*noopTracer) CaptureTxEnd(restGas uint64) {}
-func (*noopTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (*noopTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (*noopTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
// GetResult returns an empty json object.
func (t *noopTracer) GetResult() (json.RawMessage, error) {
return json.RawMessage(`{}`), nil
diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go
index 7db3437ba53..154e419210b 100644
--- a/eth/tracers/native/prestate.go
+++ b/eth/tracers/native/prestate.go
@@ -236,17 +236,6 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
}
}
-func (t *prestateTracer) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (t *prestateTracer) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (t *prestateTracer) CaptureAccountWrite(account common.Address) error {
- return nil
-}
-
// GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`).
func (t *prestateTracer) GetResult() (json.RawMessage, error) {
diff --git a/turbo/adapter/ethapi/api.go b/turbo/adapter/ethapi/api.go
index a158357550f..0560173cddf 100644
--- a/turbo/adapter/ethapi/api.go
+++ b/turbo/adapter/ethapi/api.go
@@ -28,7 +28,7 @@ import (
"github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/types"
- "github.com/ledgerwatch/erigon/core/vm"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/log/v3"
)
@@ -215,7 +215,7 @@ type StructLogRes struct {
}
// FormatLogs formats EVM returned structured logs for json output
-func FormatLogs(logs []vm.StructLog) []StructLogRes {
+func FormatLogs(logs []logger.StructLog) []StructLogRes {
formatted := make([]StructLogRes, len(logs))
for index, trace := range logs {
formatted[index] = StructLogRes{
diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go
index b67b5c5069d..755d2903aee 100644
--- a/turbo/transactions/tracing.go
+++ b/turbo/transactions/tracing.go
@@ -6,10 +6,8 @@ import (
"encoding/json"
"errors"
"fmt"
- "sort"
"time"
- "github.com/holiman/uint256"
jsoniter "github.com/json-iterator/go"
"github.com/ledgerwatch/erigon-lib/kv"
state2 "github.com/ledgerwatch/erigon-lib/state"
@@ -22,6 +20,7 @@ import (
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
"github.com/ledgerwatch/erigon/eth/stagedsync"
"github.com/ledgerwatch/erigon/eth/tracers"
+ "github.com/ledgerwatch/erigon/eth/tracers/logger"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
"github.com/ledgerwatch/erigon/turbo/services"
@@ -166,11 +165,11 @@ func TraceTx(
streaming = false
case config == nil:
- tracer = NewJsonStreamLogger(nil, ctx, stream)
+ tracer = logger.NewJsonStreamLogger(nil, ctx, stream)
streaming = true
default:
- tracer = NewJsonStreamLogger(config.LogConfig, ctx, stream)
+ tracer = logger.NewJsonStreamLogger(config.LogConfig, ctx, stream)
streaming = true
}
// Run the transaction with tracing enabled.
@@ -219,199 +218,3 @@ func TraceTx(
}
return nil
}
-
-// StructLogger is an EVM state logger and implements Tracer.
-//
-// StructLogger can capture state based on the given Log configuration and also keeps
-// a track record of modified storage which is used in reporting snapshots of the
-// contract their storage.
-type JsonStreamLogger struct {
- ctx context.Context
- cfg vm.LogConfig
- stream *jsoniter.Stream
- hexEncodeBuf [128]byte
- firstCapture bool
-
- locations common.Hashes // For sorting
- storage map[common.Address]vm.Storage
- logs []vm.StructLog
- output []byte //nolint
- err error //nolint
- env *vm.EVM
-}
-
-// NewStructLogger returns a new logger
-func NewJsonStreamLogger(cfg *vm.LogConfig, ctx context.Context, stream *jsoniter.Stream) *JsonStreamLogger {
- logger := &JsonStreamLogger{
- ctx: ctx,
- stream: stream,
- storage: make(map[common.Address]vm.Storage),
- firstCapture: true,
- }
- if cfg != nil {
- logger.cfg = *cfg
- }
- return logger
-}
-
-func (l *JsonStreamLogger) CaptureTxStart(gasLimit uint64) {}
-
-func (l *JsonStreamLogger) CaptureTxEnd(restGas uint64) {}
-
-// CaptureStart implements the Tracer interface to initialize the tracing operation.
-func (l *JsonStreamLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
- l.env = env
-}
-
-func (l *JsonStreamLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
-}
-
-// CaptureState logs a new structured log message and pushes it out to the environment
-//
-// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
-func (l *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
-
- select {
- case <-l.ctx.Done():
- return
- default:
- }
- // check if already accumulated the specified number of logs
- if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
- return
- }
- if !l.firstCapture {
- l.stream.WriteMore()
- } else {
- l.firstCapture = false
- }
- var outputStorage bool
- 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(vm.Storage)
- }
- // capture SLOAD opcodes and record the read entry in the local storage
- if op == vm.SLOAD && stack.Len() >= 1 {
- var (
- address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
- value uint256.Int
- )
- l.env.IntraBlockState().GetState(contract.Address(), &address, &value)
- l.storage[contract.Address()][address] = value.Bytes32()
- outputStorage = true
- }
- // capture SSTORE opcodes and record the written entry in the local storage.
- if op == vm.SSTORE && stack.Len() >= 2 {
- var (
- value = common.Hash(stack.Data[stack.Len()-2].Bytes32())
- address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
- )
- l.storage[contract.Address()][address] = value
- outputStorage = true
- }
- }
- // create a new snapshot of the EVM.
- l.stream.WriteObjectStart()
- l.stream.WriteObjectField("pc")
- l.stream.WriteUint64(pc)
- l.stream.WriteMore()
- l.stream.WriteObjectField("op")
- l.stream.WriteString(op.String())
- l.stream.WriteMore()
- l.stream.WriteObjectField("gas")
- l.stream.WriteUint64(gas)
- l.stream.WriteMore()
- l.stream.WriteObjectField("gasCost")
- l.stream.WriteUint64(cost)
- l.stream.WriteMore()
- l.stream.WriteObjectField("depth")
- l.stream.WriteInt(depth)
- if err != nil {
- l.stream.WriteMore()
- l.stream.WriteObjectField("error")
- l.stream.WriteObjectStart()
- l.stream.WriteObjectEnd()
- //l.stream.WriteString(err.Error())
- }
- if !l.cfg.DisableStack {
- l.stream.WriteMore()
- l.stream.WriteObjectField("stack")
- l.stream.WriteArrayStart()
- for i, stackValue := range stack.Data {
- if i > 0 {
- l.stream.WriteMore()
- }
- l.stream.WriteString(stackValue.String())
- }
- l.stream.WriteArrayEnd()
- }
- if !l.cfg.DisableMemory {
- memData := memory.Data()
- l.stream.WriteMore()
- l.stream.WriteObjectField("memory")
- l.stream.WriteArrayStart()
- for i := 0; i+32 <= len(memData); i += 32 {
- if i > 0 {
- l.stream.WriteMore()
- }
- l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], memData[i:i+32])]))
- }
- l.stream.WriteArrayEnd()
- }
- if outputStorage {
- l.stream.WriteMore()
- l.stream.WriteObjectField("storage")
- l.stream.WriteObjectStart()
- first := true
- // Sort storage by locations for easier comparison with geth
- if l.locations != nil {
- l.locations = l.locations[:0]
- }
- s := l.storage[contract.Address()]
- for loc := range s {
- l.locations = append(l.locations, loc)
- }
- sort.Sort(l.locations)
- for _, loc := range l.locations {
- value := s[loc]
- if first {
- first = false
- } else {
- l.stream.WriteMore()
- }
- l.stream.WriteObjectField(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], loc[:])]))
- l.stream.WriteString(string(l.hexEncodeBuf[0:hex.Encode(l.hexEncodeBuf[:], value[:])]))
- }
- l.stream.WriteObjectEnd()
- }
- l.stream.WriteObjectEnd()
- _ = 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) {
-}
-
-// CaptureEnd is called after the call finishes to finalize the tracing.
-func (l *JsonStreamLogger) CaptureEnd(output []byte, usedGas uint64, err error) {
-}
-
-func (l *JsonStreamLogger) CaptureExit(output []byte, usedGas uint64, err error) {
-}
-
-func (l *JsonStreamLogger) CaptureSelfDestruct(from common.Address, to common.Address, value *uint256.Int) {
-}
-
-func (l *JsonStreamLogger) CaptureAccountRead(account common.Address) error {
- return nil
-}
-
-func (l *JsonStreamLogger) CaptureAccountWrite(account common.Address) error {
- return nil
-}