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