Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tracer: expose gasLimit and postTxGas to tracers #24542

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"math"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
Expand Down Expand Up @@ -287,15 +288,15 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
if err := st.preCheck(); err != nil {
return nil, err
}
msg := st.msg
sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber)
contractCreation := msg.To() == nil
var (
msg = st.msg
sender = vm.AccountRef(msg.From())
contractCreation = msg.To() == nil
rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil)
)

// Check clauses 4-5, subtract intrinsic gas if everything is correct
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul)
if err != nil {
return nil, err
}
Expand All @@ -310,30 +311,38 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}

// Set up the initial access list.
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil); rules.IsBerlin {
if rules.IsBerlin {
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
}

var (
ret []byte
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
ret []byte
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
beforeRefund uint64
)
if st.evm.Config.Debug {
st.evm.Config.Tracer.CaptureStart(st.evm, sender.Address(), st.to(), contractCreation, st.data, st.initialGas, gas, st.value, rules)
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
st.evm.Config.Tracer.CaptureEnd(ret, startGas-beforeRefund, st.gas, time.Since(startTime), vmerr)
}(st.gas, time.Now())
}
if contractCreation {
ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
}

if !london {
beforeRefund = st.gas
if !rules.IsLondon {
// Before EIP-3529: refunds were capped to gasUsed / 2
st.refundGas(params.RefundQuotient)
} else {
// After EIP-3529: refunds are capped to gasUsed / 5
st.refundGas(params.RefundQuotientEIP3529)
}
effectiveTip := st.gasPrice
if london {
if rules.IsLondon {
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
}
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
Expand Down
21 changes: 4 additions & 17 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package vm
import (
"math/big"
"sync/atomic"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -181,10 +180,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
} else {
if evm.depth > 0 {
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.CaptureExit(ret, 0, nil)
}
Expand All @@ -197,12 +193,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas

// Capture the tracer start/end events in debug mode
if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}(gas, time.Now())
} else {
if evm.depth > 0 {
// Handle tracer events for entering and exiting a call frame
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
Expand Down Expand Up @@ -442,14 +433,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,

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

start := time.Now()

ret, err := evm.interpreter.Run(contract, nil, false)

// Check whether the max code size has been exceeded, assign err if the case.
Expand Down Expand Up @@ -486,9 +475,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}

if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
} else {
if evm.depth > 0 {
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
}
Expand Down
6 changes: 4 additions & 2 deletions core/vm/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

// EVMLogger is used to collect execution traces from an EVM transaction
Expand All @@ -29,10 +30,11 @@ import (
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type EVMLogger interface {
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gasLimit, intrinsicGas uint64, value *big.Int, rules params.Rules)
CaptureCreateTx(addr common.Address)
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)
CaptureExit(output []byte, gasUsed uint64, err error)
CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
CaptureEnd(output []byte, gasUsed, restGas uint64, t time.Duration, err error)
}
23 changes: 10 additions & 13 deletions eth/tracers/js/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
tracers2 "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"gopkg.in/olebedev/go-duktape.v3"
)

Expand Down Expand Up @@ -680,7 +680,7 @@ func wrapError(context string, err error) error {
}

// 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) {
func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gasLimit, intrinsicGas uint64, value *big.Int, rules params.Rules) {
jst.env = env
jst.ctx["type"] = "CALL"
if create {
Expand All @@ -689,25 +689,22 @@ func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
jst.ctx["from"] = from
jst.ctx["to"] = to
jst.ctx["input"] = input
jst.ctx["gas"] = gas
jst.ctx["gas"] = gasLimit - intrinsicGas
jst.ctx["gasPrice"] = env.TxContext.GasPrice
jst.ctx["value"] = value
jst.ctx["intrinsicGas"] = intrinsicGas

// Initialize the context
jst.ctx["block"] = env.Context.BlockNumber.Uint64()
// TODO: test statedb returns same values for tx sender as before
jst.dbWrapper.db = env.StateDB
// Update list of precompiles based on current block
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil)
jst.activePrecompiles = vm.ActivePrecompiles(rules)
}

// Compute intrinsic gas
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
if err != nil {
return
}
jst.ctx["intrinsicGas"] = intrinsicGas
// CaptureCreateTx is emitted for create transactions, after the contract is created.
func (jst *jsTracer) CaptureCreateTx(addr common.Address) {
jst.ctx["to"] = addr
}

