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

View file

@ -18,7 +18,7 @@ package state
import (
"fmt"
"sync/atomic"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
@ -160,7 +160,7 @@ type CachingDB struct {
// Transition-specific fields
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.
@ -182,20 +182,16 @@ func NewDatabaseForTesting() *CachingDB {
}
func (db *CachingDB) SetForkBoundary(root common.Hash) {
db.forkBoundaryRoot.Store(root)
db.forkBoundaryRoots.Store(root, struct{}{})
}
func (db *CachingDB) ClearForkBoundary() {
db.forkBoundaryRoot.Store(common.Hash{})
func (db *CachingDB) ClearForkBoundary(root common.Hash) {
db.forkBoundaryRoots.Delete(root)
}
func (db *CachingDB) isForkBoundary(root common.Hash) bool {
v := db.forkBoundaryRoot.Load()
if v == nil {
return false
}
stored, ok := v.(common.Hash)
return ok && stored != (common.Hash{}) && stored == root
_, ok := db.forkBoundaryRoots.Load(root)
return ok
}
var (

View file

@ -35,7 +35,7 @@ func TestForkBoundaryOpenTrie(t *testing.T) {
}
sdb.SetForkBoundary(root)
defer sdb.ClearForkBoundary()
defer sdb.ClearForkBoundary(root)
tr, err := sdb.OpenTrie(root)
if err != nil {
@ -80,7 +80,7 @@ func TestForkBoundaryStateReader(t *testing.T) {
}
sdb.SetForkBoundary(root)
defer sdb.ClearForkBoundary()
defer sdb.ClearForkBoundary(root)
reader, err := sdb.StateReader(root)
if err != nil {
@ -121,7 +121,7 @@ func TestForkBoundaryWrite(t *testing.T) {
}
sdb.SetForkBoundary(root)
defer sdb.ClearForkBoundary()
defer sdb.ClearForkBoundary(root)
tr, err := sdb.OpenTrie(root)
if err != nil {
@ -213,7 +213,7 @@ func TestForkBoundaryWrongRoot(t *testing.T) {
otherRoot := common.HexToHash("0xdeadbeef")
sdb.SetForkBoundary(otherRoot)
defer sdb.ClearForkBoundary()
defer sdb.ClearForkBoundary(otherRoot)
tr, err := sdb.OpenTrie(root)
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) &&
!miner.chainConfig.IsVerkle(parent.Number, parent.Time) {
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.
// Note genParams.coinbase can be different with header.Coinbase