mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
core/state: make inner statedb explicit, implement alternative logging burn
core/state, core/vm: refactor statedb hooking core/state: trace consensus finalize and system calls eth/tracers/internal/tracetest: fix tests after refactor core/state: some renaming and cleanup of statedb-hooking system core/state: remove unecessary methods, implement hooked subbalance, more testing
This commit is contained in:
parent
4de4fa4b60
commit
8ca0cd582d
15 changed files with 425 additions and 256 deletions
|
|
@ -1774,7 +1774,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
statedb.SetLogger(bc.logger)
|
|
||||||
|
|
||||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
|
|
@ -1893,13 +1892,10 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
bc.logger.OnBlockEnd(blockEndErr)
|
bc.logger.OnBlockEnd(blockEndErr)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
var wStateDb = vm.StateDB(statedb)
|
|
||||||
if w := statedb.Wrapped(); w != nil {
|
|
||||||
wStateDb = w
|
|
||||||
}
|
|
||||||
// Process block using the parent state as reference point
|
// Process block using the parent state as reference point
|
||||||
pstart := time.Now()
|
pstart := time.Now()
|
||||||
res, err := bc.processor.Process(block, wStateDb, bc.vmConfig)
|
res, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.reportBlock(block, res, err)
|
bc.reportBlock(block, res, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,6 @@ type StateDB struct {
|
||||||
db Database
|
db Database
|
||||||
prefetcher *triePrefetcher
|
prefetcher *triePrefetcher
|
||||||
trie Trie
|
trie Trie
|
||||||
|
|
||||||
onSelfDestructBurn func(common.Address, *uint256.Int)
|
|
||||||
shim *stateDBLogger
|
|
||||||
|
|
||||||
reader Reader
|
reader Reader
|
||||||
|
|
||||||
// originalRoot is the pre-state root, before any changes were made.
|
// originalRoot is the pre-state root, before any changes were made.
|
||||||
|
|
@ -192,19 +188,6 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
return sdb, nil
|
return sdb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) SetBurnCallback(fn func(common.Address, *uint256.Int)) {
|
|
||||||
s.onSelfDestructBurn = fn
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLogger sets the logger for account update hooks.
|
|
||||||
func (s *StateDB) SetLogger(l *tracing.Hooks) {
|
|
||||||
s.shim = newStateDBLogger(s, l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StateDB) Wrapped() *stateDBLogger {
|
|
||||||
return s.shim
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||||
// state trie concurrently while the state is mutated so that when we reach the
|
// state trie concurrently while the state is mutated so that when we reach the
|
||||||
// commit phase, most of the needed data is already hot.
|
// commit phase, most of the needed data is already hot.
|
||||||
|
|
@ -754,11 +737,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
||||||
delete(s.stateObjects, obj.address)
|
delete(s.stateObjects, obj.address)
|
||||||
s.markDelete(addr)
|
s.markDelete(addr)
|
||||||
|
|
||||||
// If ether was sent to account post-selfdestruct it is burnt.
|
|
||||||
if bal := obj.Balance(); bal.Sign() != 0 && obj.selfDestructed && s.onSelfDestructBurn != nil {
|
|
||||||
s.onSelfDestructBurn(obj.address, bal)
|
|
||||||
}
|
|
||||||
// We need to maintain account deletions explicitly (will remain
|
// We need to maintain account deletions explicitly (will remain
|
||||||
// set indefinitely). Note only the first occurred self-destruct
|
// set indefinitely). Note only the first occurred self-destruct
|
||||||
// event is tracked.
|
// event is tracked.
|
||||||
|
|
|
||||||
242
core/state/statedb_hooked.go
Normal file
242
core/state/statedb_hooked.go
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
// Copyright 2024 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 state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hookedStateDB represents a statedb which emits calls to tracing-hooks
|
||||||
|
// on state operations.
|
||||||
|
type hookedStateDB struct {
|
||||||
|
inner *StateDB
|
||||||
|
hooks *tracing.Hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHookedState wraps the given stateDb with the given hooks
|
||||||
|
func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB {
|
||||||
|
s := &hookedStateDB{stateDb, hooks}
|
||||||
|
if s.hooks == nil {
|
||||||
|
s.hooks = new(tracing.Hooks)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) CreateAccount(addr common.Address) {
|
||||||
|
s.inner.CreateAccount(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) CreateContract(addr common.Address) {
|
||||||
|
s.inner.CreateContract(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetBalance(addr common.Address) *uint256.Int {
|
||||||
|
return s.inner.GetBalance(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetNonce(addr common.Address) uint64 {
|
||||||
|
return s.inner.GetNonce(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||||
|
return s.inner.GetCodeHash(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetCode(addr common.Address) []byte {
|
||||||
|
return s.inner.GetCode(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetCodeSize(addr common.Address) int {
|
||||||
|
return s.inner.GetCodeSize(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) AddRefund(u uint64) {
|
||||||
|
s.inner.AddRefund(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) SubRefund(u uint64) {
|
||||||
|
s.inner.SubRefund(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetRefund() uint64 {
|
||||||
|
return s.inner.GetRefund()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
|
return s.inner.GetCommittedState(addr, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
|
return s.inner.GetState(addr, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetStorageRoot(addr common.Address) common.Hash {
|
||||||
|
return s.inner.GetStorageRoot(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
|
||||||
|
return s.inner.GetTransientState(addr, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) SetTransientState(addr common.Address, key, value common.Hash) {
|
||||||
|
s.inner.SetTransientState(addr, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) HasSelfDestructed(addr common.Address) bool {
|
||||||
|
return s.inner.HasSelfDestructed(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) Exist(addr common.Address) bool {
|
||||||
|
return s.inner.Exist(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) Empty(addr common.Address) bool {
|
||||||
|
return s.inner.Empty(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) AddressInAccessList(addr common.Address) bool {
|
||||||
|
return s.inner.AddressInAccessList(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
|
||||||
|
return s.inner.SlotInAccessList(addr, slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) {
|
||||||
|
s.inner.AddAddressToAccessList(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
||||||
|
s.inner.AddSlotToAccessList(addr, slot)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) PointCache() *utils.PointCache {
|
||||||
|
return s.inner.PointCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) {
|
||||||
|
s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) RevertToSnapshot(i int) {
|
||||||
|
s.inner.RevertToSnapshot(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) Snapshot() int {
|
||||||
|
return s.inner.Snapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) {
|
||||||
|
s.inner.Snapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) Witness() *stateless.Witness {
|
||||||
|
return s.inner.Witness()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
|
||||||
|
prev := s.inner.SubBalance(addr, amount, reason)
|
||||||
|
if s.hooks.OnBalanceChange != nil {
|
||||||
|
newBalance := new(uint256.Int).Sub(&prev, amount)
|
||||||
|
s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason)
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
|
||||||
|
prev := s.inner.AddBalance(addr, amount, reason)
|
||||||
|
if s.hooks.OnBalanceChange != nil {
|
||||||
|
newBalance := new(uint256.Int).Add(&prev, amount)
|
||||||
|
s.hooks.OnBalanceChange(addr, prev.ToBig(), newBalance.ToBig(), reason)
|
||||||
|
}
|
||||||
|
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) SetCode(address common.Address, code []byte) {
|
||||||
|
s.inner.SetCode(address, code)
|
||||||
|
if s.hooks.OnCodeChange != nil {
|
||||||
|
s.hooks.OnCodeChange(address, types.EmptyCodeHash, nil, crypto.Keccak256Hash(code), code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash {
|
||||||
|
prev := s.inner.SetState(address, key, value)
|
||||||
|
if s.hooks.OnStorageChange != nil && prev != value {
|
||||||
|
s.hooks.OnStorageChange(address, key, prev, value)
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int {
|
||||||
|
prev := s.inner.SelfDestruct(address)
|
||||||
|
if !prev.IsZero() {
|
||||||
|
if s.hooks.OnBalanceChange != nil {
|
||||||
|
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) Selfdestruct6780(address common.Address) uint256.Int {
|
||||||
|
prev := s.inner.Selfdestruct6780(address)
|
||||||
|
if !prev.IsZero() {
|
||||||
|
if s.hooks.OnBalanceChange != nil {
|
||||||
|
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) AddLog(log *types.Log) {
|
||||||
|
// The inner will modify the log (add fields), so invoke that first
|
||||||
|
s.inner.AddLog(log)
|
||||||
|
if s.hooks.OnLog != nil {
|
||||||
|
s.hooks.OnLog(log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
|
defer s.inner.Finalise(deleteEmptyObjects)
|
||||||
|
if s.hooks.OnBalanceChange == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for addr := range s.inner.journal.dirties {
|
||||||
|
obj := s.inner.stateObjects[addr]
|
||||||
|
if obj != nil && obj.selfDestructed {
|
||||||
|
// If ether was sent to account post-selfdestruct it is burnt.
|
||||||
|
if bal := obj.Balance(); bal.Sign() != 0 {
|
||||||
|
s.hooks.OnBalanceChange(addr, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
130
core/state/statedb_hooked_test.go
Normal file
130
core/state/statedb_hooked_test.go
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
// Copyright 2024 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 state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This method tests that the 'burn' from sending-to-selfdestructed accounts
|
||||||
|
// is accounted for.
|
||||||
|
// (There is also a higher-level test in eth/tracers: TestSupplySelfDestruct )
|
||||||
|
func TestBurn(t *testing.T) {
|
||||||
|
// Note: burn can happen even after EIP-6780, if within one single transaction,
|
||||||
|
// the following occur:
|
||||||
|
// 1. contract B creates contract A
|
||||||
|
// 2. contract A is destructed
|
||||||
|
// 3. constract B sends ether to A
|
||||||
|
|
||||||
|
var burned = new(uint256.Int)
|
||||||
|
s, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
|
hooked := NewHookedState(s, &tracing.Hooks{
|
||||||
|
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
|
if reason == tracing.BalanceDecreaseSelfdestructBurn {
|
||||||
|
burned.Add(burned, uint256.MustFromBig(prev))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
createAndDestroy := func(addr common.Address) {
|
||||||
|
hooked.AddBalance(addr, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
||||||
|
hooked.CreateContract(addr)
|
||||||
|
hooked.SelfDestruct(addr)
|
||||||
|
// sanity-check that balance is now 0
|
||||||
|
if have, want := hooked.GetBalance(addr), new(uint256.Int); !have.Eq(want) {
|
||||||
|
t.Fatalf("post-destruct balance wrong: have %v want %v", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addA := common.Address{0xaa}
|
||||||
|
addB := common.Address{0xbb}
|
||||||
|
addC := common.Address{0xcc}
|
||||||
|
|
||||||
|
// Tx 1: create and destroy address A and B in one tx
|
||||||
|
createAndDestroy(addA)
|
||||||
|
createAndDestroy(addB)
|
||||||
|
hooked.AddBalance(addA, uint256.NewInt(200), tracing.BalanceChangeUnspecified)
|
||||||
|
hooked.AddBalance(addB, uint256.NewInt(200), tracing.BalanceChangeUnspecified)
|
||||||
|
hooked.Finalise(true)
|
||||||
|
|
||||||
|
// Tx 2: create and destroy address C, then commit
|
||||||
|
createAndDestroy(addC)
|
||||||
|
hooked.AddBalance(addC, uint256.NewInt(200), tracing.BalanceChangeUnspecified)
|
||||||
|
hooked.Finalise(true)
|
||||||
|
|
||||||
|
s.Commit(0, false)
|
||||||
|
if have, want := burned, uint256.NewInt(600); !have.Eq(want) {
|
||||||
|
t.Fatalf("burn-count wrong, have %v want %v", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHooks is a basic sanity-check of all hooks
|
||||||
|
func TestHooks(t *testing.T) {
|
||||||
|
inner, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
|
inner.SetTxContext(common.Hash{0x11}, 100) // For the log
|
||||||
|
var result []string
|
||||||
|
var wants = []string{
|
||||||
|
"0xaa00000000000000000000000000000000000000.balance: 0->100 (BalanceChangeUnspecified)",
|
||||||
|
"0xaa00000000000000000000000000000000000000.balance: 100->50 (BalanceChangeTransfer)",
|
||||||
|
"0xaa00000000000000000000000000000000000000.nonce: 1336->1337",
|
||||||
|
"0xaa00000000000000000000000000000000000000.code: (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470) ->0x1325 (0xa12ae05590de0c93a00bc7ac773c2fdb621e44f814985e72194f921c0050f728)",
|
||||||
|
"0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000000 ->0x0000000000000000000000000000000000000000000000000000000000000011",
|
||||||
|
"0xaa00000000000000000000000000000000000000.storage slot 0x0000000000000000000000000000000000000000000000000000000000000001: 0x0000000000000000000000000000000000000000000000000000000000000011 ->0x0000000000000000000000000000000000000000000000000000000000000022",
|
||||||
|
"log 100",
|
||||||
|
}
|
||||||
|
emitF := func(format string, a ...any) {
|
||||||
|
result = append(result, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
sdb := NewHookedState(inner, &tracing.Hooks{
|
||||||
|
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
|
emitF("%v.balance: %v->%v (%v)", addr, prev, new, reason)
|
||||||
|
},
|
||||||
|
OnNonceChange: func(addr common.Address, prev, new uint64) {
|
||||||
|
emitF("%v.nonce: %v->%v", addr, prev, new)
|
||||||
|
},
|
||||||
|
OnCodeChange: func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) {
|
||||||
|
emitF("%v.code: %#x (%v) ->%#x (%v)", addr, prevCode, prevCodeHash, code, codeHash)
|
||||||
|
},
|
||||||
|
OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) {
|
||||||
|
emitF("%v.storage slot %v: %v ->%v", addr, slot, prev, new)
|
||||||
|
},
|
||||||
|
OnLog: func(log *types.Log) {
|
||||||
|
emitF("log %v", log.TxIndex)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
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.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"))
|
||||||
|
sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01"))
|
||||||
|
sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x02"))
|
||||||
|
sdb.AddLog(&types.Log{
|
||||||
|
Address: common.Address{0xbb},
|
||||||
|
})
|
||||||
|
for i, want := range wants {
|
||||||
|
if have := result[i]; have != want {
|
||||||
|
t.Fatalf("error event %d, have\n%v\nwant%v\n", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
// Copyright 2024 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 state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stateDBLogger struct {
|
|
||||||
*StateDB
|
|
||||||
hooks *tracing.Hooks
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStateDBLogger(db *StateDB, hooks *tracing.Hooks) *stateDBLogger {
|
|
||||||
s := &stateDBLogger{db, hooks}
|
|
||||||
if s.hooks == nil {
|
|
||||||
s.hooks = new(tracing.Hooks)
|
|
||||||
}
|
|
||||||
db.SetBurnCallback(func(address common.Address, amount *uint256.Int) {
|
|
||||||
if s.hooks.OnBalanceChange != nil {
|
|
||||||
s.hooks.OnBalanceChange(address, amount.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stateDBLogger) AddBalance(address common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
|
|
||||||
prev := s.StateDB.AddBalance(address, amount, reason)
|
|
||||||
if s.hooks.OnBalanceChange != nil {
|
|
||||||
newBalance := new(uint256.Int).Add(&prev, amount)
|
|
||||||
s.hooks.OnBalanceChange(address, prev.ToBig(), newBalance.ToBig(), reason)
|
|
||||||
}
|
|
||||||
return prev
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stateDBLogger) SetNonce(address common.Address, nonce uint64) {
|
|
||||||
s.StateDB.SetNonce(address, nonce)
|
|
||||||
if s.hooks.OnNonceChange != nil {
|
|
||||||
s.hooks.OnNonceChange(address, nonce-1, nonce)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stateDBLogger) SetCode(address common.Address, code []byte) {
|
|
||||||
s.StateDB.SetCode(address, code)
|
|
||||||
if s.hooks.OnCodeChange != nil {
|
|
||||||
s.hooks.OnCodeChange(address, types.EmptyCodeHash, nil, crypto.Keccak256Hash(code), code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stateDBLogger) SetState(address common.Address, key common.Hash, value common.Hash) common.Hash {
|
|
||||||
prev := s.StateDB.SetState(address, key, value)
|
|
||||||
if s.hooks.OnStorageChange != nil && prev != value {
|
|
||||||
s.hooks.OnStorageChange(address, key, prev, value)
|
|
||||||
}
|
|
||||||
return prev
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stateDBLogger) SelfDestruct(address common.Address) uint256.Int {
|
|
||||||
prev := s.StateDB.SelfDestruct(address)
|
|
||||||
if !prev.IsZero() {
|
|
||||||
if s.hooks.OnBalanceChange != nil {
|
|
||||||
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prev
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stateDBLogger) Selfdestruct6780(address common.Address) uint256.Int {
|
|
||||||
prev := s.StateDB.Selfdestruct6780(address)
|
|
||||||
if !prev.IsZero() {
|
|
||||||
if s.hooks.OnBalanceChange != nil {
|
|
||||||
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prev
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stateDBLogger) AddLog(log *types.Log) {
|
|
||||||
// The inner will modify the log (add fields), so invoke that first
|
|
||||||
s.StateDB.AddLog(log)
|
|
||||||
if s.hooks.OnLog != nil {
|
|
||||||
s.hooks.OnLog(log)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
@ -1378,53 +1377,3 @@ func TestStorageDirtiness(t *testing.T) {
|
||||||
state.RevertToSnapshot(snap)
|
state.RevertToSnapshot(snap)
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{0x1}, true)
|
checkDirty(common.Hash{0x1}, common.Hash{0x1}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method tests that the 'burn' from sending-to-selfdestructed accounts
|
|
||||||
// is accounted for.
|
|
||||||
// (There is also a higher-level test in eth/tracers: TestSupplySelfDestruct )
|
|
||||||
func TestBurn(t *testing.T) {
|
|
||||||
// Note: burn can happen even after EIP-6780, if within one single transaction,
|
|
||||||
// the following occur:
|
|
||||||
// 1. contract B creates contract A
|
|
||||||
// 2. contract A is destructed
|
|
||||||
// 3. constract B sends ether to A
|
|
||||||
|
|
||||||
var burned = new(uint256.Int)
|
|
||||||
s, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
|
||||||
sdblog := newStateDBLogger(s, &tracing.Hooks{
|
|
||||||
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
|
|
||||||
if reason == tracing.BalanceDecreaseSelfdestructBurn {
|
|
||||||
burned.Add(burned, uint256.MustFromBig(prev))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
createAndDestroy := func(addr common.Address) {
|
|
||||||
sdblog.AddBalance(addr, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
|
||||||
sdblog.CreateContract(addr)
|
|
||||||
sdblog.SelfDestruct(addr)
|
|
||||||
// sanity-check that balance is now 0
|
|
||||||
if have, want := sdblog.GetBalance(addr), new(uint256.Int); !have.Eq(want) {
|
|
||||||
t.Fatalf("post-destruct balance wrong: have %v want %v", have, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addA := common.Address{0xaa}
|
|
||||||
addB := common.Address{0xbb}
|
|
||||||
addC := common.Address{0xcc}
|
|
||||||
|
|
||||||
// Tx 1: create and destroy address A and B in one tx
|
|
||||||
createAndDestroy(addA)
|
|
||||||
createAndDestroy(addB)
|
|
||||||
sdblog.AddBalance(addA, uint256.NewInt(200), tracing.BalanceChangeUnspecified)
|
|
||||||
sdblog.AddBalance(addB, uint256.NewInt(200), tracing.BalanceChangeUnspecified)
|
|
||||||
sdblog.Finalise(true)
|
|
||||||
|
|
||||||
// Tx 2: create and destroy address C, then commit
|
|
||||||
createAndDestroy(addC)
|
|
||||||
sdblog.AddBalance(addC, uint256.NewInt(200), tracing.BalanceChangeUnspecified)
|
|
||||||
sdblog.Finalise(true)
|
|
||||||
|
|
||||||
s.Commit(0, false)
|
|
||||||
if have, want := burned, uint256.NewInt(600); !have.Eq(want) {
|
|
||||||
t.Fatalf("burn-count wrong, have %v want %v", have, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -52,7 +53,7 @@ func NewStateProcessor(config *params.ChainConfig, chain *HeaderChain) *StatePro
|
||||||
// Process returns the receipts and logs accumulated during the process and
|
// Process returns the receipts and logs accumulated during the process and
|
||||||
// returns the amount of gas that was used in the process. If any of the
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
// transactions failed to execute due to insufficient gas it will return an error.
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||||
var (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
usedGas = new(uint64)
|
usedGas = new(uint64)
|
||||||
|
|
@ -98,7 +99,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
|
var tracingStateDB = vm.StateDB(statedb)
|
||||||
|
if hooks := cfg.Tracer; hooks != nil {
|
||||||
|
tracingStateDB = state.NewHookedState(statedb, hooks)
|
||||||
|
}
|
||||||
// Read requests if Prague is enabled.
|
// Read requests if Prague is enabled.
|
||||||
var requests [][]byte
|
var requests [][]byte
|
||||||
if p.config.IsPrague(block.Number(), block.Time()) {
|
if p.config.IsPrague(block.Number(), block.Time()) {
|
||||||
|
|
@ -109,15 +113,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.
|
||||||
}
|
}
|
||||||
requests = append(requests, depositRequests)
|
requests = append(requests, depositRequests)
|
||||||
// EIP-7002 withdrawals
|
// EIP-7002 withdrawals
|
||||||
withdrawalRequests := ProcessWithdrawalQueue(vmenv, statedb)
|
withdrawalRequests := ProcessWithdrawalQueue(vmenv, tracingStateDB)
|
||||||
requests = append(requests, withdrawalRequests)
|
requests = append(requests, withdrawalRequests)
|
||||||
// EIP-7251 consolidations
|
// EIP-7251 consolidations
|
||||||
consolidationRequests := ProcessConsolidationQueue(vmenv, statedb)
|
consolidationRequests := ProcessConsolidationQueue(vmenv, tracingStateDB)
|
||||||
requests = append(requests, consolidationRequests)
|
requests = append(requests, consolidationRequests)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.chain.engine.Finalize(p.chain, header, statedb, block.Body())
|
p.chain.engine.Finalize(p.chain, header, tracingStateDB, block.Body())
|
||||||
|
|
||||||
return &ProcessResult{
|
return &ProcessResult{
|
||||||
Receipts: receipts,
|
Receipts: receipts,
|
||||||
|
|
@ -130,19 +134,21 @@ func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.
|
||||||
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||||
// this method takes an already created EVM instance as input.
|
// this method takes an already created EVM instance as input.
|
||||||
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb vm.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
|
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
|
var tracingStateDB = vm.StateDB(statedb)
|
||||||
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
if hooks := evm.Config.Tracer; hooks != nil {
|
||||||
if evm.Config.Tracer.OnTxEnd != nil {
|
tracingStateDB = state.NewHookedState(statedb, hooks)
|
||||||
defer func() {
|
if hooks.OnTxStart != nil {
|
||||||
evm.Config.Tracer.OnTxEnd(receipt, err)
|
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
}()
|
}
|
||||||
|
if hooks.OnTxEnd != nil {
|
||||||
|
defer func() { hooks.OnTxEnd(receipt, err) }()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new context to be used in the EVM environment.
|
// Create a new context to be used in the EVM environment.
|
||||||
txContext := NewEVMTxContext(msg)
|
txContext := NewEVMTxContext(msg)
|
||||||
evm.Reset(txContext, statedb)
|
evm.Reset(txContext, tracingStateDB)
|
||||||
|
|
||||||
// Apply the transaction to the current state (included in the env).
|
// Apply the transaction to the current state (included in the env).
|
||||||
result, err := ApplyMessage(evm, msg, gp)
|
result, err := ApplyMessage(evm, msg, gp)
|
||||||
|
|
@ -153,7 +159,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
|
||||||
// Update the state with pending changes.
|
// Update the state with pending changes.
|
||||||
var root []byte
|
var root []byte
|
||||||
if config.IsByzantium(blockNumber) {
|
if config.IsByzantium(blockNumber) {
|
||||||
statedb.Finalise(true)
|
tracingStateDB.Finalise(true)
|
||||||
} else {
|
} else {
|
||||||
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
|
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +169,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
||||||
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb vm.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
|
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||||
// by the tx.
|
// by the tx.
|
||||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
|
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
|
||||||
|
|
@ -204,7 +210,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb vm.StateDB, block
|
||||||
// and uses the input parameters for its environment. It returns the receipt
|
// and uses the input parameters for its environment. It returns the receipt
|
||||||
// for the transaction, gas used and an error if the transaction failed,
|
// for the transaction, gas used and an error if the transaction failed,
|
||||||
// indicating the block was invalid.
|
// indicating the block was invalid.
|
||||||
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb vm.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
|
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
|
||||||
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee)
|
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ type Processor interface {
|
||||||
// Process processes the state changes according to the Ethereum rules by running
|
// Process processes the state changes according to the Ethereum rules by running
|
||||||
// the transaction messages using the statedb and applying any rewards to both
|
// the transaction messages using the statedb and applying any rewards to both
|
||||||
// the processor (coinbase) and any included uncles.
|
// the processor (coinbase) and any included uncles.
|
||||||
Process(block *types.Block, statedb vm.StateDB, cfg vm.Config) (*ProcessResult, error)
|
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessResult contains the values computed by Process.
|
// ProcessResult contains the values computed by Process.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -91,32 +90,9 @@ type StateDB interface {
|
||||||
AddPreimage(common.Hash, []byte)
|
AddPreimage(common.Hash, []byte)
|
||||||
|
|
||||||
Witness() *stateless.Witness
|
Witness() *stateless.Witness
|
||||||
|
|
||||||
|
// Finalise must be invoked at the end of a transaction
|
||||||
Finalise(bool)
|
Finalise(bool)
|
||||||
// IntermediateRoot computes the current root hash of the state trie.
|
|
||||||
// It is called in
|
|
||||||
// - between transactions (pre-byzantium)
|
|
||||||
// - nowadays only after all transactions,
|
|
||||||
// in order to obtain the state root hash .
|
|
||||||
IntermediateRoot(deleteEmptyObjects bool) common.Hash
|
|
||||||
|
|
||||||
// GetLogs returns the logs matching the specified transaction hash, and annotates
|
|
||||||
// them with the given blockNumber and blockHash.
|
|
||||||
GetLogs(txHash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log
|
|
||||||
// TxIndex returns the current transaction index set by SetTxContext.
|
|
||||||
TxIndex() int
|
|
||||||
|
|
||||||
// SetTxContext sets the current transaction hash and index which are
|
|
||||||
// used when the EVM emits new state logs. It should be invoked before
|
|
||||||
// transaction execution.
|
|
||||||
SetTxContext(txHash common.Hash, txIndex int)
|
|
||||||
|
|
||||||
// GetTrie returns the account trie. Verkle-related
|
|
||||||
GetTrie() state.Trie
|
|
||||||
// AccessEvents returns the access-events collected for verkle-witness processing.
|
|
||||||
AccessEvents() *state.AccessEvents
|
|
||||||
|
|
||||||
// Error returns any memorized database failure occurred previously.
|
|
||||||
Error() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
// CallContext provides a basic interface for the EVM calling conventions. The EVM
|
||||||
|
|
|
||||||
|
|
@ -1018,7 +1018,6 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
||||||
}
|
}
|
||||||
// The actual TxContext will be created as part of ApplyTransactionWithEVM.
|
// The actual TxContext will be created as part of ApplyTransactionWithEVM.
|
||||||
vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice, BlobFeeCap: message.BlobGasFeeCap}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
|
vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: message.GasPrice, BlobFeeCap: message.BlobGasFeeCap}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
|
||||||
statedb.SetLogger(tracer.Hooks)
|
|
||||||
|
|
||||||
// Define a meaningful timeout of a single transaction trace
|
// Define a meaningful timeout of a single transaction trace
|
||||||
if config.Timeout != nil {
|
if config.Timeout != nil {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -116,19 +117,17 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
var (
|
var (
|
||||||
signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
|
signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
|
||||||
context = test.Context.toBlockContext(test.Genesis)
|
context = test.Context.toBlockContext(test.Genesis)
|
||||||
state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
|
st = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
|
||||||
)
|
)
|
||||||
state.Close()
|
st.Close()
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
logState := vm.StateDB(st.StateDB)
|
||||||
state.StateDB.SetLogger(tracer.Hooks)
|
if tracer.Hooks != nil {
|
||||||
logState := vm.StateDB(state.StateDB.Wrapped())
|
logState = state.NewHookedState(st.StateDB, tracer.Hooks)
|
||||||
if logState == nil {
|
|
||||||
logState = state.StateDB
|
|
||||||
}
|
}
|
||||||
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -353,7 +352,7 @@ func TestInternals(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
state := tests.MakePreState(rawdb.NewMemoryDatabase(),
|
st := tests.MakePreState(rawdb.NewMemoryDatabase(),
|
||||||
types.GenesisAlloc{
|
types.GenesisAlloc{
|
||||||
to: types.Account{
|
to: types.Account{
|
||||||
Code: tc.code,
|
Code: tc.code,
|
||||||
|
|
@ -362,12 +361,11 @@ func TestInternals(t *testing.T) {
|
||||||
Balance: big.NewInt(500000000000000),
|
Balance: big.NewInt(500000000000000),
|
||||||
},
|
},
|
||||||
}, false, rawdb.HashScheme)
|
}, false, rawdb.HashScheme)
|
||||||
defer state.Close()
|
defer st.Close()
|
||||||
state.StateDB.SetLogger(tc.tracer.Hooks)
|
|
||||||
|
|
||||||
logState := vm.StateDB(state.StateDB.Wrapped())
|
logState := vm.StateDB(st.StateDB)
|
||||||
if logState == nil {
|
if hooks := tc.tracer.Hooks; hooks != nil {
|
||||||
logState = state.StateDB
|
logState = state.NewHookedState(st.StateDB, hooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
|
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
|
||||||
return fmt.Errorf("failed to create call tracer: %v", err)
|
return fmt.Errorf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.StateDB.SetLogger(tracer.Hooks)
|
|
||||||
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
|
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.StateDB.SetLogger(tracer.Hooks)
|
|
||||||
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -1208,11 +1208,16 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
|
||||||
if precompiles != nil {
|
if precompiles != nil {
|
||||||
evm.SetPrecompiles(precompiles)
|
evm.SetPrecompiles(precompiles)
|
||||||
}
|
}
|
||||||
|
res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp)
|
||||||
return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp)
|
// If an internal state error occurred, let that have precedence. Otherwise,
|
||||||
|
// a "trie root missing" type of error will masquerade as e.g. "insufficient gas"
|
||||||
|
if err := state.Error(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state vm.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) {
|
func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) {
|
||||||
// Wait for the context to be done and cancel the evm. Even if the
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
// EVM has finished, cancelling may be done (repeatedly)
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -1222,9 +1227,6 @@ func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, st
|
||||||
|
|
||||||
// Execute the message.
|
// Execute the message.
|
||||||
result, err := core.ApplyMessage(evm, msg, gp)
|
result, err := core.ApplyMessage(evm, msg, gp)
|
||||||
if err := state.Error(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the timer caused an abort, return an appropriate error message
|
// If the timer caused an abort, return an appropriate error message
|
||||||
if evm.Cancelled() {
|
if evm.Cancelled() {
|
||||||
|
|
|
||||||
|
|
@ -187,10 +187,9 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
}
|
}
|
||||||
evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, sim.chainConfig, *vmConfig)
|
evm = vm.NewEVM(blockContext, vm.TxContext{GasPrice: new(big.Int)}, sim.state, sim.chainConfig, *vmConfig)
|
||||||
)
|
)
|
||||||
sim.state.SetLogger(tracer.Hooks())
|
var tracingStateDB = vm.StateDB(sim.state)
|
||||||
logState := vm.StateDB(sim.state.Wrapped())
|
if hooks := tracer.Hooks(); hooks != nil {
|
||||||
if logState == nil {
|
tracingStateDB = state.NewHookedState(sim.state, hooks)
|
||||||
logState = vm.StateDB(sim.state)
|
|
||||||
}
|
}
|
||||||
// It is possible to override precompiles with EVM bytecode, or
|
// It is possible to override precompiles with EVM bytecode, or
|
||||||
// move them to another address.
|
// move them to another address.
|
||||||
|
|
@ -209,8 +208,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
tracer.reset(tx.Hash(), uint(i))
|
tracer.reset(tx.Hash(), uint(i))
|
||||||
// EoA check is always skipped, even in validation mode.
|
// EoA check is always skipped, even in validation mode.
|
||||||
msg := call.ToMessage(header.BaseFee, !sim.validate, true)
|
msg := call.ToMessage(header.BaseFee, !sim.validate, true)
|
||||||
evm.Reset(core.NewEVMTxContext(msg), logState)
|
evm.Reset(core.NewEVMTxContext(msg), tracingStateDB)
|
||||||
result, err := applyMessageWithEVM(ctx, evm, msg, logState, timeout, sim.gp)
|
result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
txErr := txValidationError(err)
|
txErr := txValidationError(err)
|
||||||
return nil, nil, txErr
|
return nil, nil, txErr
|
||||||
|
|
@ -218,7 +217,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
// Update the state with pending changes.
|
// Update the state with pending changes.
|
||||||
var root []byte
|
var root []byte
|
||||||
if sim.chainConfig.IsByzantium(blockContext.BlockNumber) {
|
if sim.chainConfig.IsByzantium(blockContext.BlockNumber) {
|
||||||
sim.state.Finalise(true)
|
tracingStateDB.Finalise(true)
|
||||||
} else {
|
} else {
|
||||||
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
|
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue