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  }