github.com/koko1123/flow-go-1@v0.29.6/storage/merkle/tree_test.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package merkle 4 5 import ( 6 "encoding/hex" 7 "fmt" 8 "math/rand" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 const TreeTestLength = 100000 17 18 var expectedEmptyHash = []byte{ 19 14, 87, 81, 192, 38, 229, 67, 20 178, 232, 171, 46, 176, 96, 153, 21 218, 161, 209, 229, 223, 71, 119, 22 143, 119, 135, 250, 171, 69, 205, 23 241, 47, 227, 168} 24 25 // TestTreeInitialization verifies that tree initialization only accepts 26 // compatible key lengths. 27 func TestTreeInitialization(t *testing.T) { 28 // constructor should reject `keyLength` _outside_ of interval [1, maxKeyLength] 29 t.Run("key length outside of compatible bounds", func(t *testing.T) { 30 tree, err := NewTree(-1) 31 require.Nil(t, tree) 32 require.ErrorIs(t, err, ErrorIncompatibleKeyLength) 33 34 tree, err = NewTree(0) 35 require.Nil(t, tree) 36 require.ErrorIs(t, err, ErrorIncompatibleKeyLength) 37 38 tree, err = NewTree(maxKeyLength + 1) 39 require.Nil(t, tree) 40 require.ErrorIs(t, err, ErrorIncompatibleKeyLength) 41 }) 42 43 // constructor should accept `keyLength` values in the interval [1, maxKeyLength] 44 t.Run("compatible key length", func(t *testing.T) { 45 tree, err := NewTree(1) 46 require.NotNil(t, tree) 47 require.NoError(t, err) 48 49 tree, err = NewTree(maxKeyLength) 50 require.NotNil(t, tree) 51 require.NoError(t, err) 52 }) 53 } 54 55 // TestEmptyTreeHash verifies that an empty tree returns the expected empty hash. 56 // We test with: 57 // - different key sizes 58 // - a newly initialized trie (empty) 59 // - a trie, whose last element was removed 60 func TestEmptyTreeHash(t *testing.T) { 61 for _, keyLength := range []int{1, 32, maxKeyLength} { 62 tree, _ := NewTree(keyLength) 63 assert.Equal(t, tree.Hash(), expectedEmptyHash) 64 65 // generate random key-value pair 66 key := make([]byte, keyLength) 67 rand.Read(key) 68 val := []byte{1} 69 70 // add key-value pair: hash should be non-empty 71 replaced, err := tree.Put(key, val) 72 assert.NoError(t, err) 73 assert.False(t, replaced) 74 assert.NotEmpty(t, tree.Hash()) 75 76 // remove key: hash should now be empty again 77 removed, err := tree.Del(key) 78 assert.NoError(t, err) 79 assert.True(t, removed) 80 assert.Equal(t, tree.Hash(), expectedEmptyHash) 81 } 82 } 83 84 // Test_ReferenceSingleEntry we construct a tree with a single key-value pair 85 // and compare its hash to a pre-computed value from a python reference implementation. 86 func Test_ReferenceSingleEntry(t *testing.T) { 87 val, _ := hex.DecodeString("bab02e6213dfad3546aa473922bba0") 88 89 t.Run("2-byte path", func(t *testing.T) { 90 key := []byte{22, 83} // key: 00010110 01010011 91 expectedRootHash := "3c4fd8e7bc5572d708d7ccab0a9ee06f74aac780e68c68d0b629ecb58a1fdf9d" // from python reference impl 92 93 tree, err := NewTree(len(key)) 94 assert.NoError(t, err) 95 replaced, err := tree.Put(key, val) 96 assert.NoError(t, err) 97 assert.False(t, replaced) 98 require.Equal(t, expectedRootHash, hex.EncodeToString(tree.Hash())) 99 }) 100 101 t.Run("32-byte path", func(t *testing.T) { 102 key, _ := hex.DecodeString("1b30482d4dc8c1a8d846d05765c03a33f0267b56b9a7be8defe38958f89c95fc") 103 expectedRootHash := "10eb7e9ffa397651acc2faf8a3c56207914418ca02ff9f39694effaf83d261e0" // from python reference impl 104 105 tree, err := NewTree(len(key)) 106 assert.NoError(t, err) 107 replaced, err := tree.Put(key, val) 108 assert.NoError(t, err) 109 assert.False(t, replaced) 110 require.Equal(t, expectedRootHash, hex.EncodeToString(tree.Hash())) 111 }) 112 113 t.Run("maxKeyLength-byte path", func(t *testing.T) { 114 // as key, we just repeat the following 32 bytes 256 times 115 k, _ := hex.DecodeString("1b30482d4dc8c1a8d846d05765c03a33f0267b56b9a7be8defe38958f89c95fc") 116 key := make([]byte, 0, maxKeyLength) 117 for i := 1; i <= maxKeyLength/len(k); i++ { 118 key = append(key, k...) 119 } 120 key = append(key, k[:maxKeyLength%len(k)]...) 121 122 expectedRootHash := "bf6eab5ce259b8a936f4fe205ca49f5d6614a7bee4162cafa5a6ab4691eba40d" // from python reference impl 123 tree, err := NewTree(len(key)) 124 assert.NoError(t, err) 125 replaced, err := tree.Put(key, val) 126 assert.NoError(t, err) 127 assert.False(t, replaced) 128 assert.Equal(t, expectedRootHash, hex.EncodeToString(tree.Hash())) 129 }) 130 } 131 132 // Test_2EntryTree we construct a tree with a 2 key-value pairs and compare 133 // its hash to a pre-computed value from a python reference implementation. 134 func Test_2EntryTree(t *testing.T) { 135 keyLength := 2 136 key0 := []byte{20, 3} // 00010100 00000011 137 key1 := []byte{23, 252} // 00010111 11111100 138 val0, _ := hex.DecodeString("62b0326507ebce9d4a242908d20559") 139 val1, _ := hex.DecodeString("bab02e6213dfad3546aa473922bba0") 140 expectedRootHash := "7f372aca94b91a527539967ba966c3a91c91e97b265fc4830801b4bcca01b06e" // from python reference impl 141 142 tree, err := NewTree(keyLength) 143 assert.NoError(t, err) 144 replaced, err := tree.Put(key0, val0) 145 require.False(t, replaced) 146 require.NoError(t, err) 147 replaced, err = tree.Put(key1, val1) 148 require.False(t, replaced) 149 require.NoError(t, err) 150 require.Equal(t, expectedRootHash, hex.EncodeToString(tree.Hash())) 151 } 152 153 // Test_KeyValuesAreSafeFromExternalModification verifies that the 154 // tree implementation is _not_ vulnerable to the slices of the key-value 155 // pair being modified in-place _after_ addition to the tree. 156 func Test_KeyValuesAreSafeFromExternalModification(t *testing.T) { 157 // we re-use the same key-value pairs as in Test_2EntryTree: 158 keyLength := 2 159 key0 := []byte{20, 3} // 00010100 00000011 160 key1 := []byte{23, 252} // 00010111 11111100 161 val0, _ := hex.DecodeString("62b0326507ebce9d4a242908d20559") 162 val1, _ := hex.DecodeString("bab02e6213dfad3546aa473922bba0") 163 expectedRootHash := "7f372aca94b91a527539967ba966c3a91c91e97b265fc4830801b4bcca01b06e" // from python reference impl 164 165 // we now put the key-value pairs into a tree, 166 // but modify the key and value slices right after *in-place* 167 postKey := []byte{255, 255} 168 postVal, _ := hex.DecodeString("1b30482d4dc8c1a8d846d05765c03a") 169 tree, err := NewTree(keyLength) 170 assert.NoError(t, err) 171 replaced, err := tree.Put(key0, val0) 172 require.False(t, replaced) 173 require.NoError(t, err) 174 copy(key0, postKey) 175 copy(val0, postVal) 176 replaced, err = tree.Put(key1, val1) 177 require.False(t, replaced) 178 require.NoError(t, err) 179 copy(key1, postKey) 180 copy(val1, postVal) 181 182 // (key1, val1) and (key2, val2) should now contain the same data as (postKey, postVal) 183 require.Equal(t, postKey, key0) 184 require.Equal(t, postVal, val0) 185 require.Equal(t, postKey, key1) 186 require.Equal(t, postVal, val1) 187 // but the tree's root hash should still be the expected value: 188 require.Equal(t, expectedRootHash, hex.EncodeToString(tree.Hash())) 189 } 190 191 // Test_KeyLengthChecked verifies that the Tree implementation checks that 192 // * the key has the length as configured at construction time 193 // * rejects addition of key-value pair, if key does not conform to pre-configured length 194 func Test_KeyLengthChecked(t *testing.T) { 195 val, _ := hex.DecodeString("bab02e6213dfad3546aa473922bba0") 196 197 t.Run("nil key", func(t *testing.T) { 198 tree, err := NewTree(1) 199 assert.NoError(t, err) 200 _, err = tree.Put(nil, val) // nil key is not of length 17 and should be rejected 201 assert.ErrorIs(t, err, ErrorIncompatibleKeyLength) 202 }) 203 204 t.Run("empty key", func(t *testing.T) { 205 tree, err := NewTree(1) 206 assert.NoError(t, err) 207 _, err = tree.Put([]byte{}, val) // empty key is not of length 17 and should be rejected 208 assert.ErrorIs(t, err, ErrorIncompatibleKeyLength) 209 }) 210 211 t.Run("1-byte key", func(t *testing.T) { 212 key := make([]byte, 1) 213 tree, err := NewTree(1) 214 assert.NoError(t, err) 215 replaced, err := tree.Put(key, val) // key has the pre-configured length and should be accepted 216 assert.NoError(t, err) 217 assert.False(t, replaced) 218 }) 219 220 t.Run("maxKeyLength-byte key", func(t *testing.T) { 221 key := make([]byte, maxKeyLength) 222 tree, err := NewTree(maxKeyLength) 223 assert.NoError(t, err) 224 replaced, err := tree.Put(key, val) // key has the pre-configured length and should be accepted 225 assert.NoError(t, err) 226 assert.False(t, replaced) 227 }) 228 229 t.Run("key too long", func(t *testing.T) { 230 key := make([]byte, maxKeyLength+1) 231 tree, err := NewTree(maxKeyLength) 232 assert.NoError(t, err) 233 _, err = tree.Put(key, val) 234 assert.ErrorIs(t, err, ErrorIncompatibleKeyLength) 235 }) 236 } 237 238 // TestTreeSingle verifies addition, retrieval and deletion operations 239 // of a _single_ key-value pair to an otherwise empty tree. 240 func TestTreeSingle(t *testing.T) { 241 // initialize the random generator, tree and zero hash 242 rand.Seed(time.Now().UnixNano()) 243 keyLength := 32 244 tree, err := NewTree(keyLength) 245 assert.NoError(t, err) 246 247 // for the pre-defined number of times... 248 for i := 0; i < TreeTestLength; i++ { 249 // insert a random key with a random value and make sure it didn't 250 // exist yet; collisions are unlikely enough to never happen 251 key, val := randomKeyValuePair(keyLength, 128) 252 replaced, err := tree.Put(key, val) 253 assert.NoError(t, err) 254 assert.False(t, replaced) 255 256 // retrieve the value again, check it as successful and the same 257 out, retrieved := tree.Get(key) 258 if assert.True(t, retrieved) { 259 assert.Equal(t, val, out) 260 } 261 262 // delete the value again, check it was successful 263 deleted, err := tree.Del(key) 264 assert.NoError(t, err) 265 assert.True(t, deleted) 266 _, retrieved = tree.Get(key) 267 assert.False(t, retrieved) 268 269 // get the root hash and make sure it's empty again as the tree is empty 270 assert.Equal(t, tree.Hash(), expectedEmptyHash) 271 } 272 } 273 274 // TestTreeBatch tests addition and deletion of multiple key-value pairs. 275 // Key-value pairs are added and deleted in the same order. 276 func TestTreeBatch(t *testing.T) { 277 // initialize random generator, tree, zero hash 278 rand.Seed(time.Now().UnixNano()) 279 keyLength := 32 280 tree, err := NewTree(keyLength) 281 assert.NoError(t, err) 282 283 // insert a batch of random key-value pairs 284 keys := make([][]byte, 0, TreeTestLength) 285 vals := make([][]byte, 0, TreeTestLength) 286 for i := 0; i < TreeTestLength; i++ { 287 key, val := randomKeyValuePair(keyLength, 128) 288 keys = append(keys, key) 289 vals = append(vals, val) 290 } 291 292 // insert key-value pairs and ensure there are no collisions 293 for i, key := range keys { 294 val := vals[i] 295 replaced, err := tree.Put(key, val) 296 assert.NoError(t, err) 297 assert.False(t, replaced) 298 } 299 300 // retrieve all key-value pairs, ensure they are found and are correct 301 for i, key := range keys { 302 val := vals[i] 303 out, retrieved := tree.Get(key) 304 if assert.True(t, retrieved) { 305 assert.Equal(t, val, out) 306 } 307 } 308 309 // remove all key-value pairs, ensure it worked 310 for _, key := range keys { 311 deleted, err := tree.Del(key) 312 assert.NoError(t, err) 313 assert.True(t, deleted) 314 } 315 316 // get the root hash and make sure it's empty again as the tree is empty 317 assert.Equal(t, tree.Hash(), EmptyTreeRootHash) 318 } 319 320 // TestRandomOrder tests that root hash of tree is independent of the order 321 // in which the elements were added. 322 func TestRandomOrder(t *testing.T) { 323 // initialize random generator, two trees and zero hash 324 rand.Seed(time.Now().UnixNano()) 325 keyLength := 32 326 tree1, err := NewTree(keyLength) 327 assert.NoError(t, err) 328 tree2, err := NewTree(keyLength) 329 assert.NoError(t, err) 330 331 // generate the desired number of keys and map a value to each key 332 keys := make([][]byte, 0, TreeTestLength) 333 vals := make(map[string][]byte) 334 for i := 0; i < TreeTestLength; i++ { 335 key, val := randomKeyValuePair(32, 128) 336 keys = append(keys, key) 337 vals[string(key)] = val 338 } 339 340 // insert all key-value paris into the first tree 341 for _, key := range keys { 342 val := vals[string(key)] 343 replaced, err := tree1.Put(key, val) 344 assert.NoError(t, err) 345 require.False(t, replaced) 346 } 347 348 // shuffle the keys and insert them with random order into the second tree 349 rand.Shuffle(len(keys), func(i int, j int) { 350 keys[i], keys[j] = keys[j], keys[i] 351 }) 352 for _, key := range keys { 353 val := vals[string(key)] 354 replaced, err := tree2.Put(key, val) 355 assert.NoError(t, err) 356 require.False(t, replaced) 357 } 358 359 // make sure the tree hashes were the same, in spite of random order 360 assert.Equal(t, tree1.Hash(), tree2.Hash()) 361 362 // remove the key-value pairs from the first tree in random order 363 for _, key := range keys { 364 deleted, err := tree1.Del(key) 365 assert.NoError(t, err) 366 require.True(t, deleted) 367 } 368 369 // get the root hash and make sure it's empty again as the tree is empty 370 assert.Equal(t, tree1.Hash(), expectedEmptyHash) 371 } 372 373 func BenchmarkTree(b *testing.B) { 374 for n := 1000; n < 1000000; n *= 10 { 375 b.Run(fmt.Sprintf("put-%d", n), treePut(n)) 376 b.Run(fmt.Sprintf("get-%d", n), treeGet(n)) 377 b.Run(fmt.Sprintf("del-%d", n), treeDel(n)) 378 b.Run(fmt.Sprintf("hash-%d", n), treeHash(n)) 379 } 380 } 381 382 func randomKeyValuePair(keySize, valueSize int) ([]byte, []byte) { 383 key := make([]byte, keySize) 384 val := make([]byte, valueSize) 385 _, _ = rand.Read(key) 386 _, _ = rand.Read(val) 387 return key, val 388 } 389 390 func createTree(n int) *Tree { 391 t, err := NewTree(32) 392 if err != nil { 393 panic(err.Error()) 394 } 395 for i := 0; i < n; i++ { 396 key, val := randomKeyValuePair(32, 128) 397 _, _ = t.Put(key, val) 398 } 399 return t 400 } 401 402 func treePut(n int) func(*testing.B) { 403 return func(b *testing.B) { 404 t := createTree(n) 405 b.StopTimer() 406 b.ResetTimer() 407 for i := 0; i < b.N; i++ { 408 key, val := randomKeyValuePair(32, 128) 409 b.StartTimer() 410 _, _ = t.Put(key, val) 411 b.StopTimer() 412 _, _ = t.Del(key) 413 } 414 } 415 } 416 417 func treeGet(n int) func(*testing.B) { 418 return func(b *testing.B) { 419 t := createTree(n) 420 b.StopTimer() 421 b.ResetTimer() 422 for i := 0; i < b.N; i++ { 423 key, val := randomKeyValuePair(32, 128) 424 _, _ = t.Put(key, val) 425 b.StartTimer() 426 _, _ = t.Get(key) 427 b.StopTimer() 428 _, _ = t.Del(key) 429 } 430 } 431 } 432 433 func treeDel(n int) func(*testing.B) { 434 return func(b *testing.B) { 435 t := createTree(n) 436 b.StopTimer() 437 b.ResetTimer() 438 for i := 0; i < b.N; i++ { 439 key, val := randomKeyValuePair(32, 128) 440 _, _ = t.Put(key, val) 441 b.StartTimer() 442 _, _ = t.Del(key) 443 b.StopTimer() 444 } 445 } 446 } 447 448 func treeHash(n int) func(*testing.B) { 449 return func(b *testing.B) { 450 t := createTree(n) 451 b.ResetTimer() 452 for i := 0; i < b.N; i++ { 453 _ = t.Hash() 454 } 455 } 456 }