Skip to content

Commit

Permalink
core/state: make inner statedb explicit, implement alternative loggin…
Browse files Browse the repository at this point in the history
…g 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
  • Loading branch information
holiman committed Oct 18, 2024
1 parent 4de4fa4 commit 8ca0cd5
Show file tree
Hide file tree
Showing 15 changed files with 425 additions and 256 deletions.
8 changes: 2 additions & 6 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1774,7 +1774,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
if err != nil {
return nil, it.index, err
}
statedb.SetLogger(bc.logger)

// If we are past Byzantium, enable prefetching to pull in trie node paths
// while processing transactions. Before Byzantium the prefetcher is mostly
Expand Down Expand Up @@ -1893,13 +1892,10 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
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
pstart := time.Now()
res, err := bc.processor.Process(block, wStateDb, bc.vmConfig)
res, err := bc.processor.Process(block, statedb, bc.vmConfig)
if err != nil {
bc.reportBlock(block, res, err)
return nil, err
Expand Down
24 changes: 1 addition & 23 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@ type StateDB struct {
db Database
prefetcher *triePrefetcher
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.
// It will be updated when the Commit is called.
Expand Down Expand Up @@ -192,19 +188,6 @@ func New(root common.Hash, db Database) (*StateDB, error) {
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
// state trie concurrently while the state is mutated so that when we reach the
// commit phase, most of the needed data is already hot.
Expand Down Expand Up @@ -754,11 +737,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
delete(s.stateObjects, obj.address)
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
// set indefinitely). Note only the first occurred self-destruct
// event is tracked.
Expand Down
242 changes: 242 additions & 0 deletions core/state/statedb_hooked.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
Loading

0 comments on commit 8ca0cd5

Please sign in to comment.