Skip to content

Commit ededf79

Browse files
authored
Subtree Inclusion proof fuzz tests (transparency-dev#229)
* fuzz tests * refactor HashAt
1 parent 0de243d commit ededf79

3 files changed

Lines changed: 64 additions & 3 deletions

File tree

proof/proof.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func reverse(ids []compact.NodeID) {
223223
}
224224
}
225225

226-
// isSubTreeValid returns whether a subtree covers a valid range.
226+
// isSubtreeValid returns whether a subtree covers a valid range.
227227
// A subtree is valid if there exist a parent tree node to:
228228
// - all the subtree nodes
229229
// - no extra node to the left of the subtree

testonly/tree.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,19 @@ func (t *Tree) Hash() []byte {
8282
// HashAt returns the root hash at the given size.
8383
// Requires 0 <= size <= Size(), otherwise panics.
8484
func (t *Tree) HashAt(size uint64) []byte {
85-
if size == 0 {
85+
return t.SubtreeHashAt(0, size)
86+
}
87+
88+
// SubtreeHashAt returns the root hash of the [start, end) subtree.
89+
// Requires 0 <= start <= end <= Size() otherwise panics.
90+
func (t *Tree) SubtreeHashAt(start, end uint64) []byte {
91+
if start > end || end > t.size {
92+
panic("invalid subtree range")
93+
}
94+
if start == end {
8695
return t.hasher.EmptyRoot()
8796
}
88-
hashes := t.getNodes(compact.RangeNodes(0, size, nil))
97+
hashes := t.getNodes(compact.RangeNodes(start, end, nil))
8998

9099
hash := hashes[len(hashes)-1]
91100
for i := len(hashes) - 2; i >= 0; i-- {
@@ -105,6 +114,18 @@ func (t *Tree) InclusionProof(index, size uint64) ([][]byte, error) {
105114
return nodes.Rehash(t.getNodes(nodes.IDs), t.hasher.HashChildren)
106115
}
107116

117+
// SubtreeInclusionProof returns the inclusion proof for the given leaf index in the
118+
// [start, end) subtree.
119+
// It requires end <= Size(), and may panic otherwise.
120+
// May return and error if the subtree boundaries or the index are not valid.
121+
func (t *Tree) SubtreeInclusionProof(index, start, end uint64) ([][]byte, error) {
122+
nodes, err := proof.SubtreeInclusion(index, start, end)
123+
if err != nil {
124+
return nil, err
125+
}
126+
return nodes.Rehash(t.getNodes(nodes.IDs), t.hasher.HashChildren)
127+
}
128+
108129
// ConsistencyProof returns the consistency proof between the two given tree
109130
// sizes. Requires 0 <= size1 <= size2 <= Size(), otherwise may panic.
110131
func (t *Tree) ConsistencyProof(size1, size2 uint64) ([][]byte, error) {

testonly/tree_fuzz_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package testonly
55
import (
66
"bytes"
77
"math"
8+
"math/bits"
89
"testing"
910

1011
"github.com/google/go-cmp/cmp"
@@ -74,6 +75,45 @@ func FuzzInclusionProofAndVerify(f *testing.F) {
7475
})
7576
}
7677

78+
// Compute and verify inclusion proofs
79+
func FuzzSubtreeInclusionProofAndVerify(f *testing.F) {
80+
for end := 0; end <= 8; end++ {
81+
for start := 0; start <= end; start++ {
82+
for index := start; index <= end; index++ {
83+
f.Add(uint64(index), uint64(start), uint64(end))
84+
}
85+
}
86+
}
87+
f.Fuzz(func(t *testing.T, index, start, end uint64) {
88+
if end >= math.MaxUint16 {
89+
return
90+
}
91+
t.Logf("index=%d, start=%d, end=%d", index, start, end)
92+
if start >= end {
93+
return
94+
}
95+
if index < start {
96+
return
97+
}
98+
if index >= end {
99+
return
100+
}
101+
if bc := uint64(1) << bits.Len64(end-start-1); start%bc != 0 {
102+
return
103+
}
104+
tree := newTree(genEntries(end))
105+
p, err := tree.SubtreeInclusionProof(index, start, end)
106+
t.Logf("proof=%v", p)
107+
if err != nil {
108+
t.Error(err)
109+
}
110+
err = proof.VerifySubtreeInclusion(tree.hasher, index, start, end, tree.LeafHash(index), p, tree.SubtreeHashAt(start, end))
111+
if err != nil {
112+
t.Error(err)
113+
}
114+
})
115+
}
116+
77117
func FuzzHashAtAgainstReferenceImplementation(f *testing.F) {
78118
for size := 0; size <= 8; size++ {
79119
for index := 0; index <= size; index++ {

0 commit comments

Comments
 (0)