github.com/protolambda/zssz@v0.1.5/merkle/merkleize.go (about) 1 package merkle 2 3 import ( 4 . "github.com/protolambda/zssz/htr" 5 ) 6 7 const ( 8 mask0 = ^uint64((1 << (1 << iota)) - 1) 9 mask1 10 mask2 11 mask3 12 mask4 13 mask5 14 ) 15 16 const ( 17 bit0 = uint8(1 << iota) 18 bit1 19 bit2 20 bit3 21 bit4 22 bit5 23 ) 24 25 func GetDepth(v uint64) (out uint8) { 26 // bitmagic: binary search through a uint32, offset down by 1 to not round powers of 2 up. 27 // Then adding 1 to it to not get the index of the first bit, but the length of the bits (depth of tree) 28 // Zero is a special case, it has a 0 depth. 29 // Example: 30 // (in out): (0 0), (1 1), (2 1), (3 2), (4 2), (5 3), (6 3), (7 3), (8 3), (9 4) 31 if v == 0 { 32 return 0 33 } 34 v-- 35 if v&mask5 != 0 { 36 v >>= bit5 37 out |= bit5 38 } 39 if v&mask4 != 0 { 40 v >>= bit4 41 out |= bit4 42 } 43 if v&mask3 != 0 { 44 v >>= bit3 45 out |= bit3 46 } 47 if v&mask2 != 0 { 48 v >>= bit2 49 out |= bit2 50 } 51 if v&mask1 != 0 { 52 v >>= bit1 53 out |= bit1 54 } 55 if v&mask0 != 0 { 56 out |= bit0 57 } 58 out++ 59 return 60 } 61 62 // Merkleize with log(N) space allocation 63 func Merkleize(hasher MerkleFn, count uint64, limit uint64, leaf func(i uint64) []byte) (out [32]byte) { 64 if count > limit { 65 panic("merkleizing list that is too large, over limit") 66 } 67 if limit == 0 { 68 return 69 } 70 if limit == 1 { 71 if count == 1 { 72 copy(out[:], leaf(0)) 73 } 74 return 75 } 76 depth := GetDepth(count) 77 limitDepth := GetDepth(limit) 78 tmp := make([][32]byte, limitDepth+1, limitDepth+1) 79 80 j := uint8(0) 81 hArr := [32]byte{} 82 h := hArr[:] 83 84 merge := func(i uint64) { 85 // merge back up from bottom to top, as far as we can 86 for j = 0; ; j++ { 87 // stop merging when we are in the left side of the next combi 88 if i&(uint64(1)<<j) == 0 { 89 // if we are at the count, we want to merge in zero-hashes for padding 90 if i == count && j < depth { 91 v := hasher.Combi(hArr, ZeroHashes[j]) 92 copy(h, v[:]) 93 } else { 94 break 95 } 96 } else { 97 // keep merging up if we are the right side 98 v := hasher.Combi(tmp[j], hArr) 99 copy(h, v[:]) 100 } 101 } 102 // store the merge result (may be no merge, i.e. bottom leaf node) 103 copy(tmp[j][:], h) 104 } 105 106 // merge in leaf by leaf. 107 for i := uint64(0); i < count; i++ { 108 copy(h[:], leaf(i)) 109 merge(i) 110 } 111 112 // complement with 0 if empty, or if not the right power of 2 113 if (uint64(1) << depth) != count { 114 copy(h[:], ZeroHashes[0][:]) 115 merge(count) 116 } 117 118 // the next power of two may be smaller than the ultimate virtual size, 119 // complement with zero-hashes at each depth. 120 for j := depth; j < limitDepth; j++ { 121 tmp[j+1] = hasher.Combi(tmp[j], ZeroHashes[j]) 122 } 123 124 return tmp[limitDepth] 125 } 126 127 // ConstructProof builds a merkle-branch of the given depth, at the given index (at that depth), 128 // for a list of leafs of a balanced binary tree. 129 func ConstructProof(hasher MerkleFn, count uint64, limit uint64, leaf func(i uint64) []byte, index uint64) (branch [][32]byte) { 130 if count > limit { 131 panic("merkleizing list that is too large, over limit") 132 } 133 if index >= limit { 134 panic("index out of range, over limit") 135 } 136 if limit <= 1 { 137 return 138 } 139 depth := GetDepth(count) 140 limitDepth := GetDepth(limit) 141 branch = append(branch, ZeroHashes[:limitDepth]...) 142 143 tmp := make([][32]byte, limitDepth+1, limitDepth+1) 144 145 j := uint8(0) 146 hArr := [32]byte{} 147 h := hArr[:] 148 149 merge := func(i uint64) { 150 // merge back up from bottom to top, as far as we can 151 for j = 0; ; j++ { 152 // TODO: better than full tree allocation, but style/efficiency can be improved. 153 // if i is a sibling of index at the given depth, 154 // and i is the last index of the subtree to that depth, 155 // then put h into the branch 156 if (i>>j)^1 == (index>>j) && (((1<<j)-1)&i) == ((1<<j)-1) { 157 // insert sibling into the proof 158 branch[j] = hArr 159 } 160 // stop merging when we are in the left side of the next combi 161 if i&(uint64(1)<<j) == 0 { 162 // if we are at the count, we want to merge in zero-hashes for padding 163 if i == count && j < depth { 164 v := hasher.Combi(hArr, ZeroHashes[j]) 165 copy(h, v[:]) 166 } else { 167 break 168 } 169 } else { 170 // keep merging up if we are the right side 171 v := hasher.Combi(tmp[j], hArr) 172 copy(h, v[:]) 173 } 174 } 175 // store the merge result (may be no merge, i.e. bottom leaf node) 176 copy(tmp[j][:], h) 177 } 178 179 // merge in leaf by leaf. 180 for i := uint64(0); i < count; i++ { 181 copy(h[:], leaf(i)) 182 merge(i) 183 } 184 185 // complement with 0 if empty, or if not the right power of 2 186 if (uint64(1) << depth) != count { 187 copy(h[:], ZeroHashes[0][:]) 188 merge(count) 189 } 190 191 return 192 }