github.com/MetalBlockchain/subnet-evm@v0.4.9/core/blockchain_test.go (about) 1 // (c) 2020-2021, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package core 5 6 import ( 7 "fmt" 8 "math/big" 9 "os" 10 "testing" 11 "time" 12 13 "github.com/MetalBlockchain/subnet-evm/consensus/dummy" 14 "github.com/MetalBlockchain/subnet-evm/core/rawdb" 15 "github.com/MetalBlockchain/subnet-evm/core/state" 16 "github.com/MetalBlockchain/subnet-evm/core/state/pruner" 17 "github.com/MetalBlockchain/subnet-evm/core/types" 18 "github.com/MetalBlockchain/subnet-evm/core/vm" 19 "github.com/MetalBlockchain/subnet-evm/ethdb" 20 "github.com/MetalBlockchain/subnet-evm/params" 21 "github.com/ethereum/go-ethereum/common" 22 "github.com/ethereum/go-ethereum/crypto" 23 "github.com/fsnotify/fsnotify" 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 ) 27 28 var ( 29 archiveConfig = &CacheConfig{ 30 TrieCleanLimit: 256, 31 TrieDirtyLimit: 256, 32 TrieDirtyCommitTarget: 20, 33 Pruning: false, // Archive mode 34 SnapshotLimit: 256, 35 AcceptorQueueLimit: 64, 36 } 37 38 pruningConfig = &CacheConfig{ 39 TrieCleanLimit: 256, 40 TrieDirtyLimit: 256, 41 TrieDirtyCommitTarget: 20, 42 Pruning: true, // Enable pruning 43 CommitInterval: 4096, 44 SnapshotLimit: 256, 45 AcceptorQueueLimit: 64, 46 } 47 ) 48 49 func createBlockChain( 50 db ethdb.Database, 51 cacheConfig *CacheConfig, 52 chainConfig *params.ChainConfig, 53 lastAcceptedHash common.Hash, 54 ) (*BlockChain, error) { 55 // Import the chain. This runs all block validation rules. 56 blockchain, err := NewBlockChain( 57 db, 58 cacheConfig, 59 chainConfig, 60 dummy.NewCoinbaseFaker(), 61 vm.Config{}, 62 lastAcceptedHash, 63 ) 64 return blockchain, err 65 } 66 67 func TestArchiveBlockChain(t *testing.T) { 68 createArchiveBlockChain := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 69 return createBlockChain(db, archiveConfig, chainConfig, lastAcceptedHash) 70 } 71 for _, tt := range tests { 72 t.Run(tt.Name, func(t *testing.T) { 73 tt.testFunc(t, createArchiveBlockChain) 74 }) 75 } 76 } 77 78 // awaitWatcherEventsSubside waits for at least one event on [watcher] and then waits 79 // for at least [subsideTimeout] before returning 80 func awaitWatcherEventsSubside(watcher *fsnotify.Watcher, subsideTimeout time.Duration) { 81 done := make(chan struct{}) 82 83 go func() { 84 defer func() { 85 close(done) 86 }() 87 88 select { 89 case <-watcher.Events: 90 case <-watcher.Errors: 91 return 92 } 93 94 for { 95 select { 96 case <-watcher.Events: 97 case <-watcher.Errors: 98 return 99 case <-time.After(subsideTimeout): 100 return 101 } 102 } 103 }() 104 <-done 105 } 106 107 func TestTrieCleanJournal(t *testing.T) { 108 t.Skip("FLAKY") 109 require := require.New(t) 110 assert := assert.New(t) 111 112 trieCleanJournal := t.TempDir() 113 trieCleanJournalWatcher, err := fsnotify.NewWatcher() 114 require.NoError(err) 115 defer func() { 116 assert.NoError(trieCleanJournalWatcher.Close()) 117 }() 118 require.NoError(trieCleanJournalWatcher.Add(trieCleanJournal)) 119 120 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 121 config := *archiveConfig 122 config.TrieCleanJournal = trieCleanJournal 123 config.TrieCleanRejournal = 100 * time.Millisecond 124 return createBlockChain(db, &config, chainConfig, lastAcceptedHash) 125 } 126 127 var ( 128 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 129 key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 130 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 131 addr2 = crypto.PubkeyToAddress(key2.PublicKey) 132 // We use two separate databases since GenerateChain commits the state roots to its underlying 133 // database. 134 genDB = rawdb.NewMemoryDatabase() 135 chainDB = rawdb.NewMemoryDatabase() 136 ) 137 138 // Ensure that key1 has some funds in the genesis block. 139 genesisBalance := big.NewInt(1000000) 140 gspec := &Genesis{ 141 Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, 142 Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, 143 } 144 genesis := gspec.MustCommit(genDB) 145 _ = gspec.MustCommit(chainDB) 146 147 blockchain, err := create(chainDB, gspec.Config, common.Hash{}) 148 require.NoError(err) 149 defer blockchain.Stop() 150 151 // This call generates a chain of 3 blocks. 152 signer := types.HomesteadSigner{} 153 // Generate chain of blocks using [genDB] instead of [chainDB] to avoid writing 154 // to the BlockChain's database while generating blocks. 155 chain, _, err := GenerateChain(gspec.Config, genesis, blockchain.engine, genDB, 3, 10, func(i int, gen *BlockGen) { 156 tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) 157 gen.AddTx(tx) 158 }) 159 require.NoError(err) 160 161 // Insert and accept the generated chain 162 _, err = blockchain.InsertChain(chain) 163 require.NoError(err) 164 165 for _, block := range chain { 166 require.NoError(blockchain.Accept(block)) 167 } 168 blockchain.DrainAcceptorQueue() 169 170 awaitWatcherEventsSubside(trieCleanJournalWatcher, time.Second) 171 // Assert that a new file is created in the trie clean journal 172 dirEntries, err := os.ReadDir(trieCleanJournal) 173 require.NoError(err) 174 require.NotEmpty(dirEntries) 175 } 176 177 func TestArchiveBlockChainSnapsDisabled(t *testing.T) { 178 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 179 return createBlockChain( 180 db, 181 &CacheConfig{ 182 TrieCleanLimit: 256, 183 TrieDirtyLimit: 256, 184 TrieDirtyCommitTarget: 20, 185 Pruning: false, // Archive mode 186 SnapshotLimit: 0, // Disable snapshots 187 AcceptorQueueLimit: 64, 188 }, 189 chainConfig, 190 lastAcceptedHash, 191 ) 192 } 193 for _, tt := range tests { 194 t.Run(tt.Name, func(t *testing.T) { 195 tt.testFunc(t, create) 196 }) 197 } 198 } 199 200 func TestPruningBlockChain(t *testing.T) { 201 createPruningBlockChain := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 202 return createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash) 203 } 204 for _, tt := range tests { 205 t.Run(tt.Name, func(t *testing.T) { 206 tt.testFunc(t, createPruningBlockChain) 207 }) 208 } 209 } 210 211 func TestPruningBlockChainSnapsDisabled(t *testing.T) { 212 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 213 return createBlockChain( 214 db, 215 &CacheConfig{ 216 TrieCleanLimit: 256, 217 TrieDirtyLimit: 256, 218 TrieDirtyCommitTarget: 20, 219 Pruning: true, // Enable pruning 220 CommitInterval: 4096, 221 SnapshotLimit: 0, // Disable snapshots 222 AcceptorQueueLimit: 64, 223 }, 224 chainConfig, 225 lastAcceptedHash, 226 ) 227 } 228 for _, tt := range tests { 229 t.Run(tt.Name, func(t *testing.T) { 230 tt.testFunc(t, create) 231 }) 232 } 233 } 234 235 type wrappedStateManager struct { 236 TrieWriter 237 } 238 239 func (w *wrappedStateManager) Shutdown() error { return nil } 240 241 func TestPruningBlockChainUngracefulShutdown(t *testing.T) { 242 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 243 blockchain, err := createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash) 244 if err != nil { 245 return nil, err 246 } 247 248 // Overwrite state manager, so that Shutdown is not called. 249 // This tests to ensure that the state manager handles an ungraceful shutdown correctly. 250 blockchain.stateManager = &wrappedStateManager{TrieWriter: blockchain.stateManager} 251 return blockchain, err 252 } 253 for _, tt := range tests { 254 t.Run(tt.Name, func(t *testing.T) { 255 tt.testFunc(t, create) 256 }) 257 } 258 } 259 260 func TestPruningBlockChainUngracefulShutdownSnapsDisabled(t *testing.T) { 261 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 262 blockchain, err := createBlockChain( 263 db, 264 &CacheConfig{ 265 TrieCleanLimit: 256, 266 TrieDirtyLimit: 256, 267 TrieDirtyCommitTarget: 20, 268 Pruning: true, // Enable pruning 269 CommitInterval: 4096, 270 SnapshotLimit: 0, // Disable snapshots 271 AcceptorQueueLimit: 64, 272 }, 273 chainConfig, 274 lastAcceptedHash, 275 ) 276 if err != nil { 277 return nil, err 278 } 279 280 // Overwrite state manager, so that Shutdown is not called. 281 // This tests to ensure that the state manager handles an ungraceful shutdown correctly. 282 blockchain.stateManager = &wrappedStateManager{TrieWriter: blockchain.stateManager} 283 return blockchain, err 284 } 285 for _, tt := range tests { 286 t.Run(tt.Name, func(t *testing.T) { 287 tt.testFunc(t, create) 288 }) 289 } 290 } 291 292 func TestEnableSnapshots(t *testing.T) { 293 // Set snapshots to be disabled the first time, and then enable them on the restart 294 snapLimit := 0 295 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 296 // Import the chain. This runs all block validation rules. 297 blockchain, err := createBlockChain( 298 db, 299 &CacheConfig{ 300 TrieCleanLimit: 256, 301 TrieDirtyLimit: 256, 302 TrieDirtyCommitTarget: 20, 303 Pruning: true, // Enable pruning 304 CommitInterval: 4096, 305 SnapshotLimit: snapLimit, 306 AcceptorQueueLimit: 64, 307 }, 308 chainConfig, 309 lastAcceptedHash, 310 ) 311 if err != nil { 312 return nil, err 313 } 314 snapLimit = 256 315 316 return blockchain, err 317 } 318 for _, tt := range tests { 319 t.Run(tt.Name, func(t *testing.T) { 320 tt.testFunc(t, create) 321 }) 322 } 323 } 324 325 func TestCorruptSnapshots(t *testing.T) { 326 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 327 // Delete the snapshot block hash and state root to ensure that if we die in between writing a snapshot 328 // diff layer to disk at any point, we can still recover on restart. 329 rawdb.DeleteSnapshotBlockHash(db) 330 rawdb.DeleteSnapshotRoot(db) 331 332 return createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash) 333 } 334 for _, tt := range tests { 335 t.Run(tt.Name, func(t *testing.T) { 336 tt.testFunc(t, create) 337 }) 338 } 339 } 340 341 func TestBlockChainOfflinePruningUngracefulShutdown(t *testing.T) { 342 create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 343 // Import the chain. This runs all block validation rules. 344 blockchain, err := createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash) 345 if err != nil { 346 return nil, err 347 } 348 349 // Overwrite state manager, so that Shutdown is not called. 350 // This tests to ensure that the state manager handles an ungraceful shutdown correctly. 351 blockchain.stateManager = &wrappedStateManager{TrieWriter: blockchain.stateManager} 352 353 if lastAcceptedHash == (common.Hash{}) { 354 return blockchain, nil 355 } 356 357 targetRoot := blockchain.LastAcceptedBlock().Root() 358 if targetRoot == blockchain.Genesis().Root() { 359 return blockchain, nil 360 } 361 362 tempDir := t.TempDir() 363 if err := blockchain.CleanBlockRootsAboveLastAccepted(); err != nil { 364 return nil, err 365 } 366 pruner, err := pruner.NewPruner(db, tempDir, 256) 367 if err != nil { 368 return nil, fmt.Errorf("offline pruning failed (%s, %d): %w", tempDir, 256, err) 369 } 370 371 if err := pruner.Prune(targetRoot); err != nil { 372 return nil, fmt.Errorf("failed to prune blockchain with target root: %s due to: %w", targetRoot, err) 373 } 374 // Re-initialize the blockchain after pruning 375 return createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash) 376 } 377 for _, tt := range tests { 378 tst := tt 379 t.Run(tt.Name, func(t *testing.T) { 380 t.Parallel() 381 tst.testFunc(t, create) 382 }) 383 } 384 } 385 386 func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) { 387 var ( 388 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 389 key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 390 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 391 addr2 = crypto.PubkeyToAddress(key2.PublicKey) 392 // We use two separate databases since GenerateChain commits the state roots to its underlying 393 // database. 394 genDB = rawdb.NewMemoryDatabase() 395 chainDB = rawdb.NewMemoryDatabase() 396 lastAcceptedHash common.Hash 397 ) 398 399 // Ensure that key1 has some funds in the genesis block. 400 genesisBalance := big.NewInt(1000000) 401 gspec := &Genesis{ 402 Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, 403 Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, 404 } 405 genesis := gspec.MustCommit(genDB) 406 _ = gspec.MustCommit(chainDB) 407 408 blockchain, err := createBlockChain(chainDB, pruningConfig, gspec.Config, lastAcceptedHash) 409 if err != nil { 410 t.Fatal(err) 411 } 412 defer blockchain.Stop() 413 414 // This call generates a chain of 3 blocks. 415 signer := types.HomesteadSigner{} 416 // Generate chain of blocks using [genDB] instead of [chainDB] to avoid writing 417 // to the BlockChain's database while generating blocks. 418 chain, _, err := GenerateChain(gspec.Config, genesis, blockchain.engine, genDB, 10, 10, func(i int, gen *BlockGen) { 419 tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) 420 gen.AddTx(tx) 421 }) 422 if err != nil { 423 t.Fatal(err) 424 } 425 426 if _, err := blockchain.InsertChain(chain); err != nil { 427 t.Fatal(err) 428 } 429 for _, block := range chain { 430 if err := blockchain.Accept(block); err != nil { 431 t.Fatal(err) 432 } 433 } 434 blockchain.DrainAcceptorQueue() 435 436 lastAcceptedHash = blockchain.LastConsensusAcceptedBlock().Hash() 437 blockchain.Stop() 438 439 blockchain, err = createBlockChain(chainDB, pruningConfig, gspec.Config, lastAcceptedHash) 440 if err != nil { 441 t.Fatal(err) 442 } 443 444 // Confirm that the node does not have the state for intermediate nodes (exclude the last accepted block) 445 for _, block := range chain[:len(chain)-1] { 446 if blockchain.HasState(block.Root()) { 447 t.Fatalf("Expected blockchain to be missing state for intermediate block %d with pruning enabled", block.NumberU64()) 448 } 449 } 450 blockchain.Stop() 451 452 startHeight := uint64(1) 453 // Create a node in archival mode and re-populate the trie history. 454 blockchain, err = createBlockChain( 455 chainDB, 456 &CacheConfig{ 457 TrieCleanLimit: 256, 458 TrieDirtyLimit: 256, 459 TrieDirtyCommitTarget: 20, 460 Pruning: false, // Archive mode 461 SnapshotLimit: 256, 462 PopulateMissingTries: &startHeight, // Starting point for re-populating. 463 PopulateMissingTriesParallelism: parallelism, 464 AcceptorQueueLimit: 64, 465 }, 466 gspec.Config, 467 lastAcceptedHash, 468 ) 469 if err != nil { 470 t.Fatal(err) 471 } 472 473 for _, block := range chain { 474 if !blockchain.HasState(block.Root()) { 475 t.Fatalf("failed to re-generate state for block %d", block.NumberU64()) 476 } 477 } 478 } 479 480 func TestRepopulateMissingTries(t *testing.T) { 481 // Test with different levels of parallelism as a regression test. 482 for _, parallelism := range []int{1, 2, 4, 1024} { 483 testRepopulateMissingTriesParallel(t, parallelism) 484 } 485 } 486 487 func TestUngracefulAsyncShutdown(t *testing.T) { 488 var ( 489 create = func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 490 blockchain, err := createBlockChain(db, &CacheConfig{ 491 TrieCleanLimit: 256, 492 TrieDirtyLimit: 256, 493 TrieDirtyCommitTarget: 20, 494 Pruning: true, 495 CommitInterval: 4096, 496 SnapshotLimit: 256, 497 SkipSnapshotRebuild: true, // Ensure the test errors if snapshot initialization fails 498 AcceptorQueueLimit: 1000, // ensure channel doesn't block 499 }, chainConfig, lastAcceptedHash) 500 if err != nil { 501 return nil, err 502 } 503 return blockchain, nil 504 } 505 506 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 507 key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 508 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 509 addr2 = crypto.PubkeyToAddress(key2.PublicKey) 510 // We use two separate databases since GenerateChain commits the state roots to its underlying 511 // database. 512 genDB = rawdb.NewMemoryDatabase() 513 chainDB = rawdb.NewMemoryDatabase() 514 ) 515 516 // Ensure that key1 has some funds in the genesis block. 517 genesisBalance := big.NewInt(1000000) 518 gspec := &Genesis{ 519 Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, 520 Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}}, 521 } 522 genesis := gspec.MustCommit(genDB) 523 _ = gspec.MustCommit(chainDB) 524 525 blockchain, err := create(chainDB, gspec.Config, common.Hash{}) 526 if err != nil { 527 t.Fatal(err) 528 } 529 defer blockchain.Stop() 530 531 // This call generates a chain of 10 blocks. 532 signer := types.HomesteadSigner{} 533 // Generate chain of blocks using [genDB] instead of [chainDB] to avoid writing 534 // to the BlockChain's database while generating blocks. 535 chain, _, err := GenerateChain(gspec.Config, genesis, blockchain.engine, genDB, 10, 10, func(i int, gen *BlockGen) { 536 tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) 537 gen.AddTx(tx) 538 }) 539 if err != nil { 540 t.Fatal(err) 541 } 542 543 // Insert three blocks into the chain and accept only the first block. 544 if _, err := blockchain.InsertChain(chain); err != nil { 545 t.Fatal(err) 546 } 547 548 foundTxs := []common.Hash{} 549 missingTxs := []common.Hash{} 550 for i, block := range chain { 551 if err := blockchain.Accept(block); err != nil { 552 t.Fatal(err) 553 } 554 555 if i == 3 { 556 // At height 3, kill the async accepted block processor to force an 557 // ungraceful recovery 558 blockchain.stopAcceptor() 559 blockchain.acceptorQueue = nil 560 } 561 562 if i <= 3 { 563 // If <= height 3, all txs should be accessible on lookup 564 for _, tx := range block.Transactions() { 565 foundTxs = append(foundTxs, tx.Hash()) 566 } 567 } else { 568 // If > 3, all txs should be accessible on lookup 569 for _, tx := range block.Transactions() { 570 missingTxs = append(missingTxs, tx.Hash()) 571 } 572 } 573 } 574 575 // After inserting all blocks, we should confirm that txs added after the 576 // async worker shutdown cannot be found. 577 for _, tx := range foundTxs { 578 txLookup := blockchain.GetTransactionLookup(tx) 579 if txLookup == nil { 580 t.Fatalf("missing transaction: %v", tx) 581 } 582 } 583 for _, tx := range missingTxs { 584 txLookup := blockchain.GetTransactionLookup(tx) 585 if txLookup != nil { 586 t.Fatalf("transaction should be missing: %v", tx) 587 } 588 } 589 590 // check the state of the last accepted block 591 checkState := func(sdb *state.StateDB) error { 592 nonce := sdb.GetNonce(addr1) 593 if nonce != 10 { 594 return fmt.Errorf("expected nonce addr1: 10, found nonce: %d", nonce) 595 } 596 transferredFunds := big.NewInt(100000) 597 balance1 := sdb.GetBalance(addr1) 598 expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds) 599 if balance1.Cmp(expectedBalance1) != 0 { 600 return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1) 601 } 602 603 balance2 := sdb.GetBalance(addr2) 604 expectedBalance2 := transferredFunds 605 if balance2.Cmp(expectedBalance2) != 0 { 606 return fmt.Errorf("expected addr2 balance: %d, found balance: %d", expectedBalance2, balance2) 607 } 608 609 nonce = sdb.GetNonce(addr2) 610 if nonce != 0 { 611 return fmt.Errorf("expected addr2 nonce: 0, found nonce: %d", nonce) 612 } 613 return nil 614 } 615 616 _, newChain, restartedChain := checkBlockChainState(t, blockchain, gspec, chainDB, create, checkState) 617 618 allTxs := append(foundTxs, missingTxs...) 619 for _, bc := range []*BlockChain{newChain, restartedChain} { 620 // We should confirm that snapshots were properly initialized 621 if bc.snaps == nil { 622 t.Fatal("snapshot initialization failed") 623 } 624 625 // We should confirm all transactions can now be queried 626 for _, tx := range allTxs { 627 txLookup := bc.GetTransactionLookup(tx) 628 if txLookup == nil { 629 t.Fatalf("missing transaction: %v", tx) 630 } 631 } 632 } 633 } 634 635 // TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted 636 // correctly in case reorg is called. 637 func TestCanonicalHashMarker(t *testing.T) { 638 var cases = []struct { 639 forkA int 640 forkB int 641 }{ 642 // ForkA: 10 blocks 643 // ForkB: 1 blocks 644 // 645 // reorged: 646 // markers [2, 10] should be deleted 647 // markers [1] should be updated 648 {10, 1}, 649 650 // ForkA: 10 blocks 651 // ForkB: 2 blocks 652 // 653 // reorged: 654 // markers [3, 10] should be deleted 655 // markers [1, 2] should be updated 656 {10, 2}, 657 658 // ForkA: 10 blocks 659 // ForkB: 10 blocks 660 // 661 // reorged: 662 // markers [1, 10] should be updated 663 {10, 10}, 664 665 // ForkA: 10 blocks 666 // ForkB: 11 blocks 667 // 668 // reorged: 669 // markers [1, 11] should be updated 670 {10, 11}, 671 } 672 for _, c := range cases { 673 var ( 674 db = rawdb.NewMemoryDatabase() 675 gspec = &Genesis{ 676 Config: params.TestChainConfig, 677 Alloc: GenesisAlloc{}, 678 BaseFee: big.NewInt(params.TestInitialBaseFee), 679 } 680 genesis = gspec.MustCommit(db) 681 engine = dummy.NewCoinbaseFaker() 682 ) 683 forkA, _, err := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkA, 10, func(i int, gen *BlockGen) {}) 684 if err != nil { 685 t.Fatal(err) 686 } 687 forkB, _, err := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkB, 10, func(i int, gen *BlockGen) {}) 688 if err != nil { 689 t.Fatal(err) 690 } 691 692 // Initialize test chain 693 diskdb := rawdb.NewMemoryDatabase() 694 gspec.MustCommit(diskdb) 695 chain, err := NewBlockChain(diskdb, DefaultCacheConfig, params.TestChainConfig, engine, vm.Config{}, common.Hash{}) 696 if err != nil { 697 t.Fatalf("failed to create tester chain: %v", err) 698 } 699 // Insert forkA and forkB, the canonical should on forkA still 700 if n, err := chain.InsertChain(forkA); err != nil { 701 t.Fatalf("block %d: failed to insert into chain: %v", n, err) 702 } 703 if n, err := chain.InsertChain(forkB); err != nil { 704 t.Fatalf("block %d: failed to insert into chain: %v", n, err) 705 } 706 707 verify := func(head *types.Block) { 708 if chain.CurrentBlock().Hash() != head.Hash() { 709 t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash()) 710 } 711 if chain.CurrentHeader().Hash() != head.Hash() { 712 t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash()) 713 } 714 if !chain.HasState(head.Root()) { 715 t.Fatalf("Lost block state %v %x", head.Number(), head.Hash()) 716 } 717 } 718 719 // Switch canonical chain to forkB if necessary 720 if len(forkA) < len(forkB) { 721 verify(forkB[len(forkB)-1]) 722 } else { 723 verify(forkA[len(forkA)-1]) 724 if err := chain.SetPreference(forkB[len(forkB)-1]); err != nil { 725 t.Fatal(err) 726 } 727 verify(forkB[len(forkB)-1]) 728 } 729 730 // Ensure all hash markers are updated correctly 731 for i := 0; i < len(forkB); i++ { 732 block := forkB[i] 733 hash := chain.GetCanonicalHash(block.NumberU64()) 734 if hash != block.Hash() { 735 t.Fatalf("Unexpected canonical hash %d", block.NumberU64()) 736 } 737 } 738 if c.forkA > c.forkB { 739 for i := uint64(c.forkB) + 1; i <= uint64(c.forkA); i++ { 740 hash := chain.GetCanonicalHash(i) 741 if hash != (common.Hash{}) { 742 t.Fatalf("Unexpected canonical hash %d", i) 743 } 744 } 745 } 746 } 747 } 748 749 func TestTransactionIndices(t *testing.T) { 750 // Configure and generate a sample block chain 751 require := require.New(t) 752 var ( 753 gendb = rawdb.NewMemoryDatabase() 754 key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 755 key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") 756 addr1 = crypto.PubkeyToAddress(key1.PublicKey) 757 addr2 = crypto.PubkeyToAddress(key2.PublicKey) 758 funds = big.NewInt(10000000000000) 759 gspec = &Genesis{ 760 Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, 761 Alloc: GenesisAlloc{addr1: {Balance: funds}}, 762 } 763 genesis = gspec.MustCommit(gendb) 764 signer = types.LatestSigner(gspec.Config) 765 ) 766 height := uint64(128) 767 blocks, _, err := GenerateChain(gspec.Config, genesis, dummy.NewFaker(), gendb, int(height), 10, func(i int, block *BlockGen) { 768 tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) 769 require.NoError(err) 770 block.AddTx(tx) 771 }) 772 require.NoError(err) 773 774 blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFaker(), gendb, 10, 10, nil) 775 require.NoError(err) 776 777 check := func(tail *uint64, chain *BlockChain) { 778 stored := rawdb.ReadTxIndexTail(chain.db) 779 require.EqualValues(tail, stored) 780 781 if tail == nil { 782 return 783 } 784 for i := *tail; i <= chain.CurrentBlock().NumberU64(); i++ { 785 block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i) 786 if block.Transactions().Len() == 0 { 787 continue 788 } 789 for _, tx := range block.Transactions() { 790 index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()) 791 require.NotNilf(index, "Miss transaction indices, number %d hash %s", i, tx.Hash().Hex()) 792 } 793 } 794 795 for i := uint64(0); i < *tail; i++ { 796 block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i) 797 if block.Transactions().Len() == 0 { 798 continue 799 } 800 for _, tx := range block.Transactions() { 801 index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()) 802 require.Nilf(index, "Transaction indices should be deleted, number %d hash %s", i, tx.Hash().Hex()) 803 } 804 } 805 } 806 807 conf := &CacheConfig{ 808 TrieCleanLimit: 256, 809 TrieDirtyLimit: 256, 810 TrieDirtyCommitTarget: 20, 811 Pruning: true, 812 CommitInterval: 4096, 813 SnapshotLimit: 256, 814 SkipSnapshotRebuild: true, // Ensure the test errors if snapshot initialization fails 815 AcceptorQueueLimit: 64, 816 } 817 818 // Init block chain and check all needed indices has been indexed. 819 chainDB := rawdb.NewMemoryDatabase() 820 gspec.MustCommit(chainDB) 821 822 chain, err := createBlockChain(chainDB, conf, gspec.Config, common.Hash{}) 823 require.NoError(err) 824 825 _, err = chain.InsertChain(blocks) 826 require.NoError(err) 827 828 for _, block := range blocks { 829 err := chain.Accept(block) 830 require.NoError(err) 831 } 832 chain.DrainAcceptorQueue() 833 834 chain.Stop() 835 check(nil, chain) // check all indices has been indexed 836 837 lastAcceptedHash := chain.CurrentHeader().Hash() 838 839 // Reconstruct a block chain which only reserves limited tx indices 840 // 128 blocks were previously indexed. Now we add a new block at each test step. 841 limit := []uint64{130 /* 129 + 1 reserve all */, 64 /* drop stale */, 32 /* shorten history */} 842 tails := []uint64{0 /* reserve all */, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */} 843 for i, l := range limit { 844 conf.TxLookupLimit = l 845 846 chain, err := createBlockChain(chainDB, conf, gspec.Config, lastAcceptedHash) 847 require.NoError(err) 848 849 newBlks := blocks2[i : i+1] 850 _, err = chain.InsertChain(newBlks) // Feed chain a higher block to trigger indices updater. 851 require.NoError(err) 852 853 err = chain.Accept(newBlks[0]) // Accept the block to trigger indices updater. 854 require.NoError(err) 855 856 chain.DrainAcceptorQueue() 857 time.Sleep(50 * time.Millisecond) // Wait for indices initialisation 858 859 chain.Stop() 860 check(&tails[i], chain) 861 862 lastAcceptedHash = chain.CurrentHeader().Hash() 863 } 864 } 865 866 func TestTxLookupBlockChain(t *testing.T) { 867 cacheConf := &CacheConfig{ 868 TrieCleanLimit: 256, 869 TrieDirtyLimit: 256, 870 TrieDirtyCommitTarget: 20, 871 Pruning: true, 872 CommitInterval: 4096, 873 SnapshotLimit: 256, 874 SkipSnapshotRebuild: true, // Ensure the test errors if snapshot initialization fails 875 AcceptorQueueLimit: 64, // ensure channel doesn't block 876 TxLookupLimit: 5, 877 } 878 createTxLookupBlockChain := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) { 879 return createBlockChain(db, cacheConf, chainConfig, lastAcceptedHash) 880 } 881 for _, tt := range tests { 882 t.Run(tt.Name, func(t *testing.T) { 883 tt.testFunc(t, createTxLookupBlockChain) 884 }) 885 } 886 }