fix: bal prefetcher empty account bug caused by merging 7702 code

This commit is contained in:
Po 2025-08-05 07:28:48 +02:00
parent 86a72911a7
commit af087d0581
6 changed files with 43 additions and 101 deletions

View file

@ -47,7 +47,6 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
mgasps = float64(st.usedGas) * 1000 / float64(elapsed) mgasps = float64(st.usedGas) * 1000 / float64(elapsed)
) )
// Update the Mgas per second gauge // Update the Mgas per second gauge
chainMgaspsGauge.Update(int64(mgasps))
mgaspsHist.Update(int64(mgasps)) mgaspsHist.Update(int64(mgasps))
// If we're at the last block of the batch or report period reached, log // If we're at the last block of the batch or report period reached, log

View file

@ -52,7 +52,7 @@ func (s *SequentialPrestateProvider) PrestateAtIndex(i int) (*state.StateDB, err
} }
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
_, err = ApplyTransactionWithEVM(msg, s.gp, statedb, blockNumber, blockHash, tx, s.usedGas, s.evm) _, err = ApplyTransactionWithEVM(msg, s.gp, statedb, blockNumber, blockHash, s.block.Time(), tx, s.usedGas, s.evm)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
} }

View file

@ -197,7 +197,7 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
} }
// todo: handle gp race // todo: handle gp race
gpcp := *gp gpcp := *gp
receipt, entries, err := ApplyTransactionWithParallelEVM(msg, &gpcp, cleanStatedb, blockNumber, blockHash, tx, usedGas, evm) receipt, entries, err := ApplyTransactionWithParallelEVM(msg, &gpcp, cleanStatedb, blockNumber, blockHash, block.Time(), tx, usedGas, evm)
if err != nil { if err != nil {
return err return err
} }
@ -234,8 +234,7 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
} }
// Read requests if Prague is enabled. // Read requests if Prague is enabled.
// commented out EIP-7002, EIP-7251 and p.chain.engine.Finalize for now, since statedb might cause concurrent map writes (journal.go:211) when postState = statedb.Copy()
// evm := vm.NewEVM(context, statedb, p.config, cfg)
var requests [][]byte var requests [][]byte
if p.config.IsPrague(block.Number(), block.Time()) { if p.config.IsPrague(block.Number(), block.Time()) {
requests = [][]byte{} requests = [][]byte{}
@ -243,18 +242,25 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil { if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil {
return nil, err return nil, err
} }
// // EIP-7002 // Commented out EIP-7002, EIP-7251 and p.chain.engine.Finalize for now, since statedb might cause concurrent map writes (journal.go:211) when postState = statedb.Copy()
// if err := ProcessWithdrawalQueue(&requests, evm); err != nil { if preStateType == SeqPreState {
// return nil, err evm := vm.NewEVM(context, statedb, p.config, cfg)
// } // // EIP-7002
// // EIP-7251 if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
// if err := ProcessConsolidationQueue(&requests, evm); err != nil { return nil, err
// return nil, err }
// } // // EIP-7251
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
return nil, err
}
}
} }
// 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()) if preStateType == SeqPreState {
p.chain.engine.Finalize(p.chain, header, statedb, block.Body())
}
return &ProcessResult{ return &ProcessResult{
Receipts: receipts, Receipts: receipts,
@ -264,7 +270,7 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
}, nil }, nil
} }
func ApplyTransactionWithParallelEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, entries []state.JournalEntry, err error) { func ApplyTransactionWithParallelEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, entries []state.JournalEntry, err error) {
if hooks := evm.Config.Tracer; hooks != nil { if hooks := evm.Config.Tracer; hooks != nil {
if hooks.OnTxStart != nil { if hooks.OnTxStart != nil {
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
@ -295,5 +301,5 @@ func ApplyTransactionWithParallelEVM(msg *Message, gp *GasPool, statedb *state.S
statedb.AccessEvents().Merge(evm.AccessEvents) statedb.AccessEvents().Merge(evm.AccessEvents)
} }
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), entries, nil return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, *usedGas, root), entries, nil
} }

View file

