core/tracing: state journal wrapper (#30441)
Here we add some more changes for live tracing API v1.1: - Hook `OnSystemCallStartV2` was introduced with `VMContext` as parameter. - Hook `OnBlockHashRead` was introduced. - `GetCodeHash` was added to the state interface - The new `WrapWithJournal` construction helps with tracking EVM reverts in the tracer. --------- Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
ed1d46b3d3
commit
aaaf01d712
|
@ -419,7 +419,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
|
|||
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
||||
for addr, a := range accounts {
|
||||
statedb.SetCode(addr, a.Code)
|
||||
statedb.SetNonce(addr, a.Nonce)
|
||||
statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis)
|
||||
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||
for k, v := range a.Storage {
|
||||
statedb.SetState(addr, k, v)
|
||||
|
|
|
@ -154,7 +154,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
|||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis)
|
||||
for key, value := range account.Storage {
|
||||
statedb.SetState(addr, key, value)
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, e
|
|||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis)
|
||||
for key, value := range account.Storage {
|
||||
statedb.SetState(addr, key, value)
|
||||
}
|
||||
|
|
|
@ -432,7 +432,7 @@ func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int, reason tr
|
|||
}
|
||||
}
|
||||
|
||||
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
||||
func (s *StateDB) SetNonce(addr common.Address, nonce uint64, reason tracing.NonceChangeReason) {
|
||||
stateObject := s.getOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetNonce(nonce)
|
||||
|
|
|
@ -69,7 +69,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction
|
|||
{
|
||||
name: "SetNonce",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.SetNonce(addr, uint64(a.args[0]))
|
||||
s.SetNonce(addr, uint64(a.args[0]), tracing.NonceChangeUnspecified)
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
|
|
|
@ -179,10 +179,13 @@ func (s *hookedStateDB) AddBalance(addr common.Address, amount *uint256.Int, rea
|
|||
return prev
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64) {
|
||||
s.inner.SetNonce(address, nonce)
|
||||
if s.hooks.OnNonceChange != nil {
|
||||
s.hooks.OnNonceChange(address, nonce-1, nonce)
|
||||
func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64, reason tracing.NonceChangeReason) {
|
||||
prev := s.inner.GetNonce(address)
|
||||
s.inner.SetNonce(address, nonce, reason)
|
||||
if s.hooks.OnNonceChangeV2 != nil {
|
||||
s.hooks.OnNonceChangeV2(address, prev, nonce, reason)
|
||||
} else if s.hooks.OnNonceChange != nil {
|
||||
s.hooks.OnNonceChange(address, prev, nonce)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ func TestHooks(t *testing.T) {
|
|||
var wants = []string{
|
||||
"0xaa00000000000000000000000000000000000000.balance: 0->100 (BalanceChangeUnspecified)",
|
||||
"0xaa00000000000000000000000000000000000000.balance: 100->50 (BalanceChangeTransfer)",
|
||||
"0xaa00000000000000000000000000000000000000.nonce: 1336->1337",
|
||||
"0xaa00000000000000000000000000000000000000.nonce: 0->1337",
|
||||
"0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)",
|
||||
"0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011",
|
||||
"0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000011 ->0x0000000000000000000000000000000000000000000000000000000000000022",
|
||||
|
@ -113,7 +113,7 @@ func TestHooks(t *testing.T) {
|
|||
})
|
||||
sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
||||
sdb.SubBalance(common.Address{0xaa}, uint256.NewInt(50), tracing.BalanceChangeTransfer)
|
||||
sdb.SetNonce(common.Address{0xaa}, 1337)
|
||||
sdb.SetNonce(common.Address{0xaa}, 1337, tracing.NonceChangeGenesis)
|
||||
sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37})
|
||||
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x11"))
|
||||
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22"))
|
||||
|
|
|
@ -60,7 +60,7 @@ func TestUpdateLeaks(t *testing.T) {
|
|||
for i := byte(0); i < 255; i++ {
|
||||
addr := common.BytesToAddress([]byte{i})
|
||||
state.AddBalance(addr, uint256.NewInt(uint64(11*i)), tracing.BalanceChangeUnspecified)
|
||||
state.SetNonce(addr, uint64(42*i))
|
||||
state.SetNonce(addr, uint64(42*i), tracing.NonceChangeUnspecified)
|
||||
if i%2 == 0 {
|
||||
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
|||
|
||||
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
||||
state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak)), tracing.BalanceChangeUnspecified)
|
||||
state.SetNonce(addr, uint64(42*i+tweak))
|
||||
state.SetNonce(addr, uint64(42*i+tweak), tracing.NonceChangeUnspecified)
|
||||
if i%2 == 0 {
|
||||
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
|
||||
state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
|
||||
|
@ -357,7 +357,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
|||
{
|
||||
name: "SetNonce",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.SetNonce(addr, uint64(a.args[0]))
|
||||
s.SetNonce(addr, uint64(a.args[0]), tracing.NonceChangeUnspecified)
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
|
|
|
@ -487,7 +487,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
|
||||
} else {
|
||||
// Increment the nonce for the next transaction.
|
||||
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1)
|
||||
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
|
||||
|
||||
// Apply EIP-7702 authorizations.
|
||||
if msg.SetCodeAuthorizations != nil {
|
||||
|
@ -602,7 +602,7 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
|
|||
}
|
||||
|
||||
// Update nonce and account code.
|
||||
st.state.SetNonce(authority, auth.Nonce+1)
|
||||
st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization)
|
||||
if auth.Address == (common.Address{}) {
|
||||
// Delegation to zero address means clear.
|
||||
st.state.SetCode(authority, nil)
|
||||
|
|
|
@ -4,6 +4,53 @@ All notable changes to the tracing interface will be documented in this file.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
The tracing interface has been extended with backwards-compatible changes to support more use-cases and simplify tracer code. The most notable change is a state journaling library which emits reverse events when a call is reverted.
|
||||
|
||||
### Deprecated methods
|
||||
|
||||
- `OnSystemCallStart()`: This hook is deprecated in favor of `OnSystemCallStartV2(vm *VMContext)`.
|
||||
- `OnNonceChange(addr common.Address, prev, new uint64)`: This hook is deprecated in favor of `OnNonceChangeV2(addr common.Address, prev, new uint64, reason NonceChangeReason)`.
|
||||
|
||||
### New methods
|
||||
|
||||
- `OnBlockHashRead(blockNum uint64, hash common.Hash)`: This hook is called when a block hash is read by EVM.
|
||||
- `OnSystemCallStartV2(vm *VMContext)`. This allows access to EVM context during system calls. It is a successor to `OnSystemCallStart`.
|
||||
- `OnNonceChangeV2(addr common.Address, prev, new uint64, reason NonceChangeReason)`: This hook is called when a nonce change occurs. It is a successor to `OnNonceChange`.
|
||||
|
||||
### New types
|
||||
|
||||
- `NonceChangeReason` is a new type used to provide a reason for nonce changes. Notably it includes `NonceChangeRevert` which will be emitted by the state journaling library when a nonce change is due to a revert.
|
||||
|
||||
### Modified types
|
||||
|
||||
- `VMContext.StateDB` has been extended with `GetCodeHash(addr common.Address) common.Hash` method used to retrieve the code hash an account.
|
||||
- `BalanceChangeReason` has been extended with the `BalanceChangeRevert` reason. More on that below.
|
||||
|
||||
### State journaling
|
||||
|
||||
Tracers receive state changes events from the node. The tracer was so far expected to keep track of modified accounts and slots and revert those changes when a call frame failed. Now a utility tracer wrapper is provided which will emit "reverse change" events when a call frame fails. To use this feature the hooks have to be wrapped prior to registering the tracer. The following example demonstrates how to use the state journaling library:
|
||||
|
||||
```go
|
||||
func init() {
|
||||
tracers.LiveDirectory.Register("test", func (cfg json.RawMessage) (*tracing.Hooks, error) {
|
||||
hooks, err := newTestTracer(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tracing.WrapWithJournal(hooks)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
The state changes that are covered by the journaling library are:
|
||||
|
||||
- `OnBalanceChange`. Note that `OnBalanceChange` will carry the `BalanceChangeRevert` reason.
|
||||
- `OnNonceChange`, `OnNonceChangeV2`
|
||||
- `OnCodeChange`
|
||||
- `OnStorageChange`
|
||||
|
||||
## [v1.14.9](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.9)
|
||||
|
||||
### Modified types
|
||||
|
||||
- `GasChangeReason` has been extended with the following reasons which will be enabled only post-Verkle. There shouldn't be any gas changes with those reasons prior to the fork.
|
||||
|
|
|
@ -23,11 +23,12 @@ func _() {
|
|||
_ = x[BalanceIncreaseSelfdestruct-12]
|
||||
_ = x[BalanceDecreaseSelfdestruct-13]
|
||||
_ = x[BalanceDecreaseSelfdestructBurn-14]
|
||||
_ = x[BalanceChangeRevert-15]
|
||||
}
|
||||
|
||||
const _BalanceChangeReason_name = "BalanceChangeUnspecifiedBalanceIncreaseRewardMineUncleBalanceIncreaseRewardMineBlockBalanceIncreaseWithdrawalBalanceIncreaseGenesisBalanceBalanceIncreaseRewardTransactionFeeBalanceDecreaseGasBuyBalanceIncreaseGasReturnBalanceIncreaseDaoContractBalanceDecreaseDaoAccountBalanceChangeTransferBalanceChangeTouchAccountBalanceIncreaseSelfdestructBalanceDecreaseSelfdestructBalanceDecreaseSelfdestructBurn"
|
||||
const _BalanceChangeReason_name = "BalanceChangeUnspecifiedBalanceIncreaseRewardMineUncleBalanceIncreaseRewardMineBlockBalanceIncreaseWithdrawalBalanceIncreaseGenesisBalanceBalanceIncreaseRewardTransactionFeeBalanceDecreaseGasBuyBalanceIncreaseGasReturnBalanceIncreaseDaoContractBalanceDecreaseDaoAccountBalanceChangeTransferBalanceChangeTouchAccountBalanceIncreaseSelfdestructBalanceDecreaseSelfdestructBalanceDecreaseSelfdestructBurnBalanceChangeRevert"
|
||||
|
||||
var _BalanceChangeReason_index = [...]uint16{0, 24, 54, 84, 109, 138, 173, 194, 218, 244, 269, 290, 315, 342, 369, 400}
|
||||
var _BalanceChangeReason_index = [...]uint16{0, 24, 54, 84, 109, 138, 173, 194, 218, 244, 269, 290, 315, 342, 369, 400, 419}
|
||||
|
||||
func (i BalanceChangeReason) String() string {
|
||||
if i >= BalanceChangeReason(len(_BalanceChangeReason_index)-1) {
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package tracing defines hooks for 'live tracing' of block processing and transaction
|
||||
// execution. Here we define the low-level [Hooks] object that carries hooks which are
|
||||
// invoked by the go-ethereum core at various points in the state transition.
|
||||
//
|
||||
// To create a tracer that can be invoked with Geth, you need to register it using
|
||||
// [github.com/ethereum/go-ethereum/eth/tracers.LiveDirectory.Register].
|
||||
//
|
||||
// See https://geth.ethereum.org/docs/developers/evm-tracing/live-tracing for a tutorial.
|
||||
package tracing
|
||||
|
||||
import (
|
||||
|
@ -163,6 +171,9 @@ type (
|
|||
// NonceChangeHook is called when the nonce of an account changes.
|
||||
NonceChangeHook = func(addr common.Address, prev, new uint64)
|
||||
|
||||
// NonceChangeHookV2 is called when the nonce of an account changes.
|
||||
NonceChangeHookV2 = func(addr common.Address, prev, new uint64, reason NonceChangeReason)
|
||||
|
||||
// CodeChangeHook is called when the code of an account changes.
|
||||
CodeChangeHook = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)
|
||||
|
||||
|
@ -171,6 +182,9 @@ type (
|
|||
|
||||
// LogHook is called when a log is emitted.
|
||||
LogHook = func(log *types.Log)
|
||||
|
||||
// BlockHashReadHook is called when EVM reads the blockhash of a block.
|
||||
BlockHashReadHook = func(blockNumber uint64, hash common.Hash)
|
||||
)
|
||||
|
||||
type Hooks struct {
|
||||
|
@ -195,9 +209,12 @@ type Hooks struct {
|
|||
// State events
|
||||
OnBalanceChange BalanceChangeHook
|
||||
OnNonceChange NonceChangeHook
|
||||
OnNonceChangeV2 NonceChangeHookV2
|
||||
OnCodeChange CodeChangeHook
|
||||
OnStorageChange StorageChangeHook
|
||||
OnLog LogHook
|
||||
// Block hash read
|
||||
OnBlockHashRead BlockHashReadHook
|
||||
}
|
||||
|
||||
// BalanceChangeReason is used to indicate the reason for a balance change, useful
|
||||
|
@ -249,6 +266,10 @@ const (
|
|||
// account within the same tx (captured at end of tx).
|
||||
// Note it doesn't account for a self-destruct which appoints itself as recipient.
|
||||
BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14
|
||||
|
||||
// BalanceChangeRevert is emitted when the balance is reverted back to a previous value due to call failure.
|
||||
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
|
||||
BalanceChangeRevert BalanceChangeReason = 15
|
||||
)
|
||||
|
||||
// GasChangeReason is used to indicate the reason for a gas change, useful
|
||||
|
@ -321,3 +342,29 @@ const (
|
|||
// it will be "manually" tracked by a direct emit of the gas change event.
|
||||
GasChangeIgnored GasChangeReason = 0xFF
|
||||
)
|
||||
|
||||
// NonceChangeReason is used to indicate the reason for a nonce change.
|
||||
type NonceChangeReason byte
|
||||
|
||||
const (
|
||||
NonceChangeUnspecified NonceChangeReason = 0
|
||||
|
||||
// NonceChangeGenesis is the nonce allocated to accounts at genesis.
|
||||
NonceChangeGenesis NonceChangeReason = 1
|
||||
|
||||
// NonceChangeEoACall is the nonce change due to an EoA call.
|
||||
NonceChangeEoACall NonceChangeReason = 2
|
||||
|
||||
// NonceChangeContractCreator is the nonce change of an account creating a contract.
|
||||
NonceChangeContractCreator NonceChangeReason = 3
|
||||
|
||||
// NonceChangeNewContract is the nonce change of a newly created contract.
|
||||
NonceChangeNewContract NonceChangeReason = 4
|
||||
|
||||
// NonceChangeTransaction is the nonce change due to a EIP-7702 authorization.
|
||||
NonceChangeAuthorization NonceChangeReason = 5
|
||||
|
||||
// NonceChangeRevert is emitted when the nonce is reverted back to a previous value due to call failure.
|
||||
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
|
||||
NonceChangeRevert NonceChangeReason = 6
|
||||
)
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2025 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// journal is a state change journal to be wrapped around a tracer.
|
||||
// It will emit the state change hooks with reverse values when a call reverts.
|
||||
type journal struct {
|
||||
hooks *Hooks
|
||||
entries []entry
|
||||
revisions []int
|
||||
}
|
||||
|
||||
type entry interface {
|
||||
revert(tracer *Hooks)
|
||||
}
|
||||
|
||||
// WrapWithJournal wraps the given tracer with a journaling layer.
|
||||
func WrapWithJournal(hooks *Hooks) (*Hooks, error) {
|
||||
if hooks == nil {
|
||||
return nil, fmt.Errorf("wrapping nil tracer")
|
||||
}
|
||||
// No state change to journal, return the wrapped hooks as is
|
||||
if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && hooks.OnCodeChange == nil && hooks.OnStorageChange == nil {
|
||||
return hooks, nil
|
||||
}
|
||||
if hooks.OnNonceChange != nil && hooks.OnNonceChangeV2 != nil {
|
||||
return nil, fmt.Errorf("cannot have both OnNonceChange and OnNonceChangeV2")
|
||||
}
|
||||
|
||||
// Create a new Hooks instance and copy all hooks
|
||||
wrapped := *hooks
|
||||
|
||||
// Create journal
|
||||
j := &journal{hooks: hooks}
|
||||
// Scope hooks need to be re-implemented.
|
||||
wrapped.OnTxEnd = j.OnTxEnd
|
||||
wrapped.OnEnter = j.OnEnter
|
||||
wrapped.OnExit = j.OnExit
|
||||
// Wrap state change hooks.
|
||||
if hooks.OnBalanceChange != nil {
|
||||
wrapped.OnBalanceChange = j.OnBalanceChange
|
||||
}
|
||||
if hooks.OnNonceChange != nil || hooks.OnNonceChangeV2 != nil {
|
||||
// Regardless of which hook version is used in the tracer,
|
||||
// the journal will want to capture the nonce change reason.
|
||||
wrapped.OnNonceChangeV2 = j.OnNonceChangeV2
|
||||
// A precaution to ensure EVM doesn't call both hooks.
|
||||
wrapped.OnNonceChange = nil
|
||||
}
|
||||
if hooks.OnCodeChange != nil {
|
||||
wrapped.OnCodeChange = j.OnCodeChange
|
||||
}
|
||||
if hooks.OnStorageChange != nil {
|
||||
wrapped.OnStorageChange = j.OnStorageChange
|
||||
}
|
||||
|
||||
return &wrapped, nil
|
||||
}
|
||||
|
||||
// reset clears the journal, after this operation the journal can be used anew.
|
||||
// It is semantically similar to calling 'NewJournal', but the underlying slices
|
||||
// can be reused.
|
||||
func (j *journal) reset() {
|
||||
j.entries = j.entries[:0]
|
||||
j.revisions = j.revisions[:0]
|
||||
}
|
||||
|
||||
// snapshot records a revision and stores it to the revision stack.
|
||||
func (j *journal) snapshot() {
|
||||
rev := len(j.entries)
|
||||
j.revisions = append(j.revisions, rev)
|
||||
}
|
||||
|
||||
// revert reverts all state changes up to the last tracked revision.
|
||||
func (j *journal) revert(hooks *Hooks) {
|
||||
// Replay the journal entries above the last revision to undo changes,
|
||||
// then remove the reverted changes from the journal.
|
||||
rev := j.revisions[len(j.revisions)-1]
|
||||
for i := len(j.entries) - 1; i >= rev; i-- {
|
||||
j.entries[i].revert(hooks)
|
||||
}
|
||||
j.entries = j.entries[:rev]
|
||||
j.popRevision()
|
||||
}
|
||||
|
||||
// popRevision removes an item from the revision stack. This basically forgets about
|
||||
// the last call to snapshot() and moves to the one prior.
|
||||
func (j *journal) popRevision() {
|
||||
j.revisions = j.revisions[:len(j.revisions)-1]
|
||||
}
|
||||
|
||||
// OnTxEnd resets the journal since each transaction has its own EVM call stack.
|
||||
func (j *journal) OnTxEnd(receipt *types.Receipt, err error) {
|
||||
j.reset()
|
||||
if j.hooks.OnTxEnd != nil {
|
||||
j.hooks.OnTxEnd(receipt, err)
|
||||
}
|
||||
}
|
||||
|
||||
// OnEnter is invoked for each EVM call frame and records a journal revision.
|
||||
func (j *journal) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
j.snapshot()
|
||||
if j.hooks.OnEnter != nil {
|
||||
j.hooks.OnEnter(depth, typ, from, to, input, gas, value)
|
||||
}
|
||||
}
|
||||
|
||||
// OnExit is invoked when an EVM call frame ends.
|
||||
// If the call has reverted, all state changes made by that frame are undone.
|
||||
// If the call did not revert, we forget about changes in that revision.
|
||||
func (j *journal) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
if reverted {
|
||||
j.revert(j.hooks)
|
||||
} else {
|
||||
j.popRevision()
|
||||
}
|
||||
if j.hooks.OnExit != nil {
|
||||
j.hooks.OnExit(depth, output, gasUsed, err, reverted)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *journal) OnBalanceChange(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) {
|
||||
j.entries = append(j.entries, balanceChange{addr: addr, prev: prev, new: new})
|
||||
if j.hooks.OnBalanceChange != nil {
|
||||
j.hooks.OnBalanceChange(addr, prev, new, reason)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *journal) OnNonceChangeV2(addr common.Address, prev, new uint64, reason NonceChangeReason) {
|
||||
// When a contract is created, the nonce of the creator is incremented.
|
||||
// This change is not reverted when the creation fails.
|
||||
if reason != NonceChangeContractCreator {
|
||||
j.entries = append(j.entries, nonceChange{addr: addr, prev: prev, new: new})
|
||||
}
|
||||
if j.hooks.OnNonceChangeV2 != nil {
|
||||
j.hooks.OnNonceChangeV2(addr, prev, new, reason)
|
||||
} else if j.hooks.OnNonceChange != nil {
|
||||
j.hooks.OnNonceChange(addr, prev, new)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *journal) OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) {
|
||||
j.entries = append(j.entries, codeChange{
|
||||
addr: addr,
|
||||
prevCodeHash: prevCodeHash,
|
||||
prevCode: prevCode,
|
||||
newCodeHash: codeHash,
|
||||
newCode: code,
|
||||
})
|
||||
if j.hooks.OnCodeChange != nil {
|
||||
j.hooks.OnCodeChange(addr, prevCodeHash, prevCode, codeHash, code)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *journal) OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) {
|
||||
j.entries = append(j.entries, storageChange{addr: addr, slot: slot, prev: prev, new: new})
|
||||
if j.hooks.OnStorageChange != nil {
|
||||
j.hooks.OnStorageChange(addr, slot, prev, new)
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
balanceChange struct {
|
||||
addr common.Address
|
||||
prev *big.Int
|
||||
new *big.Int
|
||||
}
|
||||
|
||||
nonceChange struct {
|
||||
addr common.Address
|
||||
prev uint64
|
||||
new uint64
|
||||
}
|
||||
|
||||
codeChange struct {
|
||||
addr common.Address
|
||||
prevCodeHash common.Hash
|
||||
prevCode []byte
|
||||
newCodeHash common.Hash
|
||||
newCode []byte
|
||||
}
|
||||
|
||||
storageChange struct {
|
||||
addr common.Address
|
||||
slot common.Hash
|
||||
prev common.Hash
|
||||
new common.Hash
|
||||
}
|
||||
)
|
||||
|
||||
func (b balanceChange) revert(hooks *Hooks) {
|
||||
if hooks.OnBalanceChange != nil {
|
||||
hooks.OnBalanceChange(b.addr, b.new, b.prev, BalanceChangeRevert)
|
||||
}
|
||||
}
|
||||
|
||||
func (n nonceChange) revert(hooks *Hooks) {
|
||||
if hooks.OnNonceChangeV2 != nil {
|
||||
hooks.OnNonceChangeV2(n.addr, n.new, n.prev, NonceChangeRevert)
|
||||
} else if hooks.OnNonceChange != nil {
|
||||
hooks.OnNonceChange(n.addr, n.new, n.prev)
|
||||
}
|
||||
}
|
||||
|
||||
func (c codeChange) revert(hooks *Hooks) {
|
||||
if hooks.OnCodeChange != nil {
|
||||
hooks.OnCodeChange(c.addr, c.newCodeHash, c.newCode, c.prevCodeHash, c.prevCode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s storageChange) revert(hooks *Hooks) {
|
||||
if hooks.OnStorageChange != nil {
|
||||
hooks.OnStorageChange(s.addr, s.slot, s.new, s.prev)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
// Copyright 2025 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type testTracer struct {
|
||||
t *testing.T
|
||||
bal *big.Int
|
||||
nonce uint64
|
||||
code []byte
|
||||
storage map[common.Hash]common.Hash
|
||||
}
|
||||
|
||||
func (t *testTracer) OnBalanceChange(addr common.Address, prev *big.Int, new *big.Int, reason BalanceChangeReason) {
|
||||
t.t.Logf("OnBalanceChange(%v, %v -> %v, %v)", addr, prev, new, reason)
|
||||
if t.bal != nil && t.bal.Cmp(prev) != 0 {
|
||||
t.t.Errorf(" !! wrong prev balance (expected %v)", t.bal)
|
||||
}
|
||||
t.bal = new
|
||||
}
|
||||
|
||||
func (t *testTracer) OnNonceChange(addr common.Address, prev uint64, new uint64) {
|
||||
t.t.Logf("OnNonceChange(%v, %v -> %v)", addr, prev, new)
|
||||
t.nonce = new
|
||||
}
|
||||
|
||||
func (t *testTracer) OnNonceChangeV2(addr common.Address, prev uint64, new uint64, reason NonceChangeReason) {
|
||||
t.t.Logf("OnNonceChangeV2(%v, %v -> %v, %v)", addr, prev, new, reason)
|
||||
t.nonce = new
|
||||
}
|
||||
|
||||
func (t *testTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) {
|
||||
t.t.Logf("OnCodeChange(%v, %v -> %v)", addr, prevCodeHash, codeHash)
|
||||
t.code = code
|
||||
}
|
||||
|
||||
func (t *testTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) {
|
||||
t.t.Logf("OnStorageCodeChange(%v, %v, %v -> %v)", addr, slot, prev, new)
|
||||
if t.storage == nil {
|
||||
t.storage = make(map[common.Hash]common.Hash)
|
||||
}
|
||||
if new == (common.Hash{}) {
|
||||
delete(t.storage, slot)
|
||||
} else {
|
||||
t.storage[slot] = new
|
||||
}
|
||||
}
|
||||
|
||||
func TestJournalIntegration(t *testing.T) {
|
||||
tr := &testTracer{t: t}
|
||||
wr, err := WrapWithJournal(&Hooks{OnBalanceChange: tr.OnBalanceChange, OnNonceChange: tr.OnNonceChange, OnCodeChange: tr.OnCodeChange, OnStorageChange: tr.OnStorageChange})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap test tracer: %v", err)
|
||||
}
|
||||
|
||||
addr := common.HexToAddress("0x1234")
|
||||
{
|
||||
wr.OnEnter(0, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnBalanceChange(addr, nil, big.NewInt(100), BalanceChangeUnspecified)
|
||||
wr.OnCodeChange(addr, common.Hash{}, nil, common.Hash{}, []byte{1, 2, 3})
|
||||
wr.OnStorageChange(addr, common.Hash{1}, common.Hash{}, common.Hash{2})
|
||||
{
|
||||
wr.OnEnter(1, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnNonceChangeV2(addr, 0, 1, NonceChangeUnspecified)
|
||||
wr.OnBalanceChange(addr, big.NewInt(100), big.NewInt(200), BalanceChangeUnspecified)
|
||||
wr.OnBalanceChange(addr, big.NewInt(200), big.NewInt(250), BalanceChangeUnspecified)
|
||||
wr.OnStorageChange(addr, common.Hash{1}, common.Hash{2}, common.Hash{3})
|
||||
wr.OnStorageChange(addr, common.Hash{2}, common.Hash{}, common.Hash{4})
|
||||
wr.OnExit(1, nil, 100, errors.New("revert"), true)
|
||||
}
|
||||
wr.OnExit(0, nil, 150, nil, false)
|
||||
}
|
||||
|
||||
if tr.bal.Cmp(big.NewInt(100)) != 0 {
|
||||
t.Fatalf("unexpected balance: %v", tr.bal)
|
||||
}
|
||||
if tr.nonce != 0 {
|
||||
t.Fatalf("unexpected nonce: %v", tr.nonce)
|
||||
}
|
||||
if len(tr.code) != 3 {
|
||||
t.Fatalf("unexpected code: %v", tr.code)
|
||||
}
|
||||
if len(tr.storage) != 1 {
|
||||
t.Fatalf("unexpected storage len. want %d, have %d", 1, len(tr.storage))
|
||||
}
|
||||
if tr.storage[common.Hash{1}] != (common.Hash{2}) {
|
||||
t.Fatalf("unexpected storage. want %v, have %v", common.Hash{2}, tr.storage[common.Hash{1}])
|
||||
}
|
||||
}
|
||||
|
||||
func TestJournalTopRevert(t *testing.T) {
|
||||
tr := &testTracer{t: t}
|
||||
wr, err := WrapWithJournal(&Hooks{OnBalanceChange: tr.OnBalanceChange, OnNonceChange: tr.OnNonceChange})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap test tracer: %v", err)
|
||||
}
|
||||
|
||||
addr := common.HexToAddress("0x1234")
|
||||
{
|
||||
wr.OnEnter(0, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnBalanceChange(addr, big.NewInt(0), big.NewInt(100), BalanceChangeUnspecified)
|
||||
{
|
||||
wr.OnEnter(1, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnNonceChangeV2(addr, 0, 1, NonceChangeUnspecified)
|
||||
wr.OnBalanceChange(addr, big.NewInt(100), big.NewInt(200), BalanceChangeUnspecified)
|
||||
wr.OnBalanceChange(addr, big.NewInt(200), big.NewInt(250), BalanceChangeUnspecified)
|
||||
wr.OnExit(1, nil, 100, errors.New("revert"), true)
|
||||
}
|
||||
wr.OnExit(0, nil, 150, errors.New("revert"), true)
|
||||
}
|
||||
|
||||
if tr.bal.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Fatalf("unexpected balance: %v", tr.bal)
|
||||
}
|
||||
if tr.nonce != 0 {
|
||||
t.Fatalf("unexpected nonce: %v", tr.nonce)
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks that changes in nested calls are reverted properly.
|
||||
func TestJournalNestedCalls(t *testing.T) {
|
||||
tr := &testTracer{t: t}
|
||||
wr, err := WrapWithJournal(&Hooks{OnBalanceChange: tr.OnBalanceChange, OnNonceChange: tr.OnNonceChange})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap test tracer: %v", err)
|
||||
}
|
||||
|
||||
addr := common.HexToAddress("0x1234")
|
||||
{
|
||||
wr.OnEnter(0, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnBalanceChange(addr, big.NewInt(0), big.NewInt(100), BalanceChangeUnspecified)
|
||||
{
|
||||
wr.OnEnter(1, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnBalanceChange(addr, big.NewInt(100), big.NewInt(200), BalanceChangeUnspecified)
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnExit(2, nil, 100, nil, false)
|
||||
}
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnBalanceChange(addr, big.NewInt(200), big.NewInt(300), BalanceChangeUnspecified)
|
||||
wr.OnExit(2, nil, 100, nil, false)
|
||||
}
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnExit(2, nil, 100, nil, false)
|
||||
}
|
||||
wr.OnBalanceChange(addr, big.NewInt(300), big.NewInt(400), BalanceChangeUnspecified)
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnBalanceChange(addr, big.NewInt(400), big.NewInt(500), BalanceChangeUnspecified)
|
||||
wr.OnExit(2, nil, 100, errors.New("revert"), true)
|
||||
}
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnExit(2, nil, 100, errors.New("revert"), true)
|
||||
}
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnBalanceChange(addr, big.NewInt(400), big.NewInt(600), BalanceChangeUnspecified)
|
||||
wr.OnExit(2, nil, 100, nil, false)
|
||||
}
|
||||
wr.OnExit(1, nil, 100, errors.New("revert"), true)
|
||||
}
|
||||
wr.OnExit(0, nil, 150, nil, false)
|
||||
}
|
||||
|
||||
if tr.bal.Uint64() != 100 {
|
||||
t.Fatalf("unexpected balance: %v", tr.bal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonceIncOnCreate(t *testing.T) {
|
||||
const opCREATE = 0xf0
|
||||
|
||||
tr := &testTracer{t: t}
|
||||
wr, err := WrapWithJournal(&Hooks{OnNonceChange: tr.OnNonceChange})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap test tracer: %v", err)
|
||||
}
|
||||
|
||||
addr := common.HexToAddress("0x1234")
|
||||
{
|
||||
wr.OnEnter(0, opCREATE, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnNonceChangeV2(addr, 0, 1, NonceChangeContractCreator)
|
||||
wr.OnExit(0, nil, 100, errors.New("revert"), true)
|
||||
}
|
||||
|
||||
if tr.nonce != 1 {
|
||||
t.Fatalf("unexpected nonce: %v", tr.nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnNonceChangeV2(t *testing.T) {
|
||||
tr := &testTracer{t: t}
|
||||
wr, err := WrapWithJournal(&Hooks{OnNonceChangeV2: tr.OnNonceChangeV2})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap test tracer: %v", err)
|
||||
}
|
||||
|
||||
addr := common.HexToAddress("0x1234")
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnNonceChangeV2(addr, 0, 1, NonceChangeEoACall)
|
||||
wr.OnExit(2, nil, 100, nil, true)
|
||||
}
|
||||
|
||||
if tr.nonce != 0 {
|
||||
t.Fatalf("unexpected nonce: %v", tr.nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllHooksCalled(t *testing.T) {
|
||||
tracer := newTracerAllHooks()
|
||||
hooks := tracer.hooks()
|
||||
|
||||
wrapped, err := WrapWithJournal(hooks)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap hooks with journal: %v", err)
|
||||
}
|
||||
|
||||
// Get the underlying value of the wrapped hooks
|
||||
wrappedValue := reflect.ValueOf(wrapped).Elem()
|
||||
wrappedType := wrappedValue.Type()
|
||||
|
||||
// Iterate over all fields of the wrapped hooks
|
||||
for i := 0; i < wrappedType.NumField(); i++ {
|
||||
field := wrappedType.Field(i)
|
||||
|
||||
// Skip fields that are not function types
|
||||
if field.Type.Kind() != reflect.Func {
|
||||
continue
|
||||
}
|
||||
// Skip non-hooks, i.e. Copy
|
||||
if field.Name == "copy" {
|
||||
continue
|
||||
}
|
||||
// Skip if field is not set
|
||||
if wrappedValue.Field(i).IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the method
|
||||
method := wrappedValue.Field(i)
|
||||
|
||||
// Call the method with zero values
|
||||
params := make([]reflect.Value, method.Type().NumIn())
|
||||
for j := 0; j < method.Type().NumIn(); j++ {
|
||||
params[j] = reflect.Zero(method.Type().In(j))
|
||||
}
|
||||
method.Call(params)
|
||||
}
|
||||
|
||||
// Check if all hooks were called
|
||||
if tracer.numCalled() != tracer.hooksCount() {
|
||||
t.Errorf("Not all hooks were called. Expected %d, got %d", tracer.hooksCount(), tracer.numCalled())
|
||||
}
|
||||
|
||||
for hookName, called := range tracer.hooksCalled {
|
||||
if !called {
|
||||
t.Errorf("Hook %s was not called", hookName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type tracerAllHooks struct {
|
||||
hooksCalled map[string]bool
|
||||
}
|
||||
|
||||
func newTracerAllHooks() *tracerAllHooks {
|
||||
t := &tracerAllHooks{hooksCalled: make(map[string]bool)}
|
||||
// Initialize all hooks to false. We will use this to
|
||||
// get total count of hooks.
|
||||
hooksType := reflect.TypeOf((*Hooks)(nil)).Elem()
|
||||
for i := 0; i < hooksType.NumField(); i++ {
|
||||
t.hooksCalled[hooksType.Field(i).Name] = false
|
||||
}
|
||||
delete(t.hooksCalled, "OnNonceChange")
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *tracerAllHooks) hooksCount() int {
|
||||
return len(t.hooksCalled)
|
||||
}
|
||||
|
||||
func (t *tracerAllHooks) numCalled() int {
|
||||
count := 0
|
||||
for _, called := range t.hooksCalled {
|
||||
if called {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (t *tracerAllHooks) hooks() *Hooks {
|
||||
h := &Hooks{}
|
||||
// Create a function for each hook that sets the
|
||||
// corresponding hooksCalled field to true.
|
||||
hooksValue := reflect.ValueOf(h).Elem()
|
||||
for i := 0; i < hooksValue.NumField(); i++ {
|
||||
field := hooksValue.Type().Field(i)
|
||||
if field.Name == "OnNonceChange" {
|
||||
continue
|
||||
}
|
||||
hookMethod := reflect.MakeFunc(field.Type, func(args []reflect.Value) []reflect.Value {
|
||||
t.hooksCalled[field.Name] = true
|
||||
return nil
|
||||
})
|
||||
hooksValue.Field(i).Set(hookMethod)
|
||||
}
|
||||
return h
|
||||
}
|
|
@ -680,9 +680,9 @@ func TestOpenDrops(t *testing.T) {
|
|||
statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||
statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||
statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3)
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3, tracing.NonceChangeUnspecified)
|
||||
statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2)
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2, tracing.NonceChangeUnspecified)
|
||||
statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||
statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||
statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), uint256.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||
|
@ -1526,7 +1526,7 @@ func TestAdd(t *testing.T) {
|
|||
|
||||
// Seed the state database with this account
|
||||
statedb.AddBalance(addrs[acc], new(uint256.Int).SetUint64(seed.balance), tracing.BalanceChangeUnspecified)
|
||||
statedb.SetNonce(addrs[acc], seed.nonce)
|
||||
statedb.SetNonce(addrs[acc], seed.nonce, tracing.NonceChangeUnspecified)
|
||||
|
||||
// Sign the seed transactions and store them in the data store
|
||||
for _, tx := range seed.txs {
|
||||
|
@ -1581,7 +1581,7 @@ func TestAdd(t *testing.T) {
|
|||
// Apply the nonce updates to the state db
|
||||
for _, tx := range txs {
|
||||
sender, _ := types.Sender(types.LatestSigner(params.MainnetChainConfig), tx)
|
||||
chain.statedb.SetNonce(sender, tx.Nonce()+1)
|
||||
chain.statedb.SetNonce(sender, tx.Nonce()+1, tracing.NonceChangeUnspecified)
|
||||
}
|
||||
pool.Reset(chain.CurrentBlock(), header)
|
||||
verifyPoolInternals(t, pool)
|
||||
|
|
|
@ -251,7 +251,7 @@ func (c *testChain) State() (*state.StateDB, error) {
|
|||
if *c.trigger {
|
||||
c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
// simulate that the new head block included tx0 and tx1
|
||||
c.statedb.SetNonce(c.address, 2)
|
||||
c.statedb.SetNonce(c.address, 2, tracing.NonceChangeUnspecified)
|
||||
c.statedb.SetBalance(c.address, new(uint256.Int).SetUint64(params.Ether), tracing.BalanceChangeUnspecified)
|
||||
*c.trigger = false
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ func testAddBalance(pool *LegacyPool, addr common.Address, amount *big.Int) {
|
|||
|
||||
func testSetNonce(pool *LegacyPool, addr common.Address, nonce uint64) {
|
||||
pool.mu.Lock()
|
||||
pool.currentState.SetNonce(addr, nonce)
|
||||
pool.currentState.SetNonce(addr, nonce, tracing.NonceChangeUnspecified)
|
||||
pool.mu.Unlock()
|
||||
}
|
||||
|
||||
|
@ -1011,7 +1011,7 @@ func TestQueueTimeLimiting(t *testing.T) {
|
|||
}
|
||||
|
||||
// remove current transactions and increase nonce to prepare for a reset and cleanup
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(remote.PublicKey), 2)
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(remote.PublicKey), 2, tracing.NonceChangeUnspecified)
|
||||
<-pool.requestReset(nil, nil)
|
||||
|
||||
// make sure queue, pending are cleared
|
||||
|
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
@ -226,7 +227,7 @@ func TestProcessParentBlockHash(t *testing.T) {
|
|||
// block 2 parent hash is 0x0200....
|
||||
// etc
|
||||
checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
|
||||
statedb.SetNonce(params.HistoryStorageAddress, 1)
|
||||
statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
|
||||
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
|
||||
// Process n blocks, from 1 .. num
|
||||
var num = 2
|
||||
|
|
|
@ -439,7 +439,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
if nonce+1 < nonce {
|
||||
return nil, common.Address{}, gas, ErrNonceUintOverflow
|
||||
}
|
||||
evm.StateDB.SetNonce(caller.Address(), nonce+1)
|
||||
evm.StateDB.SetNonce(caller.Address(), nonce+1, tracing.NonceChangeContractCreator)
|
||||
|
||||
// Charge the contract creation init gas in verkle mode
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
|
@ -487,7 +487,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
evm.StateDB.CreateContract(address)
|
||||
|
||||
if evm.chainRules.IsEIP158 {
|
||||
evm.StateDB.SetNonce(address, 1)
|
||||
evm.StateDB.SetNonce(address, 1, tracing.NonceChangeNewContract)
|
||||
}
|
||||
// Charge the contract creation init gas in verkle mode
|
||||
if evm.chainRules.IsEIP4762 {
|
||||
|
|
|
@ -448,6 +448,9 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
|
||||
witness.AddBlockHash(num64)
|
||||
}
|
||||
if tracer := interpreter.evm.Config.Tracer; tracer != nil && tracer.OnBlockHashRead != nil {
|
||||
tracer.OnBlockHashRead(num64, res)
|
||||
}
|
||||
num.SetBytes(res[:])
|
||||
} else {
|
||||
num.Clear()
|
||||
|
|
|
@ -39,7 +39,7 @@ type StateDB interface {
|
|||
GetBalance(common.Address) *uint256.Int
|
||||
|
||||
GetNonce(common.Address) uint64
|
||||
SetNonce(common.Address, uint64)
|
||||
SetNonce(common.Address, uint64, tracing.NonceChangeReason)
|
||||
|
||||
GetCodeHash(common.Address) common.Hash
|
||||
GetCode(common.Address) []byte
|
||||
|
|
|
@ -414,7 +414,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
|||
eoa := common.HexToAddress("E0")
|
||||
{
|
||||
cfg.State.CreateAccount(eoa)
|
||||
cfg.State.SetNonce(eoa, 100)
|
||||
cfg.State.SetNonce(eoa, 100, tracing.NonceChangeUnspecified)
|
||||
}
|
||||
reverting := common.HexToAddress("EE")
|
||||
{
|
||||
|
|
|
@ -57,6 +57,7 @@ func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) {
|
|||
OnCodeChange: t.OnCodeChange,
|
||||
OnStorageChange: t.OnStorageChange,
|
||||
OnLog: t.OnLog,
|
||||
OnBlockHashRead: t.OnBlockHashRead,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -108,5 +109,7 @@ func (t *noop) OnLog(l *types.Log) {
|
|||
|
||||
}
|
||||
|
||||
func (t *noop) OnBlockHashRead(number uint64, hash common.Hash) {}
|
||||
|
||||
func (t *noop) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ func (diff *StateOverride) Apply(statedb *state.StateDB, precompiles vm.Precompi
|
|||
}
|
||||
// Override account nonce.
|
||||
if account.Nonce != nil {
|
||||
statedb.SetNonce(addr, uint64(*account.Nonce))
|
||||
statedb.SetNonce(addr, uint64(*account.Nonce), tracing.NonceChangeUnspecified)
|
||||
}
|
||||
// Override account(contract) code.
|
||||
if account.Code != nil {
|
||||
|
|
|
@ -512,7 +512,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo
|
|||
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
||||
for addr, a := range accounts {
|
||||
statedb.SetCode(addr, a.Code)
|
||||
statedb.SetNonce(addr, a.Nonce)
|
||||
statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeUnspecified)
|
||||
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceChangeUnspecified)
|
||||
for k, v := range a.Storage {
|
||||
statedb.SetState(addr, k, v)
|
||||
|
|
Loading…
Reference in New Issue