Merge branch 'extended-tracer' into feature/firehose-extended-tracer

# Conflicts:
#	core/state_transition.go
#	core/vm/contract.go
#	core/vm/evm.go
#	core/vm/instructions.go
#	core/vm/interpreter.go
#	core/vm/logger.go
This commit is contained in:
Matthieu Vachon 2023-09-12 14:07:11 -04:00
commit 8af369e181
9 changed files with 77 additions and 81 deletions

View file

@ -469,7 +469,7 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
st.state.AddBalance(st.msg.From, remaining, state.BalanceChangeGasRefund)
if st.evm.Config.Tracer != nil && st.gasRemaining > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxBuyBack)
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxLeftOverReturned)
}
// Also return remaining gas to the block gas counter so it is

View file

@ -163,7 +163,7 @@ func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason)
if c.Gas < gas {
return false
}
if logger != nil {
if logger != nil && reason != GasChangeIgnored {
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
}
c.Gas -= gas

View file

@ -181,26 +181,10 @@ func (evm *EVM) SetBlockContext(blockCtx BlockContext) {
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
// Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) { // Lazy evaluation of the parameters
if leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
evm.Config.Tracer.CaptureEnd(ret, startGas-leftOverGas, err)
}(gas)
} else {
// Handle tracer events for entering and exiting a call frame
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) {
if leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err)
}(gas)
}
evm.captureBegin(evm.depth == 0, CALL, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.captureEnd(evm.depth == 0, CALL, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
@ -269,13 +253,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
evm.captureBegin(false, CALLCODE, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
if leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err)
evm.captureEnd(false, CALLCODE, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
@ -328,13 +308,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// that caller is something other than a Contract.
parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
evm.captureBegin(false, DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
defer func(startGas uint64) {
if leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err)
evm.captureEnd(false, DELEGATECALL, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
@ -374,13 +350,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
evm.captureBegin(false, STATICCALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
if leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err)
evm.captureEnd(false, STATICCALL, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
@ -445,25 +417,10 @@ func (c *codeAndHash) Hash() common.Hash {
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
if evm.Config.Tracer != nil {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) {
if leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
evm.Config.Tracer.CaptureEnd(ret, startGas-leftOverGas, err)
}(gas)
} else {
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) {
if leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err)
}(gas)
}
evm.captureBegin(evm.depth == 0, typ, caller.Address(), address, codeAndHash.code, gas, value)
defer func(startGas uint64) {
evm.captureEnd(evm.depth == 0, typ, startGas, leftOverGas, ret, err)
}(gas)
}
// Depth check execution. Fail if we're trying to execute above the
// limit.
@ -561,3 +518,29 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *
// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) {
tracer := evm.Config.Tracer
if isRoot {
tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value)
} else {
tracer.CaptureEnter(typ, from, to, input, startGas, value)
}
tracer.OnGasChange(0, startGas, GasChangeCallInitialBalance)
}
func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) {
tracer := evm.Config.Tracer
if leftOverGas != 0 {
tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned)
}
if isRoot {
tracer.CaptureEnd(ret, startGas-leftOverGas, err)
} else {
tracer.CaptureExit(ret, startGas-leftOverGas, err)
}
}

View file

