github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/hashing_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package merkledb
     5  
     6  import (
     7  	"math/rand"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/utils/maybe"
    14  )
    15  
    16  var sha256HashNodeTests = []struct {
    17  	name         string
    18  	n            *node
    19  	expectedHash string
    20  }{
    21  	{
    22  		name:         "empty node",
    23  		n:            newNode(Key{}),
    24  		expectedHash: "rbhtxoQ1DqWHvb6w66BZdVyjmPAneZUSwQq9uKj594qvFSdav",
    25  	},
    26  	{
    27  		name: "has value",
    28  		n: func() *node {
    29  			n := newNode(Key{})
    30  			n.setValue(SHA256Hasher, maybe.Some([]byte("value1")))
    31  			return n
    32  		}(),
    33  		expectedHash: "2vx2xueNdWoH2uB4e8hbMU5jirtZkZ1c3ePCWDhXYaFRHpCbnQ",
    34  	},
    35  	{
    36  		name:         "has key",
    37  		n:            newNode(ToKey([]byte{0, 1, 2, 3, 4, 5, 6, 7})),
    38  		expectedHash: "2vA8ggXajhFEcgiF8zHTXgo8T2ALBFgffp1xfn48JEni1Uj5uK",
    39  	},
    40  	{
    41  		name: "1 child",
    42  		n: func() *node {
    43  			n := newNode(Key{})
    44  			childNode := newNode(ToKey([]byte{255}))
    45  			childNode.setValue(SHA256Hasher, maybe.Some([]byte("value1")))
    46  			n.addChildWithID(childNode, 4, SHA256Hasher.HashNode(childNode))
    47  			return n
    48  		}(),
    49  		expectedHash: "YfJRufqUKBv9ez6xZx6ogpnfDnw9fDsyebhYDaoaH57D3vRu3",
    50  	},
    51  	{
    52  		name: "2 children",
    53  		n: func() *node {
    54  			n := newNode(Key{})
    55  
    56  			childNode1 := newNode(ToKey([]byte{255}))
    57  			childNode1.setValue(SHA256Hasher, maybe.Some([]byte("value1")))
    58  
    59  			childNode2 := newNode(ToKey([]byte{237}))
    60  			childNode2.setValue(SHA256Hasher, maybe.Some([]byte("value2")))
    61  
    62  			n.addChildWithID(childNode1, 4, SHA256Hasher.HashNode(childNode1))
    63  			n.addChildWithID(childNode2, 4, SHA256Hasher.HashNode(childNode2))
    64  			return n
    65  		}(),
    66  		expectedHash: "YVmbx5MZtSKuYhzvHnCqGrswQcxmozAkv7xE1vTA2EiGpWUkv",
    67  	},
    68  	{
    69  		name: "16 children",
    70  		n: func() *node {
    71  			n := newNode(Key{})
    72  
    73  			for i := byte(0); i < 16; i++ {
    74  				childNode := newNode(ToKey([]byte{i << 4}))
    75  				childNode.setValue(SHA256Hasher, maybe.Some([]byte("some value")))
    76  
    77  				n.addChildWithID(childNode, 4, SHA256Hasher.HashNode(childNode))
    78  			}
    79  			return n
    80  		}(),
    81  		expectedHash: "5YiFLL7QV3f441See9uWePi3wVKsx9fgvX5VPhU8PRxtLqhwY",
    82  	},
    83  }
    84  
    85  // Ensure that SHA256.HashNode is deterministic
    86  func Fuzz_SHA256_HashNode(f *testing.F) {
    87  	f.Fuzz(
    88  		func(
    89  			t *testing.T,
    90  			randSeed int,
    91  		) {
    92  			require := require.New(t)
    93  			for _, bf := range validBranchFactors { // Create a random node
    94  				r := rand.New(rand.NewSource(int64(randSeed))) // #nosec G404
    95  
    96  				children := map[byte]*child{}
    97  				numChildren := r.Intn(int(bf)) // #nosec G404
    98  				for i := 0; i < numChildren; i++ {
    99  					compressedKeyLen := r.Intn(32) // #nosec G404
   100  					compressedKeyBytes := make([]byte, compressedKeyLen)
   101  					_, _ = r.Read(compressedKeyBytes) // #nosec G404
   102  
   103  					children[byte(i)] = &child{
   104  						compressedKey: ToKey(compressedKeyBytes),
   105  						id:            ids.GenerateTestID(),
   106  						hasValue:      r.Intn(2) == 1, // #nosec G404
   107  					}
   108  				}
   109  
   110  				hasValue := r.Intn(2) == 1 // #nosec G404
   111  				value := maybe.Nothing[[]byte]()
   112  				if hasValue {
   113  					valueBytes := make([]byte, r.Intn(64)) // #nosec G404
   114  					_, _ = r.Read(valueBytes)              // #nosec G404
   115  					value = maybe.Some(valueBytes)
   116  				}
   117  
   118  				key := make([]byte, r.Intn(32)) // #nosec G404
   119  				_, _ = r.Read(key)              // #nosec G404
   120  
   121  				hv := &node{
   122  					key: ToKey(key),
   123  					dbNode: dbNode{
   124  						children: children,
   125  						value:    value,
   126  					},
   127  				}
   128  
   129  				// Hash hv multiple times
   130  				hash1 := SHA256Hasher.HashNode(hv)
   131  				hash2 := SHA256Hasher.HashNode(hv)
   132  
   133  				// Make sure they're the same
   134  				require.Equal(hash1, hash2)
   135  			}
   136  		},
   137  	)
   138  }
   139  
   140  func Test_SHA256_HashNode(t *testing.T) {
   141  	for _, test := range sha256HashNodeTests {
   142  		t.Run(test.name, func(t *testing.T) {
   143  			hash := SHA256Hasher.HashNode(test.n)
   144  			require.Equal(t, test.expectedHash, hash.String())
   145  		})
   146  	}
   147  }
   148  
   149  func Benchmark_SHA256_HashNode(b *testing.B) {
   150  	for _, benchmark := range sha256HashNodeTests {
   151  		b.Run(benchmark.name, func(b *testing.B) {
   152  			for i := 0; i < b.N; i++ {
   153  				SHA256Hasher.HashNode(benchmark.n)
   154  			}
   155  		})
   156  	}
   157  }