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  }