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  }