Summary
trie/bintrie.InternalNode.InsertValuesAtStem mutates bt.left, bt.right, and bt.mustRecompute without synchronization. When core/state.IntermediateRoot parallelizes per-account updateTrie() calls via errgroup and the underlying hasher wraps a single shared *BinaryTrie (EIP-7864 unified layout), multiple goroutines call BinaryTrie.UpdateStorage concurrently on the same internal nodes — producing a data race.
Current state on master
Master's IntermediateRoot has an if s.db.TrieDB().IsVerkle() bypass that serializes all bintrie storage updates in a single sequential loop. This prevents the race today.
However, the bypass is a runtime workaround — InternalNode itself has no documentation or compile-time guard marking it as goroutine-unsafe. Any future refactor that drops or restructures the bypass (as happened on the bintrie-flat-state feature branch during a hasher-routed refactor in #34706 ) silently reintroduces the race.
Race trace (observed on bintrie-flat-state where the bypass was accidentally dropped)
WARNING: DATA RACE
Read at 0x... by goroutine 58:
trie/bintrie.(*InternalNode).InsertValuesAtStem()
trie/bintrie/internal_node.go:194
Previous write at 0x... by goroutine 59:
trie/bintrie.(*InternalNode).InsertValuesAtStem()
trie/bintrie/internal_node.go:211
Both goroutines are spawned by errgroup.Group.Go inside core/state.(*StateDB).IntermediateRoot at statedb.go:762.
Reproduced deterministically with go test ./core/state/ -race -run TestBintrieFlatStateConsistencyOracle (every run, not intermittent).
Summary
trie/bintrie.InternalNode.InsertValuesAtStemmutatesbt.left,bt.right, andbt.mustRecomputewithout synchronization. Whencore/state.IntermediateRootparallelizes per-accountupdateTrie()calls via errgroup and the underlying hasher wraps a single shared*BinaryTrie(EIP-7864 unified layout), multiple goroutines callBinaryTrie.UpdateStorageconcurrently on the same internal nodes — producing a data race.Current state on master
Master's
IntermediateRoothas anif s.db.TrieDB().IsVerkle()bypass that serializes all bintrie storage updates in a single sequential loop. This prevents the race today.However, the bypass is a runtime workaround —
InternalNodeitself has no documentation or compile-time guard marking it as goroutine-unsafe. Any future refactor that drops or restructures the bypass (as happened on thebintrie-flat-statefeature branch during a hasher-routed refactor in #34706 ) silently reintroduces the race.Race trace (observed on
bintrie-flat-statewhere the bypass was accidentally dropped)Both goroutines are spawned by
errgroup.Group.Goinsidecore/state.(*StateDB).IntermediateRootatstatedb.go:762.Reproduced deterministically with
go test ./core/state/ -race -run TestBintrieFlatStateConsistencyOracle(every run, not intermittent).