From 480f0cfa6c2ca88fe70a788bf7366514efc4e2e1 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 15 Mar 2022 13:04:20 +0100 Subject: [PATCH 1/7] core: update EVMLogger interface --- core/vm/logger.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/vm/logger.go b/core/vm/logger.go index 3af5aec19932..d2eef797020d 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -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 @@ -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) } From 69372a9096f79224ba75e7f89c0b796542f363d1 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 15 Mar 2022 13:24:54 +0100 Subject: [PATCH 2/7] core: add start,end hooks to state transition --- core/state_transition.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 05d5633075b9..6faf25f01a33 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -20,6 +20,7 @@ import ( "fmt" "math" "math/big" + "time" "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" @@ -293,6 +294,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber) london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) 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) @@ -313,10 +315,18 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil); 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 { @@ -324,7 +334,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { 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) } - + beforeRefund = st.gas if !london { // Before EIP-3529: refunds were capped to gasUsed / 2 st.refundGas(params.RefundQuotient) From d505dd893b7f133bff4ee42b34c8b7ceda3e8458 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 15 Mar 2022 13:44:11 +0100 Subject: [PATCH 3/7] core: rm start,end hooks from evm, add createTx hook --- core/vm/evm.go | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index dd55618bf812..e8142c994623 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -19,7 +19,6 @@ package vm import ( "math/big" "sync/atomic" - "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -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) } @@ -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) { @@ -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. @@ -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) } } From 04a2265d5ab13481fa096f53b11820d2e3781859 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 15 Mar 2022 16:39:25 +0100 Subject: [PATCH 4/7] simply transitionDb --- core/state_transition.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 6faf25f01a33..14fa65b50396 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -288,16 +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 - rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != 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 } @@ -312,7 +311,7 @@ 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()) } @@ -335,7 +334,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) } beforeRefund = st.gas - if !london { + if !rules.IsLondon { // Before EIP-3529: refunds were capped to gasUsed / 2 st.refundGas(params.RefundQuotient) } else { @@ -343,7 +342,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { 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)) From 659507e0abab8042b0b41685a6f46908a924fa1f Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 15 Mar 2022 18:48:40 +0100 Subject: [PATCH 5/7] update loggers to use new interface --- eth/tracers/logger/access_list_tracer.go | 8 +++-- eth/tracers/logger/logger.go | 44 ++++++++++++++---------- eth/tracers/logger/logger_json.go | 7 ++-- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 181fc47acb22..e0a06da951c7 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -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 @@ -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 @@ -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) { } diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 8461935822d8..974fe1b21546 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -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. @@ -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 { @@ -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 @@ -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. @@ -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) } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 4a7abacba249..d4373f7fb3af 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -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 { @@ -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) @@ -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"` From 594287fb50364f6e168e2b2c2428f09f2da01270 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 15 Mar 2022 18:54:04 +0100 Subject: [PATCH 6/7] update js tracer to use new interface --- eth/tracers/js/tracer.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/eth/tracers/js/tracer.go b/eth/tracers/js/tracer.go index 30c5c2cf149a..08cd60eb745b 100644 --- a/eth/tracers/js/tracer.go +++ b/eth/tracers/js/tracer.go @@ -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" ) @@ -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 { @@ -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. @@ -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 From 7aa3246ba95874000949e2a055bfd09a79bc0452 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 15 Mar 2022 19:07:15 +0100 Subject: [PATCH 7/7] update native tracers to use new interface --- eth/tracers/native/4byte.go | 9 +++++--- eth/tracers/native/call.go | 16 ++++++++------ eth/tracers/native/noop.go | 8 +++++-- eth/tracers/native/prestate.go | 39 ++++++++++++++++------------------ 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index ad1d89071c52..639fbf8d5082 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) func init() { @@ -79,11 +80,10 @@ func (t *fourByteTracer) store(id []byte, size int) { } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *fourByteTracer) 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 // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil) t.activePrecompiles = vm.ActivePrecompiles(rules) // Save the outer calldata also @@ -92,6 +92,9 @@ func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to commo } } +// CaptureCreateTx is emitted for create transactions, after the contract is created. +func (t *fourByteTracer) CaptureCreateTx(addr common.Address) {} + // CaptureState implements the EVMLogger interface to trace a single step of VM execution. func (t *fourByteTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } @@ -128,7 +131,7 @@ func (t *fourByteTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *fourByteTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { +func (t *fourByteTracer) CaptureEnd(output []byte, gasUsed, restGas uint64, _ time.Duration, err error) { } // GetResult returns the json-encoded nested list of call traces, and any diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 08dc76aa6174..168f9843fca6 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) func init() { @@ -63,23 +64,26 @@ func newCallTracer() tracers.Tracer { } // 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) { +func (t *callTracer) 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 t.callstack[0] = callFrame{ Type: "CALL", From: addrToHex(from), To: addrToHex(to), Input: bytesToHex(input), - Gas: uintToHex(gas), + Gas: uintToHex(gasLimit - intrinsicGas), Value: bigToHex(value), } - if create { - t.callstack[0].Type = "CREATE" - } +} + +// CaptureCreateTx is emitted for create transactions, after the contract is created. +func (t *callTracer) CaptureCreateTx(addr common.Address) { + t.callstack[0].Type = "CREATE" + t.callstack[0].To = addrToHex(addr) } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { +func (t *callTracer) CaptureEnd(output []byte, gasUsed, restGas uint64, _ time.Duration, err error) { t.callstack[0].GasUsed = uintToHex(gasUsed) if err != nil { t.callstack[0].Error = err.Error() diff --git a/eth/tracers/native/noop.go b/eth/tracers/native/noop.go index 15b7dbccb7cf..588a89b206a4 100644 --- a/eth/tracers/native/noop.go +++ b/eth/tracers/native/noop.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) func init() { @@ -40,11 +41,14 @@ func newNoopTracer() tracers.Tracer { } // 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) { +func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gasLimit, intrinsicGas uint64, value *big.Int, rules params.Rules) { } +// CaptureCreateTx is emitted for create transactions, after the contract is created. +func (t *noopTracer) CaptureCreateTx(addr common.Address) {} + // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { +func (t *noopTracer) CaptureEnd(output []byte, gasUsed, restGas uint64, _ time.Duration, err error) { } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 598663ac81c0..a577c67caa9a 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -24,10 +24,10 @@ 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" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" ) func init() { @@ -58,28 +58,21 @@ func newPrestateTracer() tracers.Tracer { } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *prestateTracer) 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 t.create = create t.to = to - // Compute intrinsic gas - isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber) - isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber) - intrinsicGas, err := core.IntrinsicGas(input, nil, create, isHomestead, isIstanbul) - if err != nil { - return - } - t.lookupAccount(from) - t.lookupAccount(to) - - // The recipient balance includes the value transferred. - toBal := hexutil.MustDecodeBig(t.prestate[to].Balance) - toBal = new(big.Int).Sub(toBal, value) - t.prestate[to].Balance = hexutil.EncodeBig(toBal) + if !create { + t.lookupAccount(to) + // The recipient balance includes the value transferred. + toBal := hexutil.MustDecodeBig(t.prestate[to].Balance) + toBal = new(big.Int).Sub(toBal, value) + t.prestate[to].Balance = hexutil.EncodeBig(toBal) + } - // The sender balance is after reducing: value, gasLimit, intrinsicGas. + // The sender balance is after reducing: gasLimit, intrinsicGas. // We need to re-add them to get the pre-tx balance. fromBal := hexutil.MustDecodeBig(t.prestate[from].Balance) gasPrice := env.TxContext.GasPrice @@ -87,16 +80,20 @@ func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to commo gasPrice, new(big.Int).Add( new(big.Int).SetUint64(intrinsicGas), - new(big.Int).SetUint64(gas), + new(big.Int).SetUint64(gasLimit), ), ) - fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas)) + fromBal.Add(fromBal, consumedGas) t.prestate[from].Balance = hexutil.EncodeBig(fromBal) - t.prestate[from].Nonce-- +} + +// CaptureCreateTx is emitted for create transactions, after the contract is created. +func (t *prestateTracer) CaptureCreateTx(addr common.Address) { + t.to = addr } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { +func (t *prestateTracer) CaptureEnd(output []byte, gasUsed, restGas uint64, _ time.Duration, err error) { if t.create { // Exclude created contract. delete(t.prestate, t.to)