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)
)
// Update the Mgas per second gauge
chainMgaspsGauge.Update(int64(mgasps))
mgaspsHist.Update(int64(mgasps))
// 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)
_, 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 {
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
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 {
return err
}
@ -234,8 +234,7 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
}
// 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
if p.config.IsPrague(block.Number(), block.Time()) {
requests = [][]byte{}
@ -243,18 +242,25 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil {
return nil, err
}
// // EIP-7002
// if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
// return nil, err
// }
// // EIP-7251
// if err := ProcessConsolidationQueue(&requests, evm); err != nil {
// return nil, err
// }
// 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 preStateType == SeqPreState {
evm := vm.NewEVM(context, statedb, p.config, cfg)
// // EIP-7002
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
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)
// 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{
Receipts: receipts,
@ -264,7 +270,7 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
}, 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.OnTxStart != nil {
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)
}
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
// - The returned storage slot is safe to modify after the call
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
@ -191,10 +188,6 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
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
// address and slot key.
//
@ -223,10 +216,6 @@ func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash,
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
// state from the referenced trie.
//
@ -293,14 +282,6 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
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
// address and slot key.
//
@ -347,10 +328,6 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
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,
// providing state access by leveraging all readers. The checking priority
// 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...)
}
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
// 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...)
}
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.
type reader struct {
ContractCodeReader
@ -690,6 +631,9 @@ func (r *readerWithBAL) Account(addr common.Address) (*types.StateAccount, error
acct, err := r.Reader.Account(addr)
if acct == nil || err != nil {
// 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{
Nonce: acctVal.Nonce,
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,
// this function accepts an additional Reader which is bound to the given root.
func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
sdb := &StateDB{
db: db,
trie: tr,
originalRoot: root,
reader: reader,
stateObjects: make(map[common.Address]*stateObject),
@ -373,17 +378,16 @@ func (s *StateDB) prefetchBalPreblockKeys() {
lenMaxSlots += len(acl.StorageKeys)
workers.Go(func() error {
acctBal, err := s.reader.AccountBAL(addr)
acctBal, err := s.reader.Account(addr)
var obj *stateObject
if err != nil {
log.Error("fail to fetch account from BALs:", addr)
acct, err := s.reader.Account(addr)
if err != nil {
panic("fail to fetch account from BALs")
}
obj = newObject(s, addr, acct)
panic("fail to fetch account from BALs")
} 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
return nil
@ -393,8 +397,10 @@ func (s *StateDB) prefetchBalPreblockKeys() {
for range len(preBal) {
obj := <-accounts
// must set it first to avoid accounts read later in storageBAL fetching
s.setStateObject(obj)
// must set it first to avoid accounts read later in storage fetching
if obj != nil {
s.setStateObject(obj)
}
}
close(accounts)
@ -407,7 +413,7 @@ func (s *StateDB) prefetchBalPreblockKeys() {
obj := s.stateObjects[addr]
if obj.origin == nil {
if obj == nil || obj.origin == nil {
continue
}
@ -415,16 +421,11 @@ func (s *StateDB) prefetchBalPreblockKeys() {
for _, key := range keys {
workers.Go(func() error {
val, err := s.reader.StorageBAL(addr, key)
val, err := s.reader.Storage(addr, key)
kv := &StorageKV{&addr, &key, &val}
if err != nil {
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
val, err := s.reader.Storage(addr, key)
if err != nil {
panic("fail to fetch storage from BALs")
}
kv.val = &val
panic("fail to fetch storage from BALs")
}
storages <- kv

View file

@ -7,7 +7,7 @@ import (
"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) {
// 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]
@ -34,11 +34,3 @@ func (s *StateDB) Storage(addr common.Address, slot common.Hash) (common.Hash, e
}
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")
}