@ -613,7 +613,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
scope.Stack.push(&stackvalue)
if interpreter.evm.Config.Tracer != nil {
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
}
@ -658,7 +658,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
}
scope.Stack.push(&stackvalue)
if interpreter.evm.Config.Tracer != nil {
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
}
@ -708,7 +708,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
if interpreter.evm.Config.Tracer != nil {
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
}
@ -748,7 +748,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
if interpreter.evm.Config.Tracer != nil {
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
}
@ -781,7 +781,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
if interpreter.evm.Config.Tracer != nil {
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
}
@ -814,7 +814,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
if interpreter.evm.Config.Tracer != nil {
if interpreter.evm.Config.Tracer != nil && returnGas > 0 {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
}

View file

@ -185,9 +185,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeCallOpCode) {
if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeIgnored) {
return nil, ErrOutOfGas
}
if operation.dynamicGas != nil {
// All ops with a dynamic memory usage also has a dynamic gas cost.
var memorySize uint64
@ -211,11 +212,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
cost += dynamicCost // for tracing
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeCallOpCode) {
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeIgnored) {
return nil, ErrOutOfGas
}
// Do tracing before memory expansion
if debug {
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode)
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
@ -223,9 +226,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
mem.Resize(memorySize)
}
} else if debug {
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode)
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
// execute the operation
res, err = operation.execute(&pc, in, callContext)
if err != nil {

View file

@ -63,27 +63,31 @@ const (
// one such gas change per transaction.
GasChangeTxInitialBalance
// GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
// always exactly one of those per transaction
// always exactly one of those per transaction.
GasChangeTxIntrinsicGas
// GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared)
// this generates an increase in gas. There is only one such gas change per transaction.
// this generates an increase in gas. There is at most one of such gas change per transaction.
GasChangeTxRefunds
// GasChangeTxBuyBack is the amount of gas that will be bought back by the chain and returned in Wei to the caller at the very
// end of the transaction's execution. There is only one such gas change per transaction.
GasChangeTxBuyBack
// GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned
// to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas
// left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller.
// There is at most one of such gas change per transaction.
GasChangeTxLeftOverReturned
// GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
// one such gas change per call.
GasChangeCallInitialBalance
// GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always
// be a negative change as we "drain" left over gas towards 0.
// be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even
// will be emitted.
GasChangeCallLeftOverReturned
// GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it
// executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child.
// If there was no gas left to be refunded, no such even will be emitted.
GasChangeCallLeftOverRefunded
// GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules.
// GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE.
GasChangeCallContractCreation
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2, today controlled by EIP150 rules.
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2.
GasChangeCallContractCreation2
// GasChangeCallCodeStorage is the amount of gas that will be charged for code storage.
GasChangeCallCodeStorage
@ -96,4 +100,8 @@ const (
GasChangeCallStorageColdAccess
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
GasChangeCallFailedExecution
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
// it will be "manually" tracked by a direct emit of the gas change event.
GasChangeIgnored GasChangeReason = 0xFF
)

View file

@ -782,7 +782,7 @@ func (f *Firehose) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
// Ref eb1916a67d9bea03df16a7a3e2cfac72
if reason == vm.GasChangeTxInitialBalance ||
reason == vm.GasChangeTxRefunds ||
reason == vm.GasChangeTxBuyBack ||
reason == vm.GasChangeTxLeftOverReturned ||
reason == vm.GasChangeCallInitialBalance ||
reason == vm.GasChangeCallLeftOverReturned {
return
@ -1128,12 +1128,12 @@ var gasChangeReasonToPb = map[vm.GasChangeReason]pbeth.GasChange_Reason{
//
// vm.GasChangeTxInitialBalance: pbeth.GasChange_REASON_TX_INITIAL_BALANCE,
// vm.GasChangeTxRefunds: pbeth.GasChange_REASON_TX_REFUNDS,
// vm.GasChangeTxBuyBack: pbeth.GasChange_REASON_TX_BUY_BACK,
// vm.GasChangeTxLeftOverReturned: pbeth.GasChange_REASON_TX_LEFT_OVER_RETURNED,
// vm.GasChangeCallInitialBalance: pbeth.GasChange_REASON_CALL_INITIAL_BALANCE,
// vm.GasChangeCallLeftOverReturned: pbeth.GasChange_REASON_CALL_LEFT_OVER_RETURNED,
vm.GasChangeTxInitialBalance: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeTxRefunds: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeTxBuyBack: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeTxLeftOverReturned: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeCallInitialBalance: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeCallLeftOverReturned: pbeth.GasChange_REASON_UNKNOWN,

2
go.mod
View file

@ -56,7 +56,7 @@ require (
github.com/rs/cors v1.7.0
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.2.0
github.com/streamingfast/firehose-ethereum/types v0.0.0-20230911154550-654ed552f285
github.com/streamingfast/firehose-ethereum/types v0.0.0-20230912180331-359d900a4e1e
github.com/stretchr/testify v1.8.1
github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7

4
go.sum
View file

@ -679,8 +679,8 @@ github.com/streamingfast/dstore v0.1.1-0.20220607202639-35118aeaf648 h1:xpy3HNXe
github.com/streamingfast/dstore v0.1.1-0.20220607202639-35118aeaf648/go.mod h1:SHSEIPowGeE1TfNNmGeAUUnlO3dwevmX5kFOSazU60M=
github.com/streamingfast/eth-go v0.0.0-20220421042603-ebe2c94fcc92 h1:jMLyqSyOP0Wf1wbC0qhTW8GdFr+NM5WBwBzNc74TAk8=
github.com/streamingfast/eth-go v0.0.0-20220421042603-ebe2c94fcc92/go.mod h1:GSWB6tldvuaUekmehj7yCv/8o6MIWPb+9Hf+m4uZdDM=
github.com/streamingfast/firehose-ethereum/types v0.0.0-20230911154550-654ed552f285 h1:CHi8MSyRFe4ngApM2XbR5AueTd/6pSxAvzxFhqdy08Q=
github.com/streamingfast/firehose-ethereum/types v0.0.0-20230911154550-654ed552f285/go.mod h1:Kh5fptEEMKVw/QXrdfr49ldY3SP5+/MJoUPQfUtByUU=
github.com/streamingfast/firehose-ethereum/types v0.0.0-20230912180331-359d900a4e1e h1:H7Re+AlOEonzy9/KxHa0dj6oswXjCDU8eODHG337jUU=
github.com/streamingfast/firehose-ethereum/types v0.0.0-20230912180331-359d900a4e1e/go.mod h1:Kh5fptEEMKVw/QXrdfr49ldY3SP5+/MJoUPQfUtByUU=
github.com/streamingfast/jsonpb v0.0.0-20210811021341-3670f0aa02d0 h1:g8eEYbFSykyzIyuxNMmHEUGGUvJE0ivmqZagLDK42gw=
github.com/streamingfast/jsonpb v0.0.0-20210811021341-3670f0aa02d0/go.mod h1:cTNObq2Uofb330y05JbbZZ6RwE6QUXw5iVcHk1Fx3fk=
github.com/streamingfast/logging v0.0.0-20210811175431-f3b44b61606a/go.mod h1:4GdqELhZOXj4xwc4IaBmzofzdErGynnaSzuzxy0ZIBo=