github.com/prysmaticlabs/prysm@v1.4.4/shared/htrutils/helpers.go (about) 1 // Package htrutils defines HashTreeRoot utility functions. 2 package htrutils 3 4 import ( 5 "bytes" 6 "encoding/binary" 7 8 "github.com/minio/sha256-simd" 9 "github.com/pkg/errors" 10 "github.com/prysmaticlabs/go-bitfield" 11 ) 12 13 const bytesPerChunk = 32 14 15 // BitlistRoot returns the mix in length of a bitwise Merkleized bitfield. 16 func BitlistRoot(hasher HashFn, bfield bitfield.Bitfield, maxCapacity uint64) ([32]byte, error) { 17 limit := (maxCapacity + 255) / 256 18 if bfield == nil || bfield.Len() == 0 { 19 length := make([]byte, 32) 20 root, err := BitwiseMerkleize(hasher, [][]byte{}, 0, limit) 21 if err != nil { 22 return [32]byte{}, err 23 } 24 return MixInLength(root, length), nil 25 } 26 chunks, err := Pack([][]byte{bfield.Bytes()}) 27 if err != nil { 28 return [32]byte{}, err 29 } 30 buf := new(bytes.Buffer) 31 if err := binary.Write(buf, binary.LittleEndian, bfield.Len()); err != nil { 32 return [32]byte{}, err 33 } 34 output := make([]byte, 32) 35 copy(output, buf.Bytes()) 36 root, err := BitwiseMerkleize(hasher, chunks, uint64(len(chunks)), limit) 37 if err != nil { 38 return [32]byte{}, err 39 } 40 return MixInLength(root, output), nil 41 } 42 43 // BitwiseMerkleize - given ordered BYTES_PER_CHUNK-byte chunks, if necessary utilize 44 // zero chunks so that the number of chunks is a power of two, Merkleize the chunks, 45 // and return the root. 46 // Note that merkleize on a single chunk is simply that chunk, i.e. the identity 47 // when the number of chunks is one. 48 func BitwiseMerkleize(hasher HashFn, chunks [][]byte, count, limit uint64) ([32]byte, error) { 49 if count > limit { 50 return [32]byte{}, errors.New("merkleizing list that is too large, over limit") 51 } 52 hashFn := NewHasherFunc(hasher) 53 leafIndexer := func(i uint64) []byte { 54 return chunks[i] 55 } 56 return Merkleize(hashFn, count, limit, leafIndexer), nil 57 } 58 59 // BitwiseMerkleizeArrays is used when a set of 32-byte root chunks are provided. 60 func BitwiseMerkleizeArrays(hasher HashFn, chunks [][32]byte, count, limit uint64) ([32]byte, error) { 61 if count > limit { 62 return [32]byte{}, errors.New("merkleizing list that is too large, over limit") 63 } 64 hashFn := NewHasherFunc(hasher) 65 leafIndexer := func(i uint64) []byte { 66 return chunks[i][:] 67 } 68 return Merkleize(hashFn, count, limit, leafIndexer), nil 69 } 70 71 // Pack a given byte array's final chunk with zeroes if needed. 72 func Pack(serializedItems [][]byte) ([][]byte, error) { 73 areAllEmpty := true 74 for _, item := range serializedItems { 75 if !bytes.Equal(item, []byte{}) { 76 areAllEmpty = false 77 break 78 } 79 } 80 // If there are no items, we return an empty chunk. 81 if len(serializedItems) == 0 || areAllEmpty { 82 emptyChunk := make([]byte, bytesPerChunk) 83 return [][]byte{emptyChunk}, nil 84 } else if len(serializedItems[0]) == bytesPerChunk { 85 // If each item has exactly BYTES_PER_CHUNK length, we return the list of serialized items. 86 return serializedItems, nil 87 } 88 // We flatten the list in order to pack its items into byte chunks correctly. 89 var orderedItems []byte 90 for _, item := range serializedItems { 91 orderedItems = append(orderedItems, item...) 92 } 93 numItems := len(orderedItems) 94 var chunks [][]byte 95 for i := 0; i < numItems; i += bytesPerChunk { 96 j := i + bytesPerChunk 97 // We create our upper bound index of the chunk, if it is greater than numItems, 98 // we set it as numItems itself. 99 if j > numItems { 100 j = numItems 101 } 102 // We create chunks from the list of items based on the 103 // indices determined above. 104 chunks = append(chunks, orderedItems[i:j]) 105 } 106 // Right-pad the last chunk with zero bytes if it does not 107 // have length bytesPerChunk. 108 lastChunk := chunks[len(chunks)-1] 109 for len(lastChunk) < bytesPerChunk { 110 lastChunk = append(lastChunk, 0) 111 } 112 chunks[len(chunks)-1] = lastChunk 113 return chunks, nil 114 } 115 116 // MixInLength appends hash length to root 117 func MixInLength(root [32]byte, length []byte) [32]byte { 118 var hash [32]byte 119 h := sha256.New() 120 h.Write(root[:]) 121 h.Write(length) 122 // The hash interface never returns an error, for that reason 123 // we are not handling the error below. For reference, it is 124 // stated here https://golang.org/pkg/hash/#Hash 125 // #nosec G104 126 h.Sum(hash[:0]) 127 return hash 128 }