github.com/vipernet-xyz/tendermint-core@v0.32.0/crypto/merkle/simple_tree.go (about)

     1  package merkle
     2  
     3  import (
     4  	"math/bits"
     5  )
     6  
     7  // SimpleHashFromByteSlices computes a Merkle tree where the leaves are the byte slice,
     8  // in the provided order.
     9  func SimpleHashFromByteSlices(items [][]byte) []byte {
    10  	switch len(items) {
    11  	case 0:
    12  		return nil
    13  	case 1:
    14  		return leafHash(items[0])
    15  	default:
    16  		k := getSplitPoint(len(items))
    17  		left := SimpleHashFromByteSlices(items[:k])
    18  		right := SimpleHashFromByteSlices(items[k:])
    19  		return innerHash(left, right)
    20  	}
    21  }
    22  
    23  // SimpleHashFromByteSliceIterative is an iterative alternative to
    24  // SimpleHashFromByteSlice motivated by potential performance improvements.
    25  // (#2611) had suggested that an iterative version of
    26  // SimpleHashFromByteSlice would be faster, presumably because
    27  // we can envision some overhead accumulating from stack
    28  // frames and function calls. Additionally, a recursive algorithm risks
    29  // hitting the stack limit and causing a stack overflow should the tree
    30  // be too large.
    31  //
    32  // Provided here is an iterative alternative, a simple test to assert
    33  // correctness and a benchmark. On the performance side, there appears to
    34  // be no overall difference:
    35  //
    36  // BenchmarkSimpleHashAlternatives/recursive-4                20000 77677 ns/op
    37  // BenchmarkSimpleHashAlternatives/iterative-4                20000 76802 ns/op
    38  //
    39  // On the surface it might seem that the additional overhead is due to
    40  // the different allocation patterns of the implementations. The recursive
    41  // version uses a single [][]byte slices which it then re-slices at each level of the tree.
    42  // The iterative version reproduces [][]byte once within the function and
    43  // then rewrites sub-slices of that array at each level of the tree.
    44  //
    45  // Experimenting by modifying the code to simply calculate the
    46  // hash and not store the result show little to no difference in performance.
    47  //
    48  // These preliminary results suggest:
    49  //
    50  // 1. The performance of the SimpleHashFromByteSlice is pretty good
    51  // 2. Go has low overhead for recursive functions
    52  // 3. The performance of the SimpleHashFromByteSlice routine is dominated
    53  //    by the actual hashing of data
    54  //
    55  // Although this work is in no way exhaustive, point #3 suggests that
    56  // optimization of this routine would need to take an alternative
    57  // approach to make significant improvements on the current performance.
    58  //
    59  // Finally, considering that the recursive implementation is easier to
    60  // read, it might not be worthwhile to switch to a less intuitive
    61  // implementation for so little benefit.
    62  func SimpleHashFromByteSlicesIterative(input [][]byte) []byte {
    63  	items := make([][]byte, len(input))
    64  
    65  	for i, leaf := range input {
    66  		items[i] = leafHash(leaf)
    67  	}
    68  
    69  	size := len(items)
    70  	for {
    71  		switch size {
    72  		case 0:
    73  			return nil
    74  		case 1:
    75  			return items[0]
    76  		default:
    77  			rp := 0 // read position
    78  			wp := 0 // write position
    79  			for rp < size {
    80  				if rp+1 < size {
    81  					items[wp] = innerHash(items[rp], items[rp+1])
    82  					rp += 2
    83  				} else {
    84  					items[wp] = items[rp]
    85  					rp++
    86  				}
    87  				wp++
    88  			}
    89  			size = wp
    90  		}
    91  	}
    92  }
    93  
    94  // SimpleHashFromMap computes a Merkle tree from sorted map.
    95  // Like calling SimpleHashFromHashers with
    96  // `item = []byte(Hash(key) | Hash(value))`,
    97  // sorted by `item`.
    98  func SimpleHashFromMap(m map[string][]byte) []byte {
    99  	sm := newSimpleMap()
   100  	for k, v := range m {
   101  		sm.Set(k, v)
   102  	}
   103  	return sm.Hash()
   104  }
   105  
   106  // getSplitPoint returns the largest power of 2 less than length
   107  func getSplitPoint(length int) int {
   108  	if length < 1 {
   109  		panic("Trying to split a tree with size < 1")
   110  	}
   111  	uLength := uint(length)
   112  	bitlen := bits.Len(uLength)
   113  	k := 1 << uint(bitlen-1)
   114  	if k == length {
   115  		k >>= 1
   116  	}
   117  	return k
   118  }