github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/trie_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 "context" 8 "math/rand" 9 "strconv" 10 "sync" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 15 "github.com/MetalBlockchain/metalgo/database" 16 "github.com/MetalBlockchain/metalgo/database/memdb" 17 "github.com/MetalBlockchain/metalgo/ids" 18 "github.com/MetalBlockchain/metalgo/utils/hashing" 19 ) 20 21 func getNodeValue(t Trie, key string) ([]byte, error) { 22 path := ToKey([]byte(key)) 23 if asView, ok := t.(*view); ok { 24 if err := asView.applyValueChanges(context.Background()); err != nil { 25 return nil, err 26 } 27 } 28 29 var result *node 30 31 err := visitPathToKey(t, path, func(n *node) error { 32 result = n 33 return nil 34 }) 35 if err != nil { 36 return nil, err 37 } 38 if result == nil || result.key != path { 39 return nil, database.ErrNotFound 40 } 41 42 return result.value.Value(), nil 43 } 44 45 func Test_GetValue_Safety(t *testing.T) { 46 require := require.New(t) 47 48 db, err := getBasicDB() 49 require.NoError(err) 50 51 view, err := db.NewView( 52 context.Background(), 53 ViewChanges{ 54 BatchOps: []database.BatchOp{ 55 {Key: []byte{0}, Value: []byte{0}}, 56 }, 57 }, 58 ) 59 require.NoError(err) 60 61 trieVal, err := view.GetValue(context.Background(), []byte{0}) 62 require.NoError(err) 63 require.Equal([]byte{0}, trieVal) 64 trieVal[0] = 1 65 66 // should still be []byte{0} after edit 67 trieVal, err = view.GetValue(context.Background(), []byte{0}) 68 require.NoError(err) 69 require.Equal([]byte{0}, trieVal) 70 } 71 72 func Test_GetValues_Safety(t *testing.T) { 73 require := require.New(t) 74 75 db, err := getBasicDB() 76 require.NoError(err) 77 78 view, err := db.NewView( 79 context.Background(), 80 ViewChanges{ 81 BatchOps: []database.BatchOp{ 82 {Key: []byte{0}, Value: []byte{0}}, 83 }, 84 }, 85 ) 86 require.NoError(err) 87 88 trieVals, errs := view.GetValues(context.Background(), [][]byte{{0}}) 89 require.Len(errs, 1) 90 require.NoError(errs[0]) 91 require.Equal([]byte{0}, trieVals[0]) 92 trieVals[0][0] = 1 93 require.Equal([]byte{1}, trieVals[0]) 94 95 // should still be []byte{0} after edit 96 trieVals, errs = view.GetValues(context.Background(), [][]byte{{0}}) 97 require.Len(errs, 1) 98 require.NoError(errs[0]) 99 require.Equal([]byte{0}, trieVals[0]) 100 } 101 102 func TestVisitPathToKey(t *testing.T) { 103 require := require.New(t) 104 105 db, err := getBasicDB() 106 require.NoError(err) 107 108 trieIntf, err := db.NewView(context.Background(), ViewChanges{}) 109 require.NoError(err) 110 require.IsType(&view{}, trieIntf) 111 trie := trieIntf.(*view) 112 113 var nodePath []*node 114 require.NoError(visitPathToKey(trie, ToKey(nil), func(n *node) error { 115 nodePath = append(nodePath, n) 116 return nil 117 })) 118 119 require.Empty(nodePath) 120 121 // Insert a key 122 key1 := []byte{0} 123 trieIntf, err = trie.NewView( 124 context.Background(), 125 ViewChanges{ 126 BatchOps: []database.BatchOp{ 127 {Key: key1, Value: []byte("value")}, 128 }, 129 }, 130 ) 131 require.NoError(err) 132 require.IsType(&view{}, trieIntf) 133 trie = trieIntf.(*view) 134 require.NoError(trie.applyValueChanges(context.Background())) 135 136 nodePath = make([]*node, 0, 1) 137 require.NoError(visitPathToKey(trie, ToKey(key1), func(n *node) error { 138 nodePath = append(nodePath, n) 139 return nil 140 })) 141 142 // 1 value 143 require.Len(nodePath, 1) 144 require.Equal(ToKey(key1), nodePath[0].key) 145 146 // Insert another key which is a child of the first 147 key2 := []byte{0, 1} 148 trieIntf, err = trie.NewView( 149 context.Background(), 150 ViewChanges{ 151 BatchOps: []database.BatchOp{ 152 {Key: key2, Value: []byte("value")}, 153 }, 154 }, 155 ) 156 require.NoError(err) 157 require.IsType(&view{}, trieIntf) 158 trie = trieIntf.(*view) 159 require.NoError(trie.applyValueChanges(context.Background())) 160 161 nodePath = make([]*node, 0, 2) 162 require.NoError(visitPathToKey(trie, ToKey(key2), func(n *node) error { 163 nodePath = append(nodePath, n) 164 return nil 165 })) 166 require.Len(nodePath, 2) 167 require.Equal(trie.root.Value(), nodePath[0]) 168 require.Equal(ToKey(key1), nodePath[0].key) 169 require.Equal(ToKey(key2), nodePath[1].key) 170 171 // Trie is: 172 // [0] 173 // | 174 // [0,1] 175 // Insert a key which shares no prefix with the others 176 key3 := []byte{255} 177 trieIntf, err = trie.NewView( 178 context.Background(), 179 ViewChanges{ 180 BatchOps: []database.BatchOp{ 181 {Key: key3, Value: []byte("value")}, 182 }, 183 }, 184 ) 185 require.NoError(err) 186 require.IsType(&view{}, trieIntf) 187 trie = trieIntf.(*view) 188 require.NoError(trie.applyValueChanges(context.Background())) 189 190 // Trie is: 191 // [] 192 // / \ 193 // [0] [255] 194 // | 195 // [0,1] 196 nodePath = make([]*node, 0, 2) 197 require.NoError(visitPathToKey(trie, ToKey(key3), func(n *node) error { 198 nodePath = append(nodePath, n) 199 return nil 200 })) 201 202 require.Len(nodePath, 2) 203 require.Equal(trie.root.Value(), nodePath[0]) 204 require.Zero(trie.root.Value().key.length) 205 require.Equal(ToKey(key3), nodePath[1].key) 206 207 // Other key path not affected 208 nodePath = make([]*node, 0, 3) 209 require.NoError(visitPathToKey(trie, ToKey(key2), func(n *node) error { 210 nodePath = append(nodePath, n) 211 return nil 212 })) 213 require.Len(nodePath, 3) 214 require.Equal(trie.root.Value(), nodePath[0]) 215 require.Equal(ToKey(key1), nodePath[1].key) 216 require.Equal(ToKey(key2), nodePath[2].key) 217 218 // Gets closest node when key doesn't exist 219 key4 := []byte{0, 1, 2} 220 nodePath = make([]*node, 0, 3) 221 require.NoError(visitPathToKey(trie, ToKey(key4), func(n *node) error { 222 nodePath = append(nodePath, n) 223 return nil 224 })) 225 226 require.Len(nodePath, 3) 227 require.Equal(trie.root.Value(), nodePath[0]) 228 require.Equal(ToKey(key1), nodePath[1].key) 229 require.Equal(ToKey(key2), nodePath[2].key) 230 231 // Gets just root when key doesn't exist and no key shares a prefix 232 key5 := []byte{128} 233 nodePath = make([]*node, 0, 1) 234 require.NoError(visitPathToKey(trie, ToKey(key5), func(n *node) error { 235 nodePath = append(nodePath, n) 236 return nil 237 })) 238 require.Len(nodePath, 1) 239 require.Equal(trie.root.Value(), nodePath[0]) 240 } 241 242 func Test_Trie_ViewOnCommitedView(t *testing.T) { 243 require := require.New(t) 244 245 dbTrie, err := getBasicDB() 246 require.NoError(err) 247 require.NotNil(dbTrie) 248 249 committedTrie, err := dbTrie.NewView( 250 context.Background(), 251 ViewChanges{ 252 BatchOps: []database.BatchOp{ 253 {Key: []byte{0}, Value: []byte{0}}, 254 }, 255 }, 256 ) 257 require.NoError(err) 258 259 require.NoError(committedTrie.CommitToDB(context.Background())) 260 261 view, err := committedTrie.NewView( 262 context.Background(), 263 ViewChanges{ 264 BatchOps: []database.BatchOp{ 265 {Key: []byte{1}, Value: []byte{1}}, 266 }, 267 }, 268 ) 269 require.NoError(err) 270 require.NoError(view.CommitToDB(context.Background())) 271 272 val0, err := dbTrie.GetValue(context.Background(), []byte{0}) 273 require.NoError(err) 274 require.Equal([]byte{0}, val0) 275 val1, err := dbTrie.GetValue(context.Background(), []byte{1}) 276 require.NoError(err) 277 require.Equal([]byte{1}, val1) 278 } 279 280 func Test_Trie_WriteToDB(t *testing.T) { 281 require := require.New(t) 282 283 dbTrie, err := getBasicDB() 284 require.NoError(err) 285 require.NotNil(dbTrie) 286 287 trieIntf1, err := dbTrie.NewView(context.Background(), ViewChanges{}) 288 require.NoError(err) 289 trie1 := trieIntf1.(*view) 290 291 // value hasn't been inserted so shouldn't exist 292 value, err := trie1.GetValue(context.Background(), []byte("key")) 293 require.ErrorIs(err, database.ErrNotFound) 294 require.Nil(value) 295 296 trieIntf2, err := trie1.NewView( 297 context.Background(), 298 ViewChanges{ 299 BatchOps: []database.BatchOp{ 300 {Key: []byte("key"), Value: []byte("value")}, 301 }, 302 }, 303 ) 304 require.NoError(err) 305 trie2 := trieIntf2.(*view) 306 307 value, err = getNodeValue(trie2, "key") 308 require.NoError(err) 309 require.Equal([]byte("value"), value) 310 311 require.NoError(trie1.CommitToDB(context.Background())) 312 require.NoError(trie2.CommitToDB(context.Background())) 313 314 key := []byte("key") 315 prefixedKey := make([]byte, len(key)+valueNodePrefixLen) 316 copy(prefixedKey, valueNodePrefix) 317 copy(prefixedKey[valueNodePrefixLen:], key) 318 rawBytes, err := dbTrie.baseDB.Get(prefixedKey) 319 require.NoError(err) 320 321 node, err := parseNode(dbTrie.hasher, ToKey(key), rawBytes) 322 require.NoError(err) 323 require.Equal([]byte("value"), node.value.Value()) 324 } 325 326 func Test_Trie_InsertAndRetrieve(t *testing.T) { 327 require := require.New(t) 328 329 dbTrie, err := getBasicDB() 330 require.NoError(err) 331 require.NotNil(dbTrie) 332 333 // value hasn't been inserted so shouldn't exist 334 value, err := dbTrie.Get([]byte("key")) 335 require.ErrorIs(err, database.ErrNotFound) 336 require.Nil(value) 337 338 require.NoError(dbTrie.Put([]byte("key"), []byte("value"))) 339 340 value, err = getNodeValue(dbTrie, "key") 341 require.NoError(err) 342 require.Equal([]byte("value"), value) 343 } 344 345 func Test_Trie_Overwrite(t *testing.T) { 346 require := require.New(t) 347 348 dbTrie, err := getBasicDB() 349 require.NoError(err) 350 require.NotNil(dbTrie) 351 trie, err := dbTrie.NewView( 352 context.Background(), 353 ViewChanges{ 354 BatchOps: []database.BatchOp{ 355 {Key: []byte("key"), Value: []byte("value0")}, 356 {Key: []byte("key"), Value: []byte("value1")}, 357 }, 358 }, 359 ) 360 require.NoError(err) 361 value, err := getNodeValue(trie, "key") 362 require.NoError(err) 363 require.Equal([]byte("value1"), value) 364 365 trie, err = dbTrie.NewView( 366 context.Background(), 367 ViewChanges{ 368 BatchOps: []database.BatchOp{ 369 {Key: []byte("key"), Value: []byte("value2")}, 370 }, 371 }, 372 ) 373 require.NoError(err) 374 value, err = getNodeValue(trie, "key") 375 require.NoError(err) 376 require.Equal([]byte("value2"), value) 377 } 378 379 func Test_Trie_Delete(t *testing.T) { 380 require := require.New(t) 381 382 dbTrie, err := getBasicDB() 383 require.NoError(err) 384 require.NotNil(dbTrie) 385 386 trie, err := dbTrie.NewView( 387 context.Background(), 388 ViewChanges{ 389 BatchOps: []database.BatchOp{ 390 {Key: []byte("key"), Value: []byte("value0")}, 391 }, 392 }, 393 ) 394 require.NoError(err) 395 396 value, err := getNodeValue(trie, "key") 397 require.NoError(err) 398 require.Equal([]byte("value0"), value) 399 400 trie, err = dbTrie.NewView( 401 context.Background(), 402 ViewChanges{ 403 BatchOps: []database.BatchOp{ 404 {Key: []byte("key"), Delete: true}, 405 }, 406 }, 407 ) 408 require.NoError(err) 409 410 value, err = getNodeValue(trie, "key") 411 require.ErrorIs(err, database.ErrNotFound) 412 require.Nil(value) 413 } 414 415 func Test_Trie_DeleteMissingKey(t *testing.T) { 416 require := require.New(t) 417 418 trie, err := getBasicDB() 419 require.NoError(err) 420 require.NotNil(trie) 421 422 require.NoError(trie.DeleteContext(context.Background(), []byte("key"))) 423 } 424 425 func Test_Trie_ExpandOnKeyPath(t *testing.T) { 426 require := require.New(t) 427 428 dbTrie, err := getBasicDB() 429 require.NoError(err) 430 require.NotNil(dbTrie) 431 trieIntf, err := dbTrie.NewView( 432 context.Background(), 433 ViewChanges{ 434 BatchOps: []database.BatchOp{ 435 {Key: []byte("key"), Value: []byte("value0")}, 436 }, 437 }, 438 ) 439 require.NoError(err) 440 trie := trieIntf.(*view) 441 442 value, err := getNodeValue(trie, "key") 443 require.NoError(err) 444 require.Equal([]byte("value0"), value) 445 446 trieIntf, err = trie.NewView( 447 context.Background(), 448 ViewChanges{ 449 BatchOps: []database.BatchOp{ 450 {Key: []byte("key1"), Value: []byte("value1")}, 451 }, 452 }, 453 ) 454 require.NoError(err) 455 trie = trieIntf.(*view) 456 457 value, err = getNodeValue(trie, "key") 458 require.NoError(err) 459 require.Equal([]byte("value0"), value) 460 461 value, err = getNodeValue(trie, "key1") 462 require.NoError(err) 463 require.Equal([]byte("value1"), value) 464 465 trieIntf, err = trie.NewView( 466 context.Background(), 467 ViewChanges{ 468 BatchOps: []database.BatchOp{ 469 {Key: []byte("key12"), Value: []byte("value12")}, 470 }, 471 }, 472 ) 473 require.NoError(err) 474 trie = trieIntf.(*view) 475 476 value, err = getNodeValue(trie, "key") 477 require.NoError(err) 478 require.Equal([]byte("value0"), value) 479 480 value, err = getNodeValue(trie, "key1") 481 require.NoError(err) 482 require.Equal([]byte("value1"), value) 483 484 value, err = getNodeValue(trie, "key12") 485 require.NoError(err) 486 require.Equal([]byte("value12"), value) 487 } 488 489 func Test_Trie_CompressedKeys(t *testing.T) { 490 require := require.New(t) 491 492 dbTrie, err := getBasicDB() 493 require.NoError(err) 494 require.NotNil(dbTrie) 495 trieIntf, err := dbTrie.NewView( 496 context.Background(), 497 ViewChanges{ 498 BatchOps: []database.BatchOp{ 499 {Key: []byte("key12"), Value: []byte("value12")}, 500 }, 501 }, 502 ) 503 require.NoError(err) 504 trie := trieIntf.(*view) 505 506 value, err := getNodeValue(trie, "key12") 507 require.NoError(err) 508 require.Equal([]byte("value12"), value) 509 510 trieIntf, err = trie.NewView( 511 context.Background(), 512 ViewChanges{ 513 BatchOps: []database.BatchOp{ 514 {Key: []byte("key1"), Value: []byte("value1")}, 515 }, 516 }, 517 ) 518 require.NoError(err) 519 trie = trieIntf.(*view) 520 521 value, err = getNodeValue(trie, "key12") 522 require.NoError(err) 523 require.Equal([]byte("value12"), value) 524 525 value, err = getNodeValue(trie, "key1") 526 require.NoError(err) 527 require.Equal([]byte("value1"), value) 528 529 trieIntf, err = trie.NewView( 530 context.Background(), 531 ViewChanges{ 532 BatchOps: []database.BatchOp{ 533 {Key: []byte("key"), Value: []byte("value")}, 534 }, 535 }, 536 ) 537 require.NoError(err) 538 trie = trieIntf.(*view) 539 540 value, err = getNodeValue(trie, "key12") 541 require.NoError(err) 542 require.Equal([]byte("value12"), value) 543 544 value, err = getNodeValue(trie, "key1") 545 require.NoError(err) 546 require.Equal([]byte("value1"), value) 547 548 value, err = getNodeValue(trie, "key") 549 require.NoError(err) 550 require.Equal([]byte("value"), value) 551 } 552 553 func Test_Trie_SplitBranch(t *testing.T) { 554 require := require.New(t) 555 556 dbTrie, err := getBasicDB() 557 require.NoError(err) 558 require.NotNil(dbTrie) 559 560 // force a new node to generate with common prefix "key1" and have these two nodes as children 561 trie, err := dbTrie.NewView( 562 context.Background(), 563 ViewChanges{ 564 BatchOps: []database.BatchOp{ 565 {Key: []byte("key12"), Value: []byte("value12")}, 566 {Key: []byte("key134"), Value: []byte("value134")}, 567 }, 568 }, 569 ) 570 require.NoError(err) 571 572 value, err := getNodeValue(trie, "key12") 573 require.NoError(err) 574 require.Equal([]byte("value12"), value) 575 576 value, err = getNodeValue(trie, "key134") 577 require.NoError(err) 578 require.Equal([]byte("value134"), value) 579 } 580 581 func Test_Trie_HashCountOnBranch(t *testing.T) { 582 require := require.New(t) 583 584 dbTrie, err := getBasicDB() 585 require.NoError(err) 586 require.NotNil(dbTrie) 587 588 key1, key2, keyPrefix := []byte("12"), []byte("1F"), []byte("1") 589 590 view1, err := dbTrie.NewView( 591 context.Background(), 592 ViewChanges{ 593 BatchOps: []database.BatchOp{ 594 {Key: key1, Value: []byte("")}, 595 }, 596 }) 597 require.NoError(err) 598 599 // trie is: 600 // [1] 601 602 // create new node with common prefix whose children 603 // are key1, key2 604 view2, err := view1.NewView( 605 context.Background(), 606 ViewChanges{ 607 BatchOps: []database.BatchOp{ 608 {Key: key2, Value: []byte("")}, 609 }, 610 }) 611 require.NoError(err) 612 613 // trie is: 614 // [1] 615 // / \ 616 // [12] [1F] 617 618 // clear the hash count to ignore setup 619 dbTrie.metrics.(*mockMetrics).hashCount = 0 620 621 // calculate the root 622 _, err = view2.GetMerkleRoot(context.Background()) 623 require.NoError(err) 624 625 // Make sure the root is an intermediate node with the expected common prefix. 626 // Note it's only created on call to GetMerkleRoot, not in NewView. 627 prefixNode, err := view2.getEditableNode(ToKey(keyPrefix), false) 628 require.NoError(err) 629 root := view2.getRoot().Value() 630 require.Equal(root, prefixNode) 631 require.Len(root.children, 2) 632 633 // Had to hash each of the new nodes ("12" and "1F") and the new root 634 require.Equal(int64(3), dbTrie.metrics.(*mockMetrics).hashCount) 635 } 636 637 func Test_Trie_HashCountOnDelete(t *testing.T) { 638 require := require.New(t) 639 640 dbTrie, err := getBasicDB() 641 require.NoError(err) 642 643 trie, err := dbTrie.NewView( 644 context.Background(), 645 ViewChanges{ 646 BatchOps: []database.BatchOp{ 647 {Key: []byte("k"), Value: []byte("value0")}, 648 {Key: []byte("ke"), Value: []byte("value1")}, 649 {Key: []byte("key"), Value: []byte("value2")}, 650 {Key: []byte("key1"), Value: []byte("value3")}, 651 {Key: []byte("key2"), Value: []byte("value4")}, 652 }, 653 }, 654 ) 655 require.NoError(err) 656 require.NotNil(trie) 657 658 require.NoError(trie.CommitToDB(context.Background())) 659 oldCount := dbTrie.metrics.(*mockMetrics).hashCount 660 661 // delete the middle values 662 view, err := trie.NewView( 663 context.Background(), 664 ViewChanges{ 665 BatchOps: []database.BatchOp{ 666 {Key: []byte("k"), Delete: true}, 667 {Key: []byte("ke"), Delete: true}, 668 {Key: []byte("key"), Delete: true}, 669 }, 670 }, 671 ) 672 require.NoError(err) 673 require.NoError(view.CommitToDB(context.Background())) 674 675 // trie is: 676 // [key0] (first 28 bits) 677 // / \ 678 // [key1] [key2] 679 root := view.getRoot().Value() 680 expectedRootKey := ToKey([]byte("key0")).Take(28) 681 require.Equal(expectedRootKey, root.key) 682 require.Len(root.children, 2) 683 684 // Had to hash the new root but not [key1] or [key2] nodes 685 require.Equal(oldCount+1, dbTrie.metrics.(*mockMetrics).hashCount) 686 } 687 688 func Test_Trie_NoExistingResidual(t *testing.T) { 689 require := require.New(t) 690 691 dbTrie, err := getBasicDB() 692 require.NoError(err) 693 require.NotNil(dbTrie) 694 695 trie, err := dbTrie.NewView( 696 context.Background(), 697 ViewChanges{ 698 BatchOps: []database.BatchOp{ 699 {Key: []byte("k"), Value: []byte("1")}, 700 {Key: []byte("ke"), Value: []byte("2")}, 701 {Key: []byte("key1"), Value: []byte("3")}, 702 {Key: []byte("key123"), Value: []byte("4")}, 703 }, 704 }, 705 ) 706 require.NoError(err) 707 require.NotNil(trie) 708 709 value, err := getNodeValue(trie, "k") 710 require.NoError(err) 711 require.Equal([]byte("1"), value) 712 713 value, err = getNodeValue(trie, "ke") 714 require.NoError(err) 715 require.Equal([]byte("2"), value) 716 717 value, err = getNodeValue(trie, "key1") 718 require.NoError(err) 719 require.Equal([]byte("3"), value) 720 721 value, err = getNodeValue(trie, "key123") 722 require.NoError(err) 723 require.Equal([]byte("4"), value) 724 } 725 726 func Test_Trie_BatchApply(t *testing.T) { 727 require := require.New(t) 728 729 dbTrie, err := getBasicDB() 730 require.NoError(err) 731 require.NotNil(dbTrie) 732 733 trie, err := dbTrie.NewView( 734 context.Background(), 735 ViewChanges{ 736 BatchOps: []database.BatchOp{ 737 {Key: []byte("key1"), Value: []byte("value1")}, 738 {Key: []byte("key12"), Value: []byte("value12")}, 739 {Key: []byte("key134"), Value: []byte("value134")}, 740 {Key: []byte("key1"), Delete: true}, 741 }, 742 }, 743 ) 744 require.NoError(err) 745 require.NotNil(trie) 746 747 value, err := getNodeValue(trie, "key12") 748 require.NoError(err) 749 require.Equal([]byte("value12"), value) 750 751 value, err = getNodeValue(trie, "key134") 752 require.NoError(err) 753 require.Equal([]byte("value134"), value) 754 755 _, err = getNodeValue(trie, "key1") 756 require.ErrorIs(err, database.ErrNotFound) 757 } 758 759 func Test_Trie_ChainDeletion(t *testing.T) { 760 require := require.New(t) 761 762 trie, err := getBasicDB() 763 require.NoError(err) 764 require.NotNil(trie) 765 newTrie, err := trie.NewView( 766 context.Background(), 767 ViewChanges{ 768 BatchOps: []database.BatchOp{ 769 {Key: []byte("k"), Value: []byte("value0")}, 770 {Key: []byte("ke"), Value: []byte("value1")}, 771 {Key: []byte("key"), Value: []byte("value2")}, 772 {Key: []byte("key1"), Value: []byte("value3")}, 773 }, 774 }, 775 ) 776 require.NoError(err) 777 778 require.NoError(newTrie.(*view).applyValueChanges(context.Background())) 779 maybeRoot := newTrie.getRoot() 780 require.NoError(err) 781 require.True(maybeRoot.HasValue()) 782 require.Equal([]byte("value0"), maybeRoot.Value().value.Value()) 783 require.Len(maybeRoot.Value().children, 1) 784 785 newTrie, err = newTrie.NewView( 786 context.Background(), 787 ViewChanges{ 788 BatchOps: []database.BatchOp{ 789 {Key: []byte("k"), Delete: true}, 790 {Key: []byte("ke"), Delete: true}, 791 {Key: []byte("key"), Delete: true}, 792 {Key: []byte("key1"), Delete: true}, 793 }, 794 }, 795 ) 796 require.NoError(err) 797 require.NoError(newTrie.(*view).applyValueChanges(context.Background())) 798 799 // trie should be empty 800 root := newTrie.getRoot() 801 require.False(root.HasValue()) 802 } 803 804 func Test_Trie_Invalidate_Siblings_On_Commit(t *testing.T) { 805 require := require.New(t) 806 807 dbTrie, err := getBasicDB() 808 require.NoError(err) 809 require.NotNil(dbTrie) 810 811 view1, err := dbTrie.NewView(context.Background(), ViewChanges{}) 812 require.NoError(err) 813 814 view2, err := view1.NewView( 815 context.Background(), 816 ViewChanges{ 817 BatchOps: []database.BatchOp{ 818 {Key: []byte{0}, Value: []byte{0}}, 819 }, 820 }, 821 ) 822 require.NoError(err) 823 824 // Siblings of view2 825 sibling1, err := view1.NewView(context.Background(), ViewChanges{}) 826 require.NoError(err) 827 sibling2, err := view1.NewView(context.Background(), ViewChanges{}) 828 require.NoError(err) 829 830 require.False(sibling1.(*view).isInvalid()) 831 require.False(sibling2.(*view).isInvalid()) 832 833 require.NoError(view1.CommitToDB(context.Background())) 834 require.NoError(view2.CommitToDB(context.Background())) 835 836 require.True(sibling1.(*view).isInvalid()) 837 require.True(sibling2.(*view).isInvalid()) 838 require.False(view2.(*view).isInvalid()) 839 } 840 841 func Test_Trie_NodeCollapse(t *testing.T) { 842 require := require.New(t) 843 844 dbTrie, err := getBasicDB() 845 require.NoError(err) 846 require.NotNil(dbTrie) 847 848 kvs := []database.BatchOp{ 849 {Key: []byte("k"), Value: []byte("value0")}, 850 {Key: []byte("ke"), Value: []byte("value1")}, 851 {Key: []byte("key"), Value: []byte("value2")}, 852 {Key: []byte("key1"), Value: []byte("value3")}, 853 {Key: []byte("key2"), Value: []byte("value4")}, 854 } 855 856 trie, err := dbTrie.NewView( 857 context.Background(), 858 ViewChanges{ 859 BatchOps: kvs, 860 }, 861 ) 862 require.NoError(err) 863 864 require.NoError(trie.(*view).applyValueChanges(context.Background())) 865 866 for _, kv := range kvs { 867 node, err := trie.getEditableNode(ToKey(kv.Key), true) 868 require.NoError(err) 869 870 require.Equal(kv.Value, node.value.Value()) 871 } 872 873 // delete some values 874 deletedKVs, remainingKVs := kvs[:3], kvs[3:] 875 deleteOps := make([]database.BatchOp, len(deletedKVs)) 876 for i, kv := range deletedKVs { 877 deleteOps[i] = database.BatchOp{ 878 Key: kv.Key, 879 Delete: true, 880 } 881 } 882 883 trie, err = trie.NewView( 884 context.Background(), 885 ViewChanges{ 886 BatchOps: deleteOps, 887 }, 888 ) 889 require.NoError(err) 890 891 require.NoError(trie.(*view).applyValueChanges(context.Background())) 892 893 for _, kv := range deletedKVs { 894 _, err := trie.getEditableNode(ToKey(kv.Key), true) 895 require.ErrorIs(err, database.ErrNotFound) 896 } 897 898 // make sure the other values are still there 899 for _, kv := range remainingKVs { 900 node, err := trie.getEditableNode(ToKey(kv.Key), true) 901 require.NoError(err) 902 903 require.Equal(kv.Value, node.value.Value()) 904 } 905 } 906 907 func Test_Trie_MultipleStates(t *testing.T) { 908 randCount := int64(0) 909 for _, commitApproach := range []string{"never", "before", "after"} { 910 t.Run(commitApproach, func(t *testing.T) { 911 require := require.New(t) 912 913 r := rand.New(rand.NewSource(randCount)) // #nosec G404 914 randCount++ 915 rdb := memdb.New() 916 defer rdb.Close() 917 db, err := New( 918 context.Background(), 919 rdb, 920 newDefaultConfig(), 921 ) 922 require.NoError(err) 923 defer db.Close() 924 925 initialSet := 1000 926 // Populate initial set of keys 927 ops := make([]database.BatchOp, 0, initialSet) 928 require.NoError(err) 929 kv := [][]byte{} 930 for i := 0; i < initialSet; i++ { 931 k := []byte(strconv.Itoa(i)) 932 kv = append(kv, k) 933 ops = append(ops, database.BatchOp{Key: k, Value: hashing.ComputeHash256(k)}) 934 } 935 root, err := db.NewView( 936 context.Background(), 937 ViewChanges{ 938 BatchOps: ops, 939 }, 940 ) 941 require.NoError(err) 942 943 // Get initial root 944 _, err = root.GetMerkleRoot(context.Background()) 945 require.NoError(err) 946 947 if commitApproach == "before" { 948 require.NoError(root.CommitToDB(context.Background())) 949 } 950 951 // Populate additional states 952 concurrentStates := []Trie{} 953 for i := 0; i < 5; i++ { 954 newState, err := root.NewView(context.Background(), ViewChanges{}) 955 require.NoError(err) 956 concurrentStates = append(concurrentStates, newState) 957 } 958 959 if commitApproach == "after" { 960 require.NoError(root.CommitToDB(context.Background())) 961 } 962 963 // Process ops 964 newStart := initialSet 965 concurrentOps := make([][]database.BatchOp, len(concurrentStates)) 966 for i := 0; i < 100; i++ { 967 if r.Intn(100) < 20 { 968 // New Key 969 for index := range concurrentStates { 970 k := []byte(strconv.Itoa(newStart)) 971 concurrentOps[index] = append(concurrentOps[index], database.BatchOp{Key: k, Value: hashing.ComputeHash256(k)}) 972 } 973 newStart++ 974 } else { 975 // Fetch and update old 976 selectedKey := kv[r.Intn(len(kv))] 977 var pastV []byte 978 for index, state := range concurrentStates { 979 v, err := state.GetValue(context.Background(), selectedKey) 980 require.NoError(err) 981 if pastV == nil { 982 pastV = v 983 } else { 984 require.Equal(pastV, v) 985 } 986 concurrentOps[index] = append(concurrentOps[index], database.BatchOp{Key: selectedKey, Value: hashing.ComputeHash256(v)}) 987 } 988 } 989 } 990 for index, state := range concurrentStates { 991 concurrentStates[index], err = state.NewView( 992 context.Background(), 993 ViewChanges{ 994 BatchOps: concurrentOps[index], 995 }, 996 ) 997 require.NoError(err) 998 } 999 1000 // Generate roots 1001 var pastRoot ids.ID 1002 for _, state := range concurrentStates { 1003 mroot, err := state.GetMerkleRoot(context.Background()) 1004 require.NoError(err) 1005 if pastRoot == ids.Empty { 1006 pastRoot = mroot 1007 } else { 1008 require.Equal(pastRoot, mroot) 1009 } 1010 } 1011 }) 1012 } 1013 } 1014 1015 func TestNewViewOnCommittedView(t *testing.T) { 1016 require := require.New(t) 1017 1018 db, err := getBasicDB() 1019 require.NoError(err) 1020 1021 // Create a view 1022 view1Intf, err := db.NewView(context.Background(), ViewChanges{BatchOps: []database.BatchOp{{Key: []byte{1}, Value: []byte{1}}}}) 1023 require.NoError(err) 1024 require.IsType(&view{}, view1Intf) 1025 view1 := view1Intf.(*view) 1026 1027 // view1 1028 // | 1029 // db 1030 1031 require.Len(db.childViews, 1) 1032 require.Contains(db.childViews, view1) 1033 require.Equal(db, view1.parentTrie) 1034 1035 // Commit the view 1036 require.NoError(view1.CommitToDB(context.Background())) 1037 1038 // view1 (committed) 1039 // | 1040 // db 1041 1042 require.Len(db.childViews, 1) 1043 require.Contains(db.childViews, view1) 1044 require.Equal(db, view1.parentTrie) 1045 1046 // Create a new view on the committed view 1047 view2Intf, err := view1.NewView(context.Background(), ViewChanges{}) 1048 require.NoError(err) 1049 require.IsType(&view{}, view2Intf) 1050 view2 := view2Intf.(*view) 1051 1052 // view2 1053 // | 1054 // view1 (committed) 1055 // | 1056 // db 1057 1058 require.Equal(db, view2.parentTrie) 1059 require.Contains(db.childViews, view1) 1060 require.Contains(db.childViews, view2) 1061 require.Len(db.childViews, 2) 1062 1063 // Make sure the new view has the right value 1064 got, err := view2.GetValue(context.Background(), []byte{1}) 1065 require.NoError(err) 1066 require.Equal([]byte{1}, got) 1067 1068 // Make another view 1069 view3Intf, err := view2.NewView(context.Background(), ViewChanges{}) 1070 require.NoError(err) 1071 require.IsType(&view{}, view3Intf) 1072 view3 := view3Intf.(*view) 1073 1074 // view3 1075 // | 1076 // view2 1077 // | 1078 // view1 (committed) 1079 // | 1080 // db 1081 1082 require.Equal(view2, view3.parentTrie) 1083 require.Contains(view2.childViews, view3) 1084 require.Len(view2.childViews, 1) 1085 require.Contains(db.childViews, view1) 1086 require.Contains(db.childViews, view2) 1087 require.Len(db.childViews, 2) 1088 1089 // Commit view2 1090 require.NoError(view2.CommitToDB(context.Background())) 1091 1092 // view3 1093 // | 1094 // view2 (committed) 1095 // | 1096 // view1 (committed) 1097 // | 1098 // db 1099 1100 // Note that view2 being committed invalidates view1 1101 require.True(view1.invalidated) 1102 require.Contains(db.childViews, view2) 1103 require.Contains(db.childViews, view3) 1104 require.Len(db.childViews, 2) 1105 require.Equal(db, view3.parentTrie) 1106 1107 // Commit view3 1108 require.NoError(view3.CommitToDB(context.Background())) 1109 1110 // view3 being committed invalidates view2 1111 require.True(view2.invalidated) 1112 require.Contains(db.childViews, view3) 1113 require.Len(db.childViews, 1) 1114 require.Equal(db, view3.parentTrie) 1115 } 1116 1117 func Test_View_NewView(t *testing.T) { 1118 require := require.New(t) 1119 1120 db, err := getBasicDB() 1121 require.NoError(err) 1122 1123 // Create a view 1124 view1Intf, err := db.NewView(context.Background(), ViewChanges{}) 1125 require.NoError(err) 1126 require.IsType(&view{}, view1Intf) 1127 view1 := view1Intf.(*view) 1128 1129 // Create a view atop view1 1130 view2Intf, err := view1.NewView(context.Background(), ViewChanges{}) 1131 require.NoError(err) 1132 require.IsType(&view{}, view2Intf) 1133 view2 := view2Intf.(*view) 1134 1135 // view2 1136 // | 1137 // view1 1138 // | 1139 // db 1140 1141 // Assert view2's parent is view1 1142 require.Equal(view1, view2.parentTrie) 1143 require.Contains(view1.childViews, view2) 1144 require.Len(view1.childViews, 1) 1145 1146 // Commit view1 1147 require.NoError(view1.CommitToDB(context.Background())) 1148 1149 // Make another view atop view1 1150 view3Intf, err := view1.NewView(context.Background(), ViewChanges{}) 1151 require.NoError(err) 1152 require.IsType(&view{}, view3Intf) 1153 view3 := view3Intf.(*view) 1154 1155 // view3 1156 // | 1157 // view2 1158 // | 1159 // view1 1160 // | 1161 // db 1162 1163 // Assert view3's parent is db 1164 require.Equal(db, view3.parentTrie) 1165 require.Contains(db.childViews, view3) 1166 require.NotContains(view1.childViews, view3) 1167 1168 // Assert that NewPreallocatedView on an invalid view fails 1169 invalidView := &view{invalidated: true} 1170 _, err = invalidView.NewView(context.Background(), ViewChanges{}) 1171 require.ErrorIs(err, ErrInvalid) 1172 } 1173 1174 func TestViewInvalidate(t *testing.T) { 1175 require := require.New(t) 1176 1177 db, err := getBasicDB() 1178 require.NoError(err) 1179 1180 // Create a view 1181 view1Intf, err := db.NewView(context.Background(), ViewChanges{}) 1182 require.NoError(err) 1183 require.IsType(&view{}, view1Intf) 1184 view1 := view1Intf.(*view) 1185 1186 // Create 2 views atop view1 1187 view2Intf, err := view1.NewView(context.Background(), ViewChanges{}) 1188 require.NoError(err) 1189 require.IsType(&view{}, view2Intf) 1190 view2 := view2Intf.(*view) 1191 1192 view3Intf, err := view1.NewView(context.Background(), ViewChanges{}) 1193 require.NoError(err) 1194 require.IsType(&view{}, view3Intf) 1195 view3 := view3Intf.(*view) 1196 1197 // view2 view3 1198 // | / 1199 // view1 1200 // | 1201 // db 1202 1203 // Invalidate view1 1204 view1.invalidate() 1205 1206 require.Empty(view1.childViews) 1207 require.True(view1.invalidated) 1208 require.True(view2.invalidated) 1209 require.True(view3.invalidated) 1210 } 1211 1212 func Test_Trie_ConcurrentNewViewAndCommit(t *testing.T) { 1213 require := require.New(t) 1214 1215 trie, err := getBasicDB() 1216 require.NoError(err) 1217 require.NotNil(trie) 1218 1219 newTrie, err := trie.NewView( 1220 context.Background(), 1221 ViewChanges{ 1222 BatchOps: []database.BatchOp{ 1223 {Key: []byte("key"), Value: []byte("value0")}, 1224 }, 1225 }, 1226 ) 1227 require.NoError(err) 1228 1229 var wg sync.WaitGroup 1230 defer wg.Wait() 1231 1232 wg.Add(1) 1233 go func() { 1234 defer wg.Done() 1235 require.NoError(newTrie.CommitToDB(context.Background())) 1236 }() 1237 1238 view, err := newTrie.NewView(context.Background(), ViewChanges{}) 1239 require.NoError(err) 1240 require.NotNil(view) 1241 } 1242 1243 // Returns the path of the only child of this node. 1244 // Assumes this node has exactly one child. 1245 func getSingleChildKey(n *node, tokenSize int) Key { 1246 for index, entry := range n.children { 1247 return n.key.Extend(ToToken(index, tokenSize), entry.compressedKey) 1248 } 1249 return Key{} 1250 } 1251 1252 func TestTrieCommitToDB(t *testing.T) { 1253 r := require.New(t) 1254 1255 type test struct { 1256 name string 1257 trieFunc func() View 1258 expectedErr error 1259 } 1260 1261 // Make a database 1262 db, err := getBasicDB() 1263 r.NoError(err) 1264 1265 tests := []test{ 1266 { 1267 name: "invalid", 1268 trieFunc: func() View { 1269 nView, err := db.NewView(context.Background(), ViewChanges{}) 1270 r.NoError(err) 1271 1272 // Invalidate the view 1273 nView.(*view).invalidate() 1274 1275 return nView 1276 }, 1277 expectedErr: ErrInvalid, 1278 }, 1279 { 1280 name: "committed", 1281 trieFunc: func() View { 1282 view, err := db.NewView(context.Background(), ViewChanges{}) 1283 r.NoError(err) 1284 1285 // Commit the view 1286 r.NoError(view.CommitToDB(context.Background())) 1287 1288 return view 1289 }, 1290 expectedErr: ErrCommitted, 1291 }, 1292 { 1293 name: "parent not database", 1294 trieFunc: func() View { 1295 nView, err := db.NewView(context.Background(), ViewChanges{}) 1296 r.NoError(err) 1297 1298 // Change the parent 1299 nView.(*view).parentTrie = &view{} 1300 1301 return nView 1302 }, 1303 expectedErr: ErrParentNotDatabase, 1304 }, 1305 } 1306 1307 for _, tt := range tests { 1308 require := require.New(t) 1309 1310 trie := tt.trieFunc() 1311 err := trie.CommitToDB(context.Background()) 1312 require.ErrorIs(err, tt.expectedErr) 1313 } 1314 1315 // Put 2 key-value pairs 1316 key1, value1 := []byte("key1"), []byte("value1") 1317 key2, value2 := []byte("key2"), []byte("value2") 1318 r.NoError(db.Put(key1, value1)) 1319 r.NoError(db.Put(key2, value2)) 1320 1321 // Make a view 1322 key3, value3 := []byte("key3"), []byte("value3") 1323 // Delete a key-value pair, modify a key-value pair, 1324 // and insert a new key-value pair 1325 view, err := db.NewView( 1326 context.Background(), 1327 ViewChanges{ 1328 BatchOps: []database.BatchOp{ 1329 {Key: key1, Delete: true}, 1330 {Key: key2, Value: value3}, 1331 {Key: key3, Value: value3}, 1332 }, 1333 }, 1334 ) 1335 r.NoError(err) 1336 1337 // Commit the view 1338 r.NoError(view.CommitToDB(context.Background())) 1339 1340 // Make sure the database has the right values 1341 _, err = db.Get(key1) 1342 r.ErrorIs(err, database.ErrNotFound) 1343 1344 got, err := db.Get(key2) 1345 r.NoError(err) 1346 r.Equal(value3, got) 1347 1348 got, err = db.Get(key3) 1349 r.NoError(err) 1350 r.Equal(value3, got) 1351 }