mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-04 19:13:47 +00:00
provide StateLogger for short-lived tracers
This commit is contained in:
parent
3975c7cfcc
commit
619ac2948c
11 changed files with 132 additions and 44 deletions
|
|
@ -952,6 +952,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
|||
}
|
||||
}
|
||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true})
|
||||
statedb.SetLogger(tracer)
|
||||
|
||||
// Define a meaningful timeout of a single transaction trace
|
||||
if config.Timeout != nil {
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
statedb.SetLogger(tracer)
|
||||
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||
msg, err := core.TransactionToMessage(tx, signer, nil)
|
||||
if err != nil {
|
||||
|
|
@ -360,6 +361,7 @@ func TestInternals(t *testing.T) {
|
|||
Balance: big.NewInt(500000000000000),
|
||||
},
|
||||
}, false)
|
||||
statedb.SetLogger(tc.tracer)
|
||||
evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Tracer: tc.tracer})
|
||||
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
|
||||
To: &to,
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to create call tracer: %v", err)
|
||||
}
|
||||
statedb.SetLogger(tracer)
|
||||
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||
|
||||
msg, err := core.TransactionToMessage(tx, signer, nil)
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
statedb.SetLogger(tracer)
|
||||
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||
msg, err := core.TransactionToMessage(tx, signer, nil)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -176,6 +176,19 @@ func (*AccessListTracer) CaptureTxStart(tx *types.Transaction) {}
|
|||
|
||||
func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt) {}
|
||||
|
||||
func (*AccessListTracer) OnBalanceChange(a common.Address, prev, new *big.Int) {}
|
||||
|
||||
func (*AccessListTracer) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||
|
||||
func (*AccessListTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||
}
|
||||
|
||||
func (*AccessListTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
|
||||
|
||||
func (*AccessListTracer) OnLog(log *types.Log) {}
|
||||
|
||||
func (*AccessListTracer) OnNewAccount(a common.Address) {}
|
||||
|
||||
// AccessList returns the current accesslist maintained by the tracer.
|
||||
func (a *AccessListTracer) AccessList() types.AccessList {
|
||||
return a.list.accessList()
|
||||
|
|
|
|||
|
|
@ -271,6 +271,19 @@ func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt) {
|
|||
l.usedGas = receipt.GasUsed
|
||||
}
|
||||
|
||||
func (l *StructLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {}
|
||||
|
||||
func (l *StructLogger) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||
|
||||
func (l *StructLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||
}
|
||||
|
||||
func (l *StructLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
|
||||
|
||||
func (l *StructLogger) OnLog(log *types.Log) {}
|
||||
|
||||
func (l *StructLogger) OnNewAccount(a common.Address) {}
|
||||
|
||||
// StructLogs returns the captured log entries.
|
||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
||||
|
||||
|
|
@ -404,6 +417,19 @@ func (*mdLogger) CaptureTxStart(tx *types.Transaction) {}
|
|||
|
||||
func (*mdLogger) CaptureTxEnd(receipt *types.Receipt) {}
|
||||
|
||||
func (*mdLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {}
|
||||
|
||||
func (*mdLogger) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||
|
||||
func (*mdLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||
}
|
||||
|
||||
func (*mdLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
|
||||
|
||||
func (*mdLogger) OnLog(log *types.Log) {}
|
||||
|
||||
func (*mdLogger) OnNewAccount(a common.Address) {}
|
||||
|
||||
// ExecutionResult groups all structured logs emitted by the EVM
|
||||
// while replaying a transaction in debug mode as well as transaction
|
||||
// execution status, the amount of gas used and the return value
|
||||
|
|
|
|||
|
|
@ -106,3 +106,16 @@ func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
|
|||
func (l *JSONLogger) CaptureTxStart(tx *types.Transaction) {}
|
||||
|
||||
func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt) {}
|
||||
|
||||
func (*JSONLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {}
|
||||
|
||||
func (*JSONLogger) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||
|
||||
func (*JSONLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||
}
|
||||
|
||||
func (*JSONLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
|
||||
|
||||
func (*JSONLogger) OnLog(log *types.Log) {}
|
||||
|
||||
func (*JSONLogger) OnNewAccount(a common.Address) {}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ type callTracer struct {
|
|||
callstack []callFrame
|
||||
config callTracerConfig
|
||||
gasLimit uint64
|
||||
depth int
|
||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||
reason error // Textual reason for the interruption
|
||||
}
|
||||
|
|
@ -149,51 +150,11 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|||
|
||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
||||
func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
// skip if the previous op caused an error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Only logs need to be captured via opcode processing
|
||||
if !t.config.WithLog {
|
||||
return
|
||||
}
|
||||
// Avoid processing nested calls when only caring about top call
|
||||
if t.config.OnlyTopCall && depth > 0 {
|
||||
return
|
||||
}
|
||||
// Skip if tracing was interrupted
|
||||
if t.interrupt.Load() {
|
||||
return
|
||||
}
|
||||
switch op {
|
||||
case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4:
|
||||
size := int(op - vm.LOG0)
|
||||
|
||||
stack := scope.Stack
|
||||
stackData := stack.Data()
|
||||
|
||||
// Don't modify the stack
|
||||
mStart := stackData[len(stackData)-1]
|
||||
mSize := stackData[len(stackData)-2]
|
||||
topics := make([]common.Hash, size)
|
||||
for i := 0; i < size; i++ {
|
||||
topic := stackData[len(stackData)-2-(i+1)]
|
||||
topics[i] = common.Hash(topic.Bytes32())
|
||||
}
|
||||
|
||||
data, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(mStart.Uint64()), int64(mSize.Uint64()))
|
||||
if err != nil {
|
||||
// mSize was unrealistically large
|
||||
return
|
||||
}
|
||||
|
||||
log := callLog{Address: scope.Contract.Address(), Topics: topics, Data: hexutil.Bytes(data)}
|
||||
t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log)
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
t.depth++
|
||||
if t.config.OnlyTopCall {
|
||||
return
|
||||
}
|
||||
|
|
@ -217,6 +178,7 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
|
|||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||
// execute any code.
|
||||
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||
t.depth--
|
||||
if t.config.OnlyTopCall {
|
||||
return
|
||||
}
|
||||
|
|
@ -246,6 +208,22 @@ func (t *callTracer) CaptureTxEnd(receipt *types.Receipt) {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *callTracer) OnLog(log *types.Log) {
|
||||
// Only logs need to be captured via opcode processing
|
||||
if !t.config.WithLog {
|
||||
return
|
||||
}
|
||||
// Avoid processing nested calls when only caring about top call
|
||||
if t.config.OnlyTopCall && t.depth > 0 {
|
||||
return
|
||||
}
|
||||
// Skip if tracing was interrupted
|
||||
if t.interrupt.Load() {
|
||||
return
|
||||
}
|
||||
t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, callLog{Address: log.Address, Topics: log.Topics, Data: log.Data})
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
@ -257,7 +235,7 @@ func (t *callTracer) GetResult() (json.RawMessage, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.RawMessage(res), t.reason
|
||||
return res, t.reason
|
||||
}
|
||||
|
||||
// Stop terminates execution of the tracer at the first opportune moment.
|
||||
|
|
|
|||
|
|
@ -128,6 +128,42 @@ func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt) {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnBalanceChange(a, prev, new)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnNonceChange(a common.Address, prev, new uint64) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnNonceChange(a, prev, new)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnStorageChange(a, k, prev, new)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnLog(log *types.Log) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnLog(log)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnNewAccount(a common.Address) {
|
||||
for _, t := range t.tracers {
|
||||
t.OnNewAccount(a)
|
||||
}
|
||||
}
|
||||
|
||||
// GetResult returns an empty json object.
|
||||
func (t *muxTracer) GetResult() (json.RawMessage, error) {
|
||||
resObject := make(map[string]json.RawMessage)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,19 @@ func (*NoopTracer) CaptureTxStart(tx *types.Transaction) {}
|
|||
|
||||
func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt) {}
|
||||
|
||||
func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int) {}
|
||||
|
||||
func (*NoopTracer) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||
|
||||
func (*NoopTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||
}
|
||||
|
||||
func (*NoopTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
|
||||
|
||||
func (*NoopTracer) OnLog(log *types.Log) {}
|
||||
|
||||
func (*NoopTracer) OnNewAccount(a common.Address) {}
|
||||
|
||||
// GetResult returns an empty json object.
|
||||
func (t *NoopTracer) GetResult() (json.RawMessage, error) {
|
||||
return json.RawMessage(`{}`), nil
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
|
|
@ -36,10 +37,13 @@ type Context struct {
|
|||
TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
|
||||
}
|
||||
|
||||
// Tracer interface extends vm.EVMLogger and additionally
|
||||
// allows collecting the tracing result.
|
||||
// The set of methods that must be exposed by a tracer
|
||||
// for it to be available through the RPC interface.
|
||||
// This involves a method to retrieve results and one to
|
||||
// stop tracing.
|
||||
type Tracer interface {
|
||||
vm.EVMLogger
|
||||
state.StateLogger
|
||||
GetResult() (json.RawMessage, error)
|
||||
// Stop terminates execution of the tracer at the first opportune moment.
|
||||
Stop(err error)
|
||||
|
|
|
|||
Loading…
Reference in a new issue