trie: faster snapshot generation #22504 (#1062)

This commit is contained in:
Daniel Liu 2025-06-17 13:13:14 +08:00 committed by GitHub
parent 93c2745b7b
commit 45d89bd4d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 61 additions and 29 deletions

View file

@ -136,7 +136,7 @@ func (l *liquidationPriceState) updateRoot(db Database) error {
if l.dbErr != nil {
return l.dbErr
}
root, err := l.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err := l.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil

View file

@ -245,7 +245,7 @@ func (te *tradingExchanges) CommitAsksTrie(db Database) error {
if te.dbErr != nil {
return te.dbErr
}
root, err := te.asksTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err := te.asksTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@ -307,7 +307,7 @@ func (te *tradingExchanges) CommitBidsTrie(db Database) error {
if te.dbErr != nil {
return te.dbErr
}
root, err := te.bidsTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err := te.bidsTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@ -783,7 +783,7 @@ func (t *tradingExchanges) CommitLiquidationPriceTrie(db Database) error {
if t.dbErr != nil {
return t.dbErr
}
root, err := t.liquidationPriceTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err := t.liquidationPriceTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil

View file

@ -589,7 +589,7 @@ func (t *TradingStateDB) Commit() (root common.Hash, err error) {
}
}
// Write trie changes.
root, err = t.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err = t.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var exchange tradingExchangeObject
if err := rlp.DecodeBytes(leaf, &exchange); err != nil {
return nil

View file

@ -472,7 +472,7 @@ func (le *lendingExchangeState) CommitInvestingTrie(db Database) error {
if le.dbErr != nil {
return le.dbErr
}
root, err := le.investingTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err := le.investingTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var orderList itemList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@ -493,7 +493,7 @@ func (le *lendingExchangeState) CommitBorrowingTrie(db Database) error {
if le.dbErr != nil {
return le.dbErr
}
root, err := le.borrowingTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err := le.borrowingTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var orderList itemList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@ -514,7 +514,7 @@ func (le *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error {
if le.dbErr != nil {
return le.dbErr
}
root, err := le.liquidationTimeTrie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err := le.liquidationTimeTrie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var orderList itemList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil

View file

@ -578,7 +578,7 @@ func (ls *LendingStateDB) Commit() (root common.Hash, err error) {
}
}
// Write trie changes.
root, err = ls.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
root, err = ls.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var exchange lendingObject
if err := rlp.DecodeBytes(leaf, &exchange); err != nil {
return nil

View file

@ -840,7 +840,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
// Write the account trie changes, measuing the amount of wasted time
defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now())
return s.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
return s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
var account Account
if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil

View file

@ -26,17 +26,31 @@ import (
)
// NewStateSync create a new state trie download scheduler.
func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom) *trie.Sync {
func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom, onLeaf func(paths [][]byte, leaf []byte) error) *trie.Sync {
// Register the storage slot callback if the external callback is specified.
var onSlot func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error
if onLeaf != nil {
onSlot = func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error {
return onLeaf(paths, leaf)
}
}
// Register the account callback to connect the state trie and the storage
// trie belongs to the contract.
var syncer *trie.Sync
callback := func(path []byte, leaf []byte, parent common.Hash) error {
onAccount := func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error {
if onLeaf != nil {
if err := onLeaf(paths, leaf); err != nil {
return err
}
}
var obj Account
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
return err
}
syncer.AddSubTrie(obj.Root, path, parent, nil)
syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), path, parent)
syncer.AddSubTrie(obj.Root, hexpath, parent, onSlot)
syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), hexpath, parent)
return nil
}
syncer = trie.NewSync(root, database, callback, bloom)
syncer = trie.NewSync(root, database, onAccount, bloom)
return syncer
}

View file

@ -126,7 +126,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
// Tests that an empty state is not scheduled for syncing.
func TestEmptyStateSync(t *testing.T) {
if req := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New())).Missing(1); len(req) != 0 {
if req := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New()), nil).Missing(1); len(req) != 0 {
t.Errorf("content requested for empty state: %v", req)
}
}
@ -146,7 +146,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool) {
}
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
queue := append([]common.Hash{}, sched.Missing(count)...)
for len(queue) > 0 {
@ -185,7 +185,7 @@ func TestIterativeDelayedStateSync(t *testing.T) {
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
queue := append([]common.Hash{}, sched.Missing(0)...)
for len(queue) > 0 {
@ -229,7 +229,7 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
queue := make(map[common.Hash]struct{})
for _, hash := range sched.Missing(count) {
@ -276,7 +276,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
queue := make(map[common.Hash]struct{})
for _, hash := range sched.Missing(0) {
@ -339,7 +339,7 @@ func TestIncompleteStateSync(t *testing.T) {
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb), nil)
added := []common.Hash{}
queue := append([]common.Hash{}, sched.Missing(1)...)

View file

@ -282,7 +282,7 @@ type stateTask struct {
func newStateSync(d *Downloader, root common.Hash) *stateSync {
return &stateSync{
d: d,
sched: state.NewStateSync(root, d.stateDB, trie.NewSyncBloom(1, memorydb.New())),
sched: state.NewStateSync(root, d.stateDB, trie.NewSyncBloom(1, memorydb.New()), nil),
keccak: sha3.NewLegacyKeccak256(),
tasks: make(map[common.Hash]*stateTask),
deliver: make(chan *stateReq),

View file

@ -218,13 +218,13 @@ func (c *committer) commitLoop(db *Database) {
switch n := n.(type) {
case *shortNode:
if child, ok := n.Val.(valueNode); ok {
c.onleaf(nil, child, hash)
c.onleaf(nil, nil, child, hash)
}
case *fullNode:
// For children in range [0, 15], it's impossible
// to contain valuenode. Only check the 17th child.
if n.Children[16] != nil {
c.onleaf(nil, n.Children[16].(valueNode), hash)
c.onleaf(nil, nil, n.Children[16].(valueNode), hash)
}
}
}

View file

@ -349,7 +349,14 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
// Notify any external watcher of a new key/value Node
if req.callback != nil {
if node, ok := (child.node).(valueNode); ok {
if err := req.callback(req.path, node, req.hash); err != nil {
var paths [][]byte
if len(child.path) == 2*common.HashLength {
paths = append(paths, hexToKeybytes(child.path))
} else if len(child.path) == 4*common.HashLength {
paths = append(paths, hexToKeybytes(child.path[:2*common.HashLength]))
paths = append(paths, hexToKeybytes(child.path[2*common.HashLength:]))
}
if err := req.callback(paths, child.path, node, req.hash); err != nil {
return nil, err
}
}

View file

@ -28,9 +28,20 @@ import (
)
// LeafCallback is a callback type invoked when a trie operation reaches a leaf
// Node. It's used by state sync and commit to allow handling external references
// between account and storage tries.
type LeafCallback func(path []byte, leaf []byte, parent common.Hash) error
// node.
//
// The paths is a path tuple identifying a particular trie node either in a single
// trie (account) or a layered trie (account -> storage). Each path in the tuple
// is in the raw format(32 bytes).
//
// The hexpath is a composite hexary path identifying the trie node. All the key
// bytes are converted to the hexary nibbles and composited with the parent path
// if the trie node is in a layered trie.
//
// It's used by state sync and commit to allow handling external references
// between account and storage tries. And also it's used in the state healing
// for extracting the raw states(leaf nodes) with corresponding paths.
type LeafCallback func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error
// Trie is a Merkle Patricia Trie.
// The zero value is an empty trie with no database.

View file

@ -477,7 +477,7 @@ func BenchmarkCommitAfterHash(b *testing.B) {
benchmarkCommitAfterHash(b, nil)
})
var a account
onleaf := func(path []byte, leaf []byte, parent common.Hash) error {
onleaf := func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
rlp.DecodeBytes(leaf, &a)
return nil
}