@ -73,9 +73,6 @@ type StateReader interface {
// - Returns an error only if an unexpected issue occurs // - Returns an error only if an unexpected issue occurs
// - The returned storage slot is safe to modify after the call // - The returned storage slot is safe to modify after the call
Storage(addr common.Address, slot common.Hash) (common.Hash, error) Storage(addr common.Address, slot common.Hash) (common.Hash, error)
AccountBAL(addr common.Address) (*types.StateAccount, error)
StorageBAL(addr common.Address, slot common.Hash) (common.Hash, error)
} }
// Reader defines the interface for accessing accounts, storage slots and contract // Reader defines the interface for accessing accounts, storage slots and contract
@ -191,10 +188,6 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
return acct, nil return acct, nil
} }
func (r *flatReader) AccountBAL(addr common.Address) (*types.StateAccount, error) {
return r.Account(addr)
}
// Storage implements StateReader, retrieving the storage slot specified by the // Storage implements StateReader, retrieving the storage slot specified by the
// address and slot key. // address and slot key.
// //
@ -223,10 +216,6 @@ func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash,
return value, nil return value, nil
} }
func (r *flatReader) StorageBAL(addr common.Address, key common.Hash) (common.Hash, error) {
return r.Storage(addr, key)
}
// trieReader implements the StateReader interface, providing functions to access // trieReader implements the StateReader interface, providing functions to access
// state from the referenced trie. // state from the referenced trie.
// //
@ -293,14 +282,6 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
return r.account(addr) return r.account(addr)
} }
func (r *trieReader) AccountBAL(addr common.Address) (*types.StateAccount, error) {
account, err := r.mainTrie.GetAccount(addr)
if err != nil {
return nil, err
}
return account, nil
}
// Storage implements StateReader, retrieving the storage slot specified by the // Storage implements StateReader, retrieving the storage slot specified by the
// address and slot key. // address and slot key.
// //
@ -347,10 +328,6 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
return value, nil return value, nil
} }
func (r *trieReader) StorageBAL(addr common.Address, key common.Hash) (common.Hash, error) {
return r.Storage(addr, key)
}
// multiStateReader is the aggregation of a list of StateReader interface, // multiStateReader is the aggregation of a list of StateReader interface,
// providing state access by leveraging all readers. The checking priority // providing state access by leveraging all readers. The checking priority
// is determined by the position in the reader list. // is determined by the position in the reader list.
@ -391,24 +368,6 @@ func (r *multiStateReader) Account(addr common.Address) (*types.StateAccount, er
return nil, errors.Join(errs...) return nil, errors.Join(errs...)
} }
func (r *multiStateReader) AccountBAL(addr common.Address) (*types.StateAccount, error) {
var errs []error
for _, reader := range r.readers {
switch reader.(type) {
case *flatReader:
{
acct, err := reader.AccountBAL(addr)
if err == nil {
return acct, nil
}
errs = append(errs, err)
}
}
}
errs = append(errs, errors.New("no flatReader"))
return nil, errors.Join(errs...)
}
// Storage implementing StateReader interface, retrieving the storage slot // Storage implementing StateReader interface, retrieving the storage slot
// associated with a particular account address and slot key. // associated with a particular account address and slot key.
// //
@ -427,24 +386,6 @@ func (r *multiStateReader) Storage(addr common.Address, slot common.Hash) (commo
return common.Hash{}, errors.Join(errs...) return common.Hash{}, errors.Join(errs...)
} }
func (r *multiStateReader) StorageBAL(addr common.Address, slot common.Hash) (common.Hash, error) {
var errs []error
for _, reader := range r.readers {
switch reader.(type) {
case *flatReader:
{
slot, err := reader.Storage(addr, slot)
if err == nil {
return slot, nil
}
errs = append(errs, err)
}
}
}
errs = append(errs, errors.New("no flatReader"))
return common.Hash{}, errors.Join(errs...)
}
// reader is the wrapper of ContractCodeReader and StateReader interface. // reader is the wrapper of ContractCodeReader and StateReader interface.
type reader struct { type reader struct {
ContractCodeReader ContractCodeReader
@ -690,6 +631,9 @@ func (r *readerWithBAL) Account(addr common.Address) (*types.StateAccount, error
acct, err := r.Reader.Account(addr) acct, err := r.Reader.Account(addr)
if acct == nil || err != nil { if acct == nil || err != nil {
// acct is nil, just return the postAcct // acct is nil, just return the postAcct
if acctVal.Nonce == 0 && acctVal.Balance == nil && len(acctVal.Code) == 0 {
return nil, fmt.Errorf("account not exist")
}
return &types.StateAccount{ return &types.StateAccount{
Nonce: acctVal.Nonce, Nonce: acctVal.Nonce,
Balance: acctVal.Balance, Balance: acctVal.Balance,

View file

@ -249,8 +249,13 @@ func New(root common.Hash, db Database) (*StateDB, error) {
// NewWithReader creates a new state for the specified state root. Unlike New, // NewWithReader creates a new state for the specified state root. Unlike New,
// this function accepts an additional Reader which is bound to the given root. // this function accepts an additional Reader which is bound to the given root.
func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) { func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
sdb := &StateDB{ sdb := &StateDB{
db: db, db: db,
trie: tr,
originalRoot: root, originalRoot: root,
reader: reader, reader: reader,
stateObjects: make(map[common.Address]*stateObject), stateObjects: make(map[common.Address]*stateObject),
@ -373,17 +378,16 @@ func (s *StateDB) prefetchBalPreblockKeys() {
lenMaxSlots += len(acl.StorageKeys) lenMaxSlots += len(acl.StorageKeys)
workers.Go(func() error { workers.Go(func() error {
acctBal, err := s.reader.AccountBAL(addr) acctBal, err := s.reader.Account(addr)
var obj *stateObject var obj *stateObject
if err != nil { if err != nil {
log.Error("fail to fetch account from BALs:", addr) log.Error("fail to fetch account from BALs:", addr)
acct, err := s.reader.Account(addr) panic("fail to fetch account from BALs")
if err != nil {
panic("fail to fetch account from BALs")
}
obj = newObject(s, addr, acct)
} else { } else {
obj = newObject(s, addr, acctBal) // If obj is nil, we shouldn't newObject, it will cause invalid gas used error. Because 7702 will use Exist(addr) to decide where an account exists, which should be false if account is nil, but if we newObject it'll be true.
if acctBal != nil {
obj = newObject(s, addr, acctBal)
}
} }
accounts <- obj accounts <- obj
return nil return nil
@ -393,8 +397,10 @@ func (s *StateDB) prefetchBalPreblockKeys() {
for range len(preBal) { for range len(preBal) {
obj := <-accounts obj := <-accounts
// must set it first to avoid accounts read later in storageBAL fetching // must set it first to avoid accounts read later in storage fetching
s.setStateObject(obj) if obj != nil {
s.setStateObject(obj)
}
} }
close(accounts) close(accounts)
@ -407,7 +413,7 @@ func (s *StateDB) prefetchBalPreblockKeys() {
obj := s.stateObjects[addr] obj := s.stateObjects[addr]
if obj.origin == nil { if obj == nil || obj.origin == nil {
continue continue
} }
@ -415,16 +421,11 @@ func (s *StateDB) prefetchBalPreblockKeys() {
for _, key := range keys { for _, key := range keys {
workers.Go(func() error { workers.Go(func() error {
val, err := s.reader.StorageBAL(addr, key) val, err := s.reader.Storage(addr, key)
kv := &StorageKV{&addr, &key, &val} kv := &StorageKV{&addr, &key, &val}
if err != nil { if err != nil {
log.Error("fail to fetch storage from BALs:", addr, key) log.Error("fail to fetch storage from BALs:", addr, key)
// It's not safe to concurrent read from trie with StorageBAL, so we use the locked version Storage as fallback panic("fail to fetch storage from BALs")
val, err := s.reader.Storage(addr, key)
if err != nil {
panic("fail to fetch storage from BALs")
}
kv.val = &val
} }
storages <- kv storages <- kv

View file

@ -7,7 +7,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
// All states must has been cachae to statedb. // All states must has been cached to statedb.
func (s *StateDB) Account(addr common.Address) (*types.StateAccount, error) { func (s *StateDB) Account(addr common.Address) (*types.StateAccount, error) {
// s.getStateObject() shouldn't be used, cause when acct is nil, it'll load account from DB and result concurrent map writes for accts // s.getStateObject() shouldn't be used, cause when acct is nil, it'll load account from DB and result concurrent map writes for accts
obj := s.stateObjects[addr] obj := s.stateObjects[addr]
@ -34,11 +34,3 @@ func (s *StateDB) Storage(addr common.Address, slot common.Hash) (common.Hash, e
} }
return common.Hash{}, nil return common.Hash{}, nil
} }
func (s *StateDB) AccountBAL(addr common.Address) (*types.StateAccount, error) {
panic("AccountBAL not implemented for statedb")
}
func (s *StateDB) StorageBAL(addr common.Address, slot common.Hash) (common.Hash, error) {
panic("StorageBAL not implemented for statedb")
}