Skip to content

Commit

Permalink
add statediff tracer
Browse files Browse the repository at this point in the history
  • Loading branch information
avatarH committed Oct 30, 2024
1 parent f294c6a commit b68f284
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 61 deletions.
28 changes: 19 additions & 9 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ The state transitioning model does all the necessary work to work out a valid ne
3) Create a new state object if the recipient is \0*32
4) Value transfer
== If contract creation ==
4a) Attempt to run transaction data
4b) If valid, use result as code for the new state object
4a) Attempt to run transaction data
4b) If valid, use result as code for the new state object
== end ==
5) Run Script section
6) Derive new state root
Expand Down Expand Up @@ -213,13 +215,13 @@ func (st *StateTransition) preCheck() error {
// TransitionDb will transition the state by applying the current message and
// returning the evm execution result with following fields.
//
// - used gas:
// total gas used (including gas being refunded)
// - returndata:
// the returned data from evm
// - concrete execution error:
// various **EVM** error which aborts the execution,
// e.g. ErrOutOfGas, ErrExecutionReverted
// - used gas:
// total gas used (including gas being refunded)
// - returndata:
// the returned data from evm
// - concrete execution error:
// various **EVM** error which aborts the execution,
// e.g. ErrOutOfGas, ErrExecutionReverted
//
// However if any consensus issue encountered, return the error directly with
// nil evm execution result.
Expand All @@ -238,6 +240,14 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if err := st.preCheck(); err != nil {
return nil, err
}

if st.evm.VmConfig.Debug {
st.evm.VmConfig.Tracer.CaptureTxStart(st.initialGas)
defer func() {
st.evm.VmConfig.Tracer.CaptureTxEnd(st.gas)
}()
}

msg := st.msg
sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
Expand Down
9 changes: 9 additions & 0 deletions core/vm/access_list_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,12 @@ func (a *AccessListTracer) AccessList() types.AccessList {
func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
return a.list.equal(other.list)
}

// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
// transaction processing.
func (t *AccessListTracer) CaptureTxStart(gasLimit uint64) {
}

// CaptureTxStart implements the Tracer interface and is invoked at the end of
// transaction processing.
func (t *AccessListTracer) CaptureTxEnd(restGas uint64) {}
64 changes: 32 additions & 32 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ type EVM struct {
chainRules params.Rules
// virtual machine configuration options used to initialise the
// evm.
vmConfig Config
VmConfig Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
interpreters []Interpreter
Expand All @@ -155,7 +155,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
evm.Context = blockCtx
evm.TxContext = txCtx
evm.StateDB = statedb
evm.vmConfig = vmConfig
evm.VmConfig = vmConfig
evm.chainConfig = chainConfig
evm.chainRules = chainConfig.Rules(blockCtx.BlockNumber)
evm.interpreters = make([]Interpreter, 0, 1)
Expand Down Expand Up @@ -215,7 +215,7 @@ func (evm *EVM) Interpreter() Interpreter {
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
if evm.VmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
Expand All @@ -232,13 +232,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug {
if evm.VmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
evm.VmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.VmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
} else {
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.vmConfig.Tracer.CaptureExit(ret, 0, nil)
evm.VmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.VmConfig.Tracer.CaptureExit(ret, 0, nil)
}
}
return nil, gas, nil
Expand All @@ -248,17 +248,17 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)

// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug {
if evm.VmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.VmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
evm.VmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}(gas, time.Now())
} else {
// Handle tracer events for entering and exiting a call frame
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.VmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
}
Expand Down Expand Up @@ -304,7 +304,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
if evm.VmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
Expand All @@ -321,10 +321,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
var snapshot = evm.StateDB.Snapshot()

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
if evm.VmConfig.Debug {
evm.VmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

Expand Down Expand Up @@ -355,7 +355,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
if evm.VmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
Expand All @@ -365,10 +365,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
var snapshot = evm.StateDB.Snapshot()

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
if evm.VmConfig.Debug {
evm.VmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

Expand Down Expand Up @@ -397,7 +397,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
if evm.VmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
Expand All @@ -418,10 +418,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
evm.StateDB.AddBalance(addr, big0)

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
if evm.VmConfig.Debug {
evm.VmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
evm.VmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

Expand Down Expand Up @@ -498,15 +498,15 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)

if evm.vmConfig.NoRecursion && evm.depth > 0 {
if evm.VmConfig.NoRecursion && evm.depth > 0 {
return nil, address, gas, nil
}

if evm.vmConfig.Debug {
if evm.VmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
evm.VmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
} else {
evm.vmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
evm.VmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
}
}

Expand Down Expand Up @@ -546,11 +546,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
}

if evm.vmConfig.Debug {
if evm.VmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
evm.VmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
} else {
evm.vmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err)
evm.VmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
}
return ret, address, contract.Gas, err
Expand Down
21 changes: 14 additions & 7 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
interpreter.hasher.Read(interpreter.hasherBuf[:])

