mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
fixes
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
f99258ad36
commit
26230d037f
3 changed files with 106 additions and 19 deletions
|
|
@ -230,7 +230,11 @@ func (s *nodeStore) decodeNode(serialized []byte, depth int, hn common.Hash, mus
|
|||
// CollectNodes flushes every node that needs flushing via flushfn in post-order.
|
||||
// Invariant: any ancestor of a node that needs flushing is itself marked, so a
|
||||
// clean root means the whole subtree is clean.
|
||||
func (s *nodeStore) collectNodes(ref nodeRef, path []byte, flushfn nodeFlushFn) error {
|
||||
func (s *nodeStore) collectNodes(ref nodeRef, path []byte, flushfn nodeFlushFn, groupDepth int) error {
|
||||
if groupDepth < 1 || groupDepth > MaxGroupDepth {
|
||||
return errors.New("groupDepth must be between 1 and 8")
|
||||
}
|
||||
|
||||
switch ref.Kind() {
|
||||
case kindEmpty:
|
||||
return nil
|
||||
|
|
@ -239,21 +243,19 @@ func (s *nodeStore) collectNodes(ref nodeRef, path []byte, flushfn nodeFlushFn)
|
|||
if !node.dirty {
|
||||
return nil
|
||||
}
|
||||
// Reuse path buffer across children: flushfn consumers
|
||||
// (NodeSet.AddNode, tracer.Get) clone via string(path), so in-place
|
||||
// mutation is safe.
|
||||
path = append(path, 0)
|
||||
if err := s.collectNodes(node.left, path, flushfn); err != nil {
|
||||
return err
|
||||
// Only flush at group boundaries (depth % groupDepth == 0)
|
||||
if int(node.depth)%groupDepth == 0 {
|
||||
// We're at a group boundary - first collect any nodes in deeper groups,
|
||||
// then flush this group
|
||||
if err := s.collectChildGroups(node, path, flushfn, groupDepth, groupDepth-1); err != nil {
|
||||
return err
|
||||
}
|
||||
flushfn(path, s.computeHash(ref), s.serializeNode(ref))
|
||||
return nil
|
||||
}
|
||||
path[len(path)-1] = 1
|
||||
if err := s.collectNodes(node.right, path, flushfn); err != nil {
|
||||
return err
|
||||
}
|
||||
path = path[:len(path)-1]
|
||||
flushfn(path, s.computeHash(ref), s.serializeNode(ref))
|
||||
node.dirty = false
|
||||
return nil
|
||||
// Not at a group boundary - this shouldn't happen if we're called correctly from root
|
||||
// but handle it by continuing to traverse
|
||||
return s.collectChildGroups(path, flushfn, groupDepth, groupDepth-(int(node.depth)%groupDepth)-1)
|
||||
case kindStem:
|
||||
sn := s.getStem(ref.Index())
|
||||
if !sn.dirty {
|
||||
|
|
@ -269,6 +271,91 @@ func (s *nodeStore) collectNodes(ref nodeRef, path []byte, flushfn nodeFlushFn)
|
|||
}
|
||||
}
|
||||
|
||||
// collectChildGroups traverses within a group to find and collect nodes in the next group.
|
||||
// remainingLevels is how many more levels below the current node until we reach the group boundary.
|
||||
// When remainingLevels=0, the current node's children are at the next group boundary.
|
||||
func (s *nodeStore) collectChildGroups(node *InternalNode, path []byte, flushfn nodeFlushFn, groupDepth int, remainingLevels int) error {
|
||||
if remainingLevels == 0 {
|
||||
// Current node is at depth (groupBoundary - 1), its children are at the next group boundary
|
||||
if !node.left.IsEmpty() {
|
||||
if err := s.collectNodes(node.left, appendBit(path, 0), flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !node.right.IsEmpty() {
|
||||
if err := s.collectNodes(node.right, appendBit(path, 1), flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Continue traversing within the group.
|
||||
childDepth := node.depth + 1
|
||||
|
||||
if !node.left.IsEmpty() {
|
||||
switch node.left.Kind() {
|
||||
case kindInternal:
|
||||
n := s.getInternal(node.left.Index())
|
||||
if err := s.collectChildGroups(n, appendBit(path, 0), flushfn, groupDepth, remainingLevels-1); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
extPath := s.extendPathToGroupLeaf(appendBit(path, 0), node.left, remainingLevels, int(childDepth))
|
||||
if err := s.collectNodes(node.left, extPath, flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if !node.right.IsEmpty() {
|
||||
switch node.right.Kind() {
|
||||
case kindInternal:
|
||||
n := s.getInternal(node.right.Index())
|
||||
if err := s.collectChildGroups(n, appendBit(path, 1), flushfn, groupDepth, remainingLevels-1); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
extPath := s.extendPathToGroupLeaf(appendBit(path, 1), node.right, remainingLevels, int(childDepth))
|
||||
if err := s.collectNodes(node.right, extPath, flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extendPathToGroupLeaf extends a storage path to the group's leaf boundary,
|
||||
// matching the projection done by serializeSubtree. For StemNodes, the path
|
||||
// is extended using the stem's key bits (same as serializeSubtree). For other
|
||||
// node types, the path is extended with all-zero (left) bits.
|
||||
func (s *nodeStore) extendPathToGroupLeaf(path []byte, node nodeRef, remainingLevels int, absoluteDepth int) []byte {
|
||||
if remainingLevels <= 0 {
|
||||
return path
|
||||
}
|
||||
if node.Kind() == kindStem {
|
||||
sn := s.getStem(node.Index())
|
||||
for d := 0; d < remainingLevels; d++ {
|
||||
bit := sn.Stem[(absoluteDepth+d)/8] >> (7 - ((absoluteDepth + d) % 8)) & 1
|
||||
path = appendBit(path, bit)
|
||||
}
|
||||
} else {
|
||||
// HashedNode or other: all-left extension (matches serializeSubtree's
|
||||
// position << remainingDepth behavior).
|
||||
for d := 0; d < remainingLevels; d++ {
|
||||
path = appendBit(path, 0)
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// appendBit appends a bit to a path, returning a new slice
|
||||
func appendBit(path []byte, bit byte) []byte {
|
||||
var p [256]byte
|
||||
copy(p[:], path)
|
||||
result := p[:len(path)]
|
||||
return append(result, bit)
|
||||
}
|
||||
|
||||
func (s *nodeStore) toDot(ref nodeRef, parent, path string) string {
|
||||
switch ref.Kind() {
|
||||
case kindInternal:
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ func ChunkifyCode(code []byte) ChunkedCode {
|
|||
|
||||
// BinaryTrie is the implementation of https://eips.ethereum.org/EIPS/eip-7864.
|
||||
type BinaryTrie struct {
|
||||
root BinaryNode
|
||||
store *nodeStore
|
||||
reader *trie.Reader
|
||||
tracer *trie.PrevalueTracer
|
||||
groupDepth int // Number of levels per serialized group (1-8, default 8)
|
||||
|
|
@ -130,7 +130,7 @@ func NewBinaryTrie(root common.Hash, db database.NodeDatabase, groupDepth int) (
|
|||
return nil, err
|
||||
}
|
||||
t := &BinaryTrie{
|
||||
root: NewBinaryNode(),
|
||||
store: newNodeStore(),
|
||||
reader: reader,
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: groupDepth,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import (
|
|||
// Config defines all necessary options for database.
|
||||
type Config struct {
|
||||
Preimages bool // Flag whether the preimage of node key is recorded
|
||||
IsVerkle bool // Flag whether the db is holding a verkle tree
|
||||
IsUBT bool // Flag whether the db is holding a unified binary tree
|
||||
BinTrieGroupDepth int // Number of levels per serialized group in binary trie (1-8, default 8)
|
||||
HashDB *hashdb.Config // Configs for hash-based scheme
|
||||
PathDB *pathdb.Config // Configs for experimental path-based scheme
|
||||
|
|
@ -46,7 +46,7 @@ var HashDefaults = &Config{
|
|||
HashDB: hashdb.Defaults,
|
||||
}
|
||||
|
||||
// UBTDefaults represents a config for holding verkle trie data
|
||||
// UBTDefaults represents a config for holding unified binary trie data
|
||||
// using path-based scheme with default settings.
|
||||
var UBTDefaults = &Config{
|
||||
Preimages: false,
|
||||
|
|
|
|||
Loading…
Reference in a new issue