Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 37 additions & 17 deletions x/merkledb/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,24 @@ func (proof *RangeProof) Verify(
return err
}

largestPath := maybe.Bind(end, newPath)
// [proof] allegedly provides and proves all key-value
// pairs in [smallestProvenPath, largestProvenPath].
// If [smallestProvenPath] is Nothing, [proof] should
// provide and prove all keys < [largestProvenPath].
// If [largestProvenPath] is Nothing, [proof] should
// provide and prove all keys > [smallestProvenPath].
// If both are Nothing, [proof] should prove the entire trie.
smallestProvenPath := maybe.Nothing[path]()
if start != nil {
smallestProvenPath = maybe.Some(newPath(start))
}

largestProvenPath := maybe.Bind(end, newPath)
if len(proof.KeyValues) > 0 {
// If [proof] has key-value pairs, we should insert children
// greater than [largestKey] to ancestors of the node containing
// [largestKey] so that we get the expected root ID.
largestPath = maybe.Some(newPath(proof.KeyValues[len(proof.KeyValues)-1].Key))
// greater than [largestProvenPath] to ancestors of the node containing
// [largestProvenPath] so that we get the expected root ID.
largestProvenPath = maybe.Some(newPath(proof.KeyValues[len(proof.KeyValues)-1].Key))
}

// The key-value pairs (allegedly) proven by [proof].
Expand All @@ -316,23 +328,31 @@ func (proof *RangeProof) Verify(
keyValues[newPath(keyValue.Key)] = keyValue.Value
}

smallestPath := newPath(start)

// Ensure that the start proof is valid and contains values that
// match the key/values that were sent.
if err := verifyProofPath(proof.StartProof, smallestPath); err != nil {
if err := verifyProofPath(proof.StartProof, smallestProvenPath.Value()); err != nil {
return err
}
if err := verifyAllRangeProofKeyValuesPresent(proof.StartProof, smallestPath, largestPath, keyValues); err != nil {
if err := verifyAllRangeProofKeyValuesPresent(
proof.StartProof,
smallestProvenPath.Value(),
largestProvenPath,
keyValues,
); err != nil {
return err
}

// Ensure that the end proof is valid and contains values that
// match the key/values that were sent.
if err := verifyProofPath(proof.EndProof, largestPath.Value()); err != nil {
if err := verifyProofPath(proof.EndProof, largestProvenPath.Value()); err != nil {
return err
}
if err := verifyAllRangeProofKeyValuesPresent(proof.EndProof, smallestPath, largestPath, keyValues); err != nil {
if err := verifyAllRangeProofKeyValuesPresent(
proof.EndProof,
smallestProvenPath.Value(),
largestProvenPath,
keyValues,
); err != nil {
return err
}

Expand All @@ -350,24 +370,24 @@ func (proof *RangeProof) Verify(
}

// For all the nodes along the edges of the proof, insert children
// < [smallestPath] and > [largestPath]
// < [smallestProvenPath] and > [largestProvenPath]
// into the trie so that we get the expected root ID (if this proof is valid).
// By inserting all children < [smallestPath], we prove that there are no keys
// > [largestPath] but less than the first key given.
// By inserting all children < [smallestProvenPath], we prove that there are no keys
// > [smallestProvenPath] but less than the first key given.
// That is, the peer who gave us this proof is not omitting nodes.
if err := addPathInfo(
view,
proof.StartProof,
maybe.Some(smallestPath),
largestPath,
smallestProvenPath,
largestProvenPath,
); err != nil {
return err
}
if err := addPathInfo(
view,
proof.EndProof,
maybe.Some(smallestPath),
largestPath,
smallestProvenPath,
largestProvenPath,
); err != nil {
return err
}
Expand Down
25 changes: 18 additions & 7 deletions x/merkledb/proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1691,33 +1691,44 @@ func FuzzRangeProofInvariants(f *testing.F) {

f.Fuzz(func(
t *testing.T,
start []byte,
startBytes []byte,
endBytes []byte,
maxProofLen uint,
) {
require := require.New(t)

// Make sure proof bounds are valid
if len(endBytes) != 0 && bytes.Compare(start, endBytes) > 0 {
if len(endBytes) != 0 && bytes.Compare(startBytes, endBytes) > 0 {
return
}
end := maybe.Nothing[[]byte]()
if len(endBytes) > 0 {
end = maybe.Some(endBytes)
}
// Make sure proof length is valid
if maxProofLen == 0 {
return
}

end := maybe.Nothing[[]byte]()
if len(endBytes) != 0 {
end = maybe.Some(endBytes)
}

rangeProof, err := db.GetRangeProof(
context.Background(),
start,
startBytes,
end,
int(maxProofLen),
)
require.NoError(err)

rootID, err := db.GetMerkleRoot(context.Background())
require.NoError(err)

require.NoError(rangeProof.Verify(
context.Background(),
startBytes,
end,
rootID,
))

// Make sure the start proof doesn't contain any nodes
// that are in the end proof.
endProofKeys := set.Set[path]{}
Expand Down