integrate trie batch update into statedb intermediate root calculation

This commit is contained in:
Jared Wasinger 2025-08-19 01:32:12 +09:00
parent cd191ed55a
commit 0dcd0e26f7
5 changed files with 70 additions and 10 deletions

View file

@ -160,6 +160,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
} else if res.Requests != nil { } else if res.Requests != nil {
return errors.New("block has requests before prague fork") return errors.New("block has requests before prague fork")
} }
// Validate the state root against the received state root and throw // Validate the state root against the received state root and throw
// an error if they don't match. // an error if they don't match.
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {

View file

@ -322,8 +322,10 @@ func (s *stateObject) updateTrie() (Trie, error) {
// into a shortnode. This requires `B` to be resolved from disk. // into a shortnode. This requires `B` to be resolved from disk.
// Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved. // Whereas if the created node is handled first, then the collapse is avoided, and `B` is not resolved.
var ( var (
deletions []common.Hash deletions []common.Hash
used = make([]common.Hash, 0, len(s.uncommittedStorage)) used = make([]common.Hash, 0, len(s.uncommittedStorage))
updateKeys [][]byte
updateValues [][]byte
) )
for key, origin := range s.uncommittedStorage { for key, origin := range s.uncommittedStorage {
// Skip noop changes, persist actual changes // Skip noop changes, persist actual changes
@ -337,10 +339,8 @@ func (s *stateObject) updateTrie() (Trie, error) {
continue continue
} }
if (value != common.Hash{}) { if (value != common.Hash{}) {
if err := tr.UpdateStorage(s.address, key[:], common.TrimLeftZeroes(value[:])); err != nil { updateKeys = append(updateKeys, key[:])
s.db.setError(err) updateValues = append(updateValues, common.TrimLeftZeroes(value[:]))
return nil, err
}
s.db.StorageUpdated.Add(1) s.db.StorageUpdated.Add(1)
} else { } else {
deletions = append(deletions, key) deletions = append(deletions, key)
@ -348,6 +348,12 @@ func (s *stateObject) updateTrie() (Trie, error) {
// Cache the items for preloading // Cache the items for preloading
used = append(used, key) // Copy needed for closure used = append(used, key) // Copy needed for closure
} }
if len(updateKeys) > 0 {
if err := tr.UpdateStorageBatch(common.Address{}, updateKeys, updateValues); err != nil {
s.db.setError(err)
return nil, err
}
}
for _, key := range deletions { for _, key := range deletions {
if err := tr.DeleteStorage(s.address, key[:]); err != nil { if err := tr.DeleteStorage(s.address, key[:]); err != nil {
s.db.setError(err) s.db.setError(err)

View file

@ -574,6 +574,25 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code) s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code)
} }
} }
func (s *StateDB) updateStateObjects(objs []*stateObject) {
var addrs []common.Address
var accts []*types.StateAccount
for _, obj := range objs {
addrs = append(addrs, obj.Address())
accts = append(accts, &obj.data)
}
if err := s.trie.UpdateAccountBatch(addrs, accts, nil); err != nil {
s.setError(fmt.Errorf("updateStateObjects error: %v", err))
}
for _, obj := range objs {
if obj.dirtyCode {
s.trie.UpdateContractCode(obj.Address(), common.BytesToHash(obj.CodeHash()), obj.code)
}
}
}
// deleteStateObject removes the given object from the state trie. // deleteStateObject removes the given object from the state trie.
func (s *StateDB) deleteStateObject(addr common.Address) { func (s *StateDB) deleteStateObject(addr common.Address) {
@ -883,6 +902,31 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
workers.Wait() workers.Wait()
s.StorageUpdates += time.Since(start) s.StorageUpdates += time.Since(start)
/*
fmt.Println("begin print mutations")
fmt.Println(s.txIndex)
for addr, _ := range s.mutations {
if _, ok := s.stateObjects[addr]; !ok {
continue
}
if s.stateObjects[addr].trie == nil {
continue
}
fmt.Printf("mut %x/%x:\n", addr, s.stateObjects[addr].addrHash)
it, err := s.stateObjects[addr].trie.NodeIterator([]byte{})
if err != nil {
panic(err)
}
for it.Next(true) {
if it.Leaf() {
fmt.Printf("%x: %x\n", it.Path(), it.LeafBlob())
} else {
fmt.Printf("%x: %x\n", it.Path(), it.Hash())
}
}
}
*/
// Now we're about to start to write changes to the trie. The trie is so far // Now we're about to start to write changes to the trie. The trie is so far
// _untouched_. We can check with the prefetcher, if it can give us a trie // _untouched_. We can check with the prefetcher, if it can give us a trie
// which has the same root, but also has some content loaded into it. // which has the same root, but also has some content loaded into it.
@ -911,6 +955,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
var ( var (
usedAddrs []common.Address usedAddrs []common.Address
deletedAddrs []common.Address deletedAddrs []common.Address
updatedObjs []*stateObject
) )
for addr, op := range s.mutations { for addr, op := range s.mutations {
if op.applied { if op.applied {
@ -921,11 +966,14 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
if op.isDelete() { if op.isDelete() {
deletedAddrs = append(deletedAddrs, addr) deletedAddrs = append(deletedAddrs, addr)
} else { } else {
s.updateStateObject(s.stateObjects[addr]) updatedObjs = append(updatedObjs, s.stateObjects[addr])
s.AccountUpdated += 1 s.AccountUpdated += 1
} }
usedAddrs = append(usedAddrs, addr) // Copy needed for closure usedAddrs = append(usedAddrs, addr) // Copy needed for closure
} }
if len(updatedObjs) > 0 {
s.updateStateObjects(updatedObjs)
}
for _, deletedAddr := range deletedAddrs { for _, deletedAddr := range deletedAddrs {
s.deleteStateObject(deletedAddr) s.deleteStateObject(deletedAddr)
s.AccountDeleted += 1 s.AccountDeleted += 1
@ -937,7 +985,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
} }
// Track the amount of time wasted on hashing the account trie // Track the amount of time wasted on hashing the account trie
defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now())
hash := s.trie.Hash() hash := s.trie.Hash()
// If witness building is enabled, gather the account trie witness // If witness building is enabled, gather the account trie witness

View file

@ -105,7 +105,7 @@ func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
} }
for _, snapshot := range snapshotConf { for _, snapshot := range snapshotConf {
for _, dbscheme := range dbschemeConf { for _, dbscheme := range dbschemeConf {
if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, nil, nil)); err != nil { if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, false, nil, nil)); err != nil {
t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err) t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err)
return return
} }

View file

@ -419,6 +419,7 @@ func (t *Trie) UpdateBatch(keys [][]byte, values [][]byte) error {
// Insert the entries sequentially if there are not too many // Insert the entries sequentially if there are not too many
// trie nodes in the trie. // trie nodes in the trie.
fn, ok := t.root.(*fullNode) fn, ok := t.root.(*fullNode)
if !ok || len(keys) < 4 { // TODO(rjl493456442) the parallelism threshold should be twisted if !ok || len(keys) < 4 { // TODO(rjl493456442) the parallelism threshold should be twisted
for i, key := range keys { for i, key := range keys {
err := t.Update(key, values[i]) err := t.Update(key, values[i])
@ -438,7 +439,12 @@ func (t *Trie) UpdateBatch(keys [][]byte, values [][]byte) error {
ikeys[hkey[0]] = append(ikeys[hkey[0]], hkey) ikeys[hkey[0]] = append(ikeys[hkey[0]], hkey)
ivals[hkey[0]] = append(ivals[hkey[0]], values[i]) ivals[hkey[0]] = append(ivals[hkey[0]], values[i])
} }
for pos, ks := range ikeys { if len(keys) > 0 {
fn.flags = t.newFlag()
}
for p, k := range ikeys {
pos := p
ks := k
eg.Go(func() error { eg.Go(func() error {
vs := ivals[pos] vs := ivals[pos]
for i, k := range ks { for i, k := range ks {