|
75 | 75 | AcceptorQueueLimit: 64, |
76 | 76 | } |
77 | 77 |
|
78 | | - // Firewood should only be included for non-archive, snapshot disabled tests. |
| 78 | + // Firewood should only be included for snapshot disabled tests. |
79 | 79 | schemes = []string{rawdb.HashScheme, customrawdb.FirewoodScheme} |
80 | 80 | ) |
81 | 81 |
|
@@ -114,22 +114,36 @@ func TestArchiveBlockChain(t *testing.T) { |
114 | 114 | } |
115 | 115 |
|
116 | 116 | func TestArchiveBlockChainSnapsDisabled(t *testing.T) { |
117 | | - create := func(db ethdb.Database, gspec *Genesis, lastAcceptedHash common.Hash, _ string) (*BlockChain, error) { |
| 117 | + for _, scheme := range schemes { |
| 118 | + t.Run(scheme, func(t *testing.T) { |
| 119 | + testArchiveBlockChainSnapsDisabled(t, scheme) |
| 120 | + }) |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +func testArchiveBlockChainSnapsDisabled(t *testing.T, scheme string) { |
| 125 | + create := func(db ethdb.Database, gspec *Genesis, lastAcceptedHash common.Hash, dataPath string) (*BlockChain, error) { |
| 126 | + cacheConfig := &CacheConfig{ |
| 127 | + TrieCleanLimit: 256, |
| 128 | + TrieDirtyLimit: 256, |
| 129 | + TrieDirtyCommitTarget: 20, |
| 130 | + TriePrefetcherParallelism: 4, |
| 131 | + Pruning: false, // Archive mode |
| 132 | + StateHistory: 32, // Required for Firewood's minimum Revision count |
| 133 | + SnapshotLimit: 0, // Disable snapshots |
| 134 | + AcceptorQueueLimit: 64, |
| 135 | + StateScheme: scheme, |
| 136 | + ChainDataDir: dataPath, |
| 137 | + } |
| 138 | + |
118 | 139 | return createBlockChain( |
119 | 140 | db, |
120 | | - &CacheConfig{ |
121 | | - TrieCleanLimit: 256, |
122 | | - TrieDirtyLimit: 256, |
123 | | - TrieDirtyCommitTarget: 20, |
124 | | - TriePrefetcherParallelism: 4, |
125 | | - Pruning: false, // Archive mode |
126 | | - SnapshotLimit: 0, // Disable snapshots |
127 | | - AcceptorQueueLimit: 64, |
128 | | - }, |
| 141 | + cacheConfig, |
129 | 142 | gspec, |
130 | 143 | lastAcceptedHash, |
131 | 144 | ) |
132 | 145 | } |
| 146 | + |
133 | 147 | for _, tt := range tests { |
134 | 148 | t.Run(tt.Name, func(t *testing.T) { |
135 | 149 | tt.testFunc(t, create) |
@@ -357,6 +371,164 @@ func TestBlockChainOfflinePruningUngracefulShutdown(t *testing.T) { |
357 | 371 | } |
358 | 372 | } |
359 | 373 |
|
| 374 | +// TestPruningToNonPruning tests that opening a previously pruned database as a |
| 375 | +// non-pruned database is successful. |
| 376 | +func TestPruningToNonPruning(t *testing.T) { |
| 377 | + for _, scheme := range schemes { |
| 378 | + t.Run(scheme, func(t *testing.T) { |
| 379 | + testPruningToNonPruning(t, scheme) |
| 380 | + }) |
| 381 | + } |
| 382 | +} |
| 383 | + |
| 384 | +// testPruningToNonPruning tests that opening a previously pruned database as a |
| 385 | +// non-pruned database is successful. |
| 386 | +// |
| 387 | +// This test checks the following invariants: |
| 388 | +// 1. Verifies that a pruned node does not have the state for all blocks (except |
| 389 | +// the last accepted block) upon restart. |
| 390 | +// 2. Verify that a pruned => archival node has the state for all blocks |
| 391 | +// accepted during archival mode upon restart. |
| 392 | +func testPruningToNonPruning(t *testing.T, scheme string) { |
| 393 | + var ( |
| 394 | + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") |
| 395 | + key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") |
| 396 | + addr1 = crypto.PubkeyToAddress(key1.PublicKey) |
| 397 | + addr2 = crypto.PubkeyToAddress(key2.PublicKey) |
| 398 | + chainDB = rawdb.NewMemoryDatabase() |
| 399 | + numStates = uint64(5) |
| 400 | + ) |
| 401 | + |
| 402 | + gspec := &Genesis{ |
| 403 | + Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, |
| 404 | + Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}}, |
| 405 | + } |
| 406 | + |
| 407 | + chainDataDir := t.TempDir() |
| 408 | + pruningConfig := &CacheConfig{ |
| 409 | + TrieCleanLimit: 256, |
| 410 | + TrieDirtyLimit: 256, |
| 411 | + TrieDirtyCommitTarget: 20, |
| 412 | + TriePrefetcherParallelism: 4, |
| 413 | + Pruning: true, // Enable pruning |
| 414 | + CommitInterval: 4096, |
| 415 | + StateHistory: numStates, |
| 416 | + AcceptorQueueLimit: 64, |
| 417 | + StateScheme: scheme, |
| 418 | + ChainDataDir: chainDataDir, |
| 419 | + } |
| 420 | + |
| 421 | + // Create a node in pruning mode. |
| 422 | + blockchain, err := createBlockChain(chainDB, pruningConfig, gspec, common.Hash{}) |
| 423 | + if err != nil { |
| 424 | + t.Fatal(err) |
| 425 | + } |
| 426 | + |
| 427 | + // Generate 10 (2 * numStates) blocks. |
| 428 | + signer := types.HomesteadSigner{} |
| 429 | + _, blocks, _, err := GenerateChainWithGenesis(gspec, blockchain.engine, 2*int(numStates), 10, func(i int, gen *BlockGen) { |
| 430 | + tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), ethparams.TxGas, nil, nil), signer, key1) |
| 431 | + gen.AddTx(tx) |
| 432 | + }) |
| 433 | + if err != nil { |
| 434 | + t.Fatal(err) |
| 435 | + } |
| 436 | + |
| 437 | + prunedBlocks := blocks[:numStates] |
| 438 | + nonPrunedBlocks := blocks[numStates:] |
| 439 | + |
| 440 | + // Insert the first five blocks. |
| 441 | + // The states of the first four blocks will be lost upon restart. |
| 442 | + if _, err := blockchain.InsertChain(prunedBlocks); err != nil { |
| 443 | + t.Fatal(err) |
| 444 | + } |
| 445 | + for _, block := range prunedBlocks { |
| 446 | + if err := blockchain.Accept(block); err != nil { |
| 447 | + t.Fatal(err) |
| 448 | + } |
| 449 | + } |
| 450 | + blockchain.DrainAcceptorQueue() |
| 451 | + |
| 452 | + lastAcceptedHash := blockchain.LastConsensusAcceptedBlock().Hash() |
| 453 | + blockchain.Stop() |
| 454 | + |
| 455 | + // Reopen the node. |
| 456 | + blockchain, err = createBlockChain(chainDB, pruningConfig, gspec, lastAcceptedHash) |
| 457 | + if err != nil { |
| 458 | + t.Fatal(err) |
| 459 | + } |
| 460 | + |
| 461 | + // 1. Verify that a pruned node does not have the state for all blocks (except |
| 462 | + // the last accepted block) upon restart. |
| 463 | + for _, block := range prunedBlocks[:numStates-1] { |
| 464 | + if blockchain.HasState(block.Root()) { |
| 465 | + t.Fatalf("Expected blockchain to be missing state for intermediate block %d with pruning enabled", block.NumberU64()) |
| 466 | + } |
| 467 | + } |
| 468 | + |
| 469 | + blockchain.Stop() |
| 470 | + |
| 471 | + archiveConfig := &CacheConfig{ |
| 472 | + TrieCleanLimit: 256, |
| 473 | + TrieDirtyLimit: 256, |
| 474 | + TrieDirtyCommitTarget: 20, |
| 475 | + TriePrefetcherParallelism: 4, |
| 476 | + Pruning: false, // Archive mode |
| 477 | + AcceptorQueueLimit: 64, |
| 478 | + StateScheme: scheme, |
| 479 | + StateHistory: 32, |
| 480 | + ChainDataDir: chainDataDir, |
| 481 | + } |
| 482 | + |
| 483 | + // Reopen the node, but switch from pruning to archival mode. |
| 484 | + blockchain, err = createBlockChain( |
| 485 | + chainDB, |
| 486 | + archiveConfig, |
| 487 | + gspec, |
| 488 | + lastAcceptedHash, |
| 489 | + ) |
| 490 | + if err != nil { |
| 491 | + t.Fatal(err) |
| 492 | + } |
| 493 | + |
| 494 | + // Insert the remaining five blocks. |
| 495 | + // The states of all these blocks will still be accessible on restart |
| 496 | + // since we're now in archival mode. |
| 497 | + if _, err := blockchain.InsertChain(nonPrunedBlocks); err != nil { |
| 498 | + t.Fatal(err) |
| 499 | + } |
| 500 | + |
| 501 | + for _, block := range nonPrunedBlocks { |
| 502 | + if err := blockchain.Accept(block); err != nil { |
| 503 | + t.Fatal(err) |
| 504 | + } |
| 505 | + } |
| 506 | + blockchain.DrainAcceptorQueue() |
| 507 | + |
| 508 | + lastAcceptedHash = blockchain.LastConsensusAcceptedBlock().Hash() |
| 509 | + blockchain.Stop() |
| 510 | + |
| 511 | + // Reopen the archival node. |
| 512 | + blockchain, err = createBlockChain( |
| 513 | + chainDB, |
| 514 | + archiveConfig, |
| 515 | + gspec, |
| 516 | + lastAcceptedHash, |
| 517 | + ) |
| 518 | + if err != nil { |
| 519 | + t.Fatal(err) |
| 520 | + } |
| 521 | + defer blockchain.Stop() |
| 522 | + |
| 523 | + // 2. Verify that a pruned => archival node has the state for all blocks |
| 524 | + // accepted during archival mode upon restart. |
| 525 | + for _, block := range nonPrunedBlocks { |
| 526 | + if !blockchain.HasState(block.Root()) { |
| 527 | + t.Fatalf("Expected blockchain to have the state for block %d with pruning disabled", block.NumberU64()) |
| 528 | + } |
| 529 | + } |
| 530 | +} |
| 531 | + |
360 | 532 | func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) { |
361 | 533 | var ( |
362 | 534 | key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") |
|
0 commit comments