add 2 overlay: post-bal and readerwithcache

This commit is contained in:
Po 2025-06-07 06:13:30 +02:00
parent 4fc3d374dd
commit d58b2ba803
5 changed files with 299 additions and 29 deletions

View file

@ -1902,7 +1902,15 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
bc.cacheConfig.TrieCleanNoPrefetch = true bc.cacheConfig.TrieCleanNoPrefetch = true
if bc.cacheConfig.TrieCleanNoPrefetch { if bc.cacheConfig.TrieCleanNoPrefetch {
statedb, err = state.New(parentRoot, bc.statedb) // statedb, err = state.New(parentRoot, bc.statedb)
// if err != nil {
// return nil, err
// }
reader, err := bc.statedb.ReaderWithCache(parentRoot)
if err != nil {
return nil, err
}
statedb, err = state.NewWithReader(parentRoot, bc.statedb, reader)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -35,7 +35,7 @@ func NewParallelStateProcessor(config *params.ChainConfig, chain *HeaderChain) *
} }
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) { func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
fmt.Println("ParallelStateProcessor.Process called") fmt.Println("ParallelStateProcessor.Process called:", block.NumberU64())
var ( var (
header = block.Header() header = block.Header()
gp = new(GasPool).AddGas(block.GasLimit()) gp = new(GasPool).AddGas(block.GasLimit())
@ -52,7 +52,8 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
if preStateType == BALPreState { if preStateType == BALPreState {
start := time.Now() start := time.Now()
statedb.PrefetchStateBAL(block.NumberU64()) // statedb.PrefetchStateBAL(block.NumberU64())
statedb.PreComputePostState(block.NumberU64())
PrefetchBALTime += time.Since(start) PrefetchBALTime += time.Since(start)
} }
@ -93,9 +94,8 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
case BALPreState: case BALPreState:
{ {
start := time.Now() start := time.Now()
statedb.MergePostBal() // statedb.MergePostBal()
PrefetchMergeBALTime += time.Since(start) PrefetchMergeBALTime += time.Since(start)
preStateProvider = statedb
} }
case SeqPreState: case SeqPreState:
@ -116,24 +116,42 @@ func (p *ParallelStateProcessor) executeParallel(block *types.Block, statedb *st
// Parallel executing the transaction // Parallel executing the transaction
exeStart := time.Now() exeStart := time.Now()
postEntries := make([][]state.JournalEntry, len(block.Transactions())) postEntries := make([][]state.JournalEntry, len(block.Transactions()))
for i, tx := range block.Transactions() {
cleanStatedb, err := preStateProvider.PrestateAtIndex(i)
if err != nil {
return nil, err
}
initialdb := statedb.Copy()
for i, tx := range block.Transactions() {
i := i i := i
gpcp := *gp
workers.Go(func() error { workers.Go(func() error {
var (
cleanStatedb *state.StateDB
err error
)
switch preStateType {
case BALPreState:
{
cleanStatedb = initialdb.Copy()
cleanStatedb.SetTxContext(tx.Hash(), i)
err = cleanStatedb.SetTxBALReader()
}
case SeqPreState:
{
cleanStatedb, err = preStateProvider.PrestateAtIndex(i)
cleanStatedb.SetTxContext(tx.Hash(), i)
}
}
if err != nil {
return err
}
evm := vm.NewEVM(context, cleanStatedb, p.config, cfg)
usedGas := new(uint64) usedGas := new(uint64)
msg, err := TransactionToMessage(tx, signer, header.BaseFee) msg, err := TransactionToMessage(tx, signer, header.BaseFee)
if err != nil { if err != nil {
return err return err
} }
cleanStatedb.SetTxContext(tx.Hash(), i) // todo: handle gp race
gpcp := *gp
evm := vm.NewEVM(context, cleanStatedb, p.config, cfg)
receipt, entries, err := ApplyTransactionWithParallelEVM(msg, &gpcp, cleanStatedb, blockNumber, blockHash, tx, usedGas, evm) receipt, entries, err := ApplyTransactionWithParallelEVM(msg, &gpcp, cleanStatedb, blockNumber, blockHash, tx, usedGas, evm)
if err != nil { if err != nil {
return err return err

View file

@ -18,6 +18,7 @@ package state
import ( import (
"errors" "errors"
"fmt"
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -476,3 +477,173 @@ func (r *readerWithCache) Storage(addr common.Address, slot common.Hash) (common
return value, nil return value, nil
} }
type readerWithBAL struct {
Reader // Reader with cache
txIndex int
postBal map[int]types.TxPostValues // Shared accoss txs and mustn't be changed
}
func newReaderWithBAL(reader Reader, txIndex int, postBal map[int]types.TxPostValues) *readerWithBAL {
return &readerWithBAL{
reader, txIndex, postBal,
}
}
func (r *readerWithBAL) Account(addr common.Address) (*types.StateAccount, error) {
postVals, ok := r.postBal[r.txIndex-1]
if ok {
acctVal, ok := postVals[addr]
if ok {
// Cache codeHash
if acctVal.Code != nil {
if acctVal.CodeHash == nil {
acctVal.CodeHash = crypto.Keccak256(acctVal.Code)
}
}
if acctVal.Cached {
return &types.StateAccount{
Nonce: acctVal.Nonce,
Balance: acctVal.Balance,
Root: acctVal.Root,
CodeHash: acctVal.CodeHash,
}, nil
}
acct, err := r.Reader.Account(addr)
acctVal.Cached = true
if acct == nil || err != nil {
// acct is nil, just return the postAcct
return &types.StateAccount{
Nonce: acctVal.Nonce,
Balance: acctVal.Balance,
CodeHash: acctVal.CodeHash,
}, nil
}
// acct mustn't be modified, or it'll change the cached pre-block values in readerwithcache
if acct.Nonce > acctVal.Nonce {
acctVal.Nonce = acct.Nonce
}
if acctVal.Balance == nil {
acctVal.Balance = acct.Balance
}
if acctVal.CodeHash == nil {
acctVal.CodeHash = acct.CodeHash
}
if acctVal.Root == (common.Hash{}) {
acctVal.Root = acct.Root
}
return &types.StateAccount{
Nonce: acctVal.Nonce,
Balance: acctVal.Balance,
Root: acctVal.Root,
CodeHash: acctVal.CodeHash,
}, nil
}
}
acct, err := r.Reader.Account(addr)
if err != nil {
return nil, err
}
// cache account and codeHash except the first transaction
if postVals != nil && acct != nil {
postVals[addr] = &types.AcctPostValues{
Nonce: acct.Nonce,
Balance: acct.Balance,
Destruct: false,
CodeHash: acct.CodeHash,
Root: acct.Root,
Cached: true,
}
}
return acct, nil
}
func (r *readerWithBAL) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
postVals, ok := r.postBal[r.txIndex-1]
if ok {
acct, ok := postVals[addr]
if ok {
val, ok := acct.StorageKV[slot]
if ok {
return val, nil
}
}
}
val, err := r.Reader.Storage(addr, slot)
// Cache storage
if postVals != nil && postVals[addr] != nil {
if postVals[addr].StorageKV != nil {
postVals[addr].StorageKV[slot] = val
} else {
postVals[addr].StorageKV = map[common.Hash]common.Hash{
slot: val,
}
}
}
if err != nil {
return common.Hash{}, err
}
return val, nil
}
func (r *readerWithBAL) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
postVals, ok := r.postBal[r.txIndex-1]
if ok {
acctVal, ok := postVals[addr]
if ok {
code := acctVal.Code
if code != nil {
return code, nil
}
if acctVal.CodeHash != nil {
code, err := r.Reader.Code(addr, common.Hash(acctVal.CodeHash))
// Cache code
acctVal.Code = code
if err != nil {
return nil, fmt.Errorf("cannot get code from code for addr:%x, err:%v", addr, err)
}
return code, nil
}
}
}
acct, err := r.Reader.Account(addr)
if err != nil || acct == nil {
return nil, fmt.Errorf("cannot get code from acct for addr:%x, err:%v", addr, err)
}
code, err := r.Reader.Code(addr, common.Hash(acct.CodeHash))
// Cache account, codeHash and code except the first transaction
// if postVals != nil {
// if postVals[addr] != nil {
// postVals[addr].Code = code
// postVals[addr].CodeHash = codeHash[:]
// } else {
// postVals[addr] = &types.AcctPostValues{
// Nonce: acct.Nonce,
// Balance: acct.Balance,
// Code: code,
// Destruct: false,
// CodeHash: acct.CodeHash,
// }
// }
// }
if err != nil {
return nil, fmt.Errorf("cannot get code from codeHash for addr:%x, err:%v", addr, err)
}
return code, nil
}
func (r *readerWithBAL) CodeSize(addr common.Address, codeHash common.Hash) (int, error) {
// Here codeHash is not need, cause we'll fetch it from BAL reader
code, err := r.Code(addr, common.Hash{})
if err != nil {
return 0, err
}
return len(code), nil
}

View file

@ -164,6 +164,7 @@ type StateDB struct {
blockNumber uint64 blockNumber uint64
// postState after appling tx // postState after appling tx
postStates map[int]*StateDB postStates map[int]*StateDB
postBAL map[int]types.TxPostValues
} }
type BALType int type BALType int
@ -178,26 +179,15 @@ const (
) )
type BALs struct { type BALs struct {
Pre map[uint64]types.AccessList `json:"pre"` Pre map[uint64]types.AccessList `json:"pre"`
Post map[uint64]map[int]TxPostValues `json:"post"` Post map[uint64]map[int]types.TxPostValues `json:"post"`
} }
type AcctPostValues struct {
Nonce uint64 `json:"nonce"`
Balance *uint256.Int `json:"balance"`
Code []byte `json:"code"`
StorageKV map[common.Hash]common.Hash `json:"storageKV"`
Destruct bool `json:"destruct"`
}
// For acccount destruct or storage clearing corresponding values would be 0
type TxPostValues map[common.Address]*AcctPostValues
var ( var (
AllBlockBal = BALs{} AllBlockBal = BALs{}
AllBlockAccessLists = map[uint64]types.AccessList{} AllBlockAccessLists = map[uint64]types.AccessList{}
// Blocknumber => TxIndex => TxPostValues // Blocknumber => TxIndex => TxPostValues
AllBlockTxPostValues = map[uint64]map[int]TxPostValues{} AllBlockTxPostValues = map[uint64]map[int]types.TxPostValues{}
) )
const ( const (
@ -282,6 +272,46 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
return sdb, nil return sdb, nil
} }
func (s *StateDB) PreComputePostState(blockNumber uint64) {
s.blockNumber = blockNumber
postBal := AllBlockTxPostValues[blockNumber]
for i := 0; i < len(postBal)-1; i++ {
prevVals := postBal[i]
postVals := postBal[i+1]
for addr, prevAcct := range prevVals {
if postAcct, ok := postVals[addr]; ok {
for k, v := range prevAcct.StorageKV {
if _, exists := postAcct.StorageKV[k]; !exists {
postAcct.StorageKV[k] = v
}
}
if postAcct.Balance == nil {
postAcct.Balance = prevAcct.Balance
}
if postAcct.Code == nil {
postAcct.Code = prevAcct.Code
}
// Storage changes but nonce didn't change
if postAcct.Nonce == 0 {
postAcct.Nonce = prevAcct.Nonce
}
} else {
postVals[addr] = prevAcct
}
}
}
s.postBAL = postBal
}
func (s *StateDB) SetTxBALReader() error {
if s.postBAL == nil {
return errors.New("cannot set bal reader without postBAL")
}
s.reader = newReaderWithBAL(s.reader, s.txIndex, s.postBAL)
return nil
}
func (s *StateDB) PrefetchStateBAL(blockNumber uint64) { func (s *StateDB) PrefetchStateBAL(blockNumber uint64) {
s.blockNumber = blockNumber s.blockNumber = blockNumber
switch balType { switch balType {
@ -1022,7 +1052,9 @@ func (s *StateDB) Copy() *StateDB {
transientStorage: s.transientStorage.Copy(), transientStorage: s.transientStorage.Copy(),
journal: s.journal.copy(), journal: s.journal.copy(),
// The update journal is not copies to avoid duplicated updates in later transactions. // The update journal is not copies to avoid duplicated updates in later transactions.
blockNumber: s.blockNumber,
updateJournal: newJournal(), updateJournal: newJournal(),
postBAL: s.postBAL,
} }
if s.witness != nil { if s.witness != nil {
state.witness = s.witness.Copy() state.witness = s.witness.Copy()

41
core/types/state_bal.go Normal file
View file

@ -0,0 +1,41 @@
package types
import (
"maps"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
)
type AcctPostValues struct {
Nonce uint64 `json:"nonce"`
Balance *uint256.Int `json:"balance"`
Code []byte `json:"code"`
StorageKV map[common.Hash]common.Hash `json:"storageKV"`
Destruct bool `json:"destruct"`
CodeHash []byte `json:"codeHash"`
Root common.Hash `json:"root"`
Cached bool
}
// For acccount destruct or storage clearing corresponding values would be 0
type TxPostValues map[common.Address]*AcctPostValues
// Balance and StorageKV must be cloned to avoid changing by setting postVals
func (v *AcctPostValues) Clone() *AcctPostValues {
balance := v.Balance
if balance != nil {
balance = v.Balance.Clone()
}
return &AcctPostValues{
Nonce: v.Nonce,
Balance: balance,
Code: v.Code,
StorageKV: maps.Clone(v.StorageKV),
Destruct: false,
CodeHash: v.CodeHash,
Root: v.Root,
Cached: false,
}
}