solve potential fork boundary concurrency error

This commit is contained in:
Guillaume Ballet 2026-02-04 21:01:22 +01:00
parent edbce93d07
commit 74b59cfa63
No known key found for this signature in database
6 changed files with 35 additions and 30 deletions

View file

@ -144,17 +144,20 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
return h return h
} }
var ( var (
isEIP4762 = chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp) isEIP4762 = chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762) parentIsVerkle = pre.Env.Number > 0 && chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number-1)), pre.Env.ParentTimestamp)
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) isForkBoundary = isEIP4762 && !parentIsVerkle
gaspool = new(core.GasPool) statedb *state.StateDB
blockHash = common.Hash{0x13, 0x37} signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
rejectedTxs []*rejectedTx gaspool = new(core.GasPool)
includedTxs types.Transactions blockHash = common.Hash{0x13, 0x37}
gasUsed = uint64(0) rejectedTxs []*rejectedTx
blobGasUsed = uint64(0) includedTxs types.Transactions
receipts = make(types.Receipts, 0) gasUsed = uint64(0)
blobGasUsed = uint64(0)
receipts = make(types.Receipts, 0)
) )
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762 && !isForkBoundary, isForkBoundary)
gaspool.AddGas(pre.Env.GasLimit) gaspool.AddGas(pre.Env.GasLimit)
vmContext := vm.BlockContext{ vmContext := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
@ -222,6 +225,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
) )
core.ProcessParentBlockHash(prevHash, evm) core.ProcessParentBlockHash(prevHash, evm)
} }
if isEIP4762 {
core.InitializeBinaryTransitionRegistry(statedb)
}
for i := 0; txIt.Next(); i++ { for i := 0; txIt.Next(); i++ {
tx, err := txIt.Tx() tx, err := txIt.Tx()
if err != nil { if err != nil {
@ -377,7 +383,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
return statedb, execRs, body, nil return statedb, execRs, body, nil
} }
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool) *state.StateDB { func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool, forkBoundary bool) *state.StateDB {
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsVerkle: isBintrie}) tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true, IsVerkle: isBintrie})
sdb := state.NewDatabase(tdb, nil) sdb := state.NewDatabase(tdb, nil)
@ -406,6 +412,9 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool
if isBintrie { if isBintrie {
return statedb return statedb
} }
if forkBoundary {
sdb.SetForkBoundary(root)
}
// For MPT mode, reopen the state with the committed root // For MPT mode, reopen the state with the committed root
statedb, err = state.New(root, sdb) statedb, err = state.New(root, sdb)
if err != nil { if err != nil {

View file

@ -2085,7 +2085,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
if parent != nil && !bc.chainConfig.IsVerkle(parent.Number, parent.Time) { if parent != nil && !bc.chainConfig.IsVerkle(parent.Number, parent.Time) {
bc.SetForkBoundary(parentRoot) bc.SetForkBoundary(parentRoot)
defer bc.ClearForkBoundary() defer bc.ClearForkBoundary(parentRoot)
} }
} }

View file

@ -420,8 +420,8 @@ func (bc *BlockChain) SetForkBoundary(root common.Hash) {
bc.statedb.SetForkBoundary(root) bc.statedb.SetForkBoundary(root)
} }
func (bc *BlockChain) ClearForkBoundary() { func (bc *BlockChain) ClearForkBoundary(root common.Hash) {
bc.statedb.ClearForkBoundary() bc.statedb.ClearForkBoundary(root)
} }
// HistoricState returns a historic state specified by the given root. // HistoricState returns a historic state specified by the given root.

View file