evm := interpreter.evm
if evm.vmConfig.EnablePreimageRecording {
if evm.VmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}

Expand Down Expand Up @@ -390,16 +390,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// opExtCodeHash returns the code hash of a specified account.
// There are several cases when the function is called, while we can relay everything
// to `state.GetCodeHash` function to ensure the correctness.
// (1) Caller tries to get the code hash of a normal contract account, state
//
// (1) Caller tries to get the code hash of a normal contract account, state
//
// should return the relative code hash and set it as the result.
//
// (2) Caller tries to get the code hash of a non-existent account, state should
// (2) Caller tries to get the code hash of a non-existent account, state should
//
// return common.Hash{} and zero will be set as the result.
//
// (3) Caller tries to get the code hash for an account without contract code,
// (3) Caller tries to get the code hash for an account without contract code,
//
// state should return emptyCodeHash(0xc5d246...) as the result.
//
// (4) Caller tries to get the code hash of a precompiled account, the result
// (4) Caller tries to get the code hash of a precompiled account, the result
//
// should be zero or emptyCodeHash.
//
// It is worth noting that in order to avoid unnecessary create and clean,
Expand All @@ -408,10 +413,12 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// If the precompile account is not transferred any amount on a private or
// customized chain, the return value will be zero.
//
// (5) Caller tries to get the code hash for an account which is marked as suicided
// (5) Caller tries to get the code hash for an account which is marked as suicided
//
// in the current transaction, the code hash of this account should be returned.
//
// (6) Caller tries to get the code hash for an account which is marked as deleted,
// (6) Caller tries to get the code hash for an account which is marked as deleted,
//
// this account should be regarded as a non-existent account and zero should be returned.
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
Expand Down
17 changes: 17 additions & 0 deletions core/vm/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ func (s *StructLog) ErrorString() string {
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type EVMLogger interface {
// Transaction level
CaptureTxStart(gasLimit uint64)
CaptureTxEnd(restGas uint64)

CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
Expand Down Expand Up @@ -147,6 +151,10 @@ func (l *StructLogger) Reset() {
l.err = nil
}

func (*StructLogger) CaptureTxStart(gasLimit uint64) {}

func (*StructLogger) CaptureTxEnd(restGas uint64) {}

// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
l.env = env
Expand Down Expand Up @@ -307,6 +315,15 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
return l
}

// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
// transaction processing.
func (t *mdLogger) CaptureTxStart(gasLimit uint64) {
}

// CaptureTxStart implements the Tracer interface and is invoked at the end of
// transaction processing.
func (t *mdLogger) CaptureTxEnd(restGas uint64) {}

func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
t.env = env
if !create {
Expand Down
9 changes: 9 additions & 0 deletions core/vm/logger_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
return l
}

// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
// transaction processing.
func (t *JSONLogger) CaptureTxStart(gasLimit uint64) {
}

// CaptureTxStart implements the Tracer interface and is invoked at the end of
// transaction processing.
func (t *JSONLogger) CaptureTxEnd(restGas uint64) {}

func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
l.env = env
}
Expand Down
9 changes: 9 additions & 0 deletions eth/tracers/js/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,15 @@ func wrapError(context string, err error) error {
return fmt.Errorf("%v in server-side tracer function '%v'", err, context)
}

// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
// transaction processing.
func (jst *jsTracer) CaptureTxStart(gasLimit uint64) {
}

// CaptureTxStart implements the Tracer interface and is invoked at the end of
// transaction processing.
func (t *jsTracer) CaptureTxEnd(restGas uint64) {}

// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
jst.env = env
Expand Down
9 changes: 9 additions & 0 deletions eth/tracers/native/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
return t, nil
}

// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
// transaction processing.
func (t *callTracer) CaptureTxStart(gasLimit uint64) {
}

// CaptureTxStart implements the Tracer interface and is invoked at the end of
// transaction processing.
func (t *callTracer) CaptureTxEnd(restGas uint64) {}

// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
t.env = env
Expand Down
6 changes: 6 additions & 0 deletions eth/tracers/native/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ func newNoopTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
return &noopTracer{}, nil
}

func (l *noopTracer) CaptureTxStart(gasLimit uint64) {
}

func (l *noopTracer) CaptureTxEnd(restGas uint64) {
}

// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
Expand Down
Loading

0 comments on commit b68f284

Please sign in to comment.