github.com/koko1123/flow-go-1@v0.29.6/storage/merkle/node_test.go (about)

     1  package merkle
     2  
     3  import (
     4  	"encoding/hex"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"golang.org/x/crypto/blake2b"
    11  )
    12  
    13  var payload1, _ = hex.DecodeString("62b0326507ebce9d4a242908d20559ceca965c5e9848646bd0c05047c8487aadfcb3d851e77e5a055d306e48c376f8")
    14  var payload2, _ = hex.DecodeString("bab02e6213dfad3546aa473922bba0")
    15  
    16  // TestHashTags tests the hashing tags of the node types
    17  // satisfy the required conditions for the tree security properties.
    18  func TestHashTags(t *testing.T) {
    19  	// Test tag lengths are equal
    20  	// This is required because the tags are prepended to the hashed value.
    21  	assert.Equal(t, len(leafNodeTag), len(shortNodeTag))
    22  	assert.Equal(t, len(leafNodeTag), len(fullNodeTag))
    23  
    24  	// Test tag values are not equal
    25  	// This is required to make sure the 3 node hash functions are orthogonal.
    26  	assert.NotEqual(t, leafNodeTag, shortNodeTag)
    27  	assert.NotEqual(t, leafNodeTag, fullNodeTag)
    28  	assert.NotEqual(t, shortNodeTag, fullNodeTag)
    29  }
    30  
    31  // TestBlakeMAC verifies that constructor blake2b.New256(m) never errors for any of the used MAC
    32  // values `m`. This is assumed by the Node's Hash() implementations. We test this assumption holds
    33  // at build time, but avoid runtime-checks for performance reasons.
    34  // Context: The blake2b MAC key should be less than 64 bytes. Constructor blake2b.New256(m) checks
    35  // this condition and errors, but we ignore its error return at runtime.
    36  func TestBlakeMAC(t *testing.T) {
    37  	var e error
    38  
    39  	// leaf nodes
    40  	assert.True(t, len(leafNodeTag) < 64)
    41  	_, e = blake2b.New256(leafNodeTag[:])
    42  	assert.NoError(t, e)
    43  
    44  	// full nodes
    45  	assert.True(t, len(fullNodeTag) < 64)
    46  	_, e = blake2b.New256(fullNodeTag[:])
    47  	assert.NoError(t, e)
    48  
    49  	// short nodes
    50  	assert.True(t, len(shortNodeTag) < 64)
    51  	_, e = blake2b.New256(shortNodeTag[:])
    52  	assert.NoError(t, e)
    53  
    54  }
    55  
    56  // TestLeafHash verifies that the hash of a leaf returns the expected value.
    57  // We compare with a python-reference implementation
    58  func TestLeafHash(t *testing.T) {
    59  	// reference value (from python reference implementation)
    60  	ref := "1b30482d4dc8c1a8d846d05765c03a33f0267b56b9a7be8defe38958f89c95fc"
    61  
    62  	l := leaf{val: payload1}
    63  	require.Equal(t, ref, hex.EncodeToString(l.Hash(false)))
    64  	// this first time doens't have cached value
    65  	require.Nil(t, l.cachedHashValue)
    66  	require.Equal(t, ref, hex.EncodeToString(l.Hash(true)))
    67  	// second time returns the cached value
    68  	require.NotNil(t, l.cachedHashValue)
    69  	require.Equal(t, ref, hex.EncodeToString(l.Hash(true)))
    70  }
    71  
    72  // TestShortHash verifies that the hash of a short node returns the expected value.
    73  // We compare with a python-reference implementation.
    74  func TestShortHash(t *testing.T) {
    75  	t.Run("13-bit path", func(t *testing.T) {
    76  		// expected value from python reference implementation
    77  		ref := "a5632447428968cca925e802e5251c2c2b31e4ebf5236a3a66a60fe0509d6e40"
    78  
    79  		// path of 13 bits: 1011001010011
    80  		// per convention, we pad zeros to the end to obtain full bytes:
    81  		//  -> 10110010 10011000 (padded binary representation)
    82  		//  ->      178      152 (uint8 representation)
    83  		//
    84  		path := []byte{178, 152}
    85  		s := short{
    86  			path:  path,
    87  			count: 13,
    88  			child: &leaf{val: payload1},
    89  		}
    90  		require.Equal(t, ref, hex.EncodeToString(s.Hash(false)))
    91  		// this first time doens't have cached value
    92  		require.Equal(t, ref, hex.EncodeToString(s.Hash(true)))
    93  		// second time returns the cached value
    94  		require.Equal(t, ref, hex.EncodeToString(s.Hash(true)))
    95  	})
    96  
    97  	t.Run("maxKeyLenBits-bit path", func(t *testing.T) {
    98  		// as path, we just repeat the following 32 bytes 256 times
    99  		k, _ := hex.DecodeString("1b30482d4dc8c1a8d846d05765c03a33f0267b56b9a7be8defe38958f89c95fc")
   100  		path := make([]byte, 0, maxKeyLength)
   101  		for i := 1; i <= maxKeyLength/len(k); i++ {
   102  			path = append(path, k...)
   103  		}
   104  		path = append(path, k[:maxKeyLength%len(k)]...)
   105  		// expected value from python reference implementation
   106  		ref := "e60e2abb4d45b01c2ccc3ea1c82d424478192fc37b31ca7ec84331b4d0f31846"
   107  
   108  		s := short{
   109  			path:  path,
   110  			count: maxKeyLenBits,
   111  			child: &leaf{val: payload1},
   112  		}
   113  		require.Equal(t, ref, hex.EncodeToString(s.Hash(false)))
   114  		// this first time doens't have cached value
   115  		require.Equal(t, ref, hex.EncodeToString(s.Hash(true)))
   116  		// second time returns the cached value
   117  		require.Equal(t, ref, hex.EncodeToString(s.Hash(true)))
   118  	})
   119  }
   120  
   121  // Test_ShortNodePathLengthEncoding:
   122  // The tree enforces a max key length of `maxKeyLength`. We verify that:
   123  //  1. the resulting number of bits (i.e. maxKeyLength * 8), does not
   124  //     overflow the hardware-dependent int range.
   125  //  2. the value range from [1, ..., maxKeyLength * 8] can be encoded into 2 bytes,
   126  //     as this is required by the short node (but not enforced at run time)
   127  //  3. serializedPathSegmentLength(l)
   128  func Test_ShortNodePathLengthEncoding(t *testing.T) {
   129  	// testing 1:
   130  	maxInt := int(^uint(0) >> 1) // largest int value (hardware-dependent)
   131  	require.True(t, maxKeyLength <= maxInt/8)
   132  
   133  	// testing 2:
   134  	// two bytes can encode up to 2^16-1 = 65535
   135  	require.GreaterOrEqual(t, uint64(65535), uint64(maxKeyLength)*8)
   136  
   137  	// testing 3:
   138  	require.Equal(t, [2]byte{0, 1}, serializedPathSegmentLength(1))
   139  	require.Equal(t, [2]byte{255, 255}, serializedPathSegmentLength(65535))
   140  }
   141  
   142  // TestFullHash verifies that the hash of a full node returns the expected value.
   143  // We compare with a python-reference implementation.
   144  func TestFullHash(t *testing.T) {
   145  	// reference value (from python reference implementation)
   146  	ref := "6edee16badebe695a2ff7df90e429ba66e8986f7c9d089e4ad8fccbd89b0ccc8"
   147  
   148  	f := full{
   149  		left:  &leaf{val: payload1},
   150  		right: &leaf{val: payload2},
   151  	}
   152  	require.Equal(t, ref, hex.EncodeToString(f.Hash(false)))
   153  	// this first time doens't have cached value
   154  	require.Equal(t, ref, hex.EncodeToString(f.Hash(true)))
   155  	// second time returns the cached value
   156  	require.Equal(t, ref, hex.EncodeToString(f.Hash(true)))
   157  }
   158  
   159  // TestMaxDepthOfDescendants validates the functionality of the max depth of the decendencts
   160  func TestMaxDepthOfDescendants(t *testing.T) {
   161  	////   consturcted trie
   162  	//          full
   163  	//         /    \
   164  	//       short  leaf
   165  	//         |
   166  	//        full
   167  	//       /   \
   168  	//     leaf  short
   169  	//             |
   170  	//             leaf
   171  	////
   172  
   173  	f := full{
   174  		left: &short{
   175  			child: &full{
   176  				left: &leaf{},
   177  				right: &short{
   178  					child: &leaf{},
   179  				},
   180  			},
   181  		},
   182  		right: &leaf{},
   183  	}
   184  
   185  	require.Equal(t, f.MaxDepthOfDescendants(), uint(4))
   186  }