@@ -102,6 +102,65 @@ 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+ // - C is the largest power of 2 within [start, end). Otherwise
150+ // [start, end) would be at least 2C long, which contradicts
151+ // C's definition.
152+ // - `split` is a power of two smaller than end. Since C is the largest
153+ // power of 2 within [start, end), split must be smaller than C.
154+ // - This gives us: `start` < split < C.
155+ // - Since `start` is a multiple of C, and `start` is smaller than C,
156+ // `start` MUST be 0.
157+ default :
158+ return append (
159+ refSubtreeConsistencyProof (entries [split :], size - split , 0 , end - split , hasher , false ),
160+ refRootHash (entries [:split ], hasher ))
161+ }
162+ }
163+
105164// downToPowerOfTwo returns the largest power of two smaller than x.
106165func downToPowerOfTwo (x uint64 ) uint64 {
107166 if x < 2 {
0 commit comments