From e0af166ffad5ccfbe5a9eb2382efb8e7c1f69cbe Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Wed, 2 Aug 2023 10:26:56 -0400 Subject: [PATCH 01/11] Full OnGasConsumed loop and added `GasChangeReason` With this change, the full gas loop of a transaction can be tracked going from initial balance (`gasLimit`) back down to 0. I validated on my regression test suite that those conditions applied to all transactions I have: - First `OnGasConsumed` is always going from 0 -> `trx.gasLimit` - Last `OnGasConsumed` "new value" is always 0 - trx.gasLimit - last balance of last `OnGasConsumed` is equal to `trx.gasUsed` The addition of `reason` make it possible for a logger to filter out unwanted signal, for example one could want to drop all OpCode related gas change and only cares about "transaction" level. The reason is also a good thing for visibility and for creating powerful debug tools for Ethereum transaction execution. Also, I would like that we change the `OnGasConsumed` logic from `OnGasConsumed(actual, cost uint64, reason)` to `OnGasConsumed(old, new uint64, reason)`. With the new tracing I've done, we now have negative `cost` that needs to be passed around but the `cost` is `uint64` so it creates overflow. Everything works fine if you record the new/old value `gas-cost` as the final value goes back to a valid range. But it's weird for consumer that would log `cost` that it's `18127127187219...`. An alternative would be to accept a `int64` for the cost which is also valid, not full coverage theorically but I don't see why an operation would cost more than i64 max value. Maybe it make more sense than to have signature `OnGasConsumed(gas uint64, cost int64, reason)`, I'm fine with it also, I leave the decision to you. --- core/state_transition.go | 16 +++++++- core/vm/contract.go | 4 +- core/vm/contracts.go | 2 +- core/vm/evm.go | 34 +++++++++++++++- core/vm/instructions.go | 34 +++++++++++++++- core/vm/interpreter.go | 4 +- core/vm/logger.go | 49 +++++++++++++++++++++++- core/vm/operations_acl.go | 2 +- eth/tracers/logger/access_list_tracer.go | 2 +- eth/tracers/logger/logger.go | 4 +- eth/tracers/logger/logger_json.go | 2 +- eth/tracers/native/mux.go | 4 +- eth/tracers/noop.go | 2 +- eth/tracers/printer.go | 2 +- 14 files changed, 141 insertions(+), 20 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 645fe669f2..cd406c0107 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -263,6 +263,11 @@ func (st *StateTransition) buyGas() error { if err := st.gp.SubGas(st.msg.GasLimit); err != nil { return err } + + if st.evm.Config.Tracer != nil { + st.evm.Config.Tracer.OnGasConsumed(0, -st.msg.GasLimit, vm.GasInitialBalance) + } + st.gasRemaining += st.msg.GasLimit st.initialGas = st.msg.GasLimit @@ -386,7 +391,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } if t := st.evm.Config.Tracer; t != nil { - t.OnGasConsumed(st.gasRemaining, gas) + t.OnGasConsumed(st.gasRemaining, gas, vm.GasChangeIntrinsicGas) } st.gasRemaining -= gas @@ -452,12 +457,21 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { if refund > st.state.GetRefund() { refund = st.state.GetRefund() } + + if st.evm.Config.Tracer != nil { + st.evm.Config.Tracer.OnGasConsumed(st.gasRemaining, -refund, vm.GasRefunded) + } + st.gasRemaining += refund // Return ETH for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) st.state.AddBalance(st.msg.From, remaining, state.BalanceChangeGasRefund) + if st.evm.Config.Tracer != nil { + st.evm.Config.Tracer.OnGasConsumed(st.gasRemaining, st.gasRemaining, vm.GasBuyBack) + } + // Also return remaining gas to the block gas counter so it is // available for the next transaction. st.gp.AddGas(st.gasRemaining) diff --git a/core/vm/contract.go b/core/vm/contract.go index 1d88b0e1b9..6875e01635 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -159,12 +159,12 @@ func (c *Contract) Caller() common.Address { } // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas uint64, logger EVMLogger) (ok bool) { +func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) (ok bool) { if c.Gas < gas { return false } if logger != nil { - logger.OnGasConsumed(c.Gas, gas) + logger.OnGasConsumed(c.Gas, gas, reason) } c.Gas -= gas return true diff --git a/core/vm/contracts.go b/core/vm/contracts.go index cb4cc76c02..662bce1b07 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -174,7 +174,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin return nil, 0, ErrOutOfGas } if logger != nil { - logger.OnGasConsumed(suppliedGas, gasCost) + logger.OnGasConsumed(suppliedGas, gasCost, GasChangePrecompiledContract) } suppliedGas -= gasCost output, err := p.Run(input) diff --git a/core/vm/evm.go b/core/vm/evm.go index beadc501f1..534ef5a5a6 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -184,7 +184,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, 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.OnGasConsumed(0, -gas, GasInitialBalance) defer func(startGas uint64) { + evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -233,6 +235,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } // TODO: consider clearing up unused snapshots: @@ -253,7 +259,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // 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.OnGasConsumed(0, -gas, GasInitialBalance) defer func(startGas uint64) { + evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -285,6 +293,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } } @@ -304,7 +316,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by parent := caller.(*Contract) // DELEGATECALL inherits value from parent call evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) + evm.Config.Tracer.OnGasConsumed(0, -gas, GasInitialBalance) defer func(startGas uint64) { + evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -328,6 +342,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } } @@ -342,7 +360,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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.OnGasConsumed(0, -gas, GasInitialBalance) defer func(startGas uint64) { + evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -383,6 +403,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } } @@ -411,7 +435,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, }() } else { evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) + evm.Config.Tracer.OnGasConsumed(0, -gas, GasInitialBalance) defer func() { + evm.Config.Tracer.OnGasConsumed(leftoverGas, leftoverGas, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err) }() } @@ -437,6 +463,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Ensure there's no existing contract already at the designated address contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state @@ -470,7 +500,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // by the error checking condition below. if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas, evm.Config.Tracer) { + if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -483,7 +513,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas, evm.Config.Tracer) + contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeFailedExecution) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 2e5fa9903c..5a718992db 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -592,7 +592,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // reuse size int for stackvalue stackvalue := size - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 if !value.IsZero() { @@ -612,6 +612,11 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b stackvalue.SetBytes(addr.Bytes()) } scope.Stack.push(&stackvalue) + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { @@ -635,7 +640,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ) // Apply EIP150 gas -= gas / 64 - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation2) // reuse size int for stackvalue stackvalue := size //TODO: use uint256.Int instead of converting with toBig() @@ -652,6 +657,11 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] stackvalue.SetBytes(addr.Bytes()) } scope.Stack.push(&stackvalue) + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { @@ -697,6 +707,11 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -732,6 +747,11 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -760,6 +780,11 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -788,6 +813,11 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 14aa9cee21..31ad6d142b 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -185,7 +185,7 @@ 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) { + if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeOpCode) { return nil, ErrOutOfGas } if operation.dynamicGas != nil { @@ -211,7 +211,7 @@ 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) { + if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeOpCode) { return nil, ErrOutOfGas } // Do tracing before memory expansion diff --git a/core/vm/logger.go b/core/vm/logger.go index 0408411500..1f2469777d 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -43,5 +43,52 @@ type EVMLogger interface { CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) CaptureKeccakPreimage(hash common.Hash, data []byte) // Misc - OnGasConsumed(gas, amount uint64) + OnGasConsumed(gas, amount uint64, reason GasChangeReason) } + +// GasChangeReason is used to indicate the reason for a gas change, useful +// for tracing and reporting. +type GasChangeReason byte + +const ( + GasChangeUnspecified GasChangeReason = iota + + // GasInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call + GasInitialBalance + // GasRefunded is the amount of gas that will be refunded to the caller for data returned to the chain + // PR Review: Is that the right description? Called in core/state_transition.go#StateTransition.refundGas + GasRefunded + // GasBuyBack is the amount of gas that will be bought back by the chain and returned in Wei to the caller + GasBuyBack + // GasChangeIntrinsicGas 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 + GasChangeIntrinsicGas + + // PR Review: `GasChangeContractCreation/2` are actually the EIP150 burn cost of CREATE/CREATE2 respectively. + // I think our old name `GasChangeContractCreation/2` is not really accurate. I don't + // think that using EIP150 as a name is a good idea as rules can change in the future. So + // maybe we can just call them `GasChangeCreateBurn/GasChangeCreate2Burn`? Burn might + // feels like the wrong term, `Stipend` came to mind also but I'm unsure. + // + // I would like also to keep the distinction between CREATE and CREATE2 however, it's an + // important thing IMO as they are different and could use different rules in the future. + + // GasChangeContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules + GasChangeContractCreation + // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2, today controlled by EIP150 rules + GasChangeContractCreation2 + // GasChangeCodeStorage is the amount of gas that will be charged for code storage + GasChangeCodeStorage + // GasChangeOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was + // performed can be check by `CaptureState` handling + GasChangeOpCode + // GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution + GasChangePrecompiledContract + // GasChangeStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + GasChangeStorageColdAccess + + // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the caller after the execution of the call, if there is left over at the end of execution + GasChangeCallLeftOverRefunded + // GasChangeFailedExecution is the burning of the remaining gas when the execution failed without a revert + GasChangeFailedExecution +) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 6dcd02bc05..00f46395fe 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -169,7 +169,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call - if !contract.UseGas(coldCost, evm.Config.Tracer) { + if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeStorageColdAccess) { return 0, ErrOutOfGas } } diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index ff7715f2ee..9c5e1d85c0 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -163,7 +163,7 @@ func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, func (*AccessListTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (*AccessListTracer) OnGasConsumed(gas, amount uint64) {} +func (*AccessListTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {} diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 5885782474..a653b57f7b 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -219,7 +219,7 @@ func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, s // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (l *StructLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (l *StructLogger) OnGasConsumed(gas, amount uint64) {} +func (l *StructLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} // CaptureEnd is called after the call finishes to finalize the tracing. func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { @@ -410,7 +410,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope func (t *mdLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (t *mdLogger) OnGasConsumed(gas, amount uint64) {} +func (t *mdLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index c1e25a1c3a..17e1777c31 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -81,7 +81,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (l *JSONLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (l *JSONLogger) OnGasConsumed(gas, amount uint64) {} +func (l *JSONLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} // CaptureEnd is triggered at end of execution. func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 2dcb8f2b2d..9476ab55f0 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -96,9 +96,9 @@ func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { } // CaptureGasConsumed is called when gas is consumed. -func (t *muxTracer) OnGasConsumed(gas, amount uint64) { +func (t *muxTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) { for _, t := range t.tracers { - t.OnGasConsumed(gas, amount) + t.OnGasConsumed(gas, amount, reason) } } diff --git a/eth/tracers/noop.go b/eth/tracers/noop.go index 695c99c84b..1e6ba04dd5 100644 --- a/eth/tracers/noop.go +++ b/eth/tracers/noop.go @@ -59,7 +59,7 @@ func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ * func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} // OnGasConsumed is called when gas is consumed. -func (t *NoopTracer) OnGasConsumed(gas, amount uint64) {} +func (t *NoopTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { diff --git a/eth/tracers/printer.go b/eth/tracers/printer.go index a07a7ea02b..018ac01d11 100644 --- a/eth/tracers/printer.go +++ b/eth/tracers/printer.go @@ -120,6 +120,6 @@ func (p *Printer) OnNewAccount(a common.Address) { fmt.Printf("OnNewAccount: a=%v\n", a) } -func (p *Printer) OnGasConsumed(gas, amount uint64) { +func (p *Printer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) { fmt.Printf("OnGasConsumed: gas=%v, amount=%v\n", gas, amount) } From 01ccd7126eb8a0d64792860b675ddff727ada87b Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Mon, 28 Aug 2023 15:29:22 -0400 Subject: [PATCH 02/11] Change `OnGasConsumed(gas, cost uint64, reason)` to `OnGasChange(old, new uint64, reason)` This way, we avoid having a `cost` that is negative which does not make sense for a `uint64`. Having the `old, new` also yields correct value and the delta can then be negative and be holded in a `int64`. --- core/state_transition.go | 8 +++---- core/vm/contract.go | 2 +- core/vm/contracts.go | 2 +- core/vm/evm.go | 30 ++++++++++++------------ core/vm/instructions.go | 12 +++++----- core/vm/logger.go | 2 +- eth/tracers/logger/access_list_tracer.go | 2 +- eth/tracers/logger/logger.go | 4 ++-- eth/tracers/logger/logger_json.go | 2 +- eth/tracers/native/mux.go | 4 ++-- eth/tracers/noop.go | 4 ++-- eth/tracers/printer.go | 4 ++-- 12 files changed, 38 insertions(+), 38 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index cd406c0107..99e04a7fbb 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -265,7 +265,7 @@ func (st *StateTransition) buyGas() error { } if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasConsumed(0, -st.msg.GasLimit, vm.GasInitialBalance) + st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasInitialBalance) } st.gasRemaining += st.msg.GasLimit @@ -391,7 +391,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } if t := st.evm.Config.Tracer; t != nil { - t.OnGasConsumed(st.gasRemaining, gas, vm.GasChangeIntrinsicGas) + t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeIntrinsicGas) } st.gasRemaining -= gas @@ -459,7 +459,7 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { } if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasConsumed(st.gasRemaining, -refund, vm.GasRefunded) + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasRefunded) } st.gasRemaining += refund @@ -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.evm.Config.Tracer.OnGasConsumed(st.gasRemaining, st.gasRemaining, vm.GasBuyBack) + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasBuyBack) } // Also return remaining gas to the block gas counter so it is diff --git a/core/vm/contract.go b/core/vm/contract.go index 6875e01635..f2cdb40a4c 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -164,7 +164,7 @@ func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) return false } if logger != nil { - logger.OnGasConsumed(c.Gas, gas, reason) + logger.OnGasChange(c.Gas, c.Gas-gas, reason) } c.Gas -= gas return true diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 662bce1b07..9f7b4e44d4 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -174,7 +174,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin return nil, 0, ErrOutOfGas } if logger != nil { - logger.OnGasConsumed(suppliedGas, gasCost, GasChangePrecompiledContract) + logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangePrecompiledContract) } suppliedGas -= gasCost output, err := p.Run(input) diff --git a/core/vm/evm.go b/core/vm/evm.go index 534ef5a5a6..15addfa6ab 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -184,9 +184,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, 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.OnGasConsumed(0, -gas, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -236,7 +236,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) } gas = 0 @@ -259,9 +259,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // 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.OnGasConsumed(0, -gas, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -294,7 +294,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) } gas = 0 @@ -316,9 +316,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by parent := caller.(*Contract) // DELEGATECALL inherits value from parent call evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) - evm.Config.Tracer.OnGasConsumed(0, -gas, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -343,7 +343,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) } gas = 0 @@ -360,9 +360,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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.OnGasConsumed(0, -gas, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -404,7 +404,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) } gas = 0 @@ -435,9 +435,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, }() } else { evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) - evm.Config.Tracer.OnGasConsumed(0, -gas, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance) defer func() { - evm.Config.Tracer.OnGasConsumed(leftoverGas, leftoverGas, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasBuyBack) evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err) }() } @@ -464,7 +464,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) } return nil, common.Address{}, 0, ErrContractAddressCollision diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 5a718992db..0d5b253951 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -614,7 +614,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -659,7 +659,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -709,7 +709,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -749,7 +749,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -782,7 +782,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -815,7 +815,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas diff --git a/core/vm/logger.go b/core/vm/logger.go index 1f2469777d..70da7ba9f1 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -43,7 +43,7 @@ type EVMLogger interface { CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) CaptureKeccakPreimage(hash common.Hash, data []byte) // Misc - OnGasConsumed(gas, amount uint64, reason GasChangeReason) + OnGasChange(old, new uint64, reason GasChangeReason) } // GasChangeReason is used to indicate the reason for a gas change, useful diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 9c5e1d85c0..0c246bef4c 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -163,7 +163,7 @@ func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, func (*AccessListTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (*AccessListTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} +func (*AccessListTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {} func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {} diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index a653b57f7b..3858bbd8f7 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -219,7 +219,7 @@ func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, s // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (l *StructLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (l *StructLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} +func (l *StructLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {} // CaptureEnd is called after the call finishes to finalize the tracing. func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { @@ -410,7 +410,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope func (t *mdLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (t *mdLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} +func (t *mdLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {} func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 17e1777c31..6e687c7e64 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -81,7 +81,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (l *JSONLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (l *JSONLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} +func (l *JSONLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {} // CaptureEnd is triggered at end of execution. func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 9476ab55f0..da2381b880 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -96,9 +96,9 @@ func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { } // CaptureGasConsumed is called when gas is consumed. -func (t *muxTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) { +func (t *muxTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) { for _, t := range t.tracers { - t.OnGasConsumed(gas, amount, reason) + t.OnGasChange(old, new, reason) } } diff --git a/eth/tracers/noop.go b/eth/tracers/noop.go index 1e6ba04dd5..428c13fef4 100644 --- a/eth/tracers/noop.go +++ b/eth/tracers/noop.go @@ -58,8 +58,8 @@ func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ * // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -// OnGasConsumed is called when gas is consumed. -func (t *NoopTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} +// OnGasChange is called when gas is either consumed or refunded. +func (t *NoopTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { diff --git a/eth/tracers/printer.go b/eth/tracers/printer.go index 018ac01d11..3f82107dad 100644 --- a/eth/tracers/printer.go +++ b/eth/tracers/printer.go @@ -120,6 +120,6 @@ func (p *Printer) OnNewAccount(a common.Address) { fmt.Printf("OnNewAccount: a=%v\n", a) } -func (p *Printer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) { - fmt.Printf("OnGasConsumed: gas=%v, amount=%v\n", gas, amount) +func (p *Printer) OnGasChange(old, new uint64, reason vm.GasChangeReason) { + fmt.Printf("OnGasChange: old=%v, new=%v, diff=%v\n", old, new, new-old) } From 216a4b0f5a44504ef24a6e844a6cde058f4d82dc Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Wed, 30 Aug 2023 07:45:00 -0400 Subject: [PATCH 03/11] Added writing of `genesis.Alloc` on bootstrap (#15) --- core/blockchain.go | 15 +++++++++++ core/genesis.go | 63 ++++++++++++++++++++++++++++------------------ 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 3ee20a9aa3..107b0ad9cc 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -406,6 +406,21 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } } + if bc.logger != nil { + if block := bc.CurrentBlock(); block.Number.Uint64() == 0 { + alloc, err := getGenesisState(bc.db, block.Hash()) + if err != nil { + return nil, fmt.Errorf("failed to get genesis state: %w", err) + } + + if alloc == nil { + return nil, fmt.Errorf("live blockchain tracer requires genesis alloc to be set") + } + + bc.logger.OnGenesisBlock(bc.genesisBlock, alloc) + } + } + // Load any existing snapshot, regenerating it if loading failed if bc.cacheConfig.SnapshotLimit > 0 { // If the chain was rewound past the snapshot persistent layer (causing diff --git a/core/genesis.go b/core/genesis.go index e274f5a79b..54ec15cba4 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -176,36 +176,49 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas return nil } -// CommitGenesisState loads the stored genesis state with the given block -// hash and commits it into the provided trie database. -func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error { - var alloc GenesisAlloc +func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc GenesisAlloc, err error) { blob := rawdb.ReadGenesisStateSpec(db, blockhash) if len(blob) != 0 { if err := alloc.UnmarshalJSON(blob); err != nil { - return err - } - } else { - // Genesis allocation is missing and there are several possibilities: - // the node is legacy which doesn't persist the genesis allocation or - // the persisted allocation is just lost. - // - supported networks(mainnet, testnets), recover with defined allocations - // - private network, can't recover - var genesis *Genesis - switch blockhash { - case params.MainnetGenesisHash: - genesis = DefaultGenesisBlock() - case params.GoerliGenesisHash: - genesis = DefaultGoerliGenesisBlock() - case params.SepoliaGenesisHash: - genesis = DefaultSepoliaGenesisBlock() - } - if genesis != nil { - alloc = genesis.Alloc - } else { - return errors.New("not found") + return nil, err } + + return alloc, nil } + + // Genesis allocation is missing and there are several possibilities: + // the node is legacy which doesn't persist the genesis allocation or + // the persisted allocation is just lost. + // - supported networks(mainnet, testnets), recover with defined allocations + // - private network, can't recover + var genesis *Genesis + switch blockhash { + case params.MainnetGenesisHash: + genesis = DefaultGenesisBlock() + case params.GoerliGenesisHash: + genesis = DefaultGoerliGenesisBlock() + case params.SepoliaGenesisHash: + genesis = DefaultSepoliaGenesisBlock() + } + if genesis != nil { + return genesis.Alloc, nil + } + + return nil, nil +} + +// CommitGenesisState loads the stored genesis state with the given block +// hash and commits it into the provided trie database. +func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error { + alloc, err := getGenesisState(db, blockhash) + if err != nil { + return err + } + + if alloc == nil { + return errors.New("not found") + } + return alloc.flush(db, triedb, blockhash, nil) } From 659043a1d91e194a1157a27b282fc78701576b5d Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 30 Aug 2023 18:14:06 +0200 Subject: [PATCH 04/11] pass tracer name via cli --- cmd/utils/flags.go | 16 ++++++++++------ eth/backend.go | 4 ---- eth/ethconfig/config.go | 3 --- eth/tracers/{ => live}/printer.go | 13 +++++++++---- 4 files changed, 19 insertions(+), 17 deletions(-) rename eth/tracers/{ => live}/printer.go (95%) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c05e76e1a5..c8d77192b8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -54,6 +54,7 @@ import ( "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers" + liveTracers "github.com/ethereum/go-ethereum/eth/tracers/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/remotedb" "github.com/ethereum/go-ethereum/ethstats" @@ -518,9 +519,9 @@ var ( Usage: "Record information useful for VM and contract debugging", Category: flags.VMCategory, } - VMTraceFlag = &cli.BoolFlag{ + VMTraceFlag = &cli.StringFlag{ Name: "vmtrace", - Usage: "Record internal VM operations (costly)", + Usage: "Name of tracer which should record internal VM operations (costly)", Category: flags.VMCategory, } @@ -1723,9 +1724,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) } - if ctx.IsSet(VMTraceFlag.Name) { - cfg.LiveTrace = ctx.Bool(VMTraceFlag.Name) - } if ctx.IsSet(RPCGlobalGasCapFlag.Name) { cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) @@ -2143,7 +2141,13 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} if ctx.IsSet(VMTraceFlag.Name) { - vmcfg.Tracer = tracers.NewPrinter() + if name := ctx.String(VMTraceFlag.Name); name != "" { + t, err := liveTracers.New(name) + if err != nil { + Fatalf("Failed to create tracer %q: %v", name, err) + } + vmcfg.Tracer = t + } } // Disable transaction indexing/unindexing by default. chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil) diff --git a/eth/backend.go b/eth/backend.go index 6cb5611d32..667200bced 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -44,7 +44,6 @@ import ( "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" - "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -194,9 +193,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { Preimages: config.Preimages, } ) - if config.LiveTrace { - vmConfig.Tracer = tracers.NewPrinter() - } // Override the chain config with provided settings. var overrides core.ChainOverrides if config.OverrideCancun != nil { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 55dd56316e..4bc8b8dc6c 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -140,9 +140,6 @@ type Config struct { // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool - // LiveTrace will enable tracing during normal chain processing. - LiveTrace bool - // Miscellaneous options DocRoot string `toml:"-"` diff --git a/eth/tracers/printer.go b/eth/tracers/live/printer.go similarity index 95% rename from eth/tracers/printer.go rename to eth/tracers/live/printer.go index a07a7ea02b..8d9952fb5f 100644 --- a/eth/tracers/printer.go +++ b/eth/tracers/live/printer.go @@ -1,4 +1,4 @@ -package tracers +package live import ( "encoding/json" @@ -8,14 +8,19 @@ 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/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) +func init() { + register("printer", newPrinter) +} + type Printer struct{} -func NewPrinter() *Printer { - return &Printer{} +func newPrinter() (core.BlockchainLogger, error) { + return &Printer{}, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. @@ -91,7 +96,7 @@ func (p *Printer) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) { fmt.Printf("OnGenesisBlock: b=%v, allocLength=%d\n", b.NumberU64(), len(alloc)) } -func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int) { +func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new) } From 9c999c36ff02dc49c120e3795d147959687fe6fb Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 31 Aug 2023 16:31:30 +0200 Subject: [PATCH 05/11] Use noopTracer as base for loggers --- cmd/utils/flags.go | 4 +- eth/tracers/api.go | 19 +++--- eth/tracers/directory/live.go | 30 +++++++++ eth/tracers/{ => directory}/noop.go | 2 +- eth/tracers/{ => directory}/tracers.go | 32 ++-------- eth/tracers/directory/util.go | 47 ++++++++++++++ eth/tracers/directory/util_test.go | 60 +++++++++++++++++ .../internal/tracetest/calltrace_test.go | 12 ++-- .../internal/tracetest/flat_calltrace_test.go | 6 +- .../internal/tracetest/prestate_test.go | 4 +- eth/tracers/js/goja.go | 18 +++--- eth/tracers/js/tracer_test.go | 16 ++--- eth/tracers/live/printer.go | 3 +- eth/tracers/logger/access_list_tracer.go | 38 +---------- eth/tracers/logger/logger.go | 64 +------------------ eth/tracers/logger/logger_json.go | 31 +-------- eth/tracers/logger/logger_test.go | 1 + eth/tracers/native/4byte.go | 8 +-- eth/tracers/native/call.go | 8 +-- eth/tracers/native/call_flat.go | 20 +++--- eth/tracers/native/mux.go | 12 ++-- eth/tracers/native/prestate.go | 10 +-- eth/tracers/tracers_test.go | 38 ----------- 23 files changed, 220 insertions(+), 263 deletions(-) create mode 100644 eth/tracers/directory/live.go rename eth/tracers/{ => directory}/noop.go (99%) rename eth/tracers/{ => directory}/tracers.go (80%) create mode 100644 eth/tracers/directory/util.go create mode 100644 eth/tracers/directory/util_test.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c8d77192b8..6a2d788a4a 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -54,7 +54,7 @@ import ( "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers" - liveTracers "github.com/ethereum/go-ethereum/eth/tracers/live" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/remotedb" "github.com/ethereum/go-ethereum/ethstats" @@ -2142,7 +2142,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { - t, err := liveTracers.New(name) + t, err := directory.LiveDirectory.New(name) if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 1d3c57dc7c..69fe38ba20 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -272,7 +273,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // Trace all the transactions contained within for i, tx := range task.block.Transactions() { msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee()) - txctx := &Context{ + txctx := &directory.Context{ BlockHash: task.block.Hash(), BlockNumber: task.block.Number(), TxIndex: i, @@ -591,7 +592,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac // process that generates states in one thread and traces txes // in separate worker threads. if config != nil && config.Tracer != nil && *config.Tracer != "" { - if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS { + if isJS := directory.DefaultDirectory.IsJS(*config.Tracer); isJS { return api.traceBlockParallel(ctx, block, statedb, config) } } @@ -607,7 +608,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac for i, tx := range txs { // Generate the next state snapshot fast without tracing msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) - txctx := &Context{ + txctx := &directory.Context{ BlockHash: blockHash, BlockNumber: block.Number(), TxIndex: i, @@ -650,7 +651,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat // Fetch and execute the next transaction trace tasks for task := range jobs { msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee()) - txctx := &Context{ + txctx := &directory.Context{ BlockHash: blockHash, BlockNumber: block.Number(), TxIndex: task.index, @@ -859,7 +860,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * return nil, err } - txctx := &Context{ + txctx := &directory.Context{ BlockHash: blockHash, BlockNumber: block.Number(), TxIndex: int(index), @@ -927,15 +928,15 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, tx, msg, new(directory.Context), vmctx, statedb, traceConfig) } // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { var ( - tracer Tracer + tracer directory.Tracer err error timeout = defaultTraceTimeout txContext = core.NewEVMTxContext(message) @@ -946,7 +947,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor // Default tracer is the struct logger tracer = logger.NewStructLogger(config.Config) if config.Tracer != nil { - tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) + tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) if err != nil { return nil, err } diff --git a/eth/tracers/directory/live.go b/eth/tracers/directory/live.go new file mode 100644 index 0000000000..dc6927b142 --- /dev/null +++ b/eth/tracers/directory/live.go @@ -0,0 +1,30 @@ +package directory + +import ( + "errors" + + "github.com/ethereum/go-ethereum/core" +) + +type ctorFunc func() (core.BlockchainLogger, error) + +// LiveDirectory is the collection of tracers which can be used +// during normal block import operations. +var LiveDirectory = liveDirectory{elems: make(map[string]ctorFunc)} + +type liveDirectory struct { + elems map[string]ctorFunc +} + +// Register registers a tracer constructor by name. +func (d *liveDirectory) Register(name string, f ctorFunc) { + d.elems[name] = f +} + +// New instantiates a tracer by name. +func (d *liveDirectory) New(name string) (core.BlockchainLogger, error) { + if f, ok := d.elems[name]; ok { + return f() + } + return nil, errors.New("not found") +} diff --git a/eth/tracers/noop.go b/eth/tracers/directory/noop.go similarity index 99% rename from eth/tracers/noop.go rename to eth/tracers/directory/noop.go index 695c99c84b..06902061f4 100644 --- a/eth/tracers/noop.go +++ b/eth/tracers/directory/noop.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package tracers +package directory import ( "encoding/json" diff --git a/eth/tracers/tracers.go b/eth/tracers/directory/tracers.go similarity index 80% rename from eth/tracers/tracers.go rename to eth/tracers/directory/tracers.go index c82fcf92ac..b16d9d2fe6 100644 --- a/eth/tracers/tracers.go +++ b/eth/tracers/directory/tracers.go @@ -14,13 +14,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package tracers is a manager for transaction tracing engines. -package tracers +// Package directory provides functionality to lookup tracers by name. +// It also includes utility functions that are imported by the other +// tracing packages. +package directory import ( "encoding/json" - "errors" - "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -101,27 +101,3 @@ func (d *directory) IsJS(name string) bool { // JS eval will execute JS code return true } - -const ( - memoryPadLimit = 1024 * 1024 -) - -// GetMemoryCopyPadded returns offset + size as a new slice. -// It zero-pads the slice if it extends beyond memory bounds. -func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) { - if offset < 0 || size < 0 { - return nil, errors.New("offset or size must not be negative") - } - if int(offset+size) < m.Len() { // slice fully inside memory - return m.GetCopy(offset, size), nil - } - paddingNeeded := int(offset+size) - m.Len() - if paddingNeeded > memoryPadLimit { - return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) - } - cpy := make([]byte, size) - if overlap := int64(m.Len()) - offset; overlap > 0 { - copy(cpy, m.GetPtr(offset, overlap)) - } - return cpy, nil -} diff --git a/eth/tracers/directory/util.go b/eth/tracers/directory/util.go new file mode 100644 index 0000000000..92de735291 --- /dev/null +++ b/eth/tracers/directory/util.go @@ -0,0 +1,47 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . +package directory + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/core/vm" +) + +const ( + memoryPadLimit = 1024 * 1024 +) + +// GetMemoryCopyPadded returns offset + size as a new slice. +// It zero-pads the slice if it extends beyond memory bounds. +func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) { + if offset < 0 || size < 0 { + return nil, errors.New("offset or size must not be negative") + } + if int(offset+size) < m.Len() { // slice fully inside memory + return m.GetCopy(offset, size), nil + } + paddingNeeded := int(offset+size) - m.Len() + if paddingNeeded > memoryPadLimit { + return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) + } + cpy := make([]byte, size) + if overlap := int64(m.Len()) - offset; overlap > 0 { + copy(cpy, m.GetPtr(offset, overlap)) + } + return cpy, nil +} diff --git a/eth/tracers/directory/util_test.go b/eth/tracers/directory/util_test.go new file mode 100644 index 0000000000..c72096708d --- /dev/null +++ b/eth/tracers/directory/util_test.go @@ -0,0 +1,60 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . +package directory + +import ( + "testing" + + "github.com/ethereum/go-ethereum/core/vm" +) + +func TestMemCopying(t *testing.T) { + for i, tc := range []struct { + memsize int64 + offset int64 + size int64 + wantErr string + wantSize int + }{ + {0, 0, 100, "", 100}, // Should pad up to 100 + {0, 100, 0, "", 0}, // No need to pad (0 size) + {100, 50, 100, "", 100}, // Should pad 100-150 + {100, 50, 5, "", 5}, // Wanted range fully within memory + {100, -50, 0, "offset or size must not be negative", 0}, // Errror + {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror + {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror + + } { + mem := vm.NewMemory() + mem.Resize(uint64(tc.memsize)) + cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size) + if want := tc.wantErr; want != "" { + if err == nil { + t.Fatalf("test %d: want '%v' have no error", i, want) + } + if have := err.Error(); want != have { + t.Fatalf("test %d: want '%v' have '%v'", i, want, have) + } + continue + } + if err != nil { + t.Fatalf("test %d: unexpected error: %v", i, err) + } + if want, have := tc.wantSize, len(cpy); have != want { + t.Fatalf("test %d: want %v have %v", i, want, have) + } + } +} diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index a0001118a8..d342f2109d 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -33,7 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "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/eth/tracers/directory" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" @@ -141,7 +141,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { } _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) ) - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } @@ -247,7 +247,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), nil) if err != nil { b.Fatalf("failed to create call tracer: %v", err) } @@ -283,8 +283,8 @@ func TestInternals(t *testing.T) { BaseFee: new(big.Int), } ) - mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer { - tr, err := tracers.DefaultDirectory.New(name, nil, cfg) + mkTracer := func(name string, cfg json.RawMessage) directory.Tracer { + tr, err := directory.DefaultDirectory.New(name, nil, cfg) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } @@ -294,7 +294,7 @@ func TestInternals(t *testing.T) { for _, tc := range []struct { name string code []byte - tracer tracers.Tracer + tracer directory.Tracer want string }{ { diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index f8ca103314..be552ccfd5 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -16,11 +16,9 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" - - // Force-load the native, to trigger registration - "github.com/ethereum/go-ethereum/eth/tracers" ) // flatCallTrace is the result of a callTracerParity run. @@ -103,7 +101,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) // Create the tracer, the EVM environment and run it - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) if err != nil { return fmt.Errorf("failed to create call tracer: %v", err) } diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 74d5492d52..dc2e7293b9 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/tests" ) @@ -110,7 +110,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { } _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false) ) - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 8ed55ef1c6..c21b01525a 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -24,12 +24,12 @@ import ( "github.com/dop251/goja" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers" jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers" ) @@ -42,16 +42,16 @@ func init() { if err != nil { panic(err) } - type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error) + type ctorFn = func(*directory.Context, json.RawMessage) (directory.Tracer, error) lookup := func(code string) ctorFn { - return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { + return func(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { return newJsTracer(code, ctx, cfg) } } for name, code := range assetTracers { - tracers.DefaultDirectory.Register(name, lookup(code), true) + directory.DefaultDirectory.Register(name, lookup(code), true) } - tracers.DefaultDirectory.RegisterJSEval(newJsTracer) + directory.DefaultDirectory.RegisterJSEval(newJsTracer) } // bigIntProgram is compiled once and the exported function mostly invoked to convert @@ -96,7 +96,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b // jsTracer is an implementation of the Tracer interface which evaluates // JS functions on the relevant EVM hooks. It uses Goja as its JS engine. type jsTracer struct { - tracers.NoopTracer + directory.NoopTracer vm *goja.Runtime env *vm.EVM @@ -136,7 +136,7 @@ type jsTracer struct { // The methods `result` and `fault` are required to be present. // The methods `step`, `enter`, and `exit` are optional, but note that // `enter` and `exit` always go together. -func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { vm := goja.New() // By default field names are exported to JS as is, i.e. capitalized. vm.SetFieldNameMapper(goja.UncapFieldNameMapper()) @@ -145,7 +145,7 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer ctx: make(map[string]goja.Value), } if ctx == nil { - ctx = new(tracers.Context) + ctx = new(directory.Context) } if ctx.BlockHash != (common.Hash{}) { t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes()) @@ -576,7 +576,7 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) { if end < begin || begin < 0 { return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end) } - slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin) + slice, err := directory.GetMemoryCopyPadded(mo.memory, begin, end-begin) if err != nil { return nil, err } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 4a7d6c5bcd..0706690c89 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/params" ) @@ -61,7 +61,7 @@ func testCtx() *vmContext { return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} } -func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { +func runTrace(tracer directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { var ( env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer}) gasLimit uint64 = 31000 @@ -264,14 +264,14 @@ func TestIsPrecompile(t *testing.T) { func TestEnterExit(t *testing.T) { // test that either both or none of enter() and exit() are defined - if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(directory.Context), nil); err == nil { t.Fatal("tracer creation should've failed without exit() definition") } - if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(directory.Context), nil); err != nil { t.Fatal(err) } // test that the enter and exit method are correctly invoked and the values passed - tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil) + tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(directory.Context), nil) if err != nil { t.Fatal(err) } @@ -293,7 +293,7 @@ func TestEnterExit(t *testing.T) { func TestSetup(t *testing.T) { // Test empty config - _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil) + _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(directory.Context), nil) if err != nil { t.Error(err) } @@ -303,12 +303,12 @@ func TestSetup(t *testing.T) { t.Fatal(err) } // Test no setup func - _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg) + _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(directory.Context), cfg) if err != nil { t.Fatal(err) } // Test config value - tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg) + tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(directory.Context), cfg) if err != nil { t.Fatal(err) } diff --git a/eth/tracers/live/printer.go b/eth/tracers/live/printer.go index 8d9952fb5f..3542c63293 100644 --- a/eth/tracers/live/printer.go +++ b/eth/tracers/live/printer.go @@ -11,10 +11,11 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) func init() { - register("printer", newPrinter) + directory.LiveDirectory.Register("printer", newPrinter) } type Printer struct{} diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index ff7715f2ee..7df66066b5 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -17,11 +17,10 @@ package logger import ( - "math/big" - "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/eth/tracers/directory" ) // accessList is an accumulator for the set of accounts and storage slots an EVM @@ -103,6 +102,7 @@ func (al accessList) accessList() types.AccessList { // AccessListTracer is a tracer that accumulates touched accounts and storage // slots into an internal set. type AccessListTracer struct { + directory.NoopTracer excl map[common.Address]struct{} // Set of account to exclude from the list list accessList // Set of accounts and storage slots touched } @@ -132,9 +132,6 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi } } -func (a *AccessListTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { -} - // 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 @@ -158,37 +155,6 @@ 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) CaptureKeccakPreimage(hash common.Hash, data []byte) {} - -func (*AccessListTracer) OnGasConsumed(gas, amount uint64) {} - -func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {} - -func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {} - -func (*AccessListTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {} - -func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} - -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. func (a *AccessListTracer) AccessList() types.AccessList { return a.list.accessList() diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 5885782474..22294252af 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -28,9 +28,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -107,6 +107,7 @@ func (s *StructLog) ErrorString() string { // a track record of modified storage which is used in reporting snapshots of the // contract their storage. type StructLogger struct { + directory.NoopTracer cfg Config env *vm.EVM @@ -139,10 +140,6 @@ func (l *StructLogger) Reset() { l.err = nil } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { -} - // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. @@ -211,16 +208,6 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s l.logs = append(l.logs, log) } -// CaptureFault implements the EVMLogger interface to trace an execution fault -// while running an opcode. -func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { -} - -// CaptureKeccakPreimage is called during the KECCAK256 opcode. -func (l *StructLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} - -func (l *StructLogger) OnGasConsumed(gas, amount uint64) {} - // CaptureEnd is called after the call finishes to finalize the tracing. func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { l.output = output @@ -233,12 +220,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { } } -func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) { -} - func (l *StructLogger) GetResult() (json.RawMessage, error) { // Tracing aborted if l.reason != nil { @@ -280,20 +261,6 @@ func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) { l.usedGas = receipt.GasUsed } -func (l *StructLogger) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { -} - -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. func (l *StructLogger) StructLogs() []StructLog { return l.logs } @@ -351,6 +318,7 @@ func WriteLogs(writer io.Writer, logs []*types.Log) { } type mdLogger struct { + directory.NoopTracer out io.Writer cfg *Config env *vm.EVM @@ -408,37 +376,11 @@ 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) CaptureKeccakPreimage(hash common.Hash, data []byte) {} - -func (t *mdLogger) OnGasConsumed(gas, amount uint64) {} - func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", output, gasUsed, err) } -func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} - -func (*mdLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {} - -func (*mdLogger) CaptureTxEnd(receipt *types.Receipt, err error) {} - -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 // while replaying a transaction in debug mode as well as transaction // execution status, the amount of gas used and the return value diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index c1e25a1c3a..1ea014a0d6 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -19,15 +19,16 @@ package logger import ( "encoding/json" "io" - "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) type JSONLogger struct { + directory.NoopTracer encoder *json.Encoder cfg *Config env *vm.EVM @@ -43,9 +44,6 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { return l } -func (l *JSONLogger) CaptureStart(from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { -} - 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) @@ -78,11 +76,6 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco l.encoder.Encode(log) } -// CaptureKeccakPreimage is called during the KECCAK256 opcode. -func (l *JSONLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} - -func (l *JSONLogger) OnGasConsumed(gas, amount uint64) {} - // CaptureEnd is triggered at end of execution. func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { type endLog struct { @@ -97,26 +90,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg}) } -func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} - func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { l.env = env } - -func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt, err error) {} - -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) {} diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index 4a7db6e72f..e6c3be41cd 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -60,6 +60,7 @@ func TestStoreCapture(t *testing.T) { ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash + logger.CaptureTxStart(env, nil) logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil) _, err := env.Interpreter().Run(contract, []byte{}, false) if err != nil { diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 25152a098e..b25ff06ae2 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -25,11 +25,11 @@ 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/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) func init() { - tracers.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false) + directory.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false) } // fourByteTracer searches for 4byte-identifiers, and collects them for post-processing. @@ -47,7 +47,7 @@ func init() { // 0xc281d19e-0: 1 // } type fourByteTracer struct { - tracers.NoopTracer + directory.NoopTracer env *vm.EVM ids map[string]int // ids aggregates the 4byte ids found interrupt atomic.Bool // Atomic flag to signal execution interruption @@ -57,7 +57,7 @@ type fourByteTracer struct { // newFourByteTracer returns a native go tracer which collects // 4 byte-identifiers of a tx, and implements vm.EVMLogger. -func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { +func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (directory.Tracer, error) { t := &fourByteTracer{ ids: make(map[string]int), } diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 3af18cd3ce..bb2ea19b54 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -27,13 +27,13 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) //go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go func init() { - tracers.DefaultDirectory.Register("callTracer", newCallTracer, false) + directory.DefaultDirectory.Register("callTracer", newCallTracer, false) } type callLog struct { @@ -99,7 +99,7 @@ type callFrameMarshaling struct { } type callTracer struct { - tracers.NoopTracer + directory.NoopTracer callstack []callFrame config callTracerConfig gasLimit uint64 @@ -115,7 +115,7 @@ type callTracerConfig struct { // newCallTracer returns a native go tracer which tracks // call frames of a tx, and implements vm.EVMLogger. -func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config callTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 084df593a8..d5aebc00fd 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -27,14 +27,14 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) //go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go //go:generate go run github.com/fjl/gencodec -type flatCallResult -field-override flatCallResultMarshaling -out gen_flatcallresult_json.go func init() { - tracers.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false) + directory.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false) } var parityErrorMapping = map[string]string{ @@ -109,12 +109,12 @@ type flatCallResultMarshaling struct { // flatCallTracer reports call frame information of a tx in a flat format, i.e. // as opposed to the nested format of `callTracer`. type flatCallTracer struct { - tracers.NoopTracer + directory.NoopTracer tracer *callTracer config flatCallTracerConfig - ctx *tracers.Context // Holds tracer context data - reason error // Textual reason for the interruption - activePrecompiles []common.Address // Updated on CaptureStart based on given rules + ctx *directory.Context // Holds tracer context data + reason error // Textual reason for the interruption + activePrecompiles []common.Address // Updated on CaptureStart based on given rules } type flatCallTracerConfig struct { @@ -123,7 +123,7 @@ type flatCallTracerConfig struct { } // newFlatCallTracer returns a new flatCallTracer. -func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config flatCallTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -133,7 +133,7 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace // Create inner call tracer with default configuration, don't forward // the OnlyTopCall or WithLog to inner for now - tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, nil) + tracer, err := directory.DefaultDirectory.New("callTracer", ctx, nil) if err != nil { return nil, err } @@ -244,7 +244,7 @@ func (t *flatCallTracer) isPrecompiled(addr common.Address) bool { return false } -func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) { +func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *directory.Context) (output []flatCallFrame, err error) { var frame *flatCallFrame switch input.Type { case vm.CREATE, vm.CREATE2: @@ -343,7 +343,7 @@ func newFlatSelfdestruct(input *callFrame) *flatCallFrame { } } -func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) { +func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *directory.Context) { if ctx == nil { return } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 2dcb8f2b2d..880aca888d 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -24,32 +24,32 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) func init() { - tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false) + directory.DefaultDirectory.Register("muxTracer", newMuxTracer, false) } // muxTracer is a go implementation of the Tracer interface which // runs multiple tracers in one go. type muxTracer struct { names []string - tracers []tracers.Tracer + tracers []directory.Tracer } // newMuxTracer returns a new mux tracer. -func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config map[string]json.RawMessage if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { return nil, err } } - objects := make([]tracers.Tracer, 0, len(config)) + objects := make([]directory.Tracer, 0, len(config)) names := make([]string, 0, len(config)) for k, v := range config { - t, err := tracers.DefaultDirectory.New(k, ctx, v) + t, err := directory.DefaultDirectory.New(k, ctx, v) if err != nil { return nil, err } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 9375279cb4..71cd7ce791 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -28,14 +28,14 @@ import ( "github.com/ethereum/go-ethereum/core/types" "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/eth/tracers/directory" "github.com/ethereum/go-ethereum/log" ) //go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go func init() { - tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false) + directory.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false) } type stateMap = map[common.Address]*account @@ -57,7 +57,7 @@ type accountMarshaling struct { } type prestateTracer struct { - tracers.NoopTracer + directory.NoopTracer env *vm.EVM pre stateMap post stateMap @@ -74,7 +74,7 @@ type prestateTracerConfig struct { DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications } -func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config prestateTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -143,7 +143,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, case stackLen >= 4 && op == vm.CREATE2: offset := stackData[stackLen-2] size := stackData[stackLen-3] - init, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64())) + init, err := directory.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64())) if err != nil { log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size) return diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 759e3a4dd3..b9c0ac526d 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -109,41 +109,3 @@ func BenchmarkTransactionTrace(b *testing.B) { tracer.Reset() } } - -func TestMemCopying(t *testing.T) { - for i, tc := range []struct { - memsize int64 - offset int64 - size int64 - wantErr string - wantSize int - }{ - {0, 0, 100, "", 100}, // Should pad up to 100 - {0, 100, 0, "", 0}, // No need to pad (0 size) - {100, 50, 100, "", 100}, // Should pad 100-150 - {100, 50, 5, "", 5}, // Wanted range fully within memory - {100, -50, 0, "offset or size must not be negative", 0}, // Errror - {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror - {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror - - } { - mem := vm.NewMemory() - mem.Resize(uint64(tc.memsize)) - cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size) - if want := tc.wantErr; want != "" { - if err == nil { - t.Fatalf("test %d: want '%v' have no error", i, want) - } - if have := err.Error(); want != have { - t.Fatalf("test %d: want '%v' have '%v'", i, want, have) - } - continue - } - if err != nil { - t.Fatalf("test %d: unexpected error: %v", i, err) - } - if want, have := tc.wantSize, len(cpy); have != want { - t.Fatalf("test %d: want %v have %v", i, want, have) - } - } -} From 0be6e22eafa003d12d2407ab4f2c6e1488cd85b7 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 31 Aug 2023 16:35:43 +0200 Subject: [PATCH 06/11] add comment to statedb logger --- core/state/statedb.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/state/statedb.go b/core/state/statedb.go index 3f52b9f736..b3ee524fc1 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -56,6 +56,8 @@ func (n *proofList) Delete(key []byte) error { // StateLogger is used to collect state update traces from EVM transaction // execution. +// The following hooks are invoked post execution. I.e. looking up state +// after the hook should reflect the new value. // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. type StateLogger interface { From abd880712babb5c251c4d4cb31ea7aed26b4c47c Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 31 Aug 2023 18:46:45 +0200 Subject: [PATCH 07/11] use defer for OnBlockEnd --- core/blockchain.go | 207 ++++++++++++++++++++++----------------------- 1 file changed, 102 insertions(+), 105 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 107b0ad9cc..75c4e3f9bf 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1774,120 +1774,117 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) // Process block using the parent state as reference point pstart := time.Now() - if bc.logger != nil { - td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) - bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock()) - } - receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) - if err != nil { - bc.reportBlock(block, receipts, err) + // The traced section of block import. + err, stop := func() (blockEndErr error, _ bool) { + if bc.logger != nil { + td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) + bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock()) + defer func() { + bc.logger.OnBlockEnd(blockEndErr) + }() + } + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) + if err != nil { + bc.reportBlock(block, receipts, err) + followupInterrupt.Store(true) + return err, true + } + ptime := time.Since(pstart) + + vstart := time.Now() + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { + bc.reportBlock(block, receipts, err) + followupInterrupt.Store(true) + return err, true + } + vtime := time.Since(vstart) + proctime := time.Since(start) // processing + validation + + // Update the metrics touched during block processing and validation + accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing) + storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing) + snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing) + snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing) + accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) + storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) + accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) + storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation) + triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing + trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update + trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read + trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read + blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing + blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation + + // Write the block to the chain and get the status. + var ( + wstart = time.Now() + status WriteStatus + ) + if !setHead { + // Don't set the head, only insert the block + err = bc.writeBlockWithState(block, receipts, statedb) + } else { + status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false) + } followupInterrupt.Store(true) - if bc.logger != nil { - bc.logger.OnBlockEnd(err) + if err != nil { + return err, true } - return it.index, err - } - ptime := time.Since(pstart) + // Update the metrics touched during block commit + accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them + storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them + snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them + triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them - vstart := time.Now() - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { - bc.reportBlock(block, receipts, err) - followupInterrupt.Store(true) - if bc.logger != nil { - bc.logger.OnBlockEnd(err) + blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits) + blockInsertTimer.UpdateSince(start) + + // Report the import stats before returning the various results + stats.processed++ + stats.usedGas += usedGas + + dirty, _ := bc.triedb.Size() + stats.report(chain, it.index, dirty, setHead) + + if !setHead { + // After merge we expect few side chains. Simply count + // all blocks the CL gives us for GC processing time + bc.gcproc += proctime + + return nil, true // Direct block insertion of a single block } - return it.index, err - } - vtime := time.Since(vstart) - proctime := time.Since(start) // processing + validation + switch status { + case CanonStatTy: + log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), + "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), + "elapsed", common.PrettyDuration(time.Since(start)), + "root", block.Root()) - // Update the metrics touched during block processing and validation - accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing) - storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing) - snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing) - snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing) - accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) - storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) - accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) - storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation) - triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing - trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update - trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read - trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read - blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing - blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation + lastCanon = block - // Write the block to the chain and get the status. - var ( - wstart = time.Now() - status WriteStatus - ) - if !setHead { - // Don't set the head, only insert the block - err = bc.writeBlockWithState(block, receipts, statedb) - } else { - status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false) - } - followupInterrupt.Store(true) - if err != nil { - if bc.logger != nil { - bc.logger.OnBlockEnd(err) + // Only count canonical blocks for GC processing time + bc.gcproc += proctime + + case SideStatTy: + log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), + "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), + "root", block.Root()) + + default: + // This in theory is impossible, but lets be nice to our future selves and leave + // a log, instead of trying to track down blocks imports that don't emit logs. + log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(), + "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), + "root", block.Root()) } + return nil, false + }() + if err != nil || stop { return it.index, err } - // Update the metrics touched during block commit - accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them - storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them - snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them - triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them - - blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits) - blockInsertTimer.UpdateSince(start) - - // Report the import stats before returning the various results - stats.processed++ - stats.usedGas += usedGas - - dirty, _ := bc.triedb.Size() - stats.report(chain, it.index, dirty, setHead) - - if bc.logger != nil { - bc.logger.OnBlockEnd(nil) - } - - if !setHead { - // After merge we expect few side chains. Simply count - // all blocks the CL gives us for GC processing time - bc.gcproc += proctime - - return it.index, nil // Direct block insertion of a single block - } - switch status { - case CanonStatTy: - log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), - "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), - "elapsed", common.PrettyDuration(time.Since(start)), - "root", block.Root()) - - lastCanon = block - - // Only count canonical blocks for GC processing time - bc.gcproc += proctime - - case SideStatTy: - log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), - "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), - "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), - "root", block.Root()) - - default: - // This in theory is impossible, but lets be nice to our future selves and leave - // a log, instead of trying to track down blocks imports that don't emit logs. - log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(), - "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), - "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), - "root", block.Root()) - } } // Any blocks remaining here? The only ones we care about are the future ones From aad080db33f4458d0a4d2993a1274cc12019ead2 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 1 Sep 2023 09:32:19 -0400 Subject: [PATCH 08/11] Normalized `GasChange` constats, emit `Refund/BuyBack` only if doing something, updated comments --- core/state_transition.go | 12 +++---- core/vm/contracts.go | 2 +- core/vm/evm.go | 34 ++++++++++---------- core/vm/instructions.go | 4 +-- core/vm/interpreter.go | 4 +-- core/vm/logger.go | 66 +++++++++++++++++++-------------------- core/vm/operations_acl.go | 2 +- 7 files changed, 62 insertions(+), 62 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 99e04a7fbb..79b076ab88 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -265,7 +265,7 @@ func (st *StateTransition) buyGas() error { } if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasInitialBalance) + st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasChangeTxInitialBalance) } st.gasRemaining += st.msg.GasLimit @@ -391,7 +391,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } if t := st.evm.Config.Tracer; t != nil { - t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeIntrinsicGas) + t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeTxIntrinsicGas) } st.gasRemaining -= gas @@ -458,8 +458,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { refund = st.state.GetRefund() } - if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasRefunded) + if st.evm.Config.Tracer != nil && refund > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds) } st.gasRemaining += refund @@ -468,8 +468,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) st.state.AddBalance(st.msg.From, remaining, state.BalanceChangeGasRefund) - if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasBuyBack) + if st.evm.Config.Tracer != nil && st.gasRemaining > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxBuyBack) } // Also return remaining gas to the block gas counter so it is diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 9f7b4e44d4..7c1cf888bb 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -174,7 +174,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin return nil, 0, ErrOutOfGas } if logger != nil { - logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangePrecompiledContract) + logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract) } suppliedGas -= gasCost output, err := p.Run(input) diff --git a/core/vm/evm.go b/core/vm/evm.go index 15addfa6ab..8266ed3aa9 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -184,9 +184,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -236,7 +236,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -259,9 +259,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -294,7 +294,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -316,9 +316,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -343,7 +343,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -360,9 +360,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -404,7 +404,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -435,9 +435,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, }() } else { evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) - evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func() { - evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err) }() } @@ -464,7 +464,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } return nil, common.Address{}, 0, ErrContractAddressCollision @@ -500,7 +500,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // by the error checking condition below. if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCodeStorage) { + if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCallCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -513,7 +513,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeFailedExecution) + contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeCallFailedExecution) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0d5b253951..1146856cb0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -592,7 +592,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // reuse size int for stackvalue stackvalue := size - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 if !value.IsZero() { @@ -640,7 +640,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ) // Apply EIP150 gas -= gas / 64 - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation2) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation2) // reuse size int for stackvalue stackvalue := size //TODO: use uint256.Int instead of converting with toBig() diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 31ad6d142b..b02011fa0f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -185,7 +185,7 @@ 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, GasChangeOpCode) { + if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeCallOpCode) { return nil, ErrOutOfGas } if operation.dynamicGas != nil { @@ -211,7 +211,7 @@ 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, GasChangeOpCode) { + if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeCallOpCode) { return nil, ErrOutOfGas } // Do tracing before memory expansion diff --git a/core/vm/logger.go b/core/vm/logger.go index 70da7ba9f1..bd3d2723ac 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -48,47 +48,47 @@ type EVMLogger interface { // GasChangeReason is used to indicate the reason for a gas change, useful // for tracing and reporting. +// +// There is essentially two types of gas changes, those that can be emitted once per transaction +// and those that can be emitted on a call basis, so possibly multiple times per transaction. +// +// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted +// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis. type GasChangeReason byte const ( GasChangeUnspecified GasChangeReason = iota - // GasInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call - GasInitialBalance - // GasRefunded is the amount of gas that will be refunded to the caller for data returned to the chain - // PR Review: Is that the right description? Called in core/state_transition.go#StateTransition.refundGas - GasRefunded - // GasBuyBack is the amount of gas that will be bought back by the chain and returned in Wei to the caller - GasBuyBack - // GasChangeIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is + // GasChangeTxInitialBalance 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 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 - GasChangeIntrinsicGas + 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. + 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 - // PR Review: `GasChangeContractCreation/2` are actually the EIP150 burn cost of CREATE/CREATE2 respectively. - // I think our old name `GasChangeContractCreation/2` is not really accurate. I don't - // think that using EIP150 as a name is a good idea as rules can change in the future. So - // maybe we can just call them `GasChangeCreateBurn/GasChangeCreate2Burn`? Burn might - // feels like the wrong term, `Stipend` came to mind also but I'm unsure. - // - // I would like also to keep the distinction between CREATE and CREATE2 however, it's an - // important thing IMO as they are different and could use different rules in the future. - - // GasChangeContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules - GasChangeContractCreation + // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules + GasChangeCallContractCreation // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2, today controlled by EIP150 rules - GasChangeContractCreation2 - // GasChangeCodeStorage is the amount of gas that will be charged for code storage - GasChangeCodeStorage - // GasChangeOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was + GasChangeCallContractCreation2 + // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage + GasChangeCallCodeStorage + // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was // performed can be check by `CaptureState` handling - GasChangeOpCode - // GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution - GasChangePrecompiledContract - // GasChangeStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules - GasChangeStorageColdAccess - - // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the caller after the execution of the call, if there is left over at the end of execution + GasChangeCallOpCode + // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution + GasChangeCallPrecompiledContract + // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + GasChangeCallStorageColdAccess + // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the caller after the execution of the call, if + // there is left over at the end of call's execution. This can change can happen multiple times within a single transaction as + // each call is independent of each other. GasChangeCallLeftOverRefunded - // GasChangeFailedExecution is the burning of the remaining gas when the execution failed without a revert - GasChangeFailedExecution + // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert + GasChangeCallFailedExecution ) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 00f46395fe..55c9bf34b9 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -169,7 +169,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call - if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeStorageColdAccess) { + if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeCallStorageColdAccess) { return 0, ErrOutOfGas } } From f7ca31eb78e9b995a3ebd4be481d4135997c355a Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 4 Sep 2023 17:51:42 +0200 Subject: [PATCH 09/11] fix import cycle in blockchain test --- core/blockchain_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 1b4b569575..b5b9ff4e48 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -36,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -3024,7 +3023,7 @@ func TestDeleteRecreateSlots(t *testing.T) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout), + //Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -3101,7 +3100,7 @@ func TestDeleteRecreateAccount(t *testing.T) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout), + //Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -4307,7 +4306,7 @@ func TestEIP3651(t *testing.T) { b.AddTx(tx) }) - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{ /*Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)*/ }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } From 651c4386212a8576da52e4b89c7ddd4f71b011b2 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Mon, 4 Sep 2023 17:55:01 +0200 Subject: [PATCH 10/11] fix runtime tests --- core/vm/runtime/runtime_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index f411a67f8d..51d8cbe62f 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -32,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/params" @@ -330,7 +330,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) cfg.GasLimit = gas if len(tracerCode) > 0 { - tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(tracerCode, new(directory.Context), nil) if err != nil { b.Fatal(err) } @@ -826,7 +826,7 @@ func TestRuntimeJSTracer(t *testing.T) { statedb.SetCode(common.HexToAddress("0xee"), calleeCode) statedb.SetCode(common.HexToAddress("0xff"), suicideCode) - tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil) if err != nil { t.Fatal(err) } @@ -861,7 +861,7 @@ func TestJSTracerCreateTx(t *testing.T) { code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)} statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil) if err != nil { t.Fatal(err) } From 8f17c19c637f95659d65923ccb0424a722023f50 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Wed, 6 Sep 2023 17:05:02 -0400 Subject: [PATCH 11/11] Fixed wrong split of trx and call --- core/vm/evm.go | 20 ++++++++++---------- core/vm/instructions.go | 12 ++++++------ core/vm/logger.go | 27 ++++++++++++++++----------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 8266ed3aa9..17f9b9bb25 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -184,9 +184,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, 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, GasChangeTxInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -259,9 +259,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // 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, GasChangeTxInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -316,9 +316,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by 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, GasChangeTxInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -360,9 +360,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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, GasChangeTxInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -435,9 +435,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, }() } else { evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) - evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) defer func() { - evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasChangeTxBuyBack) + evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasChangeCallLeftOverRefunded) evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err) }() } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1146856cb0..42e55fcfa9 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -614,7 +614,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned) } scope.Contract.Gas += returnGas @@ -659,7 +659,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned) } scope.Contract.Gas += returnGas @@ -709,7 +709,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned) } scope.Contract.Gas += returnGas @@ -749,7 +749,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned) } scope.Contract.Gas += returnGas @@ -782,7 +782,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned) } scope.Contract.Gas += returnGas @@ -815,7 +815,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } if interpreter.evm.Config.Tracer != nil { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned) } scope.Contract.Gas += returnGas diff --git a/core/vm/logger.go b/core/vm/logger.go index bd3d2723ac..2175c32ad2 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -72,23 +72,28 @@ const ( // end of the transaction's execution. There is only one such gas change per transaction. GasChangeTxBuyBack - // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules + // 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. + 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. + GasChangeCallLeftOverRefunded + // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules. 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, today controlled by EIP150 rules. GasChangeCallContractCreation2 - // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage + // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. GasChangeCallCodeStorage // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was - // performed can be check by `CaptureState` handling + // performed can be check by `CaptureState` handling. GasChangeCallOpCode - // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution + // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. GasChangeCallPrecompiledContract - // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules. GasChangeCallStorageColdAccess - // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the caller after the execution of the call, if - // there is left over at the end of call's execution. This can change can happen multiple times within a single transaction as - // each call is independent of each other. - GasChangeCallLeftOverRefunded - // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert + // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. GasChangeCallFailedExecution )