github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/db_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 "bytes" 8 "context" 9 "encoding/binary" 10 "fmt" 11 "math/rand" 12 "slices" 13 "strconv" 14 "testing" 15 "time" 16 17 "github.com/prometheus/client_golang/prometheus" 18 "github.com/stretchr/testify/require" 19 20 "github.com/MetalBlockchain/metalgo/database" 21 "github.com/MetalBlockchain/metalgo/database/memdb" 22 "github.com/MetalBlockchain/metalgo/ids" 23 "github.com/MetalBlockchain/metalgo/trace" 24 "github.com/MetalBlockchain/metalgo/utils/hashing" 25 "github.com/MetalBlockchain/metalgo/utils/maybe" 26 "github.com/MetalBlockchain/metalgo/utils/set" 27 "github.com/MetalBlockchain/metalgo/utils/units" 28 ) 29 30 const defaultHistoryLength = 300 31 32 // newDB returns a new merkle database with the underlying type so that tests can access unexported fields 33 func newDB(ctx context.Context, db database.Database, config Config) (*merkleDB, error) { 34 db, err := New(ctx, db, config) 35 if err != nil { 36 return nil, err 37 } 38 return db.(*merkleDB), nil 39 } 40 41 func newDefaultConfig() Config { 42 return Config{ 43 BranchFactor: BranchFactor16, 44 Hasher: DefaultHasher, 45 RootGenConcurrency: 0, 46 HistoryLength: defaultHistoryLength, 47 ValueNodeCacheSize: units.MiB, 48 IntermediateNodeCacheSize: units.MiB, 49 IntermediateWriteBufferSize: units.KiB, 50 IntermediateWriteBatchSize: 256 * units.KiB, 51 Reg: prometheus.NewRegistry(), 52 TraceLevel: InfoTrace, 53 Tracer: trace.Noop, 54 } 55 } 56 57 func Test_MerkleDB_Get_Safety(t *testing.T) { 58 require := require.New(t) 59 60 db, err := getBasicDB() 61 require.NoError(err) 62 63 keyBytes := []byte{0} 64 require.NoError(db.Put(keyBytes, []byte{0, 1, 2})) 65 66 val, err := db.Get(keyBytes) 67 require.NoError(err) 68 69 n, err := db.getNode(ToKey(keyBytes), true) 70 require.NoError(err) 71 72 // node's value shouldn't be affected by the edit 73 originalVal := slices.Clone(val) 74 val[0]++ 75 require.Equal(originalVal, n.value.Value()) 76 } 77 78 func Test_MerkleDB_GetValues_Safety(t *testing.T) { 79 require := require.New(t) 80 81 db, err := getBasicDB() 82 require.NoError(err) 83 84 keyBytes := []byte{0} 85 value := []byte{0, 1, 2} 86 require.NoError(db.Put(keyBytes, value)) 87 88 gotValues, errs := db.GetValues(context.Background(), [][]byte{keyBytes}) 89 require.Len(errs, 1) 90 require.NoError(errs[0]) 91 require.Equal(value, gotValues[0]) 92 gotValues[0][0]++ 93 94 // editing the value array shouldn't affect the db 95 gotValues, errs = db.GetValues(context.Background(), [][]byte{keyBytes}) 96 require.Len(errs, 1) 97 require.NoError(errs[0]) 98 require.Equal(value, gotValues[0]) 99 } 100 101 func Test_MerkleDB_DB_Interface(t *testing.T) { 102 for _, bf := range validBranchFactors { 103 for name, test := range database.Tests { 104 t.Run(fmt.Sprintf("%s_%d", name, bf), func(t *testing.T) { 105 db, err := getBasicDBWithBranchFactor(bf) 106 require.NoError(t, err) 107 test(t, db) 108 }) 109 } 110 } 111 } 112 113 func Benchmark_MerkleDB_DBInterface(b *testing.B) { 114 for _, size := range database.BenchmarkSizes { 115 keys, values := database.SetupBenchmark(b, size[0], size[1], size[2]) 116 for _, bf := range validBranchFactors { 117 for name, bench := range database.Benchmarks { 118 b.Run(fmt.Sprintf("merkledb_%d_%d_pairs_%d_keys_%d_values_%s", bf, size[0], size[1], size[2], name), func(b *testing.B) { 119 db, err := getBasicDBWithBranchFactor(bf) 120 require.NoError(b, err) 121 bench(b, db, keys, values) 122 }) 123 } 124 } 125 } 126 } 127 128 func Test_MerkleDB_DB_Load_Root_From_DB(t *testing.T) { 129 require := require.New(t) 130 baseDB := memdb.New() 131 defer baseDB.Close() 132 133 db, err := New( 134 context.Background(), 135 baseDB, 136 newDefaultConfig(), 137 ) 138 require.NoError(err) 139 140 // Populate initial set of key-value pairs 141 keyCount := 100 142 ops := make([]database.BatchOp, 0, keyCount) 143 require.NoError(err) 144 for i := 0; i < keyCount; i++ { 145 k := []byte(strconv.Itoa(i)) 146 ops = append(ops, database.BatchOp{ 147 Key: k, 148 Value: hashing.ComputeHash256(k), 149 }) 150 } 151 view, err := db.NewView(context.Background(), ViewChanges{BatchOps: ops}) 152 require.NoError(err) 153 require.NoError(view.CommitToDB(context.Background())) 154 155 root, err := db.GetMerkleRoot(context.Background()) 156 require.NoError(err) 157 158 require.NoError(db.Close()) 159 160 // reloading the db should set the root back to the one that was saved to [baseDB] 161 db, err = New( 162 context.Background(), 163 baseDB, 164 newDefaultConfig(), 165 ) 166 require.NoError(err) 167 168 reloadedRoot, err := db.GetMerkleRoot(context.Background()) 169 require.NoError(err) 170 require.Equal(root, reloadedRoot) 171 } 172 173 func Test_MerkleDB_DB_Rebuild(t *testing.T) { 174 require := require.New(t) 175 176 initialSize := 5_000 177 178 config := newDefaultConfig() 179 config.ValueNodeCacheSize = uint(initialSize) 180 config.IntermediateNodeCacheSize = uint(initialSize) 181 182 db, err := newDB( 183 context.Background(), 184 memdb.New(), 185 config, 186 ) 187 require.NoError(err) 188 189 // Populate initial set of keys 190 ops := make([]database.BatchOp, 0, initialSize) 191 require.NoError(err) 192 for i := 0; i < initialSize; i++ { 193 k := []byte(strconv.Itoa(i)) 194 ops = append(ops, database.BatchOp{ 195 Key: k, 196 Value: hashing.ComputeHash256(k), 197 }) 198 } 199 view, err := db.NewView(context.Background(), ViewChanges{BatchOps: ops}) 200 require.NoError(err) 201 require.NoError(view.CommitToDB(context.Background())) 202 203 // Get root 204 root, err := db.GetMerkleRoot(context.Background()) 205 require.NoError(err) 206 207 // Rebuild 208 require.NoError(db.rebuild(context.Background(), initialSize)) 209 210 // Assert root is the same after rebuild 211 rebuiltRoot, err := db.GetMerkleRoot(context.Background()) 212 require.NoError(err) 213 require.Equal(root, rebuiltRoot) 214 215 // add variation where root has a value 216 require.NoError(db.Put(nil, []byte{})) 217 218 root, err = db.GetMerkleRoot(context.Background()) 219 require.NoError(err) 220 221 require.NoError(db.rebuild(context.Background(), initialSize)) 222 223 rebuiltRoot, err = db.GetMerkleRoot(context.Background()) 224 require.NoError(err) 225 require.Equal(root, rebuiltRoot) 226 } 227 228 func Test_MerkleDB_Failed_Batch_Commit(t *testing.T) { 229 require := require.New(t) 230 231 memDB := memdb.New() 232 db, err := New( 233 context.Background(), 234 memDB, 235 newDefaultConfig(), 236 ) 237 require.NoError(err) 238 239 _ = memDB.Close() 240 241 batch := db.NewBatch() 242 require.NoError(batch.Put([]byte("key1"), []byte("1"))) 243 require.NoError(batch.Put([]byte("key2"), []byte("2"))) 244 require.NoError(batch.Put([]byte("key3"), []byte("3"))) 245 err = batch.Write() 246 require.ErrorIs(err, database.ErrClosed) 247 } 248 249 func Test_MerkleDB_Value_Cache(t *testing.T) { 250 require := require.New(t) 251 252 memDB := memdb.New() 253 db, err := New( 254 context.Background(), 255 memDB, 256 newDefaultConfig(), 257 ) 258 require.NoError(err) 259 260 batch := db.NewBatch() 261 key1, key2 := []byte("key1"), []byte("key2") 262 require.NoError(batch.Put(key1, []byte("1"))) 263 require.NoError(batch.Put([]byte("key2"), []byte("2"))) 264 require.NoError(batch.Write()) 265 266 batch = db.NewBatch() 267 // force key2 to be inserted into the cache as not found 268 require.NoError(batch.Delete(key2)) 269 require.NoError(batch.Write()) 270 271 require.NoError(memDB.Close()) 272 273 // still works because key1 is read from cache 274 value, err := db.Get(key1) 275 require.NoError(err) 276 require.Equal([]byte("1"), value) 277 278 // still returns missing instead of closed because key2 is read from cache 279 _, err = db.Get(key2) 280 require.ErrorIs(err, database.ErrNotFound) 281 } 282 283 func Test_MerkleDB_Invalidate_Siblings_On_Commit(t *testing.T) { 284 require := require.New(t) 285 286 dbTrie, err := getBasicDB() 287 require.NoError(err) 288 require.NotNil(dbTrie) 289 290 viewToCommit, err := dbTrie.NewView( 291 context.Background(), 292 ViewChanges{ 293 BatchOps: []database.BatchOp{ 294 {Key: []byte{0}, Value: []byte{0}}, 295 }, 296 }, 297 ) 298 require.NoError(err) 299 300 // Create siblings of viewToCommit 301 sibling1, err := dbTrie.NewView(context.Background(), ViewChanges{}) 302 require.NoError(err) 303 sibling2, err := dbTrie.NewView(context.Background(), ViewChanges{}) 304 require.NoError(err) 305 306 require.False(sibling1.(*view).isInvalid()) 307 require.False(sibling2.(*view).isInvalid()) 308 309 // Committing viewToCommit should invalidate siblings 310 require.NoError(viewToCommit.CommitToDB(context.Background())) 311 312 require.True(sibling1.(*view).isInvalid()) 313 require.True(sibling2.(*view).isInvalid()) 314 require.False(viewToCommit.(*view).isInvalid()) 315 } 316 317 func Test_MerkleDB_CommitRangeProof_DeletesValuesInRange(t *testing.T) { 318 require := require.New(t) 319 320 db, err := getBasicDB() 321 require.NoError(err) 322 323 // value that shouldn't be deleted 324 require.NoError(db.Put([]byte("key6"), []byte("3"))) 325 326 startRoot, err := db.GetMerkleRoot(context.Background()) 327 require.NoError(err) 328 329 // Get an empty proof 330 proof, err := db.GetRangeProof( 331 context.Background(), 332 maybe.Nothing[[]byte](), 333 maybe.Some([]byte("key3")), 334 10, 335 ) 336 require.NoError(err) 337 338 // confirm there are no key.values in the proof 339 require.Empty(proof.KeyValues) 340 341 // add values to be deleted by proof commit 342 batch := db.NewBatch() 343 require.NoError(batch.Put([]byte("key1"), []byte("1"))) 344 require.NoError(batch.Put([]byte("key2"), []byte("2"))) 345 require.NoError(batch.Put([]byte("key3"), []byte("3"))) 346 require.NoError(batch.Write()) 347 348 // despite having no key/values in it, committing this proof should delete key1-key3. 349 require.NoError(db.CommitRangeProof(context.Background(), maybe.Nothing[[]byte](), maybe.Some([]byte("key3")), proof)) 350 351 afterCommitRoot, err := db.GetMerkleRoot(context.Background()) 352 require.NoError(err) 353 354 require.Equal(startRoot, afterCommitRoot) 355 } 356 357 func Test_MerkleDB_CommitRangeProof_EmptyTrie(t *testing.T) { 358 require := require.New(t) 359 360 // Populate [db1] with 3 key-value pairs. 361 db1, err := getBasicDB() 362 require.NoError(err) 363 batch := db1.NewBatch() 364 require.NoError(batch.Put([]byte("key1"), []byte("1"))) 365 require.NoError(batch.Put([]byte("key2"), []byte("2"))) 366 require.NoError(batch.Put([]byte("key3"), []byte("3"))) 367 require.NoError(batch.Write()) 368 369 // Get a proof for the range [key1, key3]. 370 proof, err := db1.GetRangeProof( 371 context.Background(), 372 maybe.Some([]byte("key1")), 373 maybe.Some([]byte("key3")), 374 10, 375 ) 376 require.NoError(err) 377 378 // Commit the proof to a fresh database. 379 db2, err := getBasicDB() 380 require.NoError(err) 381 382 require.NoError(db2.CommitRangeProof(context.Background(), maybe.Some([]byte("key1")), maybe.Some([]byte("key3")), proof)) 383 384 // [db2] should have the same key-value pairs as [db1]. 385 db2Root, err := db2.GetMerkleRoot(context.Background()) 386 require.NoError(err) 387 388 db1Root, err := db1.GetMerkleRoot(context.Background()) 389 require.NoError(err) 390 391 require.Equal(db1Root, db2Root) 392 } 393 394 func Test_MerkleDB_CommitRangeProof_TrieWithInitialValues(t *testing.T) { 395 require := require.New(t) 396 397 // Populate [db1] with 3 key-value pairs. 398 db1, err := getBasicDB() 399 require.NoError(err) 400 batch := db1.NewBatch() 401 require.NoError(batch.Put([]byte("key1"), []byte("1"))) 402 require.NoError(batch.Put([]byte("key2"), []byte("2"))) 403 require.NoError(batch.Put([]byte("key3"), []byte("3"))) 404 require.NoError(batch.Write()) 405 406 // Get a proof for the range [key1, key3]. 407 proof, err := db1.GetRangeProof( 408 context.Background(), 409 maybe.Some([]byte("key1")), 410 maybe.Some([]byte("key3")), 411 10, 412 ) 413 require.NoError(err) 414 415 // Populate [db2] with key-value pairs where some of the keys 416 // have different values than in [db1]. 417 db2, err := getBasicDB() 418 require.NoError(err) 419 batch = db2.NewBatch() 420 require.NoError(batch.Put([]byte("key1"), []byte("3"))) 421 require.NoError(batch.Put([]byte("key2"), []byte("4"))) 422 require.NoError(batch.Put([]byte("key3"), []byte("5"))) 423 require.NoError(batch.Put([]byte("key25"), []byte("5"))) 424 require.NoError(batch.Write()) 425 426 // Commit the proof from [db1] to [db2] 427 require.NoError(db2.CommitRangeProof( 428 context.Background(), 429 maybe.Some([]byte("key1")), 430 maybe.Some([]byte("key3")), 431 proof, 432 )) 433 434 // [db2] should have the same key-value pairs as [db1]. 435 // Note that "key25" was in the range covered by the proof, 436 // so it's deleted from [db2]. 437 db2Root, err := db2.GetMerkleRoot(context.Background()) 438 require.NoError(err) 439 440 db1Root, err := db1.GetMerkleRoot(context.Background()) 441 require.NoError(err) 442 443 require.Equal(db1Root, db2Root) 444 } 445 446 func Test_MerkleDB_GetValues(t *testing.T) { 447 require := require.New(t) 448 449 db, err := getBasicDB() 450 require.NoError(err) 451 452 writeBasicBatch(t, db) 453 keys := [][]byte{{0}, {1}, {2}, {10}} 454 values, errors := db.GetValues(context.Background(), keys) 455 require.Len(values, len(keys)) 456 require.Len(errors, len(keys)) 457 458 // first 3 have values 459 // last was not found 460 require.NoError(errors[0]) 461 require.NoError(errors[1]) 462 require.NoError(errors[2]) 463 require.ErrorIs(errors[3], database.ErrNotFound) 464 465 require.Equal([]byte{0}, values[0]) 466 require.Equal([]byte{1}, values[1]) 467 require.Equal([]byte{2}, values[2]) 468 require.Nil(values[3]) 469 } 470 471 func Test_MerkleDB_InsertNil(t *testing.T) { 472 require := require.New(t) 473 474 db, err := getBasicDB() 475 require.NoError(err) 476 477 batch := db.NewBatch() 478 key := []byte("key0") 479 require.NoError(batch.Put(key, nil)) 480 require.NoError(batch.Write()) 481 482 value, err := db.Get(key) 483 require.NoError(err) 484 require.Empty(value) 485 486 value, err = getNodeValue(db, string(key)) 487 require.NoError(err) 488 require.Empty(value) 489 } 490 491 func Test_MerkleDB_HealthCheck(t *testing.T) { 492 require := require.New(t) 493 494 db, err := getBasicDB() 495 require.NoError(err) 496 497 val, err := db.HealthCheck(context.Background()) 498 require.NoError(err) 499 require.Nil(val) 500 } 501 502 // Test that untracked views aren't tracked in [db.childViews]. 503 func TestDatabaseNewUntrackedView(t *testing.T) { 504 require := require.New(t) 505 506 db, err := getBasicDB() 507 require.NoError(err) 508 509 // Create a new untracked view. 510 view, err := newView( 511 db, 512 db, 513 ViewChanges{ 514 BatchOps: []database.BatchOp{ 515 {Key: []byte{1}, Value: []byte{1}}, 516 }, 517 }, 518 ) 519 require.NoError(err) 520 require.Empty(db.childViews) 521 522 // Commit the view 523 require.NoError(view.CommitToDB(context.Background())) 524 525 // The untracked view should not be tracked by the parent database. 526 require.Empty(db.childViews) 527 } 528 529 // Test that tracked views are persisted to [db.childViews]. 530 func TestDatabaseNewViewFromBatchOpsTracked(t *testing.T) { 531 require := require.New(t) 532 533 db, err := getBasicDB() 534 require.NoError(err) 535 536 // Create a new tracked view. 537 view, err := db.NewView( 538 context.Background(), 539 ViewChanges{ 540 BatchOps: []database.BatchOp{ 541 {Key: []byte{1}, Value: []byte{1}}, 542 }, 543 }, 544 ) 545 require.NoError(err) 546 require.Len(db.childViews, 1) 547 548 // Commit the view 549 require.NoError(view.CommitToDB(context.Background())) 550 551 // The view should be tracked by the parent database. 552 require.Contains(db.childViews, view) 553 require.Len(db.childViews, 1) 554 } 555 556 func TestDatabaseCommitChanges(t *testing.T) { 557 require := require.New(t) 558 559 db, err := getBasicDB() 560 require.NoError(err) 561 dbRoot := db.getMerkleRoot() 562 563 // Committing a nil view should be a no-op. 564 require.NoError(db.CommitToDB(context.Background())) 565 require.Equal(dbRoot, db.getMerkleRoot()) // Root didn't change 566 567 // Committing an invalid view should fail. 568 invalidView, err := db.NewView(context.Background(), ViewChanges{}) 569 require.NoError(err) 570 invalidView.(*view).invalidate() 571 err = invalidView.CommitToDB(context.Background()) 572 require.ErrorIs(err, ErrInvalid) 573 574 // Add key-value pairs to the database 575 key1, key2, key3 := []byte{1}, []byte{2}, []byte{3} 576 value1, value2, value3 := []byte{1}, []byte{2}, []byte{3} 577 require.NoError(db.Put(key1, value1)) 578 require.NoError(db.Put(key2, value2)) 579 580 // Make a view and insert/delete a key-value pair. 581 view1Intf, err := db.NewView( 582 context.Background(), 583 ViewChanges{ 584 BatchOps: []database.BatchOp{ 585 {Key: key3, Value: value3}, // New k-v pair 586 {Key: key1, Delete: true}, // Delete k-v pair 587 }, 588 }, 589 ) 590 require.NoError(err) 591 require.IsType(&view{}, view1Intf) 592 view1 := view1Intf.(*view) 593 view1Root, err := view1.GetMerkleRoot(context.Background()) 594 require.NoError(err) 595 596 // Make a second view 597 view2Intf, err := db.NewView(context.Background(), ViewChanges{}) 598 require.NoError(err) 599 require.IsType(&view{}, view2Intf) 600 view2 := view2Intf.(*view) 601 602 // Make a view atop a view 603 view3Intf, err := view1.NewView(context.Background(), ViewChanges{}) 604 require.NoError(err) 605 require.IsType(&view{}, view3Intf) 606 view3 := view3Intf.(*view) 607 608 // view3 609 // | 610 // view1 view2 611 // \ / 612 // db 613 614 // Commit view1 615 require.NoError(view1.commitToDB(context.Background())) 616 617 // Make sure the key-value pairs are correct. 618 _, err = db.Get(key1) 619 require.ErrorIs(err, database.ErrNotFound) 620 gotValue, err := db.Get(key2) 621 require.NoError(err) 622 require.Equal(value2, gotValue) 623 gotValue, err = db.Get(key3) 624 require.NoError(err) 625 require.Equal(value3, gotValue) 626 627 // Make sure the root is right 628 require.Equal(view1Root, db.getMerkleRoot()) 629 630 // Make sure view2 is invalid and view1 and view3 is valid. 631 require.False(view1.invalidated) 632 require.True(view2.invalidated) 633 require.False(view3.invalidated) 634 635 // Make sure view2 isn't tracked by the database. 636 require.NotContains(db.childViews, view2) 637 638 // Make sure view1 and view3 is tracked by the database. 639 require.Contains(db.childViews, view1) 640 require.Contains(db.childViews, view3) 641 642 // Make sure view3 is now a child of db. 643 require.Equal(db, view3.parentTrie) 644 } 645 646 func TestDatabaseInvalidateChildrenExcept(t *testing.T) { 647 require := require.New(t) 648 649 db, err := getBasicDB() 650 require.NoError(err) 651 652 // Create children 653 view1Intf, err := db.NewView(context.Background(), ViewChanges{}) 654 require.NoError(err) 655 require.IsType(&view{}, view1Intf) 656 view1 := view1Intf.(*view) 657 658 view2Intf, err := db.NewView(context.Background(), ViewChanges{}) 659 require.NoError(err) 660 require.IsType(&view{}, view2Intf) 661 view2 := view2Intf.(*view) 662 663 view3Intf, err := db.NewView(context.Background(), ViewChanges{}) 664 require.NoError(err) 665 require.IsType(&view{}, view3Intf) 666 view3 := view3Intf.(*view) 667 668 db.invalidateChildrenExcept(view1) 669 670 // Make sure view1 is valid and view2 and view3 are invalid. 671 require.False(view1.invalidated) 672 require.True(view2.invalidated) 673 require.True(view3.invalidated) 674 require.Contains(db.childViews, view1) 675 require.Len(db.childViews, 1) 676 677 db.invalidateChildrenExcept(nil) 678 679 // Make sure all views are invalid. 680 require.True(view1.invalidated) 681 require.True(view2.invalidated) 682 require.True(view3.invalidated) 683 require.Empty(db.childViews) 684 685 // Calling with an untracked view doesn't add the untracked view 686 db.invalidateChildrenExcept(view1) 687 require.Empty(db.childViews) 688 } 689 690 func Test_MerkleDB_Random_Insert_Ordering(t *testing.T) { 691 require := require.New(t) 692 693 var ( 694 numRuns = 3 695 numShuffles = 3 696 numKeyValues = 1_000 697 prefixProbability = .1 698 nilValueProbability = 0.05 699 keys [][]byte 700 keysSet set.Set[string] 701 ) 702 703 // Returns a random key. 704 // With probability approximately [prefixProbability], the returned key 705 // will be a prefix of a previously returned key. 706 genKey := func(r *rand.Rand) []byte { 707 for { 708 var key []byte 709 shouldPrefix := r.Float64() < prefixProbability 710 if len(keys) > 2 && shouldPrefix { 711 // Return a key that is a prefix of a previously returned key. 712 prefix := keys[r.Intn(len(keys))] 713 key = make([]byte, r.Intn(50)+len(prefix)) 714 copy(key, prefix) 715 _, _ = r.Read(key[len(prefix):]) 716 } else { 717 key = make([]byte, r.Intn(50)) 718 _, _ = r.Read(key) 719 } 720 721 // If the key has already been returned, try again. 722 // This test would flake if we allowed duplicate keys 723 // because then the order of insertion matters. 724 if !keysSet.Contains(string(key)) { 725 keysSet.Add(string(key)) 726 keys = append(keys, key) 727 return key 728 } 729 } 730 } 731 732 for i := 0; i < numRuns; i++ { 733 now := time.Now().UnixNano() 734 t.Logf("seed for iter %d: %d", i, now) 735 r := rand.New(rand.NewSource(now)) // #nosec G404 736 737 // Insert key-value pairs into a database. 738 ops := make([]database.BatchOp, 0, numKeyValues) 739 keys = [][]byte{} 740 for x := 0; x < numKeyValues; x++ { 741 key := genKey(r) 742 value := make([]byte, r.Intn(51)) 743 if r.Float64() < nilValueProbability { 744 value = nil 745 } else { 746 _, _ = r.Read(value) 747 } 748 ops = append(ops, database.BatchOp{ 749 Key: key, 750 Value: value, 751 }) 752 } 753 754 db, err := getBasicDB() 755 require.NoError(err) 756 757 view1, err := db.NewView(context.Background(), ViewChanges{BatchOps: ops}) 758 require.NoError(err) 759 760 // Get the root of the trie after applying [ops]. 761 view1Root, err := view1.GetMerkleRoot(context.Background()) 762 require.NoError(err) 763 764 // Assert that the same operations applied in a different order 765 // result in the same root. Note this is only true because 766 // all keys inserted are unique. 767 for shuffleIndex := 0; shuffleIndex < numShuffles; shuffleIndex++ { 768 r.Shuffle(numKeyValues, func(i, j int) { 769 ops[i], ops[j] = ops[j], ops[i] 770 }) 771 772 view2, err := db.NewView(context.Background(), ViewChanges{BatchOps: ops}) 773 require.NoError(err) 774 775 view2Root, err := view2.GetMerkleRoot(context.Background()) 776 require.NoError(err) 777 778 require.Equal(view1Root, view2Root) 779 } 780 } 781 } 782 783 func TestMerkleDBClear(t *testing.T) { 784 require := require.New(t) 785 786 // Make a database and insert some key-value pairs. 787 db, err := getBasicDB() 788 require.NoError(err) 789 790 emptyRootID := db.getMerkleRoot() 791 792 now := time.Now().UnixNano() 793 t.Logf("seed: %d", now) 794 r := rand.New(rand.NewSource(now)) // #nosec G404 795 796 insertRandomKeyValues( 797 require, 798 r, 799 []database.Database{db}, 800 1_000, 801 0.25, 802 ) 803 804 // Clear the database. 805 require.NoError(db.Clear()) 806 807 // Assert that the database is empty. 808 iter := db.NewIterator() 809 defer iter.Release() 810 require.False(iter.Next()) 811 require.Equal(ids.Empty, db.getMerkleRoot()) 812 require.True(db.root.IsNothing()) 813 814 // Assert caches are empty. 815 require.Zero(db.valueNodeDB.nodeCache.Len()) 816 require.Zero(db.intermediateNodeDB.writeBuffer.currentSize) 817 818 // Assert history has only the clearing change. 819 require.Len(db.history.lastChanges, 1) 820 change, ok := db.history.lastChanges[emptyRootID] 821 require.True(ok) 822 require.Empty(change.nodes) 823 require.Empty(change.values) 824 } 825 826 func FuzzMerkleDBEmptyRandomizedActions(f *testing.F) { 827 f.Fuzz( 828 func( 829 t *testing.T, 830 randSeed int64, 831 size uint, 832 ) { 833 if size == 0 { 834 t.SkipNow() 835 } 836 require := require.New(t) 837 r := rand.New(rand.NewSource(randSeed)) // #nosec G404 838 for _, ts := range validTokenSizes { 839 runRandDBTest( 840 require, 841 r, 842 generateRandTest( 843 require, 844 r, 845 size, 846 0.01, /*checkHashProbability*/ 847 ), 848 ts, 849 ) 850 } 851 }) 852 } 853 854 func FuzzMerkleDBInitialValuesRandomizedActions(f *testing.F) { 855 f.Fuzz(func( 856 t *testing.T, 857 initialValues uint, 858 numSteps uint, 859 randSeed int64, 860 ) { 861 if numSteps == 0 { 862 t.SkipNow() 863 } 864 require := require.New(t) 865 r := rand.New(rand.NewSource(randSeed)) // #nosec G404 866 for _, ts := range validTokenSizes { 867 runRandDBTest( 868 require, 869 r, 870 generateInitialValues( 871 require, 872 r, 873 initialValues, 874 numSteps, 875 0.001, /*checkHashProbability*/ 876 ), 877 ts, 878 ) 879 } 880 }) 881 } 882 883 // randTest performs random trie operations. 884 // Instances of this test are created by Generate. 885 type randTest []randTestStep 886 887 type randTestStep struct { 888 op int 889 key []byte // for opUpdate, opDelete, opGet 890 value []byte // for opUpdate 891 } 892 893 const ( 894 opUpdate = iota 895 opDelete 896 opGet 897 opWriteBatch 898 opGenerateRangeProof 899 opGenerateChangeProof 900 opCheckhash 901 opMax // boundary value, not an actual op 902 ) 903 904 func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest, tokenSize int) { 905 db, err := getBasicDBWithBranchFactor(tokenSizeToBranchFactor[tokenSize]) 906 require.NoError(err) 907 908 const ( 909 maxProofLen = 100 910 maxPastRoots = defaultHistoryLength 911 ) 912 913 var ( 914 values = make(map[Key][]byte) // tracks content of the trie 915 currentBatch = db.NewBatch() 916 uncommittedKeyValues = make(map[Key][]byte) 917 uncommittedDeletes = set.Set[Key]{} 918 pastRoots = []ids.ID{} 919 ) 920 921 startRoot, err := db.GetMerkleRoot(context.Background()) 922 require.NoError(err) 923 924 for i, step := range rt { 925 require.LessOrEqual(i, len(rt)) 926 switch step.op { 927 case opUpdate: 928 require.NoError(currentBatch.Put(step.key, step.value)) 929 930 uncommittedKeyValues[ToKey(step.key)] = step.value 931 uncommittedDeletes.Remove(ToKey(step.key)) 932 case opDelete: 933 require.NoError(currentBatch.Delete(step.key)) 934 935 uncommittedDeletes.Add(ToKey(step.key)) 936 delete(uncommittedKeyValues, ToKey(step.key)) 937 case opGenerateRangeProof: 938 root, err := db.GetMerkleRoot(context.Background()) 939 require.NoError(err) 940 941 if len(pastRoots) > 0 { 942 root = pastRoots[r.Intn(len(pastRoots))] 943 } 944 945 start := maybe.Nothing[[]byte]() 946 if len(step.key) > 0 { 947 start = maybe.Some(step.key) 948 } 949 end := maybe.Nothing[[]byte]() 950 if len(step.value) > 0 { 951 end = maybe.Some(step.value) 952 } 953 954 rangeProof, err := db.GetRangeProofAtRoot(context.Background(), root, start, end, maxProofLen) 955 if root == ids.Empty { 956 require.ErrorIs(err, ErrEmptyProof) 957 continue 958 } 959 require.NoError(err) 960 require.LessOrEqual(len(rangeProof.KeyValues), maxProofLen) 961 962 require.NoError(rangeProof.Verify( 963 context.Background(), 964 start, 965 end, 966 root, 967 tokenSize, 968 db.hasher, 969 )) 970 case opGenerateChangeProof: 971 root, err := db.GetMerkleRoot(context.Background()) 972 require.NoError(err) 973 974 if len(pastRoots) > 1 { 975 root = pastRoots[r.Intn(len(pastRoots))] 976 } 977 978 start := maybe.Nothing[[]byte]() 979 if len(step.key) > 0 { 980 start = maybe.Some(step.key) 981 } 982 983 end := maybe.Nothing[[]byte]() 984 if len(step.value) > 0 { 985 end = maybe.Some(step.value) 986 } 987 988 changeProof, err := db.GetChangeProof(context.Background(), startRoot, root, start, end, maxProofLen) 989 if startRoot == root { 990 require.ErrorIs(err, errSameRoot) 991 continue 992 } 993 if root == ids.Empty { 994 require.ErrorIs(err, ErrEmptyProof) 995 continue 996 } 997 require.NoError(err) 998 require.LessOrEqual(len(changeProof.KeyChanges), maxProofLen) 999 1000 changeProofDB, err := getBasicDBWithBranchFactor(tokenSizeToBranchFactor[tokenSize]) 1001 require.NoError(err) 1002 1003 require.NoError(changeProofDB.VerifyChangeProof( 1004 context.Background(), 1005 changeProof, 1006 start, 1007 end, 1008 root, 1009 )) 1010 case opWriteBatch: 1011 oldRoot, err := db.GetMerkleRoot(context.Background()) 1012 require.NoError(err) 1013 1014 require.NoError(currentBatch.Write()) 1015 currentBatch.Reset() 1016 1017 if len(uncommittedKeyValues) == 0 && len(uncommittedDeletes) == 0 { 1018 continue 1019 } 1020 1021 for key, value := range uncommittedKeyValues { 1022 values[key] = value 1023 } 1024 clear(uncommittedKeyValues) 1025 1026 for key := range uncommittedDeletes { 1027 delete(values, key) 1028 } 1029 uncommittedDeletes.Clear() 1030 1031 newRoot, err := db.GetMerkleRoot(context.Background()) 1032 require.NoError(err) 1033 1034 if oldRoot != newRoot { 1035 pastRoots = append(pastRoots, newRoot) 1036 if len(pastRoots) > maxPastRoots { 1037 pastRoots = pastRoots[len(pastRoots)-maxPastRoots:] 1038 } 1039 } 1040 1041 case opGet: 1042 v, err := db.Get(step.key) 1043 if err != nil { 1044 require.ErrorIs(err, database.ErrNotFound) 1045 } 1046 1047 want := values[ToKey(step.key)] 1048 require.True(bytes.Equal(want, v)) // Use bytes.Equal so nil treated equal to []byte{} 1049 1050 trieValue, err := getNodeValue(db, string(step.key)) 1051 if err != nil { 1052 require.ErrorIs(err, database.ErrNotFound) 1053 } 1054 1055 require.True(bytes.Equal(want, trieValue)) // Use bytes.Equal so nil treated equal to []byte{} 1056 case opCheckhash: 1057 // Create a view with the same key-values as [db] 1058 newDB, err := getBasicDBWithBranchFactor(tokenSizeToBranchFactor[tokenSize]) 1059 require.NoError(err) 1060 1061 ops := make([]database.BatchOp, 0, len(values)) 1062 for key, value := range values { 1063 ops = append(ops, database.BatchOp{ 1064 Key: key.Bytes(), 1065 Value: value, 1066 }) 1067 } 1068 1069 view, err := newDB.NewView(context.Background(), ViewChanges{BatchOps: ops}) 1070 require.NoError(err) 1071 1072 // Check that the root of the view is the same as the root of [db] 1073 newRoot, err := view.GetMerkleRoot(context.Background()) 1074 require.NoError(err) 1075 1076 dbRoot, err := db.GetMerkleRoot(context.Background()) 1077 require.NoError(err) 1078 require.Equal(dbRoot, newRoot) 1079 default: 1080 require.FailNow("unknown op") 1081 } 1082 } 1083 } 1084 1085 func generateRandTestWithKeys( 1086 require *require.Assertions, 1087 r *rand.Rand, 1088 allKeys [][]byte, 1089 size uint, 1090 checkHashProbability float64, 1091 ) randTest { 1092 const nilEndProbability = 0.1 1093 1094 genKey := func() []byte { 1095 if len(allKeys) < 2 || r.Intn(100) < 10 { 1096 // new key 1097 key := make([]byte, r.Intn(50)) 1098 _, err := r.Read(key) 1099 require.NoError(err) 1100 allKeys = append(allKeys, key) 1101 return key 1102 } 1103 if len(allKeys) > 2 && r.Intn(100) < 10 { 1104 // new prefixed key 1105 prefix := allKeys[r.Intn(len(allKeys))] 1106 key := make([]byte, r.Intn(50)+len(prefix)) 1107 copy(key, prefix) 1108 _, err := r.Read(key[len(prefix):]) 1109 require.NoError(err) 1110 allKeys = append(allKeys, key) 1111 return key 1112 } 1113 // use existing key 1114 return allKeys[r.Intn(len(allKeys))] 1115 } 1116 1117 genEnd := func(key []byte) []byte { 1118 // got is defined because if a rand method is used 1119 // in an if statement, the nosec directive doesn't work. 1120 got := r.Float64() // #nosec G404 1121 if got < nilEndProbability { 1122 return nil 1123 } 1124 1125 endKey := make([]byte, len(key)) 1126 copy(endKey, key) 1127 for i := 0; i < len(endKey); i += 2 { 1128 n := r.Intn(len(endKey)) 1129 if endKey[n] < 250 { 1130 endKey[n] += byte(r.Intn(int(255 - endKey[n]))) 1131 } 1132 } 1133 return endKey 1134 } 1135 1136 var steps randTest 1137 for i := uint(0); i < size-1; { 1138 step := randTestStep{op: r.Intn(opMax)} 1139 switch step.op { 1140 case opUpdate: 1141 step.key = genKey() 1142 step.value = make([]byte, r.Intn(50)) 1143 if len(step.value) == 51 { 1144 step.value = nil 1145 } else { 1146 _, err := r.Read(step.value) 1147 require.NoError(err) 1148 } 1149 case opGet, opDelete: 1150 step.key = genKey() 1151 case opGenerateRangeProof, opGenerateChangeProof: 1152 step.key = genKey() 1153 step.value = genEnd(step.key) 1154 case opCheckhash: 1155 // this gets really expensive so control how often it happens 1156 if r.Float64() > checkHashProbability { 1157 continue 1158 } 1159 } 1160 steps = append(steps, step) 1161 i++ 1162 } 1163 // always end with a full hash of the trie 1164 steps = append(steps, randTestStep{op: opCheckhash}) 1165 return steps 1166 } 1167 1168 func generateInitialValues( 1169 require *require.Assertions, 1170 r *rand.Rand, 1171 numInitialKeyValues uint, 1172 size uint, 1173 percentChanceToFullHash float64, 1174 ) randTest { 1175 const ( 1176 prefixProbability = 0.1 1177 nilValueProbability = 0.05 1178 ) 1179 1180 var allKeys [][]byte 1181 genKey := func() []byte { 1182 // new prefixed key 1183 if len(allKeys) > 2 && r.Float64() < prefixProbability { 1184 prefix := allKeys[r.Intn(len(allKeys))] 1185 key := make([]byte, r.Intn(50)+len(prefix)) 1186 copy(key, prefix) 1187 _, _ = r.Read(key[len(prefix):]) 1188 allKeys = append(allKeys, key) 1189 return key 1190 } 1191 1192 // new key 1193 key := make([]byte, r.Intn(50)) 1194 _, _ = r.Read(key) 1195 allKeys = append(allKeys, key) 1196 return key 1197 } 1198 1199 var steps randTest 1200 for i := uint(0); i < numInitialKeyValues; i++ { 1201 step := randTestStep{ 1202 op: opUpdate, 1203 key: genKey(), 1204 value: make([]byte, r.Intn(50)), 1205 } 1206 // got is defined because if a rand method is used 1207 // in an if statement, the nosec directive doesn't work. 1208 got := r.Float64() // #nosec G404 1209 if got < nilValueProbability { 1210 step.value = nil 1211 } else { 1212 _, _ = r.Read(step.value) 1213 } 1214 steps = append(steps, step) 1215 } 1216 steps = append(steps, randTestStep{op: opWriteBatch}) 1217 steps = append(steps, generateRandTestWithKeys(require, r, allKeys, size, percentChanceToFullHash)...) 1218 return steps 1219 } 1220 1221 func generateRandTest(require *require.Assertions, r *rand.Rand, size uint, percentChanceToFullHash float64) randTest { 1222 return generateRandTestWithKeys(require, r, [][]byte{}, size, percentChanceToFullHash) 1223 } 1224 1225 // Inserts [n] random key/value pairs into each database. 1226 // Deletes [deletePortion] of the key/value pairs after insertion. 1227 func insertRandomKeyValues( 1228 require *require.Assertions, 1229 rand *rand.Rand, 1230 dbs []database.Database, 1231 numKeyValues uint, 1232 deletePortion float64, 1233 ) { 1234 maxKeyLen := units.KiB 1235 maxValLen := 4 * units.KiB 1236 1237 require.GreaterOrEqual(deletePortion, float64(0)) 1238 require.LessOrEqual(deletePortion, float64(1)) 1239 for i := uint(0); i < numKeyValues; i++ { 1240 keyLen := rand.Intn(maxKeyLen) 1241 key := make([]byte, keyLen) 1242 _, _ = rand.Read(key) 1243 1244 valueLen := rand.Intn(maxValLen) 1245 value := make([]byte, valueLen) 1246 _, _ = rand.Read(value) 1247 for _, db := range dbs { 1248 require.NoError(db.Put(key, value)) 1249 } 1250 1251 if rand.Float64() < deletePortion { 1252 for _, db := range dbs { 1253 require.NoError(db.Delete(key)) 1254 } 1255 } 1256 } 1257 } 1258 1259 func TestGetRangeProofAtRootEmptyRootID(t *testing.T) { 1260 require := require.New(t) 1261 1262 db, err := getBasicDB() 1263 require.NoError(err) 1264 1265 _, err = db.GetRangeProofAtRoot( 1266 context.Background(), 1267 ids.Empty, 1268 maybe.Nothing[[]byte](), 1269 maybe.Nothing[[]byte](), 1270 10, 1271 ) 1272 require.ErrorIs(err, ErrEmptyProof) 1273 } 1274 1275 func TestGetChangeProofEmptyRootID(t *testing.T) { 1276 require := require.New(t) 1277 1278 db, err := getBasicDB() 1279 require.NoError(err) 1280 1281 require.NoError(db.Put([]byte("key"), []byte("value"))) 1282 1283 rootID := db.getMerkleRoot() 1284 1285 _, err = db.GetChangeProof( 1286 context.Background(), 1287 rootID, 1288 ids.Empty, 1289 maybe.Nothing[[]byte](), 1290 maybe.Nothing[[]byte](), 1291 10, 1292 ) 1293 require.ErrorIs(err, ErrEmptyProof) 1294 } 1295 1296 func TestCrashRecovery(t *testing.T) { 1297 require := require.New(t) 1298 1299 baseDB := memdb.New() 1300 merkleDB, err := newDatabase( 1301 context.Background(), 1302 baseDB, 1303 newDefaultConfig(), 1304 &mockMetrics{}, 1305 ) 1306 require.NoError(err) 1307 1308 merkleDBBatch := merkleDB.NewBatch() 1309 require.NoError(merkleDBBatch.Put([]byte("is this"), []byte("hope"))) 1310 require.NoError(merkleDBBatch.Put([]byte("expected?"), []byte("so"))) 1311 require.NoError(merkleDBBatch.Write()) 1312 1313 expectedRoot, err := merkleDB.GetMerkleRoot(context.Background()) 1314 require.NoError(err) 1315 1316 // Do not `.Close()` the database to simulate a process crash. 1317 1318 newMerkleDB, err := newDatabase( 1319 context.Background(), 1320 baseDB, 1321 newDefaultConfig(), 1322 &mockMetrics{}, 1323 ) 1324 require.NoError(err) 1325 1326 value, err := newMerkleDB.Get([]byte("is this")) 1327 require.NoError(err) 1328 require.Equal([]byte("hope"), value) 1329 1330 value, err = newMerkleDB.Get([]byte("expected?")) 1331 require.NoError(err) 1332 require.Equal([]byte("so"), value) 1333 1334 rootAfterRecovery, err := newMerkleDB.GetMerkleRoot(context.Background()) 1335 require.NoError(err) 1336 require.Equal(expectedRoot, rootAfterRecovery) 1337 } 1338 1339 func BenchmarkCommitView(b *testing.B) { 1340 db, err := getBasicDB() 1341 require.NoError(b, err) 1342 1343 ops := make([]database.BatchOp, 1_000) 1344 for i := range ops { 1345 k := binary.AppendUvarint(nil, uint64(i)) 1346 ops[i] = database.BatchOp{ 1347 Key: k, 1348 Value: hashing.ComputeHash256(k), 1349 } 1350 } 1351 1352 ctx := context.Background() 1353 viewIntf, err := db.NewView(ctx, ViewChanges{BatchOps: ops}) 1354 require.NoError(b, err) 1355 1356 view := viewIntf.(*view) 1357 require.NoError(b, view.applyValueChanges(ctx)) 1358 1359 b.Run("apply and commit changes", func(b *testing.B) { 1360 require := require.New(b) 1361 1362 for i := 0; i < b.N; i++ { 1363 db.baseDB = memdb.New() // Keep each iteration independent 1364 1365 valueNodeBatch := db.baseDB.NewBatch() 1366 require.NoError(db.applyChanges(ctx, valueNodeBatch, view.changes)) 1367 require.NoError(db.commitValueChanges(ctx, valueNodeBatch)) 1368 } 1369 }) 1370 } 1371 1372 func BenchmarkIteration(b *testing.B) { 1373 db, err := getBasicDB() 1374 require.NoError(b, err) 1375 1376 ops := make([]database.BatchOp, 1_000) 1377 for i := range ops { 1378 k := binary.AppendUvarint(nil, uint64(i)) 1379 ops[i] = database.BatchOp{ 1380 Key: k, 1381 Value: hashing.ComputeHash256(k), 1382 } 1383 } 1384 1385 ctx := context.Background() 1386 view, err := db.NewView(ctx, ViewChanges{BatchOps: ops}) 1387 require.NoError(b, err) 1388 1389 require.NoError(b, view.CommitToDB(ctx)) 1390 1391 b.Run("create iterator", func(b *testing.B) { 1392 for i := 0; i < b.N; i++ { 1393 it := db.NewIterator() 1394 it.Release() 1395 } 1396 }) 1397 1398 b.Run("iterate", func(b *testing.B) { 1399 for i := 0; i < b.N; i++ { 1400 it := db.NewIterator() 1401 for it.Next() { 1402 } 1403 it.Release() 1404 } 1405 }) 1406 }