-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(tree): safety check for all node types #422
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -329,9 +329,14 @@ func (n *InternalNode) Children() []VerkleNode { | |
|
||
// SetChild *replaces* the child at the given index with the given node. | ||
func (n *InternalNode) SetChild(i int, c VerkleNode) error { | ||
if c == nil { | ||
return errNilNodeType | ||
} | ||
|
||
if i >= NodeWidth { | ||
return errors.New("child index higher than node width") | ||
} | ||
|
||
n.children[i] = c | ||
return nil | ||
} | ||
|
@@ -608,8 +613,14 @@ func (n *InternalNode) Delete(key []byte, resolver NodeResolverFn) (bool, error) | |
// signal that this node should be deleted | ||
// as well. | ||
for _, c := range n.children { | ||
if _, ok := c.(Empty); !ok { | ||
switch c.(type) { | ||
case *InternalNode, *LeafNode: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't |
||
break | ||
case Empty, HashedNode: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you are trying to figure out if a node is empty here, to see if it can be deleted. So |
||
case UnknownNode: | ||
panic(errUnknownNodeType) | ||
Comment on lines
+620
to
+621
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't panic here but return an error. This situation happening isn't a bug, but someone trying to operate in a state tree rebuilt from a proof. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that case is a valid tree. If anything, one should try to resolve the node. |
||
default: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a |
||
panic(errNilNodeType) | ||
Comment on lines
+622
to
+623
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two points about this:
|
||
} | ||
} | ||
|
||
|
@@ -639,14 +650,20 @@ func (n *InternalNode) Flush(flush NodeFlushFn) { | |
|
||
n.Commit() | ||
for i, child := range n.children { | ||
if c, ok := child.(*InternalNode); ok { | ||
switch c := child.(type) { | ||
case *InternalNode: | ||
c.Commit() | ||
c.Flush(flushAndCapturePath) | ||
n.children[i] = HashedNode{} | ||
} else if c, ok := child.(*LeafNode); ok { | ||
case *LeafNode: | ||
c.Commit() | ||
flushAndCapturePath(c.stem[:n.depth+1], n.children[i]) | ||
flushAndCapturePath(c.stem[:n.depth+1], c) | ||
n.children[i] = HashedNode{} | ||
case Empty, HashedNode: | ||
case UnknownNode: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't panic here!
|
||
panic(errUnknownNodeType) | ||
default: | ||
panic(errNilNodeType) | ||
Comment on lines
+663
to
+666
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar as before, I'd take There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nil is also valid, although deprecated. feel free to put a warning in that case, but it's no cause for error or panic. |
||
} | ||
} | ||
flush(path, n) | ||
|
@@ -875,33 +892,32 @@ func (n *InternalNode) GetProofItems(keys keylist, resolver NodeResolverFn) (*Pr | |
var fiPtrs [NodeWidth]*Fr | ||
var points [NodeWidth]*Point | ||
for i, child := range n.children { | ||
var c VerkleNode | ||
|
||
fiPtrs[i] = &fi[i] | ||
if child != nil { | ||
var c VerkleNode | ||
if _, ok := child.(HashedNode); ok { | ||
childpath := make([]byte, n.depth+1) | ||
copy(childpath[:n.depth+1], keys[0][:n.depth]) | ||
childpath[n.depth] = byte(i) | ||
if resolver == nil { | ||
return nil, nil, nil, fmt.Errorf("no resolver for path %x", childpath) | ||
} | ||
serialized, err := resolver(childpath) | ||
if err != nil { | ||
return nil, nil, nil, fmt.Errorf("error resolving for path %x: %w", childpath, err) | ||
} | ||
c, err = ParseNode(serialized, n.depth+1) | ||
if err != nil { | ||
return nil, nil, nil, err | ||
} | ||
n.children[i] = c | ||
} else { | ||
c = child | ||
switch child := child.(type) { | ||
case HashedNode: | ||
childpath := make([]byte, n.depth+1) | ||
copy(childpath[:n.depth+1], keys[0][:n.depth]) | ||
childpath[n.depth] = byte(i) | ||
if resolver == nil { | ||
return nil, nil, nil, fmt.Errorf("no resolver for path %x", childpath) | ||
} | ||
points[i] = c.Commitment() | ||
} else { | ||
// TODO: add a test case to cover this scenario. | ||
points[i] = new(Point) | ||
serialized, err := resolver(childpath) | ||
if err != nil { | ||
return nil, nil, nil, fmt.Errorf("error resolving for path %x: %w", childpath, err) | ||
} | ||
c, err = ParseNode(serialized, n.depth+1) | ||
if err != nil { | ||
return nil, nil, nil, err | ||
} | ||
n.children[i] = c | ||
case *InternalNode, *LeafNode, Empty, UnknownNode: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uhm, this @gballet, unfortunately, I think in L920 (below) the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, or at least return an error. We don't want all the nodes on the network to panic. The error allows us to quit without destroying the db. |
||
c = child | ||
default: | ||
panic(errNilNodeType) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you switch to |
||
} | ||
points[i] = c.Commitment() | ||
} | ||
if err := banderwagon.BatchMapToScalarField(fiPtrs[:], points[:]); err != nil { | ||
return nil, nil, nil, fmt.Errorf("batch mapping to scalar fields: %s", err) | ||
|
@@ -972,8 +988,14 @@ func (n *InternalNode) Serialize() ([]byte, error) { | |
// Write the <bitlist>. | ||
bitlist := ret[internalBitlistOffset:internalCommitmentOffset] | ||
for i, c := range n.children { | ||
if _, ok := c.(Empty); !ok { | ||
switch c.(type) { | ||
case *InternalNode, *LeafNode: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's a mistake here, it should also include There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that was previously true, but now the serialization of an internal node is simply its commitment, so it doesn't matter. |
||
setBit(bitlist, i) | ||
case Empty, HashedNode: | ||
case UnknownNode: | ||
panic(errUnknownNodeType) | ||
default: | ||
panic(errNilNodeType) | ||
Comment on lines
+995
to
+998
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return errors. |
||
} | ||
} | ||
|
||
|
@@ -995,6 +1017,9 @@ func (n *InternalNode) Copy() VerkleNode { | |
} | ||
|
||
for i, child := range n.children { | ||
if child == nil { | ||
panic(errNilNodeType) | ||
} | ||
Comment on lines
+1020
to
+1022
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. log a warning, don't panic |
||
ret.children[i] = child.Copy() | ||
} | ||
|
||
|
@@ -1024,7 +1049,7 @@ func (n *InternalNode) toDot(parent, path string) string { | |
|
||
for i, child := range n.children { | ||
if child == nil { | ||
continue | ||
panic(errNilNodeType) | ||
} | ||
ret = fmt.Sprintf("%s%s", ret, child.toDot(me, fmt.Sprintf("%s%02x", path, i))) | ||
} | ||
|
@@ -1719,6 +1744,11 @@ func (n *InternalNode) collectNonHashedNodes(list []VerkleNode, paths [][]byte, | |
copy(childpath, path) | ||
childpath[len(path)] = byte(i) | ||
list, paths = childNode.collectNonHashedNodes(list, paths, childpath) | ||
case Empty, HashedNode: | ||
case UnknownNode: | ||
panic(errUnknownNodeType) | ||
default: | ||
panic(errNilNodeType) | ||
Comment on lines
+1748
to
+1751
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you switch to return an error by changing this API to also return an error? |
||
} | ||
} | ||
return list, paths | ||
|
@@ -1729,8 +1759,14 @@ func (n *InternalNode) serializeInternalWithUncompressedCommitment(pointsIdx map | |
serialized := make([]byte, nodeTypeSize+bitlistSize+banderwagon.UncompressedSize) | ||
bitlist := serialized[internalBitlistOffset:internalCommitmentOffset] | ||
for i, c := range n.children { | ||
if _, ok := c.(Empty); !ok { | ||
switch c.(type) { | ||
case *InternalNode, *LeafNode: | ||
setBit(bitlist, i) | ||
case Empty, HashedNode: | ||
case UnknownNode: | ||
panic(errUnknownNodeType) | ||
default: | ||
panic(errNilNodeType) | ||
Comment on lines
+1766
to
+1769
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return errors. |
||
} | ||
} | ||
serialized[nodeTypeOffset] = internalRLPType | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gballet, we should plan to remove this
SetChild(...)
since it opens the door for external clients to nuke a lot of invariants on how the tree is constructed.I've looked where this is used in geth, and is for the Verkle Tree iterator to "resolve" a
HashedNode
and then callSetChild(...)
to put the resulting Internal/Leaf node. I believe we can resolve this in a simpler way than requiring this super powerfulSetChild(...)
thing.LMK your thoughts. This isn't urgent in anyway, but if makes sense we can track this by creating an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah ok, PRs welcome :)