trie/bintrie: fix NodeIterator Empty node handling

Fix three issues in the binary trie NodeIterator:

1. Empty nodes now properly backtrack to parent and continue iteration
   instead of terminating the entire walk early.

2. HashedNode resolver handles nil data (all-zeros hash) gracefully by
   treating it as Empty rather than panicking.

3. Parent update after node resolution guards against stack underflow
   when resolving the root node itself.
This commit is contained in:
tellabg 2026-03-19 19:46:01 +01:00
parent 35b91092c5
commit 6cc4dc5f10

View file

@ -119,10 +119,17 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
return it.Next(descend) return it.Next(descend)
case HashedNode: case HashedNode:
// resolve the node // resolve the node
data, err := it.trie.nodeResolver(it.Path(), common.Hash(node)) resolverPath := it.Path()
data, err := it.trie.nodeResolver(resolverPath, common.Hash(node))
if err != nil { if err != nil {
panic(err) panic(err)
} }
if data == nil {
// Empty/nil node — treat as Empty, backtrack
it.current = Empty{}
it.stack[len(it.stack)-1].Node = it.current
return it.Next(descend)
}
it.current, err = DeserializeNodeWithHash(data, len(it.stack)-1, common.Hash(node)) it.current, err = DeserializeNodeWithHash(data, len(it.stack)-1, common.Hash(node))
if err != nil { if err != nil {
panic(err) panic(err)
@ -130,16 +137,25 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
// update the stack and parent with the resolved node // update the stack and parent with the resolved node
it.stack[len(it.stack)-1].Node = it.current it.stack[len(it.stack)-1].Node = it.current
parent := &it.stack[len(it.stack)-2] if len(it.stack) >= 2 {
if parent.Index == 0 { parent := &it.stack[len(it.stack)-2]
parent.Node.(*InternalNode).left = it.current if parent.Index == 0 {
} else { parent.Node.(*InternalNode).left = it.current
parent.Node.(*InternalNode).right = it.current } else {
parent.Node.(*InternalNode).right = it.current
}
} }
return it.Next(descend) return it.Next(descend)
case Empty: case Empty:
// do nothing // Empty node - go back to parent and continue
return false if len(it.stack) <= 1 {
it.lastErr = errIteratorEnd
return false
}
it.stack = it.stack[:len(it.stack)-1]
it.current = it.stack[len(it.stack)-1].Node
it.stack[len(it.stack)-1].Index++
return it.Next(descend)
default: default:
panic("invalid node type") panic("invalid node type")
} }