Skip to content

Commit 25d4cfd

Browse files
committed
consistency proof: fix edge case with 0 sized tree
1 parent 53bebf1 commit 25d4cfd

8 files changed

Lines changed: 31 additions & 10 deletions

File tree

cmd/proofgen/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ func staticConsistencyProbes(dir string) error {
403403
{0, 0, root1, root2, proof1, "sizes are equal (zero) but roots are not", true},
404404
{1, 1, root1, root2, proof1, "sizes are equal (one) but roots are not", true},
405405
// Sizes that are always consistent.
406-
{0, 0, root1, root1, proof1, "sizes are equal (zero) and proof is empty", false},
406+
{0, 0, root1, root1, proof1, "sizes are equal (zero) and proof is empty", true},
407407
{0, 1, root1, root2, proof1, "size1 is zero and does not equal size2", true},
408408
{1, 1, root2, root2, proof1, "sizes are equal (one) and proof is empty", false},
409409
// Time travel to the past.
@@ -418,6 +418,7 @@ func staticConsistencyProbes(dir string) error {
418418
{0, 0, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, "sizes match but proof is not empty and sizes are zero", true},
419419
{1, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, "sizes match but proof is not empty and sizes are one", true},
420420
// Fail to validate empty tree
421+
{0, 1, sha256EmptyTreeHash, root2, proof1, "size1 is zero and size2 is not zero", true},
421422
{0, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, "consistency check on empty tree (size1 is zero) is useless", true},
422423
}
423424

proof/proof.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,15 @@ func SubtreeInclusion(index, start, end uint64) (Nodes, error) {
8282

8383
// Consistency returns the information on how to fetch and construct a
8484
// consistency proof between the two given tree sizes of a log Merkle tree. It
85-
// requires 0 <= size1 <= size2.
85+
// requires 0 < size1 <= size2.
8686
func Consistency(size1, size2 uint64) (Nodes, error) {
8787
if size1 > size2 {
8888
return Nodes{}, fmt.Errorf("tree size %d > %d", size1, size2)
8989
}
90-
if size1 == size2 || size1 == 0 {
90+
if size1 == 0 {
91+
return Nodes{}, fmt.Errorf("consistency proof from empty tree is meaningless")
92+
}
93+
if size1 == size2 {
9194
return Nodes{IDs: []compact.NodeID{}}, nil
9295
}
9396

proof/proof_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ func TestConsistency(t *testing.T) {
341341
// Errors.
342342
{size1: 5, size2: 0, wantErr: true},
343343
{size1: 9, size2: 8, wantErr: true},
344+
{size1: 0, size2: 5, wantErr: true},
345+
{size1: 0, size2: 0, wantErr: true},
344346

345347
{size1: 1, size2: 2, want: nodes(id(0, 1))}, // b
346348
{size1: 1, size2: 4, want: nodes(id(0, 1), id(1, 1))}, // b h

proof/verify.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ func RootFromConsistencyProof(hasher merkle.LogHasher, size1, size2 uint64, proo
112112
switch {
113113
case size2 < size1:
114114
return nil, fmt.Errorf("size2 (%d) < size1 (%d)", size1, size2)
115+
case size1 == 0:
116+
return nil, errors.New("consistency proof from empty tree is meaningless")
115117
case size1 == size2:
116118
if len(proof) > 0 {
117119
return nil, errors.New("size1=size2, but proof is not empty")
118120
}
119121
return root1, nil
120-
case size1 == 0:
121-
return nil, errors.New("consistency proof from empty tree is meaningless")
122122
case len(proof) == 0:
123123
return nil, errors.New("empty proof")
124124
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"size1": 0,
3+
"size2": 1,
4+
"root1": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
5+
"root2": "ZG9uJ3QgY2FyZSAy",
6+
"proof": [],
7+
"desc": "size1 is zero and size2 is not zero",
8+
"wantErr": true
9+
}

testdata/consistency/additional/sizes-are-equal-zero-and-proof-is-empty.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
"root2": "ZG9uJ3QgY2FyZSAx",
66
"proof": [],
77
"desc": "sizes are equal (zero) and proof is empty",
8-
"wantErr": false
8+
"wantErr": true
99
}

testonly/tree_fuzz_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func FuzzConsistencyProofAndVerify(f *testing.F) {
2828
if begin > end || end > size {
2929
return
3030
}
31-
if begin == 0 && end > 0 {
31+
if begin == 0 {
3232
return
3333
}
3434
tree := newTree(genEntries(size))
@@ -176,6 +176,9 @@ func FuzzConsistencyProofAgainstReferenceImplementation(f *testing.F) {
176176
if begin > end || end > size {
177177
return
178178
}
179+
if begin == 0 {
180+
return
181+
}
179182
entries := genEntries(size)
180183
tree := newTree(entries)
181184
got, err := tree.ConsistencyProof(begin, end)

testonly/tree_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,11 @@ func TestTreeConsistencyProof(t *testing.T) {
154154
if _, err := mt.ConsistencyProof(6, 3); err == nil {
155155
t.Error("ConsistencyProof(6, 3) succeeded unexpectedly")
156156
}
157+
if _, err := mt.ConsistencyProof(0, 3); err == nil {
158+
t.Error("ConsistencyProof(0, 3) succeeded unexpectedly")
159+
}
157160

158-
for size1 := range uint64(8) + 1 {
161+
for size1 := uint64(1); size1 <= 8; size1++ {
159162
for size2 := size1; size2 <= 8; size2++ {
160163
t.Run(fmt.Sprintf("%d:%d", size1, size2), func(t *testing.T) {
161164
got, err := mt.ConsistencyProof(size1, size2)
@@ -178,8 +181,8 @@ func TestTreeConsistencyProofFuzz(t *testing.T) {
178181
for treeSize := uint64(1); treeSize <= 256; treeSize++ {
179182
mt := newTree(entries[:treeSize])
180183
for range 8 {
181-
size2 := rand.Uint64N(treeSize + 1)
182-
size1 := rand.Uint64N(size2 + 1)
184+
size2 := rand.Uint64N(treeSize) + 1
185+
size1 := rand.Uint64N(size2) + 1
183186

184187
got, err := mt.ConsistencyProof(size1, size2)
185188
if err != nil {

0 commit comments

Comments
 (0)