mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-12 01:41:36 +00:00
fix: Strip group-depth from GC
This commit is contained in:
parent
8a5e777fde
commit
c1fb257e2e
10 changed files with 80 additions and 329 deletions
|
|
@ -27,19 +27,8 @@ const (
|
|||
NodeTypeBytes = 1 // Size of node type prefix in serialization
|
||||
HashSize = 32 // Size of a hash in bytes
|
||||
StemBitmapSize = 32 // Size of the bitmap in a stem node (256 values = 32 bytes)
|
||||
|
||||
// MaxGroupDepth is the maximum allowed group depth for InternalNode serialization.
|
||||
MaxGroupDepth = 8
|
||||
)
|
||||
|
||||
// BitmapSizeForDepth returns the bitmap size in bytes for a given group depth.
|
||||
func BitmapSizeForDepth(groupDepth int) int {
|
||||
if groupDepth <= 3 {
|
||||
return 1
|
||||
}
|
||||
return 1 << (groupDepth - 3)
|
||||
}
|
||||
|
||||
const (
|
||||
nodeTypeStem = iota + 1
|
||||
nodeTypeInternal
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// TestSerializeDeserializeInternalNode tests serialization and deserialization of InternalNode
|
||||
// with the grouped subtree format through NodeStore.
|
||||
// TestSerializeDeserializeInternalNode tests flat 65-byte serialization and
|
||||
// deserialization of InternalNode through NodeStore.
|
||||
func TestSerializeDeserializeInternalNode(t *testing.T) {
|
||||
leftHash := common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
|
||||
rightHash := common.HexToHash("0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321")
|
||||
|
|
@ -39,30 +39,25 @@ func TestSerializeDeserializeInternalNode(t *testing.T) {
|
|||
rootNode.right = rightRef
|
||||
s.SetRoot(rootRef)
|
||||
|
||||
// Serialize the node with default group depth of 8
|
||||
serialized := s.SerializeNode(rootRef, MaxGroupDepth)
|
||||
// Serialize the node — flat 65-byte format
|
||||
serialized := s.SerializeNode(rootRef)
|
||||
|
||||
// Check the serialized format
|
||||
// Check the serialized format: [type(1)][leftHash(32)][rightHash(32)]
|
||||
if serialized[0] != nodeTypeInternal {
|
||||
t.Errorf("Expected type byte to be %d, got %d", nodeTypeInternal, serialized[0])
|
||||
}
|
||||
if serialized[1] != MaxGroupDepth {
|
||||
t.Errorf("Expected group depth to be %d, got %d", MaxGroupDepth, serialized[1])
|
||||
}
|
||||
|
||||
bitmapSize := BitmapSizeForDepth(MaxGroupDepth)
|
||||
expectedLen := 1 + 1 + bitmapSize + 2*HashSize
|
||||
expectedLen := NodeTypeBytes + 2*HashSize // 1 + 64 = 65
|
||||
if len(serialized) != expectedLen {
|
||||
t.Errorf("Expected serialized length to be %d, got %d", expectedLen, len(serialized))
|
||||
}
|
||||
|
||||
// Check bitmap bits
|
||||
bitmap := serialized[2 : 2+bitmapSize]
|
||||
if bitmap[0]&0x80 == 0 {
|
||||
t.Error("Expected bit 0 to be set in bitmap (left child)")
|
||||
// Check that left and right hashes are embedded directly
|
||||
if !bytes.Equal(serialized[NodeTypeBytes:NodeTypeBytes+HashSize], leftHash[:]) {
|
||||
t.Error("Left hash not found at expected position")
|
||||
}
|
||||
if bitmap[16]&0x80 == 0 {
|
||||
t.Error("Expected bit 128 to be set in bitmap (right child)")
|
||||
if !bytes.Equal(serialized[NodeTypeBytes+HashSize:], rightHash[:]) {
|
||||
t.Error("Right hash not found at expected position")
|
||||
}
|
||||
|
||||
// Deserialize into a new store
|
||||
|
|
@ -82,35 +77,21 @@ func TestSerializeDeserializeInternalNode(t *testing.T) {
|
|||
t.Errorf("Expected depth 0, got %d", internalNode.depth)
|
||||
}
|
||||
|
||||
// Navigate to position 0 (8 left turns) to find the left hash
|
||||
node0 := navigateToLeafRef(ds, deserialized, 0, 8)
|
||||
if ds.ComputeHash(node0) != leftHash {
|
||||
t.Errorf("Left hash mismatch: expected %x, got %x", leftHash, ds.ComputeHash(node0))
|
||||
// Left child should be a HashedNode with the correct hash
|
||||
if internalNode.left.Kind() != KindHashed {
|
||||
t.Fatalf("Expected left child to be KindHashed, got %d", internalNode.left.Kind())
|
||||
}
|
||||
if ds.ComputeHash(internalNode.left) != leftHash {
|
||||
t.Errorf("Left hash mismatch: expected %x, got %x", leftHash, ds.ComputeHash(internalNode.left))
|
||||
}
|
||||
|
||||
// Navigate to position 128 (right, then 7 lefts) to find the right hash
|
||||
node128 := navigateToLeafRef(ds, deserialized, 128, 8)
|
||||
if ds.ComputeHash(node128) != rightHash {
|
||||
t.Errorf("Right hash mismatch: expected %x, got %x", rightHash, ds.ComputeHash(node128))
|
||||
// Right child should be a HashedNode with the correct hash
|
||||
if internalNode.right.Kind() != KindHashed {
|
||||
t.Fatalf("Expected right child to be KindHashed, got %d", internalNode.right.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
// navigateToLeafRef navigates to a specific position in the tree using NodeStore.
|
||||
func navigateToLeafRef(s *NodeStore, ref NodeRef, position, depth int) NodeRef {
|
||||
cur := ref
|
||||
for d := 0; d < depth; d++ {
|
||||
if cur.Kind() != KindInternal {
|
||||
return cur
|
||||
}
|
||||
in := s.getInternal(cur.Index())
|
||||
bit := (position >> (depth - 1 - d)) & 1
|
||||
if bit == 0 {
|
||||
cur = in.left
|
||||
} else {
|
||||
cur = in.right
|
||||
}
|
||||
if ds.ComputeHash(internalNode.right) != rightHash {
|
||||
t.Errorf("Right hash mismatch: expected %x, got %x", rightHash, ds.ComputeHash(internalNode.right))
|
||||
}
|
||||
return cur
|
||||
}
|
||||
|
||||
// TestSerializeDeserializeStemNode tests serialization and deserialization of StemNode through NodeStore.
|
||||
|
|
@ -135,7 +116,7 @@ func TestSerializeDeserializeStemNode(t *testing.T) {
|
|||
}
|
||||
|
||||
// Serialize the node
|
||||
serialized := s.SerializeNode(ref, MaxGroupDepth)
|
||||
serialized := s.SerializeNode(ref)
|
||||
|
||||
// Check the serialized format
|
||||
if serialized[0] != nodeTypeStem {
|
||||
|
|
@ -214,8 +195,8 @@ func TestDeserializeInvalidType(t *testing.T) {
|
|||
// TestDeserializeInvalidLength tests deserialization with invalid data length.
|
||||
func TestDeserializeInvalidLength(t *testing.T) {
|
||||
s := NewNodeStore()
|
||||
// InternalNode with valid type byte and group depth but too short for bitmap
|
||||
invalidData := []byte{nodeTypeInternal, 8, 0, 0} // Too short for bitmap (needs 32 bytes)
|
||||
// InternalNode with valid type byte but wrong length (needs exactly 65 bytes)
|
||||
invalidData := []byte{nodeTypeInternal, 0, 0, 0}
|
||||
|
||||
_, err := s.DeserializeNode(invalidData, 0)
|
||||
if err == nil {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ func TestHashedNodeInsertValuesAtStem(t *testing.T) {
|
|||
sn.setValue(byte(i), v)
|
||||
}
|
||||
}
|
||||
serialized := rs.SerializeNode(ref, MaxGroupDepth)
|
||||
serialized := rs.SerializeNode(ref)
|
||||
|
||||
validResolver := func(path []byte, hash common.Hash) ([]byte, error) {
|
||||
return serialized, nil
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ func TestInternalNodeGetWithResolver(t *testing.T) {
|
|||
ref := rs.newStemRef(stem, 1)
|
||||
sn := rs.getStem(ref.Index())
|
||||
sn.setValue(5, common.HexToHash("0xabcd").Bytes())
|
||||
return rs.SerializeNode(ref, MaxGroupDepth), nil
|
||||
return rs.SerializeNode(ref), nil
|
||||
}
|
||||
return nil, errors.New("node not found")
|
||||
}
|
||||
|
|
@ -290,7 +290,7 @@ func TestInternalNodeCollectNodes(t *testing.T) {
|
|||
collectedPaths = append(collectedPaths, pathCopy)
|
||||
}
|
||||
|
||||
err := s.CollectNodes(s.Root(), []byte{1}, flushFn, MaxGroupDepth)
|
||||
err := s.CollectNodes(s.Root(), []byte{1}, flushFn)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to collect nodes: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ func (it *binaryNodeIterator) Path() []byte {
|
|||
|
||||
// NodeBlob returns the serialized bytes of the current node.
|
||||
func (it *binaryNodeIterator) NodeBlob() []byte {
|
||||
return it.store.SerializeNode(it.current, MaxGroupDepth)
|
||||
return it.store.SerializeNode(it.current)
|
||||
}
|
||||
|
||||
// Leaf returns true iff the current node is a leaf node.
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ func makeTrie(t *testing.T, entries [][2]common.Hash) *BinaryTrie {
|
|||
tr := &BinaryTrie{
|
||||
store: store,
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
for _, kv := range entries {
|
||||
if err := store.Insert(kv[0][:], kv[1][:], nil); err != nil {
|
||||
|
|
@ -66,7 +65,6 @@ func TestIteratorEmptyTrie(t *testing.T) {
|
|||
tr := &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
it, err := newBinaryNodeIterator(tr, nil)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ func TestStemNodeCollectNodes(t *testing.T) {
|
|||
collectedPaths = append(collectedPaths, pathCopy)
|
||||
}
|
||||
|
||||
err := s.CollectNodes(s.Root(), []byte{0, 1, 0}, flushFn, MaxGroupDepth)
|
||||
err := s.CollectNodes(s.Root(), []byte{0, 1, 0}, flushFn)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to collect nodes: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,86 +98,20 @@ func (s *NodeStore) hashInternal(idx uint32) common.Hash {
|
|||
|
||||
// --- Serialization ---
|
||||
|
||||
// countSubtreeChildren counts non-empty children at the bottom layer of a subtree.
|
||||
func (s *NodeStore) countSubtreeChildren(ref NodeRef, remainingDepth int) int {
|
||||
if remainingDepth == 0 {
|
||||
if ref.IsEmpty() {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
if ref.Kind() == KindInternal {
|
||||
node := s.getInternal(ref.Index())
|
||||
return s.countSubtreeChildren(node.left, remainingDepth-1) + s.countSubtreeChildren(node.right, remainingDepth-1)
|
||||
}
|
||||
if ref.IsEmpty() {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// serializeSubtreeDirect writes child hashes directly into the serialized buffer.
|
||||
func (s *NodeStore) serializeSubtreeDirect(ref NodeRef, remainingDepth int, position int, absoluteDepth int, bitmap []byte, out []byte, hashOffset *int) {
|
||||
if remainingDepth == 0 {
|
||||
if ref.IsEmpty() {
|
||||
return
|
||||
}
|
||||
bitmap[position/8] |= 1 << (7 - (position % 8))
|
||||
h := s.ComputeHash(ref)
|
||||
copy(out[*hashOffset:*hashOffset+HashSize], h[:])
|
||||
*hashOffset += HashSize
|
||||
return
|
||||
}
|
||||
|
||||
if ref.Kind() == KindInternal {
|
||||
node := s.getInternal(ref.Index())
|
||||
leftPos := position * 2
|
||||
rightPos := position*2 + 1
|
||||
s.serializeSubtreeDirect(node.left, remainingDepth-1, leftPos, absoluteDepth+1, bitmap, out, hashOffset)
|
||||
s.serializeSubtreeDirect(node.right, remainingDepth-1, rightPos, absoluteDepth+1, bitmap, out, hashOffset)
|
||||
return
|
||||
}
|
||||
|
||||
if ref.IsEmpty() {
|
||||
return
|
||||
}
|
||||
|
||||
// Leaf (StemNode or HashedNode) at a non-zero remaining depth: compute its position.
|
||||
leafPos := position
|
||||
if ref.Kind() == KindStem {
|
||||
sn := s.getStem(ref.Index())
|
||||
for d := 0; d < remainingDepth; d++ {
|
||||
bit := sn.Stem[(absoluteDepth+d)/8] >> (7 - ((absoluteDepth + d) % 8)) & 1
|
||||
leafPos = leafPos*2 + int(bit)
|
||||
}
|
||||
} else {
|
||||
leafPos = position << remainingDepth
|
||||
}
|
||||
bitmap[leafPos/8] |= 1 << (7 - (leafPos % 8))
|
||||
h := s.ComputeHash(ref)
|
||||
copy(out[*hashOffset:*hashOffset+HashSize], h[:])
|
||||
*hashOffset += HashSize
|
||||
}
|
||||
|
||||
// SerializeNode serializes a node referenced by ref.
|
||||
func (s *NodeStore) SerializeNode(ref NodeRef, groupDepth int) []byte {
|
||||
if groupDepth < 1 || groupDepth > MaxGroupDepth {
|
||||
panic("groupDepth must be between 1 and 8")
|
||||
}
|
||||
|
||||
// SerializeNode serializes a node referenced by ref into the flat format:
|
||||
// - InternalNode: [nodeTypeInternal(1)][leftHash(32)][rightHash(32)] = 65 bytes
|
||||
// - StemNode: [nodeTypeStem(1)][stem(31)][bitmap(32)][valueData(variable)]
|
||||
func (s *NodeStore) SerializeNode(ref NodeRef) []byte {
|
||||
switch ref.Kind() {
|
||||
case KindInternal:
|
||||
node := s.getInternal(ref.Index())
|
||||
bitmapSize := BitmapSizeForDepth(groupDepth)
|
||||
hashCount := s.countSubtreeChildren(ref, groupDepth)
|
||||
serializedLen := NodeTypeBytes + 1 + bitmapSize + hashCount*HashSize
|
||||
serialized := make([]byte, serializedLen)
|
||||
var serialized [NodeTypeBytes + HashSize + HashSize]byte
|
||||
serialized[0] = nodeTypeInternal
|
||||
serialized[1] = byte(groupDepth)
|
||||
bitmap := serialized[2 : 2+bitmapSize]
|
||||
hashOffset := 2 + bitmapSize
|
||||
s.serializeSubtreeDirect(ref, groupDepth, 0, int(node.depth), bitmap, serialized, &hashOffset)
|
||||
return serialized
|
||||
lh := s.ComputeHash(node.left)
|
||||
rh := s.ComputeHash(node.right)
|
||||
copy(serialized[NodeTypeBytes:NodeTypeBytes+HashSize], lh[:])
|
||||
copy(serialized[NodeTypeBytes+HashSize:], rh[:])
|
||||
return serialized[:]
|
||||
|
||||
case KindStem:
|
||||
sn := s.getStem(ref.Index())
|
||||
|
|
@ -215,27 +149,23 @@ func (s *NodeStore) deserializeNode(serialized []byte, depth int, hn common.Hash
|
|||
|
||||
switch serialized[0] {
|
||||
case nodeTypeInternal:
|
||||
if len(serialized) < NodeTypeBytes+1 {
|
||||
if len(serialized) != NodeTypeBytes+2*HashSize {
|
||||
return EmptyRef, errInvalidSerializedLength
|
||||
}
|
||||
groupDepth := int(serialized[1])
|
||||
if groupDepth < 1 || groupDepth > MaxGroupDepth {
|
||||
return EmptyRef, errors.New("invalid group depth")
|
||||
}
|
||||
bitmapSize := BitmapSizeForDepth(groupDepth)
|
||||
if len(serialized) < NodeTypeBytes+1+bitmapSize {
|
||||
return EmptyRef, errInvalidSerializedLength
|
||||
}
|
||||
bitmap := serialized[2 : 2+bitmapSize]
|
||||
hashData := serialized[2+bitmapSize:]
|
||||
var leftHash, rightHash common.Hash
|
||||
copy(leftHash[:], serialized[NodeTypeBytes:NodeTypeBytes+HashSize])
|
||||
copy(rightHash[:], serialized[NodeTypeBytes+HashSize:])
|
||||
|
||||
hashIdx := 0
|
||||
ref, err := s.deserializeSubtree(groupDepth, 0, depth, bitmap, hashData, &hashIdx, mustRecompute)
|
||||
if err != nil {
|
||||
return EmptyRef, err
|
||||
}
|
||||
if ref.Kind() == KindInternal && !mustRecompute {
|
||||
s.getInternal(ref.Index()).hash = hn
|
||||
leftRef := s.newHashedRef(leftHash)
|
||||
rightRef := s.newHashedRef(rightHash)
|
||||
|
||||
ref := s.newInternalRef(depth)
|
||||
node := s.getInternal(ref.Index())
|
||||
node.left = leftRef
|
||||
node.right = rightRef
|
||||
if !mustRecompute {
|
||||
node.hash = hn
|
||||
node.mustRecompute = false
|
||||
}
|
||||
return ref, nil
|
||||
|
||||
|
|
@ -271,167 +201,34 @@ func (s *NodeStore) deserializeNode(serialized []byte, depth int, hn common.Hash
|
|||
}
|
||||
}
|
||||
|
||||
func (s *NodeStore) deserializeSubtree(remainingDepth int, position int, nodeDepth int, bitmap []byte, hashData []byte, hashIdx *int, mustRecompute bool) (NodeRef, error) {
|
||||
if remainingDepth == 0 {
|
||||
if bitmap[position/8]>>(7-(position%8))&1 == 1 {
|
||||
if len(hashData) < (*hashIdx+1)*HashSize {
|
||||
return EmptyRef, errInvalidSerializedLength
|
||||
}
|
||||
hash := common.BytesToHash(hashData[*hashIdx*HashSize : (*hashIdx+1)*HashSize])
|
||||
*hashIdx++
|
||||
return s.newHashedRef(hash), nil
|
||||
}
|
||||
return EmptyRef, nil
|
||||
}
|
||||
|
||||
leftPos := position * 2
|
||||
rightPos := position*2 + 1
|
||||
|
||||
left, err := s.deserializeSubtree(remainingDepth-1, leftPos, nodeDepth+1, bitmap, hashData, hashIdx, true)
|
||||
if err != nil {
|
||||
return EmptyRef, err
|
||||
}
|
||||
right, err := s.deserializeSubtree(remainingDepth-1, rightPos, nodeDepth+1, bitmap, hashData, hashIdx, true)
|
||||
if err != nil {
|
||||
return EmptyRef, err
|
||||
}
|
||||
|
||||
if left.IsEmpty() && right.IsEmpty() {
|
||||
return EmptyRef, nil
|
||||
}
|
||||
|
||||
if nodeDepth > 248 {
|
||||
panic("node depth exceeds maximum binary trie depth")
|
||||
}
|
||||
idx := s.allocInternal()
|
||||
n := s.getInternal(idx)
|
||||
n.depth = uint8(nodeDepth)
|
||||
n.left = left
|
||||
n.right = right
|
||||
n.mustRecompute = mustRecompute
|
||||
return MakeRef(KindInternal, idx), nil
|
||||
}
|
||||
|
||||
// --- CollectNodes (Commit) ---
|
||||
|
||||
// CollectNodes traverses the trie, flushing nodes at group boundaries.
|
||||
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")
|
||||
}
|
||||
buf := make([]byte, len(path), len(path)+MaxGroupDepth+1)
|
||||
copy(buf, path)
|
||||
return s.collectNodesBuf(ref, buf, flushfn, groupDepth)
|
||||
}
|
||||
|
||||
func (s *NodeStore) collectNodesBuf(ref NodeRef, buf []byte, flushfn NodeFlushFn, groupDepth int) error {
|
||||
// CollectNodes traverses the trie, serializing and flushing each node via flushfn.
|
||||
// Children are flushed before their parents (post-order traversal).
|
||||
func (s *NodeStore) CollectNodes(ref NodeRef, path []byte, flushfn NodeFlushFn) error {
|
||||
switch ref.Kind() {
|
||||
case KindEmpty:
|
||||
return nil
|
||||
case KindInternal:
|
||||
node := s.getInternal(ref.Index())
|
||||
if int(node.depth)%groupDepth == 0 {
|
||||
if err := s.collectChildGroupsBuf(ref, buf, flushfn, groupDepth, groupDepth-1); err != nil {
|
||||
return err
|
||||
}
|
||||
serialized := s.SerializeNode(ref, groupDepth)
|
||||
flushfn(buf, s.ComputeHash(ref), serialized)
|
||||
return nil
|
||||
if err := s.CollectNodes(node.left, append(path, 0), flushfn); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.collectChildGroupsBuf(ref, buf, flushfn, groupDepth, groupDepth-(int(node.depth)%groupDepth)-1)
|
||||
|
||||
if err := s.CollectNodes(node.right, append(path, 1), flushfn); err != nil {
|
||||
return err
|
||||
}
|
||||
flushfn(path, s.ComputeHash(ref), s.SerializeNode(ref))
|
||||
return nil
|
||||
case KindStem:
|
||||
serialized := s.SerializeNode(ref, groupDepth)
|
||||
flushfn(buf, s.ComputeHash(ref), serialized)
|
||||
flushfn(path, s.ComputeHash(ref), s.SerializeNode(ref))
|
||||
return nil
|
||||
|
||||
case KindHashed, KindEmpty:
|
||||
return nil
|
||||
|
||||
case KindHashed:
|
||||
return nil // Already committed
|
||||
default:
|
||||
return fmt.Errorf("collectNodesBuf: unexpected kind %d", ref.Kind())
|
||||
return fmt.Errorf("CollectNodes: unexpected kind %d", ref.Kind())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NodeStore) collectChildGroupsBuf(ref NodeRef, buf []byte, flushfn NodeFlushFn, groupDepth int, remainingLevels int) error {
|
||||
if ref.Kind() != KindInternal {
|
||||
return nil
|
||||
}
|
||||
node := s.getInternal(ref.Index())
|
||||
saved := len(buf)
|
||||
childDepth := int(node.depth) + 1
|
||||
|
||||
if remainingLevels == 0 {
|
||||
if !node.left.IsEmpty() {
|
||||
buf = append(buf, 0)
|
||||
if err := s.collectNodesBuf(node.left, buf, flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[:saved]
|
||||
}
|
||||
if !node.right.IsEmpty() {
|
||||
buf = append(buf, 1)
|
||||
if err := s.collectNodesBuf(node.right, buf, flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Left child
|
||||
if !node.left.IsEmpty() {
|
||||
if node.left.Kind() == KindInternal {
|
||||
buf = append(buf, 0)
|
||||
if err := s.collectChildGroupsBuf(node.left, buf, flushfn, groupDepth, remainingLevels-1); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[:saved]
|
||||
} else {
|
||||
buf = append(buf, 0)
|
||||
buf = s.extendPathBuf(buf, node.left, remainingLevels, childDepth)
|
||||
if err := s.collectNodesBuf(node.left, buf, flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = buf[:saved]
|
||||
}
|
||||
}
|
||||
|
||||
// Right child
|
||||
if !node.right.IsEmpty() {
|
||||
if node.right.Kind() == KindInternal {
|
||||
buf = append(buf, 1)
|
||||
if err := s.collectChildGroupsBuf(node.right, buf, flushfn, groupDepth, remainingLevels-1); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
buf = append(buf, 1)
|
||||
buf = s.extendPathBuf(buf, node.right, remainingLevels, childDepth)
|
||||
if err := s.collectNodesBuf(node.right, buf, flushfn, groupDepth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// extendPathBuf extends the path buffer to the group's leaf boundary.
|
||||
func (s *NodeStore) extendPathBuf(buf []byte, ref NodeRef, remainingLevels int, absoluteDepth int) []byte {
|
||||
if remainingLevels <= 0 {
|
||||
return buf
|
||||
}
|
||||
if ref.Kind() == KindStem {
|
||||
sn := s.getStem(ref.Index())
|
||||
for d := 0; d < remainingLevels; d++ {
|
||||
bit := sn.Stem[(absoluteDepth+d)/8] >> (7 - ((absoluteDepth + d) % 8)) & 1
|
||||
buf = append(buf, bit)
|
||||
}
|
||||
} else {
|
||||
for d := 0; d < remainingLevels; d++ {
|
||||
buf = append(buf, 0)
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// ToDot generates a DOT representation for debugging.
|
||||
func (s *NodeStore) ToDot(ref NodeRef, parent, path string) string {
|
||||
switch ref.Kind() {
|
||||
|
|
|
|||
|
|
@ -107,10 +107,9 @@ func ChunkifyCode(code []byte) ChunkedCode {
|
|||
|
||||
// BinaryTrie is the implementation of https://eips.ethereum.org/EIPS/eip-7864.
|
||||
type BinaryTrie struct {
|
||||
store *NodeStore
|
||||
reader *trie.Reader
|
||||
tracer *trie.PrevalueTracer
|
||||
groupDepth int
|
||||
store *NodeStore
|
||||
reader *trie.Reader
|
||||
tracer *trie.PrevalueTracer
|
||||
}
|
||||
|
||||
// ToDot converts the binary trie to a DOT language representation. Useful for debugging.
|
||||
|
|
@ -126,10 +125,9 @@ func NewBinaryTrie(root common.Hash, db database.NodeDatabase) (*BinaryTrie, err
|
|||
return nil, err
|
||||
}
|
||||
t := &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
reader: reader,
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: MaxGroupDepth,
|
||||
store: NewNodeStore(),
|
||||
reader: reader,
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
}
|
||||
// Parse the root node if it's not empty
|
||||
if root != types.EmptyBinaryHash && root != types.EmptyRootHash {
|
||||
|
|
@ -311,14 +309,9 @@ func (t *BinaryTrie) Hash() common.Hash {
|
|||
func (t *BinaryTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet) {
|
||||
nodeset := trienode.NewNodeSet(common.Hash{})
|
||||
|
||||
groupDepth := t.groupDepth
|
||||
if groupDepth == 0 {
|
||||
groupDepth = MaxGroupDepth
|
||||
}
|
||||
|
||||
err := t.store.CollectNodes(t.store.Root(), nil, func(path []byte, hash common.Hash, serialized []byte) {
|
||||
nodeset.AddNode(path, trienode.NewNodeWithPrev(hash, serialized, t.tracer.Get(path)))
|
||||
}, groupDepth)
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("CollectNodes failed: %v", err))
|
||||
}
|
||||
|
|
@ -345,10 +338,9 @@ func (t *BinaryTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
|
|||
// Copy creates a deep copy of the trie.
|
||||
func (t *BinaryTrie) Copy() *BinaryTrie {
|
||||
return &BinaryTrie{
|
||||
store: t.store.Copy(),
|
||||
reader: t.reader,
|
||||
tracer: t.tracer.Copy(),
|
||||
groupDepth: t.groupDepth,
|
||||
store: t.store.Copy(),
|
||||
reader: t.reader,
|
||||
tracer: t.tracer.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,6 @@ func TestStorageRoundTrip(t *testing.T) {
|
|||
tr := &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
tracer: tracer,
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
addr := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
|
||||
|
||||
|
|
@ -259,7 +258,6 @@ func newEmptyTestTrie(t *testing.T) *BinaryTrie {
|
|||
return &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -585,7 +583,6 @@ func TestBinaryTrieWitness(t *testing.T) {
|
|||
tr := &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
tracer: tracer,
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
if w := tr.Witness(); len(w) != 0 {
|
||||
t.Fatal("expected empty witness for fresh trie")
|
||||
|
|
@ -613,7 +610,6 @@ func testAccount(t *testing.T, addr common.Address, nonce uint64, balance uint64
|
|||
tr := &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
acc := &types.StateAccount{
|
||||
Nonce: nonce,
|
||||
|
|
@ -668,7 +664,6 @@ func TestGetAccountNonMembershipInternalRoot(t *testing.T) {
|
|||
tr := &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
|
||||
// Insert two accounts whose binary tree keys have different first bits
|
||||
|
|
@ -732,7 +727,6 @@ func TestGetStorageNonMembershipInternalRoot(t *testing.T) {
|
|||
tr := &BinaryTrie{
|
||||
store: NewNodeStore(),
|
||||
tracer: trie.NewPrevalueTracer(),
|
||||
groupDepth: MaxGroupDepth,
|
||||
}
|
||||
|
||||
addr := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
|
||||
|
|
|
|||
Loading…
Reference in a new issue