core/state, core/vm, eth/tracers: move state log mechanisms to separate layer

core/state: wip move state log mechanism in to a separate layer
core/state: fix tests
core/state: fix miscalc in OnBalanceChange
eth/tracers: fix tests
core/state: re-enable verkle witness generation
internal/ethapi: fix simulation + new logging schema
This commit is contained in:
Martin Holst Swende 2024-10-10 14:35:42 +02:00
parent 3ff73d46b3
commit ccaab0ae10
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
21 changed files with 266 additions and 108 deletions

View file

@ -28,6 +28,7 @@ import (
"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/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
@ -349,7 +350,7 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H
}
// Finalize implements consensus.Engine and processes withdrawals on top.
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
if !beacon.IsPoSHeader(header) {
beacon.ethone.Finalize(chain, header, state, body)
return

View file

@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@ -580,7 +581,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header
// Finalize implements consensus.Engine. There is no post-transaction
// consensus rules in clique, do nothing here.
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
// No block rewards in PoA, so the state remains as is
}

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@ -88,7 +89,7 @@ type Engine interface {
//
// Note: The state database might be updated to reflect any consensus rules
// that happen at finalization (e.g. block rewards).
Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body)
Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body)
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
// rewards or process withdrawals) and assembles the final block.

View file

@ -31,6 +31,7 @@ import (
"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/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
@ -502,7 +503,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
}
// Finalize implements consensus.Engine, accumulating the block and uncle rewards.
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body) {
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
// Accumulate any block and uncle rewards
accumulateRewards(chain.Config(), state, header, body.Uncles)
}
@ -565,7 +566,7 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
// accumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) {
func accumulateRewards(config *params.ChainConfig, stateDB vm.StateDB, header *types.Header, uncles []*types.Header) {
// Select the correct block reward based on chain progression
blockReward := FrontierBlockReward
if config.IsByzantium(header.Number) {

View file

@ -21,11 +21,10 @@ import (
"errors"
"math/big"
"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/params"
"github.com/holiman/uint256"
)
var (
@ -74,7 +73,7 @@ func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header)
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
// rules, transferring all balances of a set of DAO accounts to a single refund
// contract.
func ApplyDAOHardFork(statedb *state.StateDB) {
func ApplyDAOHardFork(statedb vm.StateDB) {
// Retrieve the contract to refund balances into
if !statedb.Exist(params.DAORefundContract) {
statedb.CreateAccount(params.DAORefundContract)
@ -82,7 +81,8 @@ func ApplyDAOHardFork(statedb *state.StateDB) {
// Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList() {
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract)
statedb.SetBalance(addr, new(uint256.Int), tracing.BalanceDecreaseDaoAccount)
balance := statedb.GetBalance(addr)
statedb.AddBalance(params.DAORefundContract, balance, tracing.BalanceIncreaseDaoContract)
statedb.SubBalance(addr, balance, tracing.BalanceDecreaseDaoAccount)
}
}

View file

@ -1893,10 +1893,13 @@ 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, statedb, bc.vmConfig)
res, err := bc.processor.Process(block, wStateDb, bc.vmConfig)
if err != nil {
bc.reportBlock(block, res, err)
return nil, err

View file

@ -23,7 +23,6 @@ import (
"time"
"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/ethereum/go-ethereum/log"
@ -208,19 +207,18 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
}
// SetState updates a value in account storage.
func (s *stateObject) SetState(key, value common.Hash) {
// It returns the previous value
func (s *stateObject) SetState(key, value common.Hash) common.Hash {
// If the new value is the same as old, don't set. Otherwise, track only the
// dirty changes, supporting reverting all of it back to no change.
prev, origin := s.getState(key)
if prev == value {
return
return prev
}
// New value is different, update and journal the change
s.db.journal.storageChange(s.address, key, prev, origin)
s.setState(key, value, origin)
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
s.db.logger.OnStorageChange(s.address, key, prev, value)
}
return prev
}
// setState updates a value in account dirty storage. The dirtiness will be
@ -448,33 +446,25 @@ func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) {
// AddBalance adds amount to s's balance.
// It is used to add funds to the destination account of a transfer.
func (s *stateObject) AddBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
// returns the previous balance
func (s *stateObject) AddBalance(amount *uint256.Int) uint256.Int {
// EIP161: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect.
if amount.IsZero() {
if s.empty() {
s.touch()
}
return
return *(s.Balance())
}
s.SetBalance(new(uint256.Int).Add(s.Balance(), amount), reason)
return s.SetBalance(new(uint256.Int).Add(s.Balance(), amount))
}
// SubBalance removes amount from s's balance.
// It is used to remove funds from the origin account of a transfer.
func (s *stateObject) SubBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
if amount.IsZero() {
return
}
s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount), reason)
}
func (s *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
// SetBalance sets the balance for the object, and returns the prevous balance
func (s *stateObject) SetBalance(amount *uint256.Int) uint256.Int {
prev := *s.data.Balance
s.db.journal.balanceChange(s.address, s.data.Balance)
if s.db.logger != nil && s.db.logger.OnBalanceChange != nil {
s.db.logger.OnBalanceChange(s.address, s.Balance().ToBig(), amount.ToBig(), reason)
}
s.setBalance(amount)
return prev
}
func (s *stateObject) setBalance(amount *uint256.Int) {
@ -547,10 +537,6 @@ func (s *stateObject) CodeSize() int {
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
s.db.journal.setCode(s.address)
if s.db.logger != nil && s.db.logger.OnCodeChange != nil {
// TODO remove prevcode from this callback
s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), nil, codeHash, code)
}
s.setCode(codeHash, code)
}
@ -562,9 +548,6 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
func (s *stateObject) SetNonce(nonce uint64) {
s.db.journal.nonceChange(s.address, s.data.Nonce)
if s.db.logger != nil && s.db.logger.OnNonceChange != nil {
s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce)
}
s.setNonce(nonce)
}

