eth/protocols/snap: integrate stacktrie handling

This commit is contained in:
Martin Holst Swende 2023-10-09 08:15:39 +02:00
parent 1e837e21f9
commit d3b0894f3f
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 36 additions and 21 deletions

View file

@ -145,6 +145,11 @@ type accountResponse struct {
hashes []common.Hash // Account hashes in the returned range hashes []common.Hash // Account hashes in the returned range
accounts []*types.StateAccount // Expanded accounts in the returned range accounts []*types.StateAccount // Expanded accounts in the returned range
// origin requested, and, more importantly, the path for which the proof is valid.
origin common.Hash
root common.Hash // root for which this response is valid
proof ethdb.KeyValueReader // proof for origin and last value.
cont bool // Whether the account range has a continuation cont bool // Whether the account range has a continuation
} }
@ -223,6 +228,10 @@ type storageResponse struct {
hashes [][]common.Hash // Storage slot hashes in the returned range hashes [][]common.Hash // Storage slot hashes in the returned range
slots [][][]byte // Storage slot values in the returned range slots [][][]byte // Storage slot values in the returned range
// origin requested, and, more importantly, the path for which the proof is valid.
origin common.Hash
proof ethdb.KeyValueReader // proof for origin and last value.
cont bool // Whether the last storage range has a continuation cont bool // Whether the last storage range has a continuation
} }
@ -313,7 +322,7 @@ type accountTask struct {
stateTasks map[common.Hash]common.Hash // Account hashes->roots that need full state retrieval stateTasks map[common.Hash]common.Hash // Account hashes->roots that need full state retrieval
genBatch ethdb.Batch // Batch used by the node generator genBatch ethdb.Batch // Batch used by the node generator
genTrie *trie.StackTrie // Node generator from storage slots genTrie *trie.GenerativeTrie // Node generator from storage slots
done bool // Flag whether the task can be removed done bool // Flag whether the task can be removed
} }
@ -328,7 +337,7 @@ type storageTask struct {
req *storageRequest // Pending request to fill this task req *storageRequest // Pending request to fill this task
genBatch ethdb.Batch // Batch used by the node generator genBatch ethdb.Batch // Batch used by the node generator
genTrie *trie.StackTrie // Node generator from storage slots genTrie *trie.GenerativeTrie // Node generator from storage slots
done bool // Flag whether the task can be removed done bool // Flag whether the task can be removed
} }
@ -738,7 +747,7 @@ func (s *Syncer) loadSyncStatus() {
s.accountBytes += common.StorageSize(len(key) + len(value)) s.accountBytes += common.StorageSize(len(key) + len(value))
}, },
} }
task.genTrie = trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) { task.genTrie = trie.NewGentrie(task.Next, task.Last, func(path []byte, hash common.Hash, val []byte) {
rawdb.WriteTrieNode(task.genBatch, common.Hash{}, path, hash, val, s.scheme) rawdb.WriteTrieNode(task.genBatch, common.Hash{}, path, hash, val, s.scheme)
}) })
for accountHash, subtasks := range task.SubTasks { for accountHash, subtasks := range task.SubTasks {
@ -752,7 +761,7 @@ func (s *Syncer) loadSyncStatus() {
}, },
} }
owner := accountHash // local assignment for stacktrie writer closure owner := accountHash // local assignment for stacktrie writer closure
subtask.genTrie = trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) { subtask.genTrie = trie.NewGentrie(subtask.Next, subtask.Last, func(path []byte, hash common.Hash, val []byte) {
rawdb.WriteTrieNode(subtask.genBatch, owner, path, hash, val, s.scheme) rawdb.WriteTrieNode(subtask.genBatch, owner, path, hash, val, s.scheme)
}) })
} }
@ -811,7 +820,7 @@ func (s *Syncer) loadSyncStatus() {
Last: last, Last: last,
SubTasks: make(map[common.Hash][]*storageTask), SubTasks: make(map[common.Hash][]*storageTask),
genBatch: batch, genBatch: batch,
genTrie: trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) { genTrie: trie.NewGentrie(next, last, func(path []byte, hash common.Hash, val []byte) {
rawdb.WriteTrieNode(batch, common.Hash{}, path, hash, val, s.scheme) rawdb.WriteTrieNode(batch, common.Hash{}, path, hash, val, s.scheme)
}), }),
}) })
@ -2011,7 +2020,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
Last: r.End(), Last: r.End(),
root: acc.Root, root: acc.Root,
genBatch: batch, genBatch: batch,
genTrie: trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) { genTrie: trie.NewGentrie((common.Hash{}), r.End(), func(path []byte, hash common.Hash, val []byte) {
rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme) rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
}), }),
}) })
@ -2027,7 +2036,7 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
Last: r.End(), Last: r.End(),
root: acc.Root, root: acc.Root,
genBatch: batch, genBatch: batch,
genTrie: trie.NewStackTrie(func(path []byte, hash common.Hash, val []byte) { genTrie: trie.NewGentrie(r.Start(), r.End(), func(path []byte, hash common.Hash, val []byte) {
rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme) rawdb.WriteTrieNode(batch, owner, path, hash, val, s.scheme)
}), }),
}) })
@ -2083,17 +2092,17 @@ func (s *Syncer) processStorageResponse(res *storageResponse) {
} }
tr.Commit() tr.Commit()
} }
// If we're storing large contracts, generate the trie nodes
// on the fly to not trash the gluing points
if i == len(res.hashes)-1 && res.subTask != nil {
res.subTask.genTrie.AddProof(res.roots[i], res.origin[:], res.proof)
res.subTask.genTrie.UpdateAll(res.hashes[i], res.slots[i])
}
// Persist the received storage segments. These flat state maybe // Persist the received storage segments. These flat state maybe
// outdated during the sync, but it can be fixed later during the // outdated during the sync, but it can be fixed later during the
// snapshot generation. // snapshot generation.
for j := 0; j < len(res.hashes[i]); j++ { for j := 0; j < len(res.hashes[i]); j++ {
rawdb.WriteStorageSnapshot(batch, account, res.hashes[i][j], res.slots[i][j]) rawdb.WriteStorageSnapshot(batch, account, res.hashes[i][j], res.slots[i][j])
// If we're storing large contracts, generate the trie nodes
// on the fly to not trash the gluing points
if i == len(res.hashes)-1 && res.subTask != nil {
res.subTask.genTrie.Update(res.hashes[i][j][:], res.slots[i][j])
}
} }
} }
// Large contracts could have generated new trie nodes, flush them to disk // Large contracts could have generated new trie nodes, flush them to disk
@ -2279,6 +2288,7 @@ func (s *Syncer) forwardAccountTask(task *accountTask) {
s.accountBytes += common.StorageSize(len(key) + len(value)) s.accountBytes += common.StorageSize(len(key) + len(value))
}, },
} }
task.genTrie.AddProof(res.root, res.origin[:], res.proof)
for i, hash := range res.hashes { for i, hash := range res.hashes {
if task.needCode[i] || task.needState[i] { if task.needCode[i] || task.needState[i] {
break break
@ -2420,6 +2430,9 @@ func (s *Syncer) OnAccounts(peer SyncPeer, id uint64, hashes []common.Hash, acco
task: req.task, task: req.task,
hashes: hashes, hashes: hashes,
accounts: accs, accounts: accs,
proof: nodes.Set(),
origin: req.origin,
root: root,
cont: cont, cont: cont,
} }
select { select {
@ -2637,6 +2650,8 @@ func (s *Syncer) OnStorage(peer SyncPeer, id uint64, hashes [][]common.Hash, slo
hashes = append(hashes, []common.Hash{}) hashes = append(hashes, []common.Hash{})
slots = append(slots, [][]byte{}) slots = append(slots, [][]byte{})
} }
var proofdb ethdb.KeyValueReader
for i := 0; i < len(hashes); i++ { for i := 0; i < len(hashes); i++ {
// Convert the keys and proofs into an internal format // Convert the keys and proofs into an internal format
keys := make([][]byte, len(hashes[i])) keys := make([][]byte, len(hashes[i]))
@ -2662,7 +2677,7 @@ func (s *Syncer) OnStorage(peer SyncPeer, id uint64, hashes [][]common.Hash, slo
} else { } else {
// A proof was attached, the response is only partial, check that the // A proof was attached, the response is only partial, check that the
// returned data is indeed part of the storage trie // returned data is indeed part of the storage trie
proofdb := nodes.Set() proofdb = nodes.Set()
cont, err = trie.VerifyRangeProof(req.roots[i], req.origin[:], keys, slots[i], proofdb) cont, err = trie.VerifyRangeProof(req.roots[i], req.origin[:], keys, slots[i], proofdb)
if err != nil { if err != nil {
@ -2680,6 +2695,8 @@ func (s *Syncer) OnStorage(peer SyncPeer, id uint64, hashes [][]common.Hash, slo
roots: req.roots, roots: req.roots,
hashes: hashes, hashes: hashes,
slots: slots, slots: slots,
origin: req.origin,
proof: proofdb,
cont: cont, cont: cont,
} }
select { select {

View file

@ -418,25 +418,24 @@ func wrapWriteFunction(origin, last []byte, w NodeWriteFunc) NodeWriteFunc {
type GenerativeTrie struct { type GenerativeTrie struct {
proofsSet bool proofsSet bool
owner common.Hash
writeFn NodeWriteFunc writeFn NodeWriteFunc
stack *StackTrie stack *StackTrie
rightHandBorder []byte rightHandBorder []byte
} }
func NewGentrieWithOwner(origin, end, owner common.Hash, writeFn NodeWriteFunc) *GenerativeTrie { func NewGentrie(origin, end common.Hash, writeFn NodeWriteFunc) *GenerativeTrie {
// Wrap the write function // Wrap the write function
var originBorder = keybytesToHex(origin[:]) var originBorder = keybytesToHex(origin[:])
var g = &GenerativeTrie{} var g = &GenerativeTrie{}
wrapper := func(origin common.Hash, path []byte, hash common.Hash, blob []byte) { wrapper := func(path []byte, hash common.Hash, blob []byte) {
if bytes.HasPrefix(originBorder, path) { if bytes.HasPrefix(originBorder, path) {
return return
} }
if bytes.HasPrefix(g.rightHandBorder, path) { if bytes.HasPrefix(g.rightHandBorder, path) {
return return
} }
writeFn(origin, path, hash, blob) writeFn(path, hash, blob)
} }
return &GenerativeTrie{writeFn: wrapper} return &GenerativeTrie{writeFn: wrapper}
} }
@ -449,7 +448,6 @@ func (g *GenerativeTrie) AddProof(rootHash common.Hash, origin []byte, proof eth
panic(err) panic(err)
} }
g.stack = stack g.stack = stack
g.stack.owner = g.owner
} }
} }