|
24 | 24 | LeafSpec: &ics23.LeafOp{ |
25 | 25 | Prefix: leafPrefix, |
26 | 26 | PrehashKey: ics23.HashOp_NO_HASH, |
27 | | - PrehashValue: ics23.HashOp_NO_HASH, |
| 27 | + PrehashValue: ics23.HashOp_SHA256, |
28 | 28 | Hash: ics23.HashOp_SHA256, |
29 | 29 | Length: ics23.LengthOp_VAR_PROTO, |
30 | 30 | }, |
@@ -122,31 +122,70 @@ func (op CommitmentOp) Run(args [][]byte) ([][]byte, error) { |
122 | 122 | return [][]byte{root}, nil |
123 | 123 | } |
124 | 124 |
|
125 | | -// ProofFromByteSlices computes the proof from the given leaves. |
| 125 | +// ProofFromByteSlices computes the proof from the given leaves. An iteration will be |
| 126 | +// perfomed for each level of the tree, where each iteration hashes together the bottom most |
| 127 | +// nodes. If the length of the bottom most nodes is odd, then the last node will be saved |
| 128 | +// for the next iteration. |
| 129 | +// |
| 130 | +// Example: |
| 131 | +// Iteration 1: |
| 132 | +// n = 5 |
| 133 | +// leaves = a, b, c, d, e. |
| 134 | +// index = 2 (prove c) |
| 135 | +// |
| 136 | +// Iteration 2: |
| 137 | +// n = 3 |
| 138 | +// leaves = ab, cd, e |
| 139 | +// index = 1 (prove c, so index of cd) |
| 140 | +// |
| 141 | +// Iteration 3: |
| 142 | +// n = 2 |
| 143 | +// leaves = abcd, e |
| 144 | +// index = 0 (prove c, so index of abcd) |
| 145 | +// |
| 146 | +// Final iteration: |
| 147 | +// n = 1 |
| 148 | +// leaves = abcde |
| 149 | +// index = 0 |
| 150 | +// |
| 151 | +// The bitwise & operator allows us to determine if the index or length is odd or even. |
| 152 | +// The bitwise ^ operator allows us to increment when the value is even and decrement when it is odd. |
126 | 153 | func ProofFromByteSlices(leaves [][]byte, index int) (rootHash []byte, inners []*ics23.InnerOp) { |
127 | 154 | if len(leaves) == 0 { |
128 | 155 | return emptyHash(), nil |
129 | 156 | } |
130 | 157 |
|
131 | 158 | n := len(leaves) |
132 | 159 | for n > 1 { |
| 160 | + // Begin by constructing the proof for the inner node of the requested index. |
| 161 | + // A proof of the inner node is skipped only in the case where the requested index |
| 162 | + // is the last element and it does not have a leaf pair (resulting in it being |
| 163 | + // saved until the next iteration). |
133 | 164 | if index < n-1 || index&1 == 1 { |
134 | 165 | inner := &ics23.InnerOp{Hash: ics23.HashOp_SHA256} |
| 166 | + //Iif proof index is even then child is from left, suffix is populated |
| 167 | + // otherwise, child is from right and the prefix is populated. |
135 | 168 | if index&1 == 0 { |
| 169 | + // inner op(prefix=0x01 | child | suffix=leaves[index+1]) |
136 | 170 | inner.Prefix = innerPrefix |
137 | | - inner.Suffix = leaves[index^1] |
| 171 | + inner.Suffix = leaves[index^1] // XOR op is index+1 because index is even |
138 | 172 | } else { |
139 | | - inner.Prefix = append(innerPrefix, leaves[index^1]...) |
| 173 | + // inner op(prefix=0x01 | leaves[index-1] | child | suffix=nil) |
| 174 | + inner.Prefix = append(innerPrefix, leaves[index^1]...) // XOR op is index-1 because index is odd |
140 | 175 | } |
141 | 176 | inners = append(inners, inner) |
142 | 177 | } |
| 178 | + |
| 179 | + // hash together all leaf pairs |
143 | 180 | for i := 0; i < n/2; i++ { |
144 | 181 | leaves[i] = InnerHash(leaves[2*i], leaves[2*i+1]) |
145 | 182 | } |
| 183 | + |
| 184 | + // save any leftover leaf for the next iteration |
146 | 185 | if n&1 == 1 { |
147 | 186 | leaves[n/2] = leaves[n-1] |
148 | 187 | } |
149 | | - n = (n + 1) / 2 |
| 188 | + n = (n + 1) / 2 // n + 1 accounts for any leaves which are added to the next iteration |
150 | 189 | index /= 2 |
151 | 190 | } |
152 | 191 |
|
@@ -178,7 +217,8 @@ func LeafHash(key, value []byte) ([]byte, error) { |
178 | 217 | return SimpleMerkleSpec.LeafSpec.Apply(key, value) |
179 | 218 | } |
180 | 219 |
|
181 | | -// InnerHash computes the hash of an inner node. |
| 220 | +// InnerHash computes the hash of an inner node as defined by ics23: |
| 221 | +// https://github.com/cosmos/ics23/blob/go/v0.10.0/proto/cosmos/ics23/v1/proofs.proto#L130 |
182 | 222 | func InnerHash(left, right []byte) []byte { |
183 | 223 | data := make([]byte, len(innerPrefix)+len(left)+len(right)) |
184 | 224 | n := copy(data, innerPrefix) |
|
0 commit comments