@ -18,7 +18,7 @@ package state
import ( import (
"fmt" "fmt"
"sync/atomic" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru" "github.com/ethereum/go-ethereum/common/lru"
@ -160,7 +160,7 @@ type CachingDB struct {
// Transition-specific fields // Transition-specific fields
TransitionStatePerRoot *lru.Cache[common.Hash, *overlay.TransitionState] TransitionStatePerRoot *lru.Cache[common.Hash, *overlay.TransitionState]
forkBoundaryRoot atomic.Value // stores common.Hash; non-zero means fork boundary active for this root forkBoundaryRoots sync.Map // map[common.Hash]struct{}; set of roots at fork boundary
} }
// NewDatabase creates a state database with the provided data sources. // NewDatabase creates a state database with the provided data sources.
@ -182,20 +182,16 @@ func NewDatabaseForTesting() *CachingDB {
} }
func (db *CachingDB) SetForkBoundary(root common.Hash) { func (db *CachingDB) SetForkBoundary(root common.Hash) {
db.forkBoundaryRoot.Store(root) db.forkBoundaryRoots.Store(root, struct{}{})
} }
func (db *CachingDB) ClearForkBoundary() { func (db *CachingDB) ClearForkBoundary(root common.Hash) {
db.forkBoundaryRoot.Store(common.Hash{}) db.forkBoundaryRoots.Delete(root)
} }
func (db *CachingDB) isForkBoundary(root common.Hash) bool { func (db *CachingDB) isForkBoundary(root common.Hash) bool {
v := db.forkBoundaryRoot.Load() _, ok := db.forkBoundaryRoots.Load(root)
if v == nil { return ok
return false
}
stored, ok := v.(common.Hash)
return ok && stored != (common.Hash{}) && stored == root
} }
var ( var (

View file

@ -35,7 +35,7 @@ func TestForkBoundaryOpenTrie(t *testing.T) {
} }
sdb.SetForkBoundary(root) sdb.SetForkBoundary(root)
defer sdb.ClearForkBoundary() defer sdb.ClearForkBoundary(root)
tr, err := sdb.OpenTrie(root) tr, err := sdb.OpenTrie(root)
if err != nil { if err != nil {
@ -80,7 +80,7 @@ func TestForkBoundaryStateReader(t *testing.T) {
} }
sdb.SetForkBoundary(root) sdb.SetForkBoundary(root)
defer sdb.ClearForkBoundary() defer sdb.ClearForkBoundary(root)
reader, err := sdb.StateReader(root) reader, err := sdb.StateReader(root)
if err != nil { if err != nil {
@ -121,7 +121,7 @@ func TestForkBoundaryWrite(t *testing.T) {
} }
sdb.SetForkBoundary(root) sdb.SetForkBoundary(root)
defer sdb.ClearForkBoundary() defer sdb.ClearForkBoundary(root)
tr, err := sdb.OpenTrie(root) tr, err := sdb.OpenTrie(root)
if err != nil { if err != nil {
@ -213,7 +213,7 @@ func TestForkBoundaryWrongRoot(t *testing.T) {
otherRoot := common.HexToHash("0xdeadbeef") otherRoot := common.HexToHash("0xdeadbeef")
sdb.SetForkBoundary(otherRoot) sdb.SetForkBoundary(otherRoot)
defer sdb.ClearForkBoundary() defer sdb.ClearForkBoundary(otherRoot)
tr, err := sdb.OpenTrie(root) tr, err := sdb.OpenTrie(root)
if err != nil { if err != nil {

View file

@ -255,7 +255,7 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir
if miner.chainConfig.IsVerkle(header.Number, header.Time) && if miner.chainConfig.IsVerkle(header.Number, header.Time) &&
!miner.chainConfig.IsVerkle(parent.Number, parent.Time) { !miner.chainConfig.IsVerkle(parent.Number, parent.Time) {
miner.chain.SetForkBoundary(parent.Root) miner.chain.SetForkBoundary(parent.Root)
defer miner.chain.ClearForkBoundary() defer miner.chain.ClearForkBoundary(parent.Root)
} }
// Could potentially happen if starting to mine in an odd state. // Could potentially happen if starting to mine in an odd state.
// Note genParams.coinbase can be different with header.Coinbase // Note genParams.coinbase can be different with header.Coinbase