View file

@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"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/triedb"
@ -48,11 +47,11 @@ func TestDump(t *testing.T) {
// generate a few entries
obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
obj1.AddBalance(uint256.NewInt(22), tracing.BalanceChangeUnspecified)
obj1.AddBalance(uint256.NewInt(22))
obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02}))
obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified)
obj3.SetBalance(uint256.NewInt(44))
// write some of them to the trie
s.state.updateStateObject(obj1)
@ -106,13 +105,13 @@ func TestIterativeDump(t *testing.T) {
// generate a few entries
obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
obj1.AddBalance(uint256.NewInt(22), tracing.BalanceChangeUnspecified)
obj1.AddBalance(uint256.NewInt(22))
obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02}))
obj3.SetBalance(uint256.NewInt(44), tracing.BalanceChangeUnspecified)
obj3.SetBalance(uint256.NewInt(44))
obj4 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x00}))
obj4.AddBalance(uint256.NewInt(1337), tracing.BalanceChangeUnspecified)
obj4.AddBalance(uint256.NewInt(1337))
// write some of them to the trie
s.state.updateStateObject(obj1)
@ -200,7 +199,7 @@ func TestCreateObjectRevert(t *testing.T) {
state.CreateAccount(addr)
so0 := state.getStateObject(addr)
so0.SetBalance(uint256.NewInt(42), tracing.BalanceChangeUnspecified)
so0.SetBalance(uint256.NewInt(42))
so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
state.setStateObject(so0)

View file

@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"maps"
"math/big"
"slices"
"sync"
"sync/atomic"
@ -82,8 +81,11 @@ type StateDB struct {
db Database
prefetcher *triePrefetcher
trie Trie
logger *tracing.Hooks
reader Reader
onSelfDestructBurn func(common.Address, *uint256.Int)
shim *stateDBLogger
reader Reader
// originalRoot is the pre-state root, before any changes were made.
// It will be updated when the Commit is called.
@ -190,9 +192,17 @@ 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.logger = l
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
@ -248,9 +258,6 @@ func (s *StateDB) AddLog(log *types.Log) {
log.TxHash = s.thash
log.TxIndex = uint(s.txIndex)
log.Index = s.logSize
if s.logger != nil && s.logger.OnLog != nil {
s.logger.OnLog(log)
}
s.logs[s.thash] = append(s.logs[s.thash], log)
s.logSize++
}
@ -410,25 +417,32 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
*/
// AddBalance adds amount to the account associated with addr.
func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) {
func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount, reason)
return stateObject.AddBalance(amount)
}
return uint256.Int{}
}
// SubBalance subtracts amount from the account associated with addr.
func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) {
func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) uint256.Int {
stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SubBalance(amount, reason)
var prev uint256.Int
if amount.IsZero() {
return prev
}
if stateObject != nil {
prev = *(stateObject.Balance())
stateObject.SetBalance(new(uint256.Int).Sub(stateObject.Balance(), amount))
}
return prev
}
func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) {
stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalance(amount, reason)
stateObject.SetBalance(amount)
}
}
@ -446,11 +460,11 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) {
}
}
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetState(key, value)
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash {
if stateObject := s.getOrNewStateObject(addr); stateObject != nil {
return stateObject.SetState(key, value)
}
return common.Hash{}
}
// SetStorage replaces the entire storage for the specified account with given
@ -478,7 +492,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
if obj != nil {
newObj.SetCode(common.BytesToHash(obj.CodeHash()), obj.code)
newObj.SetNonce(obj.Nonce())
newObj.SetBalance(obj.Balance(), tracing.BalanceChangeUnspecified)
newObj.SetBalance(obj.Balance())
}
}
@ -487,15 +501,17 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
//
// The account's state object is still available until the state is committed,
// getStateObject will return a non-nil account after SelfDestruct.
func (s *StateDB) SelfDestruct(addr common.Address) {
func (s *StateDB) SelfDestruct(addr common.Address) uint256.Int {
stateObject := s.getStateObject(addr)
var prevBalance uint256.Int
if stateObject == nil {
return
return prevBalance
}
prevBalance = *(stateObject.Balance())
// Regardless of whether it is already destructed or not, we do have to
// journal the balance-change, if we set it to zero here.
if !stateObject.Balance().IsZero() {
stateObject.SetBalance(new(uint256.Int), tracing.BalanceDecreaseSelfdestruct)
stateObject.SetBalance(new(uint256.Int))
}
// If it is already marked as self-destructed, we do not need to add it
// for journalling a second time.
@ -503,16 +519,18 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
s.journal.destruct(addr)
stateObject.markSelfdestructed()
}
return prevBalance
}
func (s *StateDB) Selfdestruct6780(addr common.Address) {
func (s *StateDB) Selfdestruct6780(addr common.Address) uint256.Int {
stateObject := s.getStateObject(addr)
if stateObject == nil {
return
return uint256.Int{}
}
if stateObject.newContract {
s.SelfDestruct(addr)
return s.SelfDestruct(addr)
}
return *(stateObject.Balance())
}
// SetTransientState sets transient storage for a given account. It
@ -738,8 +756,8 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
s.markDelete(addr)
// If ether was sent to account post-selfdestruct it is burnt.
if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 {
s.logger.OnBalanceChange(obj.address, bal.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
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

View file

@ -0,0 +1,104 @@
// 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)
}
}

View file

@ -170,7 +170,7 @@ func TestCopy(t *testing.T) {
for i := byte(0); i < 255; i++ {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified)
obj.AddBalance(uint256.NewInt(uint64(i)))
orig.updateStateObject(obj)
}
orig.Finalise(false)
@ -187,9 +187,9 @@ func TestCopy(t *testing.T) {
copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
origObj.AddBalance(uint256.NewInt(2*uint64(i)), tracing.BalanceChangeUnspecified)
copyObj.AddBalance(uint256.NewInt(3*uint64(i)), tracing.BalanceChangeUnspecified)
ccopyObj.AddBalance(uint256.NewInt(4*uint64(i)), tracing.BalanceChangeUnspecified)
origObj.AddBalance(uint256.NewInt(2 * uint64(i)))
copyObj.AddBalance(uint256.NewInt(3 * uint64(i)))
ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i)))
orig.updateStateObject(origObj)
copy.updateStateObject(copyObj)
@ -236,7 +236,7 @@ func TestCopyWithDirtyJournal(t *testing.T) {
// Fill up the initial states
for i := byte(0); i < 255; i++ {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified)
obj.AddBalance(uint256.NewInt(uint64(i)))
obj.data.Root = common.HexToHash("0xdeadbeef")
orig.updateStateObject(obj)
}
@ -246,7 +246,9 @@ func TestCopyWithDirtyJournal(t *testing.T) {
// modify all in memory without finalizing
for i := byte(0); i < 255; i++ {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.SubBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified)
amount := uint256.NewInt(uint64(i))
obj.SetBalance(new(uint256.Int).Sub(obj.Balance(), amount))
orig.updateStateObject(obj)
}
cpy := orig.Copy()
@ -280,7 +282,7 @@ func TestCopyObjectState(t *testing.T) {
// Fill up the initial states
for i := byte(0); i < 5; i++ {
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(uint256.NewInt(uint64(i)), tracing.BalanceChangeUnspecified)
obj.AddBalance(uint256.NewInt(uint64(i)))
obj.data.Root = common.HexToHash("0xdeadbeef")
orig.updateStateObject(obj)
}

View file

@ -22,7 +22,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"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/ethdb"
@ -62,7 +61,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c
obj := state.getOrNewStateObject(common.BytesToAddress([]byte{i}))
acc := &testAccount{address: common.BytesToAddress([]byte{i})}
obj.AddBalance(uint256.NewInt(uint64(11*i)), tracing.BalanceChangeUnspecified)
obj.AddBalance(uint256.NewInt(uint64(11 * i)))
acc.balance = uint256.NewInt(uint64(11 * i))
obj.SetNonce(uint64(42 * i))

View file

@ -22,7 +22,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"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/vm"
"github.com/ethereum/go-ethereum/crypto"
@ -53,7 +52,7 @@ func NewStateProcessor(config *params.ChainConfig, chain *HeaderChain) *StatePro
// 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
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.Config) (*ProcessResult, error) {
var (
receipts types.Receipts
usedGas = new(uint64)
@ -75,6 +74,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// Apply pre-execution system calls.
context = NewEVMBlockContext(header, p.chain, nil)
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
@ -130,7 +130,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
// and uses the input parameters for its environment similar to ApplyTransaction. However,
// this method takes an already created EVM instance as input.
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) {
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) {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
if evm.Config.Tracer.OnTxEnd != nil {
@ -139,6 +139,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
}()
}
}
// Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb)
@ -162,7 +163,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
}
// MakeReceipt generates the receipt object for a transaction given its execution result.
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 {
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 {
// Create a new receipt for the transaction, storing the intermediate root and gas used
// by the tx.
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
@ -203,7 +204,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
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) {
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) {
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee)
if err != nil {
return nil, err
@ -217,7 +218,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
// contract. This method is exported to be used in tests.
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb vm.StateDB) {
if tracer := vmenv.Config.Tracer; tracer != nil {
if tracer.OnSystemCallStart != nil {
tracer.OnSystemCallStart()
@ -243,7 +244,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
// ProcessParentBlockHash stores the parent block hash in the history storage contract
// as per EIP-2935.
func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb vm.StateDB) {
if tracer := vmenv.Config.Tracer; tracer != nil {
if tracer.OnSystemCallStart != nil {
tracer.OnSystemCallStart()
@ -269,17 +270,17 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state.
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
// It returns the opaque request data returned by the contract.
func ProcessWithdrawalQueue(vmenv *vm.EVM, statedb *state.StateDB) []byte {
func ProcessWithdrawalQueue(vmenv *vm.EVM, statedb vm.StateDB) []byte {
return processRequestsSystemCall(vmenv, statedb, 0x01, params.WithdrawalQueueAddress)
}
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
// It returns the opaque request data returned by the contract.
func ProcessConsolidationQueue(vmenv *vm.EVM, statedb *state.StateDB) []byte {
func ProcessConsolidationQueue(vmenv *vm.EVM, statedb vm.StateDB) []byte {
return processRequestsSystemCall(vmenv, statedb, 0x02, params.ConsolidationQueueAddress)
}
func processRequestsSystemCall(vmenv *vm.EVM, statedb *state.StateDB, requestType byte, addr common.Address) []byte {
func processRequestsSystemCall(vmenv *vm.EVM, statedb vm.StateDB, requestType byte, addr common.Address) []byte {
if tracer := vmenv.Config.Tracer; tracer != nil {
if tracer.OnSystemCallStart != nil {
tracer.OnSystemCallStart()

View file

@ -48,7 +48,7 @@ type Processor interface {
// 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 processor (coinbase) and any included uncles.
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error)
Process(block *types.Block, statedb vm.StateDB, cfg vm.Config) (*ProcessResult, error)
}
// ProcessResult contains the values computed by Process.

View file

@ -20,6 +20,7 @@ import (
"math/big"
"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/tracing"
"github.com/ethereum/go-ethereum/core/types"
@ -33,8 +34,8 @@ type StateDB interface {
CreateAccount(common.Address)
CreateContract(common.Address)
SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason)
AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason)
SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int
AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int
GetBalance(common.Address) *uint256.Int
GetNonce(common.Address) uint64
@ -51,16 +52,16 @@ type StateDB interface {
GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
SetState(common.Address, common.Hash, common.Hash) common.Hash
GetStorageRoot(addr common.Address) common.Hash
GetTransientState(addr common.Address, key common.Hash) common.Hash
SetTransientState(addr common.Address, key, value common.Hash)
SelfDestruct(common.Address)
SelfDestruct(common.Address) uint256.Int
HasSelfDestructed(common.Address) bool
Selfdestruct6780(common.Address)
Selfdestruct6780(common.Address) uint256.Int
// Exist reports whether the given account exists in state.
// Notably this should also return true for self-destructed accounts.
@ -90,6 +91,32 @@ type StateDB interface {
AddPreimage(common.Hash, []byte)
Witness() *stateless.Witness
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

View file

@ -126,11 +126,15 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
}
state.StateDB.SetLogger(tracer.Hooks)
logState := vm.StateDB(state.StateDB.Wrapped())
if logState == nil {
logState = state.StateDB
}
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
@ -360,6 +364,12 @@ func TestInternals(t *testing.T) {
}, false, rawdb.HashScheme)
defer state.Close()
state.StateDB.SetLogger(tc.tracer.Hooks)
logState := vm.StateDB(state.StateDB.Wrapped())
if logState == nil {
logState = state.StateDB
}
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
To: &to,
Value: big.NewInt(0),
@ -373,7 +383,7 @@ func TestInternals(t *testing.T) {
Origin: origin,
GasPrice: tx.GasPrice(),
}
evm := vm.NewEVM(context, txContext, state.StateDB, config, vm.Config{Tracer: tc.tracer.Hooks})
evm := vm.NewEVM(context, txContext, logState, config, vm.Config{Tracer: tc.tracer.Hooks})
msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0))
if err != nil {
t.Fatalf("test %v: failed to create message: %v", tc.name, err)

View file

@ -597,6 +597,7 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG
}
func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) {
t.Helper()
want, err := json.Marshal(expected)
if err != nil {
t.Fatalf("failed to marshal expected value to JSON: %v", err)
@ -608,6 +609,6 @@ func compareAsJSON(t *testing.T, expected interface{}, actual interface{}) {
}
if !bytes.Equal(want, have) {
t.Fatalf("incorrect supply info: expected %s, got %s", string(want), string(have))
t.Fatalf("incorrect supply info:\nwant %s\nhave %s", string(want), string(have))
}
}

View file

@ -49,9 +49,11 @@ type dummyStatedb struct {
state.StateDB
}
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} }
func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {}
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func (*dummyStatedb) GetState(_ common.Address, _ common.Hash) common.Hash { return common.Hash{} }
func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) common.Hash {
return common.Hash{}
}
func TestStoreCapture(t *testing.T) {
var (

View file

@ -1212,7 +1212,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
return applyMessageWithEVM(ctx, evm, msg, state, timeout, gp)
}
func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state *state.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) {
func applyMessageWithEVM(ctx context.Context, evm *vm.EVM, msg *core.Message, state vm.StateDB, timeout time.Duration, gp *core.GasPool) (*core.ExecutionResult, error) {
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
go func() {

View file

@ -2192,6 +2192,7 @@ func TestSimulateV1(t *testing.T) {
t.Fatalf("failed to unmarshal result: %v", err)
}
if !reflect.DeepEqual(have, tc.want) {
t.Log(string(resBytes))
t.Errorf("test %s, result mismatch, have\n%v\n, want\n%v\n", tc.name, have, tc.want)
}
})

View file

@ -188,6 +188,10 @@ 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)
)
sim.state.SetLogger(tracer.Hooks())
logState := vm.StateDB(sim.state.Wrapped())
if logState == nil {
logState = vm.StateDB(sim.state)
}
// It is possible to override precompiles with EVM bytecode, or
// move them to another address.
if precompiles != nil {
@ -205,8 +209,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
tracer.reset(tx.Hash(), uint(i))
// EoA check is always skipped, even in validation mode.
msg := call.ToMessage(header.BaseFee, !sim.validate, true)
evm.Reset(core.NewEVMTxContext(msg), sim.state)
result, err := applyMessageWithEVM(ctx, evm, msg, sim.state, timeout, sim.gp)
evm.Reset(core.NewEVMTxContext(msg), logState)
result, err := applyMessageWithEVM(ctx, evm, msg, logState, timeout, sim.gp)
if err != nil {
txErr := txValidationError(err)
return nil, nil, txErr