// CaptureState implements the Tracer interface to trace a single step of VM execution.
Expand Down Expand Up @@ -761,7 +758,7 @@ func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, sco
}

// CaptureEnd is called after the call finishes to finalize the tracing.
func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
func (jst *jsTracer) CaptureEnd(output []byte, gasUsed, restGas uint64, t time.Duration, err error) {
jst.ctx["output"] = output
jst.ctx["time"] = t.String()
jst.ctx["gasUsed"] = gasUsed
Expand Down
8 changes: 6 additions & 2 deletions eth/tracers/logger/access_list_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)

// accessList is an accumulator for the set of accounts and storage slots an EVM
Expand Down Expand Up @@ -138,9 +139,11 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
}
}

func (a *AccessListTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (a *AccessListTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gasLimit, intrinsicGas uint64, value *big.Int, rules params.Rules) {
}

func (a *AccessListTracer) CaptureCreateTx(addr common.Address) {}

// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
stack := scope.Stack
Expand All @@ -167,7 +170,8 @@ func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}

func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed, restGas uint64, t time.Duration, err error) {
}

func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
Expand Down
44 changes: 26 additions & 18 deletions eth/tracers/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,13 @@ func (l *StructLogger) Reset() {
}

// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gasLimit, intrinsicGas uint64, value *big.Int, rules params.Rules) {
l.env = env
}

// CaptureCreateTx is emitted for create transactions, after the contract is created.
func (l *StructLogger) CaptureCreateTx(addr common.Address) {}

// CaptureState logs a new structured log message and pushes it out to the environment
//
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
Expand Down Expand Up @@ -207,7 +210,7 @@ func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, s
}

// CaptureEnd is called after the call finishes to finalize the tracing.
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
func (l *StructLogger) CaptureEnd(output []byte, gasUsed, restGas uint64, t time.Duration, err error) {
l.output = output
l.err = err
if l.cfg.Debug {
Expand Down Expand Up @@ -280,9 +283,10 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
}

type mdLogger struct {
out io.Writer
cfg *Config
env *vm.EVM
out io.Writer
cfg *Config
env *vm.EVM
header string
}

// NewMarkdownLogger creates a logger which outputs information in a format adapted
Expand All @@ -295,22 +299,26 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
return l
}

func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gasLimit, intrinsicGas uint64, value *big.Int, rules params.Rules) {
t.env = env
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)
gas := gasLimit - intrinsicGas
getHeader := func(from string, to string, input []byte, gas uint64, value *big.Int) string {
return fmt.Sprintf("From: `%v`\n%s\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n"+`
| Pc | Op | Cost | Stack | RStack | Refund |
|-------|-------------|------|-----------|-----------|---------|
`, from, to, input, gas, value)
}
if create {
// To address will be filled in CaptureCreateTx
t.header = getHeader(from.String(), "Create at: `%v`", 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.Fprint(t.out, getHeader(from.String(), fmt.Sprintf("To: `%v`", to.String()), input, gas, value))
}
}

fmt.Fprintf(t.out, `
| Pc | Op | Cost | Stack | RStack | Refund |
|-------|-------------|------|-----------|-----------|---------|
`)
// CaptureCreateTx is emitted for create transactions, after the contract is created.
func (t *mdLogger) CaptureCreateTx(addr common.Address) {
fmt.Fprintf(t.out, t.header, addr.String())
}

// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
Expand Down Expand Up @@ -338,7 +346,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}

func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
func (t *mdLogger) CaptureEnd(output []byte, gasUsed, restGas uint64, tm time.Duration, err error) {
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
Expand Down
7 changes: 5 additions & 2 deletions eth/tracers/logger/logger_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)

type JSONLogger struct {
Expand All @@ -43,10 +44,12 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
return l
}

func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gasLimit, intrinsicGas uint64, value *big.Int, rules params.Rules) {
l.env = env
}

func (l *JSONLogger) CaptureCreateTx(addr common.Address) {}

func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.ScopeContext, depth int, err error) {
// TODO: Add rData to this interface as well
l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
Expand Down Expand Up @@ -80,7 +83,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
}

// CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed, restGas uint64, t time.Duration, err error) {
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
Expand Down
Loading