Skip to content

Commit

Permalink
Check for placeholder nodes in proof verification
Browse files Browse the repository at this point in the history
Certain proofs (like those for a sparse Merkle tree) can contain empty nodes which are equal to the
EmptyChild, but currently these will cause verification to fail for non-existence proofs if they
appear where a real sibling node would be invalid, i.e. on a path that must be the leftmost or
rightmost path of a subtree.

This adds a check to detect and ignore such nodes during verification of left- and rightmost paths.
  • Loading branch information
roysc committed Dec 12, 2021
1 parent dc210c0 commit 5824866
Showing 1 changed file with 42 additions and 0 deletions.
42 changes: 42 additions & 0 deletions go/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ func IsLeftMost(spec *InnerSpec, path []*InnerOp) bool {
// ensure every step has a prefix and suffix defined to be leftmost
for _, step := range path {
if !hasPadding(step, minPrefix, maxPrefix, suffix) {
// if this is a placeholder node, skip it
if isPlaceholder(spec, 0, step, true) {
continue
}
return false
}
}
Expand All @@ -231,6 +235,10 @@ func IsRightMost(spec *InnerSpec, path []*InnerOp) bool {
// ensure every step has a prefix and suffix defined to be rightmost
for _, step := range path {
if !hasPadding(step, minPrefix, maxPrefix, suffix) {
// if this is a placeholder node, skip it
if isPlaceholder(spec, int32(last), step, false) {
continue
}
return false
}
}
Expand Down Expand Up @@ -309,6 +317,40 @@ func getPadding(spec *InnerSpec, branch int32) (minPrefix, maxPrefix, suffix int
return
}

// isPlaceholder returns true if the padding bytes correspond to all empty children
// on a given side of this branch, ie. it's a valid placeholder for this position
func isPlaceholder(spec *InnerSpec, branch int32, op *InnerOp, inPrefix bool) bool {
idx := getPosition(spec.ChildOrder, branch)

// compare the prefix bytes with the appropriate number of empty children
if inPrefix {
prefixCt := len(spec.ChildOrder) - 1 - idx
lenDiff := len(op.Prefix) - prefixCt*int(spec.ChildSize)
if lenDiff < int(spec.MinPrefixLength) || lenDiff > int(spec.MaxPrefixLength) {
return false
}
for i := 0; i < prefixCt; i++ {
// walk back from the end of the prefix, since its length could vary
from := len(op.Prefix) - (prefixCt-i)*int(spec.ChildSize)
if bytes.Compare(spec.EmptyChild, op.Prefix[from:from+int(spec.ChildSize)]) != 0 {
return false
}
}
return true
}
// otherwise, compare the suffix bytes
if len(op.Suffix) != idx*int(spec.ChildSize) {
return false
}
for i := 0; i < idx; i++ {
from := i * int(spec.ChildSize)
if bytes.Compare(spec.EmptyChild, op.Suffix[from:from+int(spec.ChildSize)]) != 0 {
return false
}
}
return true
}

// getPosition checks where the branch is in the order and returns
// the index of this branch
func getPosition(order []int32, branch int32) int {
Expand Down

0 comments on commit 5824866

Please sign in to comment.