fix: Strip group-depth from GC

This commit is contained in:
CPerezz 2026-04-15 16:54:54 +02:00
parent 8a5e777fde
commit c1fb257e2e
No known key found for this signature in database
GPG key ID: 62045F34B97177DD
10 changed files with 80 additions and 329 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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)
}

View file

@ -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.

View file

@ -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 {

View file

@ -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)
}

View file

@ -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() {

View file

@ -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(),
}
}

View file

@ -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")