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