mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-16 17:03:46 +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})
|
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
|
// Define a meaningful timeout of a single transaction trace
|
||||||
if config.Timeout != nil {
|
if config.Timeout != nil {
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
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})
|
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil)
|
msg, err := core.TransactionToMessage(tx, signer, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -360,6 +361,7 @@ func TestInternals(t *testing.T) {
|
||||||
Balance: big.NewInt(500000000000000),
|
Balance: big.NewInt(500000000000000),
|
||||||
},
|
},
|
||||||
}, false)
|
}, false)
|
||||||
|
statedb.SetLogger(tc.tracer)
|
||||||
evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Tracer: tc.tracer})
|
evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Tracer: tc.tracer})
|
||||||
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
|
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
|
||||||
To: &to,
|
To: &to,
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create call tracer: %v", err)
|
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})
|
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||||
|
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil)
|
msg, err := core.TransactionToMessage(tx, signer, nil)
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
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})
|
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil)
|
msg, err := core.TransactionToMessage(tx, signer, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,19 @@ func (*AccessListTracer) CaptureTxStart(tx *types.Transaction) {}
|
||||||
|
|
||||||
func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt) {}
|
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.
|
// AccessList returns the current accesslist maintained by the tracer.
|
||||||
func (a *AccessListTracer) AccessList() types.AccessList {
|
func (a *AccessListTracer) AccessList() types.AccessList {
|
||||||
return a.list.accessList()
|
return a.list.accessList()
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,19 @@ func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt) {
|
||||||
l.usedGas = receipt.GasUsed
|
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.
|
// StructLogs returns the captured log entries.
|
||||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
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) 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
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
// while replaying a transaction in debug mode as well as transaction
|
// while replaying a transaction in debug mode as well as transaction
|
||||||
// execution status, the amount of gas used and the return value
|
// 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) CaptureTxStart(tx *types.Transaction) {}
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt) {}
|
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
|
callstack []callFrame
|
||||||
config callTracerConfig
|
config callTracerConfig
|
||||||
gasLimit uint64
|
gasLimit uint64
|
||||||
|
depth int
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
reason error // Textual reason for the 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.
|
// 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) {
|
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).
|
// 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) {
|
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 {
|
if t.config.OnlyTopCall {
|
||||||
return
|
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
|
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||||
// execute any code.
|
// execute any code.
|
||||||
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||||
|
t.depth--
|
||||||
if t.config.OnlyTopCall {
|
if t.config.OnlyTopCall {
|
||||||
return
|
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
|
// GetResult returns the json-encoded nested list of call traces, and any
|
||||||
// error arising from the encoding or forceful termination (via `Stop`).
|
// error arising from the encoding or forceful termination (via `Stop`).
|
||||||
func (t *callTracer) GetResult() (json.RawMessage, error) {
|
func (t *callTracer) GetResult() (json.RawMessage, error) {
|
||||||
|
|
@ -257,7 +235,7 @@ func (t *callTracer) GetResult() (json.RawMessage, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return json.RawMessage(res), t.reason
|
return res, t.reason
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates execution of the tracer at the first opportune moment.
|
// 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.
|
// GetResult returns an empty json object.
|
||||||
func (t *muxTracer) GetResult() (json.RawMessage, error) {
|
func (t *muxTracer) GetResult() (json.RawMessage, error) {
|
||||||
resObject := make(map[string]json.RawMessage)
|
resObject := make(map[string]json.RawMessage)
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,19 @@ func (*NoopTracer) CaptureTxStart(tx *types.Transaction) {}
|
||||||
|
|
||||||
func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt) {}
|
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.
|
// GetResult returns an empty json object.
|
||||||
func (t *NoopTracer) GetResult() (json.RawMessage, error) {
|
func (t *NoopTracer) GetResult() (json.RawMessage, error) {
|
||||||
return json.RawMessage(`{}`), nil
|
return json.RawMessage(`{}`), nil
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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)
|
TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracer interface extends vm.EVMLogger and additionally
|
// The set of methods that must be exposed by a tracer
|
||||||
// allows collecting the tracing result.
|
// for it to be available through the RPC interface.
|
||||||
|
// This involves a method to retrieve results and one to
|
||||||
|
// stop tracing.
|
||||||
type Tracer interface {
|
type Tracer interface {
|
||||||
vm.EVMLogger
|
vm.EVMLogger
|
||||||
|
state.StateLogger
|
||||||
GetResult() (json.RawMessage, error)
|
GetResult() (json.RawMessage, error)
|
||||||
// Stop terminates execution of the tracer at the first opportune moment.
|
// Stop terminates execution of the tracer at the first opportune moment.
|
||||||
Stop(err error)
|
Stop(err error)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue