github.com/core-coin/go-core/v2@v2.1.9/core/blockchain_snapshot_test.go (about) 1 // Copyright 2020 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Tests that abnormal program termination (i.e.crash) and restart can recovery 18 // the snapshot properly if the snapshot is enabled. 19 20 package core 21 22 import ( 23 "bytes" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "strings" 28 "testing" 29 "time" 30 31 "github.com/core-coin/go-core/v2/consensus/cryptore" 32 33 "github.com/core-coin/go-core/v2/core/rawdb" 34 "github.com/core-coin/go-core/v2/core/vm" 35 "github.com/core-coin/go-core/v2/params" 36 ) 37 38 // snapshotTest is a test case for snapshot recovery. It can be used for 39 // simulating these scenarios: 40 // (i) Gocore restarts normally with valid legacy snapshot 41 // (ii) Gocore restarts normally with valid new-format snapshot 42 // (iii) Gocore restarts after the crash, with broken legacy snapshot 43 // (iv) Gocore restarts after the crash, with broken new-format snapshot 44 // (v) Gocore restarts normally, but it's requested to be rewound to a lower point via SetHead 45 // (vi) Gocore restarts normally with a stale snapshot 46 type snapshotTest struct { 47 legacy bool // Flag whether the loaded snapshot is in legacy format 48 crash bool // Flag whether the Gocore restarts from the previous crash 49 restartCrash int // Number of blocks to insert after the normal stop, then the crash happens 50 gapped int // Number of blocks to insert without enabling snapshot 51 setHead uint64 // Block number to set head back to 52 53 chainBlocks int // Number of blocks to generate for the canonical chain 54 snapshotBlock uint64 // Block number of the relevant snapshot disk layer 55 commitBlock uint64 // Block number for which to commit the state to disk 56 57 expCanonicalBlocks int // Number of canonical blocks expected to remain in the database (excl. genesis) 58 expHeadHeader uint64 // Block number of the expected head header 59 expHeadFastBlock uint64 // Block number of the expected head fast sync block 60 expHeadBlock uint64 // Block number of the expected head full block 61 expSnapshotBottom uint64 // The block height corresponding to the snapshot disk layer 62 } 63 64 func (tt *snapshotTest) dump() string { 65 buffer := new(strings.Builder) 66 67 fmt.Fprint(buffer, "Chain:\n G") 68 for i := 0; i < tt.chainBlocks; i++ { 69 fmt.Fprintf(buffer, "->C%d", i+1) 70 } 71 fmt.Fprint(buffer, " (HEAD)\n\n") 72 73 fmt.Fprintf(buffer, "Commit: G") 74 if tt.commitBlock > 0 { 75 fmt.Fprintf(buffer, ", C%d", tt.commitBlock) 76 } 77 fmt.Fprint(buffer, "\n") 78 79 fmt.Fprintf(buffer, "Snapshot: G") 80 if tt.snapshotBlock > 0 { 81 fmt.Fprintf(buffer, ", C%d", tt.snapshotBlock) 82 } 83 fmt.Fprint(buffer, "\n") 84 85 if tt.crash { 86 fmt.Fprintf(buffer, "\nCRASH\n\n") 87 } else { 88 fmt.Fprintf(buffer, "\nSetHead(%d)\n\n", tt.setHead) 89 } 90 fmt.Fprintf(buffer, "------------------------------\n\n") 91 92 fmt.Fprint(buffer, "Expected in leveldb:\n G") 93 for i := 0; i < tt.expCanonicalBlocks; i++ { 94 fmt.Fprintf(buffer, "->C%d", i+1) 95 } 96 fmt.Fprintf(buffer, "\n\n") 97 fmt.Fprintf(buffer, "Expected head header : C%d\n", tt.expHeadHeader) 98 fmt.Fprintf(buffer, "Expected head fast block: C%d\n", tt.expHeadFastBlock) 99 if tt.expHeadBlock == 0 { 100 fmt.Fprintf(buffer, "Expected head block : G\n") 101 } else { 102 fmt.Fprintf(buffer, "Expected head block : C%d\n", tt.expHeadBlock) 103 } 104 if tt.expSnapshotBottom == 0 { 105 fmt.Fprintf(buffer, "Expected snapshot disk : G\n") 106 } else { 107 fmt.Fprintf(buffer, "Expected snapshot disk : C%d\n", tt.expSnapshotBottom) 108 } 109 return buffer.String() 110 } 111 112 // Tests a Gocore restart with valid snapshot. Before the shutdown, all snapshot 113 // journal will be persisted correctly. In this case no snapshot recovery is 114 // required. 115 func TestRestartWithNewSnapshot(t *testing.T) { 116 // Chain: 117 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 118 // 119 // Commit: G 120 // Snapshot: G 121 // 122 // SetHead(0) 123 // 124 // ------------------------------ 125 // 126 // Expected in leveldb: 127 // G->C1->C2->C3->C4->C5->C6->C7->C8 128 // 129 // Expected head header : C8 130 // Expected head fast block: C8 131 // Expected head block : C8 132 // Expected snapshot disk : G 133 testSnapshot(t, &snapshotTest{ 134 legacy: false, 135 crash: false, 136 gapped: 0, 137 setHead: 0, 138 chainBlocks: 8, 139 snapshotBlock: 0, 140 commitBlock: 0, 141 expCanonicalBlocks: 8, 142 expHeadHeader: 8, 143 expHeadFastBlock: 8, 144 expHeadBlock: 8, 145 expSnapshotBottom: 0, // Initial disk layer built from genesis 146 }) 147 } 148 149 // Tests a Gocore restart with valid but "legacy" snapshot. Before the shutdown, 150 // all snapshot journal will be persisted correctly. In this case no snapshot 151 // recovery is required. 152 func TestRestartWithLegacySnapshot(t *testing.T) { 153 // Chain: 154 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 155 // 156 // Commit: G 157 // Snapshot: G 158 // 159 // SetHead(0) 160 // 161 // ------------------------------ 162 // 163 // Expected in leveldb: 164 // G->C1->C2->C3->C4->C5->C6->C7->C8 165 // 166 // Expected head header : C8 167 // Expected head fast block: C8 168 // Expected head block : C8 169 // Expected snapshot disk : G 170 testSnapshot(t, &snapshotTest{ 171 legacy: true, 172 crash: false, 173 gapped: 0, 174 setHead: 0, 175 chainBlocks: 8, 176 snapshotBlock: 0, 177 commitBlock: 0, 178 expCanonicalBlocks: 8, 179 expHeadHeader: 8, 180 expHeadFastBlock: 8, 181 expHeadBlock: 8, 182 expSnapshotBottom: 0, // Initial disk layer built from genesis 183 }) 184 } 185 186 // Tests a Gocore was crashed and restarts with a broken snapshot. In this case the 187 // chain head should be rewound to the point with available state. And also the 188 // new head should must be lower than disk layer. But there is no committed point 189 // so the chain should be rewound to genesis and the disk layer should be left 190 // for recovery. 191 func TestNoCommitCrashWithNewSnapshot(t *testing.T) { 192 // Chain: 193 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 194 // 195 // Commit: G 196 // Snapshot: G, C4 197 // 198 // CRASH 199 // 200 // ------------------------------ 201 // 202 // Expected in leveldb: 203 // G->C1->C2->C3->C4->C5->C6->C7->C8 204 // 205 // Expected head header : C8 206 // Expected head fast block: C8 207 // Expected head block : G 208 // Expected snapshot disk : C4 209 testSnapshot(t, &snapshotTest{ 210 legacy: false, 211 crash: true, 212 gapped: 0, 213 setHead: 0, 214 chainBlocks: 8, 215 snapshotBlock: 4, 216 commitBlock: 0, 217 expCanonicalBlocks: 8, 218 expHeadHeader: 8, 219 expHeadFastBlock: 8, 220 expHeadBlock: 0, 221 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 222 }) 223 } 224 225 // Tests a Gocore was crashed and restarts with a broken snapshot. In this case the 226 // chain head should be rewound to the point with available state. And also the 227 // new head should must be lower than disk layer. But there is only a low committed 228 // point so the chain should be rewound to committed point and the disk layer 229 // should be left for recovery. 230 func TestLowCommitCrashWithNewSnapshot(t *testing.T) { 231 // Chain: 232 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 233 // 234 // Commit: G, C2 235 // Snapshot: G, C4 236 // 237 // CRASH 238 // 239 // ------------------------------ 240 // 241 // Expected in leveldb: 242 // G->C1->C2->C3->C4->C5->C6->C7->C8 243 // 244 // Expected head header : C8 245 // Expected head fast block: C8 246 // Expected head block : C2 247 // Expected snapshot disk : C4 248 testSnapshot(t, &snapshotTest{ 249 legacy: false, 250 crash: true, 251 gapped: 0, 252 setHead: 0, 253 chainBlocks: 8, 254 snapshotBlock: 4, 255 commitBlock: 2, 256 expCanonicalBlocks: 8, 257 expHeadHeader: 8, 258 expHeadFastBlock: 8, 259 expHeadBlock: 2, 260 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 261 }) 262 } 263 264 // Tests a Gocore was crashed and restarts with a broken snapshot. In this case 265 // the chain head should be rewound to the point with available state. And also 266 // the new head should must be lower than disk layer. But there is only a high 267 // committed point so the chain should be rewound to genesis and the disk layer 268 // should be left for recovery. 269 func TestHighCommitCrashWithNewSnapshot(t *testing.T) { 270 // Chain: 271 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 272 // 273 // Commit: G, C6 274 // Snapshot: G, C4 275 // 276 // CRASH 277 // 278 // ------------------------------ 279 // 280 // Expected in leveldb: 281 // G->C1->C2->C3->C4->C5->C6->C7->C8 282 // 283 // Expected head header : C8 284 // Expected head fast block: C8 285 // Expected head block : G 286 // Expected snapshot disk : C4 287 testSnapshot(t, &snapshotTest{ 288 legacy: false, 289 crash: true, 290 gapped: 0, 291 setHead: 0, 292 chainBlocks: 8, 293 snapshotBlock: 4, 294 commitBlock: 6, 295 expCanonicalBlocks: 8, 296 expHeadHeader: 8, 297 expHeadFastBlock: 8, 298 expHeadBlock: 0, 299 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 300 }) 301 } 302 303 // Tests a Gocore was crashed and restarts with a broken and "legacy format" 304 // snapshot. In this case the entire legacy snapshot should be discared 305 // and rebuild from the new chain head. The new head here refers to the 306 // genesis because there is no committed point. 307 func TestNoCommitCrashWithLegacySnapshot(t *testing.T) { 308 // Chain: 309 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 310 // 311 // Commit: G 312 // Snapshot: G, C4 313 // 314 // CRASH 315 // 316 // ------------------------------ 317 // 318 // Expected in leveldb: 319 // G->C1->C2->C3->C4->C5->C6->C7->C8 320 // 321 // Expected head header : C8 322 // Expected head fast block: C8 323 // Expected head block : G 324 // Expected snapshot disk : G 325 testSnapshot(t, &snapshotTest{ 326 legacy: true, 327 crash: true, 328 gapped: 0, 329 setHead: 0, 330 chainBlocks: 8, 331 snapshotBlock: 4, 332 commitBlock: 0, 333 expCanonicalBlocks: 8, 334 expHeadHeader: 8, 335 expHeadFastBlock: 8, 336 expHeadBlock: 0, 337 expSnapshotBottom: 0, // Rebuilt snapshot from the latest HEAD(genesis) 338 }) 339 } 340 341 // Tests a Gocore was crashed and restarts with a broken and "legacy format" 342 // snapshot. In this case the entire legacy snapshot should be discared 343 // and rebuild from the new chain head. The new head here refers to the 344 // block-2 because it's committed into the disk. 345 func TestLowCommitCrashWithLegacySnapshot(t *testing.T) { 346 // Chain: 347 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 348 // 349 // Commit: G, C2 350 // Snapshot: G, C4 351 // 352 // CRASH 353 // 354 // ------------------------------ 355 // 356 // Expected in leveldb: 357 // G->C1->C2->C3->C4->C5->C6->C7->C8 358 // 359 // Expected head header : C8 360 // Expected head fast block: C8 361 // Expected head block : C2 362 // Expected snapshot disk : C2 363 testSnapshot(t, &snapshotTest{ 364 legacy: true, 365 crash: true, 366 gapped: 0, 367 setHead: 0, 368 chainBlocks: 8, 369 snapshotBlock: 4, 370 commitBlock: 2, 371 expCanonicalBlocks: 8, 372 expHeadHeader: 8, 373 expHeadFastBlock: 8, 374 expHeadBlock: 2, 375 expSnapshotBottom: 2, // Rebuilt snapshot from the latest HEAD 376 }) 377 } 378 379 // Tests a Gocore was crashed and restarts with a broken and "legacy format" 380 // snapshot. In this case the entire legacy snapshot should be discared 381 // and rebuild from the new chain head. 382 // 383 // The new head here refers to the the genesis, the reason is: 384 // - the state of block-6 is committed into the disk 385 // - the legacy disk layer of block-4 is committed into the disk 386 // - the head is rewound the genesis in order to find an available 387 // state lower than disk layer 388 func TestHighCommitCrashWithLegacySnapshot(t *testing.T) { 389 // Chain: 390 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 391 // 392 // Commit: G, C6 393 // Snapshot: G, C4 394 // 395 // CRASH 396 // 397 // ------------------------------ 398 // 399 // Expected in leveldb: 400 // G->C1->C2->C3->C4->C5->C6->C7->C8 401 // 402 // Expected head header : C8 403 // Expected head fast block: C8 404 // Expected head block : G 405 // Expected snapshot disk : G 406 testSnapshot(t, &snapshotTest{ 407 legacy: true, 408 crash: true, 409 gapped: 0, 410 setHead: 0, 411 chainBlocks: 8, 412 snapshotBlock: 4, 413 commitBlock: 6, 414 expCanonicalBlocks: 8, 415 expHeadHeader: 8, 416 expHeadFastBlock: 8, 417 expHeadBlock: 0, 418 expSnapshotBottom: 0, // Rebuilt snapshot from the latest HEAD(genesis) 419 }) 420 } 421 422 // Tests a Gocore was running with snapshot enabled. Then restarts without 423 // enabling snapshot and after that re-enable the snapshot again. In this 424 // case the snapshot should be rebuilt with latest chain head. 425 func TestGappedNewSnapshot(t *testing.T) { 426 // Chain: 427 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 428 // 429 // Commit: G 430 // Snapshot: G 431 // 432 // SetHead(0) 433 // 434 // ------------------------------ 435 // 436 // Expected in leveldb: 437 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 438 // 439 // Expected head header : C10 440 // Expected head fast block: C10 441 // Expected head block : C10 442 // Expected snapshot disk : C10 443 testSnapshot(t, &snapshotTest{ 444 legacy: false, 445 crash: false, 446 gapped: 2, 447 setHead: 0, 448 chainBlocks: 8, 449 snapshotBlock: 0, 450 commitBlock: 0, 451 expCanonicalBlocks: 10, 452 expHeadHeader: 10, 453 expHeadFastBlock: 10, 454 expHeadBlock: 10, 455 expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD 456 }) 457 } 458 459 // Tests a Gocore was running with leagcy snapshot enabled. Then restarts 460 // without enabling snapshot and after that re-enable the snapshot again. 461 // In this case the snapshot should be rebuilt with latest chain head. 462 func TestGappedLegacySnapshot(t *testing.T) { 463 // Chain: 464 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 465 // 466 // Commit: G 467 // Snapshot: G 468 // 469 // SetHead(0) 470 // 471 // ------------------------------ 472 // 473 // Expected in leveldb: 474 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 475 // 476 // Expected head header : C10 477 // Expected head fast block: C10 478 // Expected head block : C10 479 // Expected snapshot disk : C10 480 testSnapshot(t, &snapshotTest{ 481 legacy: true, 482 crash: false, 483 gapped: 2, 484 setHead: 0, 485 chainBlocks: 8, 486 snapshotBlock: 0, 487 commitBlock: 0, 488 expCanonicalBlocks: 10, 489 expHeadHeader: 10, 490 expHeadFastBlock: 10, 491 expHeadBlock: 10, 492 expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD 493 }) 494 } 495 496 // Tests the Gocore was running with snapshot enabled and resetHead is applied. 497 // In this case the head is rewound to the target(with state available). After 498 // that the chain is restarted and the original disk layer is kept. 499 func TestSetHeadWithNewSnapshot(t *testing.T) { 500 // Chain: 501 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 502 // 503 // Commit: G 504 // Snapshot: G 505 // 506 // SetHead(4) 507 // 508 // ------------------------------ 509 // 510 // Expected in leveldb: 511 // G->C1->C2->C3->C4 512 // 513 // Expected head header : C4 514 // Expected head fast block: C4 515 // Expected head block : C4 516 // Expected snapshot disk : G 517 testSnapshot(t, &snapshotTest{ 518 legacy: false, 519 crash: false, 520 gapped: 0, 521 setHead: 4, 522 chainBlocks: 8, 523 snapshotBlock: 0, 524 commitBlock: 0, 525 expCanonicalBlocks: 4, 526 expHeadHeader: 4, 527 expHeadFastBlock: 4, 528 expHeadBlock: 4, 529 expSnapshotBottom: 0, // The initial disk layer is built from the genesis 530 }) 531 } 532 533 // Tests the Gocore was running with snapshot(legacy-format) enabled and resetHead 534 // is applied. In this case the head is rewound to the target(with state available). 535 // After that the chain is restarted and the original disk layer is kept. 536 func TestSetHeadWithLegacySnapshot(t *testing.T) { 537 // Chain: 538 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 539 // 540 // Commit: G 541 // Snapshot: G 542 // 543 // SetHead(4) 544 // 545 // ------------------------------ 546 // 547 // Expected in leveldb: 548 // G->C1->C2->C3->C4 549 // 550 // Expected head header : C4 551 // Expected head fast block: C4 552 // Expected head block : C4 553 // Expected snapshot disk : G 554 testSnapshot(t, &snapshotTest{ 555 legacy: true, 556 crash: false, 557 gapped: 0, 558 setHead: 4, 559 chainBlocks: 8, 560 snapshotBlock: 0, 561 commitBlock: 0, 562 expCanonicalBlocks: 4, 563 expHeadHeader: 4, 564 expHeadFastBlock: 4, 565 expHeadBlock: 4, 566 expSnapshotBottom: 0, // The initial disk layer is built from the genesis 567 }) 568 } 569 570 // Tests the Gocore was running with snapshot(legacy-format) enabled and upgrades 571 // the disk layer journal(journal generator) to latest format. After that the Gocore 572 // is restarted from a crash. In this case Gocore will find the new-format disk layer 573 // journal but with legacy-format diff journal(the new-format is never committed), 574 // and the invalid diff journal is expected to be dropped. 575 func TestRecoverSnapshotFromCrashWithLegacyDiffJournal(t *testing.T) { 576 // Chain: 577 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 578 // 579 // Commit: G 580 // Snapshot: G 581 // 582 // SetHead(0) 583 // 584 // ------------------------------ 585 // 586 // Expected in leveldb: 587 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 588 // 589 // Expected head header : C10 590 // Expected head fast block: C10 591 // Expected head block : C8 592 // Expected snapshot disk : C10 593 testSnapshot(t, &snapshotTest{ 594 legacy: true, 595 crash: false, 596 restartCrash: 2, 597 gapped: 0, 598 setHead: 0, 599 chainBlocks: 8, 600 snapshotBlock: 0, 601 commitBlock: 0, 602 expCanonicalBlocks: 10, 603 expHeadHeader: 10, 604 expHeadFastBlock: 10, 605 expHeadBlock: 8, // The persisted state in the first running 606 expSnapshotBottom: 10, // The persisted disk layer in the second running 607 }) 608 } 609 610 func testSnapshot(t *testing.T, tt *snapshotTest) { 611 // It's hard to follow the test case, visualize the input 612 // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 613 // fmt.Println(tt.dump()) 614 615 // Create a temporary persistent database 616 datadir, err := ioutil.TempDir("", "") 617 if err != nil { 618 t.Fatalf("Failed to create temporary datadir: %v", err) 619 } 620 os.RemoveAll(datadir) 621 622 db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") 623 if err != nil { 624 t.Fatalf("Failed to create persistent database: %v", err) 625 } 626 defer db.Close() // Might double close, should be fine 627 628 // Initialize a fresh chain 629 var ( 630 genesis = new(Genesis).MustCommit(db) 631 engine = cryptore.NewFullFaker() 632 gendb = rawdb.NewMemoryDatabase() 633 634 // Snapshot is enabled, the first snapshot is created from the Genesis. 635 // The snapshot memory allowance is 256MB, it means no snapshot flush 636 // will happen during the block insertion. 637 cacheConfig = defaultCacheConfig 638 ) 639 chain, err := NewBlockChain(db, cacheConfig, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 640 if err != nil { 641 t.Fatalf("Failed to create chain: %v", err) 642 } 643 blocks, _ := GenerateChain(params.MainnetChainConfig, genesis, engine, gendb, tt.chainBlocks, func(i int, b *BlockGen) {}) 644 645 // Insert the blocks with configured settings. 646 var breakpoints []uint64 647 if tt.commitBlock > tt.snapshotBlock { 648 breakpoints = append(breakpoints, tt.snapshotBlock, tt.commitBlock) 649 } else { 650 breakpoints = append(breakpoints, tt.commitBlock, tt.snapshotBlock) 651 } 652 var startPoint uint64 653 for _, point := range breakpoints { 654 if _, err := chain.InsertChain(blocks[startPoint:point]); err != nil { 655 t.Fatalf("Failed to import canonical chain start: %v", err) 656 } 657 startPoint = point 658 659 if tt.commitBlock > 0 && tt.commitBlock == point { 660 chain.stateCache.TrieDB().Commit(blocks[point-1].Root(), true, nil) 661 } 662 if tt.snapshotBlock > 0 && tt.snapshotBlock == point { 663 if tt.legacy { 664 // Here we commit the snapshot disk root to simulate 665 // committing the legacy snapshot. 666 rawdb.WriteSnapshotRoot(db, blocks[point-1].Root()) 667 } else { 668 chain.snaps.Cap(blocks[point-1].Root(), 0) 669 diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root() 670 if !bytes.Equal(diskRoot.Bytes(), blockRoot.Bytes()) { 671 t.Fatalf("Failed to flush disk layer change, want %x, got %x", blockRoot, diskRoot) 672 } 673 } 674 } 675 } 676 if _, err := chain.InsertChain(blocks[startPoint:]); err != nil { 677 t.Fatalf("Failed to import canonical chain tail: %v", err) 678 } 679 // Set the flag for writing legacy journal if necessary 680 if tt.legacy { 681 chain.writeLegacyJournal = true 682 } 683 // Pull the plug on the database, simulating a hard crash 684 if tt.crash { 685 db.Close() 686 687 // Start a new blockchain back up and see where the repair leads us 688 db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "") 689 if err != nil { 690 t.Fatalf("Failed to reopen persistent database: %v", err) 691 } 692 defer db.Close() 693 694 // The interesting thing is: instead of start the blockchain after 695 // the crash, we do restart twice here: one after the crash and one 696 // after the normal stop. It's used to ensure the broken snapshot 697 // can be detected all the time. 698 chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 699 if err != nil { 700 t.Fatalf("Failed to recreate chain: %v", err) 701 } 702 chain.Stop() 703 704 chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 705 if err != nil { 706 t.Fatalf("Failed to recreate chain: %v", err) 707 } 708 defer chain.Stop() 709 } else if tt.gapped > 0 { 710 // Insert blocks without enabling snapshot if gapping is required. 711 chain.Stop() 712 gappedBlocks, _ := GenerateChain(params.MainnetChainConfig, blocks[len(blocks)-1], engine, gendb, tt.gapped, func(i int, b *BlockGen) {}) 713 714 // Insert a few more blocks without enabling snapshot 715 var cacheConfig = &CacheConfig{ 716 TrieCleanLimit: 256, 717 TrieDirtyLimit: 256, 718 TrieTimeLimit: 5 * time.Minute, 719 SnapshotLimit: 0, 720 } 721 chain, err = NewBlockChain(db, cacheConfig, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 722 if err != nil { 723 t.Fatalf("Failed to recreate chain: %v", err) 724 } 725 chain.InsertChain(gappedBlocks) 726 chain.Stop() 727 728 chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 729 if err != nil { 730 t.Fatalf("Failed to recreate chain: %v", err) 731 } 732 defer chain.Stop() 733 } else if tt.setHead != 0 { 734 // Rewind the chain if setHead operation is required. 735 chain.SetHead(tt.setHead) 736 chain.Stop() 737 738 chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 739 if err != nil { 740 t.Fatalf("Failed to recreate chain: %v", err) 741 } 742 defer chain.Stop() 743 } else if tt.restartCrash != 0 { 744 // Firstly, stop the chain properly, with all snapshot journal 745 // and state committed. 746 chain.Stop() 747 748 // Restart chain, forcibly flush the disk layer journal with new format 749 newBlocks, _ := GenerateChain(params.MainnetChainConfig, blocks[len(blocks)-1], engine, gendb, tt.restartCrash, func(i int, b *BlockGen) {}) 750 chain, err = NewBlockChain(db, cacheConfig, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 751 if err != nil { 752 t.Fatalf("Failed to recreate chain: %v", err) 753 } 754 chain.InsertChain(newBlocks) 755 chain.Snapshot().Cap(newBlocks[len(newBlocks)-1].Root(), 0) 756 757 // Simulate the blockchain crash 758 // Don't call chain.Stop here, so that no snapshot 759 // journal and latest state will be committed 760 761 // Restart the chain after the crash 762 chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 763 if err != nil { 764 t.Fatalf("Failed to recreate chain: %v", err) 765 } 766 defer chain.Stop() 767 } else { 768 chain.Stop() 769 770 // Restart the chain normally 771 chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil) 772 if err != nil { 773 t.Fatalf("Failed to recreate chain: %v", err) 774 } 775 defer chain.Stop() 776 } 777 778 // Iterate over all the remaining blocks and ensure there are no gaps 779 verifyNoGaps(t, chain, true, blocks) 780 verifyCutoff(t, chain, true, blocks, tt.expCanonicalBlocks) 781 782 if head := chain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader { 783 t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader) 784 } 785 if head := chain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock { 786 t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock) 787 } 788 if head := chain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock { 789 t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock) 790 } 791 // Check the disk layer, ensure they are matched 792 block := chain.GetBlockByNumber(tt.expSnapshotBottom) 793 if block == nil { 794 t.Errorf("The correspnding block[%d] of snapshot disk layer is missing", tt.expSnapshotBottom) 795 } else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { 796 t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) 797 } 798 }