@@ -102,6 +102,67 @@ func refConsistencyProof(entries [][]byte, size2, size1 uint64, hasher merkle.Lo
102102 refRootHash (entries [:split ], hasher ))
103103}
104104
105+ // refSubtreeConsistencyProof returns the subtree consistency proof for the
106+ // subtree [start, end) in a Merkle tree with the given entries and size.
107+ // This is a reference implementation for cross-checking.
108+ func refSubtreeConsistencyProof (entries [][]byte , size , start , end uint64 , hasher merkle.LogHasher , haveRoot1 bool ) [][]byte {
109+ if start >= end {
110+ return nil
111+ }
112+ if end == 0 || end > size {
113+ return nil
114+ }
115+ // Consistency proof between a tree and itself is empty.
116+ if start == 0 && end == size {
117+ // Record the hash of this subtree if it's not the root for which the proof
118+ // was originally requested (which happens when [start, end) is a full subtree).
119+ if ! haveRoot1 {
120+ return [][]byte {refRootHash (entries [:size ], hasher )}
121+ }
122+ return nil
123+ }
124+
125+ // At this point: end < size.
126+ split := downToPowerOfTwo (size )
127+ switch {
128+ // The subtree is on the left of split. Prove that the subtree is consistent
129+ // with the subtree on the left of split, and record the root of the right
130+ // subtree.
131+ case end <= split :
132+ return append (
133+ refSubtreeConsistencyProof (entries [:split ], split , start , end , hasher , haveRoot1 ),
134+ refRootHash (entries [split :], hasher ))
135+ // The subtree is on the right of split. Prove that the subtree is consistent
136+ // with the subtree on the right of split, and record the root of the left
137+ // subtree.
138+ case split <= start :
139+ return append (
140+ refSubtreeConsistencyProof (entries [split :], size - split , start - split , end - split , hasher , haveRoot1 ),
141+ refRootHash (entries [:split ], hasher ))
142+ // Otherwise, split is between start and end.
143+ // This means that start is 0.
144+ // Prove that the subtree is consistent with the subtree on right of split,
145+ // and record the root of the left subtree.
146+ //
147+ // Proof that start is 0:
148+ // With C = bitCeil(len([start, end))):
149+ // - By definition, end - start <= C.
150+ // - Since the subtree is valid, start is a multiple of C (start = k * C).
151+ // - In this case, start < split < end <= start + C and
152+ // so k * C < split < (k+1) * C
153+ // - Since split and C are both powers of 2:
154+ // - If split < C, then if k >= 1, split < C <= start, contradicting
155+ // start < split.
156+ // - If split >= C, split must be a multiple of C, but no multiple of
157+ // C lies strictly between k * C and (k + 1) * C.
158+ // - Thus, k must be 0, meaning start is 0.
159+ default :
160+ return append (
161+ refSubtreeConsistencyProof (entries [split :], size - split , 0 , end - split , hasher , false ),
162+ refRootHash (entries [:split ], hasher ))
163+ }
164+ }
165+
105166// downToPowerOfTwo returns the largest power of two smaller than x.
106167func downToPowerOfTwo (x uint64 ) uint64 {
107168 if x < 2 {
0 commit comments