github.com/MetalBlockchain/subnet-evm@v0.4.9/core/blockchain_repair_test.go (about) 1 // (c) 2019-2021, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2020 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 // Tests that abnormal program termination (i.e.crash) and restart doesn't leave 28 // the database in some strange state with gaps in the chain, nor with block data 29 // dangling in the future. 30 31 package core 32 33 import ( 34 "math/big" 35 "testing" 36 37 "github.com/MetalBlockchain/subnet-evm/consensus/dummy" 38 "github.com/MetalBlockchain/subnet-evm/core/rawdb" 39 "github.com/MetalBlockchain/subnet-evm/core/types" 40 "github.com/MetalBlockchain/subnet-evm/core/vm" 41 "github.com/MetalBlockchain/subnet-evm/params" 42 "github.com/ethereum/go-ethereum/common" 43 ) 44 45 // rewindTest is a test case for chain rollback upon user request. 46 type rewindTest struct { 47 canonicalBlocks int // Number of blocks to generate for the canonical chain (heavier) 48 sidechainBlocks int // Number of blocks to generate for the side chain (lighter) 49 commitBlock uint64 // Block number for which to commit the state to disk 50 51 expCanonicalBlocks int // Number of canonical blocks expected to remain in the database (excl. genesis) 52 expSidechainBlocks int // Number of sidechain blocks expected to remain in the database (excl. genesis) 53 expHeadBlock uint64 // Block number of the expected head full block 54 } 55 56 // Tests a recovery for a short canonical chain where a recent block was already 57 // committed to disk and then the process crashed. In this case we expect the full 58 // chain to be rolled back to the committed block, but the chain data itself left 59 // in the database for replaying. 60 func TestShortRepair(t *testing.T) { testShortRepair(t, false) } 61 func TestShortRepairWithSnapshots(t *testing.T) { testShortRepair(t, true) } 62 63 func testShortRepair(t *testing.T, snapshots bool) { 64 // Chain: 65 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 66 // 67 // Commit: G, C4 68 // 69 // CRASH 70 // 71 // ------------------------------ 72 // 73 // Expected in leveldb: 74 // G->C1->C2->C3->C4->C5->C6->C7->C8 75 // 76 // Expected head block : C4 (C0 with no snapshots) 77 rt := &rewindTest{ 78 canonicalBlocks: 8, 79 sidechainBlocks: 0, 80 commitBlock: 4, 81 expCanonicalBlocks: 8, 82 expSidechainBlocks: 0, 83 expHeadBlock: 0, 84 } 85 if snapshots { 86 rt.expHeadBlock = 4 87 } 88 testRepair(t, rt, snapshots) 89 } 90 91 // Tests a recovery for a short canonical chain and a shorter side chain, where a 92 // recent block was already committed to disk and then the process crashed. In this 93 // test scenario the side chain is below the committed block. In this case we expect 94 // the canonical chain to be rolled back to the committed block, but the chain data 95 // itself left in the database for replaying. 96 func TestShortOldForkedRepair(t *testing.T) { testShortOldForkedRepair(t, false) } 97 func TestShortOldForkedRepairWithSnapshots(t *testing.T) { testShortOldForkedRepair(t, true) } 98 99 func testShortOldForkedRepair(t *testing.T, snapshots bool) { 100 // Chain: 101 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 102 // └->S1->S2->S3 103 // 104 // Commit: G, C4 105 // 106 // CRASH 107 // 108 // ------------------------------ 109 // 110 // Expected in leveldb: 111 // G->C1->C2->C3->C4->C5->C6->C7->C8 112 // └->S1->S2->S3 113 // 114 // Expected head block : C4 (C0 with no snapshots) 115 rt := &rewindTest{ 116 canonicalBlocks: 8, 117 sidechainBlocks: 3, 118 commitBlock: 4, 119 expCanonicalBlocks: 8, 120 expSidechainBlocks: 3, 121 expHeadBlock: 0, 122 } 123 if snapshots { 124 rt.expHeadBlock = 4 125 } 126 testRepair(t, rt, snapshots) 127 } 128 129 // Tests a recovery for a short canonical chain and a shorter side chain, where a 130 // recent block was already committed to disk and then the process crashed. In this 131 // test scenario the side chain reaches above the committed block. In this case we 132 // expect the canonical chain to be rolled back to the committed block, but the 133 // chain data itself left in the database for replaying. 134 func TestShortNewlyForkedRepair(t *testing.T) { testShortNewlyForkedRepair(t, false) } 135 func TestShortNewlyForkedRepairWithSnapshots(t *testing.T) { testShortNewlyForkedRepair(t, true) } 136 137 func testShortNewlyForkedRepair(t *testing.T, snapshots bool) { 138 // Chain: 139 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 140 // └->S1->S2->S3->S4->S5->S6 141 // 142 // Commit: G, C4 143 // 144 // CRASH 145 // 146 // ------------------------------ 147 // 148 // Expected in leveldb: 149 // G->C1->C2->C3->C4->C5->C6->C7->C8 150 // └->S1->S2->S3->S4->S5->S6 151 // 152 // Expected head block : C4 (C0 with no snapshots) 153 rt := &rewindTest{ 154 canonicalBlocks: 8, 155 sidechainBlocks: 6, 156 commitBlock: 4, 157 expCanonicalBlocks: 8, 158 expSidechainBlocks: 6, 159 expHeadBlock: 0, 160 } 161 if snapshots { 162 rt.expHeadBlock = 4 163 } 164 testRepair(t, rt, snapshots) 165 } 166 167 // Tests a recovery for a short canonical chain and a longer side chain, where a 168 // recent block was already committed to disk and then the process crashed. In this 169 // case we expect the canonical chain to be rolled back to the committed block, but 170 // the chain data itself left in the database for replaying. 171 func TestShortReorgedRepair(t *testing.T) { testShortReorgedRepair(t, false) } 172 func TestShortReorgedRepairWithSnapshots(t *testing.T) { testShortReorgedRepair(t, true) } 173 174 func testShortReorgedRepair(t *testing.T, snapshots bool) { 175 // Chain: 176 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 177 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 178 // 179 // Commit: G, C4 180 // 181 // CRASH 182 // 183 // ------------------------------ 184 // 185 // Expected in leveldb: 186 // G->C1->C2->C3->C4->C5->C6->C7->C8 187 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10 188 // 189 // Expected head block : C4 (C0 with no snapshots) 190 rt := &rewindTest{ 191 canonicalBlocks: 8, 192 sidechainBlocks: 10, 193 commitBlock: 4, 194 expCanonicalBlocks: 8, 195 expSidechainBlocks: 10, 196 expHeadBlock: 0, 197 } 198 if snapshots { 199 rt.expHeadBlock = 4 200 } 201 testRepair(t, rt, snapshots) 202 } 203 204 // Tests a recovery for a long canonical chain where a recent block was already 205 // committed to disk and then the process crashed. In this case we expect the chain 206 // to be rolled back to the committed block, but the chain data itself left in the 207 // database for replaying. 208 func TestLongShallowRepair(t *testing.T) { testLongShallowRepair(t, false) } 209 func TestLongShallowRepairWithSnapshots(t *testing.T) { testLongShallowRepair(t, true) } 210 211 func testLongShallowRepair(t *testing.T, snapshots bool) { 212 // Chain: 213 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) 214 // 215 // Commit: G, C4 216 // 217 // CRASH 218 // 219 // ------------------------------ 220 // 221 // Expected in leveldb: 222 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 223 // 224 // Expected head block : C4 (C0 with no snapshots) 225 rt := &rewindTest{ 226 canonicalBlocks: 18, 227 sidechainBlocks: 0, 228 commitBlock: 4, 229 expCanonicalBlocks: 18, 230 expSidechainBlocks: 0, 231 expHeadBlock: 0, 232 } 233 if snapshots { 234 rt.expHeadBlock = 4 235 } 236 testRepair(t, rt, snapshots) 237 } 238 239 // Tests a recovery for a long canonical chain where a recent block was already committed 240 // to disk and then the process crashed. In this case we expect the chain to be rolled 241 // back to the committed block, but the chain data itself left in the database for replaying. 242 func TestLongDeepRepair(t *testing.T) { testLongDeepRepair(t, false) } 243 func TestLongDeepRepairWithSnapshots(t *testing.T) { testLongDeepRepair(t, true) } 244 245 func testLongDeepRepair(t *testing.T, snapshots bool) { 246 // Chain: 247 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) 248 // 249 // Commit: G, C4 250 // 251 // CRASH 252 // 253 // ------------------------------ 254 // 255 // Expected in leveldb: none 256 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 257 // 258 // Expected head block : C4 (C0 with no snapshots) 259 rt := &rewindTest{ 260 canonicalBlocks: 24, 261 sidechainBlocks: 0, 262 commitBlock: 4, 263 expCanonicalBlocks: 24, 264 expSidechainBlocks: 0, 265 expHeadBlock: 0, 266 } 267 if snapshots { 268 rt.expHeadBlock = 4 269 } 270 testRepair(t, rt, snapshots) 271 } 272 273 // Tests a recovery for a long canonical chain with a shorter side chain, where a recent 274 // block was already committed to disk and then the process crashed. In this test scenario 275 // the side chain is below the committed block. In this case we expect the chain to be 276 // rolled back to the committed block, but the chain data itself left in the database 277 // for replaying. 278 func TestLongOldForkedShallowRepair(t *testing.T) { 279 testLongOldForkedShallowRepair(t, false) 280 } 281 func TestLongOldForkedShallowRepairWithSnapshots(t *testing.T) { 282 testLongOldForkedShallowRepair(t, true) 283 } 284 285 func testLongOldForkedShallowRepair(t *testing.T, snapshots bool) { 286 // Chain: 287 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) 288 // └->S1->S2->S3 289 // 290 // Commit: G, C4 291 // 292 // CRASH 293 // 294 // ------------------------------ 295 // 296 // Expected in leveldb: 297 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 298 // └->S1->S2->S3 299 // 300 // Expected head block : C4 (C0 with no snapshots) 301 rt := &rewindTest{ 302 canonicalBlocks: 18, 303 sidechainBlocks: 3, 304 commitBlock: 4, 305 expCanonicalBlocks: 18, 306 expSidechainBlocks: 3, 307 expHeadBlock: 0, 308 } 309 if snapshots { 310 rt.expHeadBlock = 4 311 } 312 testRepair(t, rt, snapshots) 313 } 314 315 // Tests a recovery for a long canonical chain a shorter side chain, where a recent block 316 // was already committed to disk and then the process crashed. In this test scenario the side 317 // chain is below the committed block. In this case we expect the canonical chain to be 318 // rolled back to the committed block, but the chain data itself left in the database for replaying. 319 func TestLongOldForkedDeepRepair(t *testing.T) { testLongOldForkedDeepRepair(t, false) } 320 func TestLongOldForkedDeepRepairWithSnapshots(t *testing.T) { testLongOldForkedDeepRepair(t, true) } 321 322 func testLongOldForkedDeepRepair(t *testing.T, snapshots bool) { 323 // Chain: 324 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) 325 // └->S1->S2->S3 326 // 327 // Commit: G, C4 328 // 329 // CRASH 330 // 331 // ------------------------------ 332 // 333 // Expected in leveldb: 334 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 335 // └->S1->S2->S3 336 // 337 // Expected head block : C4 (C0 with no snapshots) 338 rt := &rewindTest{ 339 canonicalBlocks: 24, 340 sidechainBlocks: 3, 341 commitBlock: 4, 342 expCanonicalBlocks: 24, 343 expSidechainBlocks: 3, 344 expHeadBlock: 0, 345 } 346 if snapshots { 347 rt.expHeadBlock = 4 348 } 349 testRepair(t, rt, snapshots) 350 } 351 352 // Tests a recovery for a long canonical chain with a shorter side chain, where a recent 353 // block was already committed to disk and then the process crashed. In this test scenario 354 // the side chain is above the committed block. In this case we expect the chain to be 355 // rolled back to the committed block, but the chain data itself left in the database for replaying. 356 func TestLongNewerForkedShallowRepair(t *testing.T) { 357 testLongNewerForkedShallowRepair(t, false) 358 } 359 func TestLongNewerForkedShallowRepairWithSnapshots(t *testing.T) { 360 testLongNewerForkedShallowRepair(t, true) 361 } 362 363 func testLongNewerForkedShallowRepair(t *testing.T, snapshots bool) { 364 // Chain: 365 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) 366 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12 367 // 368 // Commit: G, C4 369 // 370 // CRASH 371 // 372 // ------------------------------ 373 // 374 // Expected in leveldb: 375 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 376 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12 377 // 378 // Expected head block : C4 (C0 with no snapshots) 379 rt := &rewindTest{ 380 canonicalBlocks: 18, 381 sidechainBlocks: 12, 382 commitBlock: 4, 383 expCanonicalBlocks: 18, 384 expSidechainBlocks: 12, 385 expHeadBlock: 0, 386 } 387 if snapshots { 388 rt.expHeadBlock = 4 389 } 390 testRepair(t, rt, snapshots) 391 } 392 393 // Tests a recovery for a long canonical chain with a shorter side chain, where a recent block 394 // was already committed to disk and then the process crashed. In this test scenario the side 395 // chain is above the committed block. In this case we expect the canonical chain to be rolled 396 // back to the committed block, but the chain data itself left in the database for replaying. 397 func TestLongNewerForkedDeepRepair(t *testing.T) { testLongNewerForkedDeepRepair(t, false) } 398 func TestLongNewerForkedDeepRepairWithSnapshots(t *testing.T) { testLongNewerForkedDeepRepair(t, true) } 399 400 func testLongNewerForkedDeepRepair(t *testing.T, snapshots bool) { 401 // Chain: 402 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) 403 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12 404 // 405 // Commit: G, C4 406 // 407 // CRASH 408 // 409 // ------------------------------ 410 // 411 // Expected in leveldb: 412 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 413 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12 414 // 415 // Expected head block : C4 (C0 with no snapshots) 416 rt := &rewindTest{ 417 canonicalBlocks: 24, 418 sidechainBlocks: 12, 419 commitBlock: 4, 420 expCanonicalBlocks: 24, 421 expSidechainBlocks: 12, 422 expHeadBlock: 0, 423 } 424 if snapshots { 425 rt.expHeadBlock = 4 426 } 427 testRepair(t, rt, snapshots) 428 } 429 430 // Tests a recovery for a long canonical chain with a longer side chain, where a recent block 431 // was already committed to disk and then the process crashed. In this case we expect the chain to be 432 // rolled back to the committed block, but the chain data itself left in the database for replaying. 433 func TestLongReorgedShallowRepair(t *testing.T) { testLongReorgedShallowRepair(t, false) } 434 func TestLongReorgedShallowRepairWithSnapshots(t *testing.T) { testLongReorgedShallowRepair(t, true) } 435 436 func testLongReorgedShallowRepair(t *testing.T, snapshots bool) { 437 // Chain: 438 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD) 439 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 440 // 441 // Commit: G, C4 442 // 443 // CRASH 444 // 445 // ------------------------------ 446 // 447 // Expected in leveldb: 448 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 449 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 450 // 451 // Expected head block : C4 (C0 with no snapshots) 452 rt := &rewindTest{ 453 canonicalBlocks: 18, 454 sidechainBlocks: 26, 455 commitBlock: 4, 456 expCanonicalBlocks: 18, 457 expSidechainBlocks: 26, 458 expHeadBlock: 0, 459 } 460 if snapshots { 461 rt.expHeadBlock = 4 462 } 463 testRepair(t, rt, snapshots) 464 } 465 466 // Tests a recovery for a long canonical chain with a longer side chain, where a recent block 467 // was already committed to disk and then the process crashed. In this case we expect the canonical 468 // chains to be rolled back to the committed block, but the chain data itself left in the database 469 // for replaying. 470 func TestLongReorgedDeepRepair(t *testing.T) { testLongReorgedDeepRepair(t, false) } 471 func TestLongReorgedDeepRepairWithSnapshots(t *testing.T) { testLongReorgedDeepRepair(t, true) } 472 473 func testLongReorgedDeepRepair(t *testing.T, snapshots bool) { 474 // Chain: 475 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD) 476 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 477 // 478 // Commit: G, C4 479 // 480 // CRASH 481 // 482 // ------------------------------ 483 // 484 // Expected in leveldb: 485 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 486 // └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26 487 // 488 // Expected head block : C4 (C0 with no snapshots) 489 rt := &rewindTest{ 490 canonicalBlocks: 24, 491 sidechainBlocks: 26, 492 commitBlock: 4, 493 expCanonicalBlocks: 24, 494 expSidechainBlocks: 26, 495 expHeadBlock: 0, 496 } 497 if snapshots { 498 rt.expHeadBlock = 4 499 } 500 testRepair(t, rt, snapshots) 501 } 502 503 func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { 504 // It's hard to follow the test case, visualize the input 505 //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 506 // fmt.Println(tt.dump(true)) 507 508 // Create a temporary persistent database 509 datadir := t.TempDir() 510 511 db, err := rawdb.NewLevelDBDatabase(datadir, 0, 0, "", false) 512 if err != nil { 513 t.Fatalf("Failed to create persistent database: %v", err) 514 } 515 defer db.Close() // Might double close, should be fine 516 517 // Initialize a fresh chain 518 var ( 519 genesis = (&Genesis{Config: params.TestChainConfig, BaseFee: big.NewInt(params.TestInitialBaseFee)}).MustCommit(db) 520 engine = dummy.NewFullFaker() 521 config = &CacheConfig{ 522 TrieCleanLimit: 256, 523 TrieDirtyLimit: 256, 524 SnapshotLimit: 0, // Disable snapshot by default 525 } 526 ) 527 defer engine.Close() 528 if snapshots { 529 config.SnapshotLimit = 256 530 } 531 chain, err := NewBlockChain(db, config, params.TestChainConfig, engine, vm.Config{}, common.Hash{}) 532 if err != nil { 533 t.Fatalf("Failed to create chain: %v", err) 534 } 535 lastAcceptedHash := chain.GetBlockByNumber(0).Hash() 536 537 // If sidechain blocks are needed, make a light chain and import it 538 var sideblocks types.Blocks 539 if tt.sidechainBlocks > 0 { 540 sideblocks, _, _ = GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, 10, func(i int, b *BlockGen) { 541 b.SetCoinbase(common.Address{0x01}) 542 }) 543 if _, err := chain.InsertChain(sideblocks); err != nil { 544 t.Fatalf("Failed to import side chain: %v", err) 545 } 546 } 547 canonblocks, _, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, 10, func(i int, b *BlockGen) { 548 b.SetCoinbase(common.Address{0x02}) 549 b.SetDifficulty(big.NewInt(1000000)) 550 }) 551 if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil { 552 t.Fatalf("Failed to import canonical chain start: %v", err) 553 } 554 if tt.commitBlock > 0 { 555 if snapshots { 556 for i := uint64(0); i < tt.commitBlock; i++ { 557 if err := chain.Accept(canonblocks[i]); err != nil { 558 t.Fatalf("Failed to accept block %v: %v", i, err) 559 } 560 lastAcceptedHash = canonblocks[i].Hash() 561 } 562 chain.DrainAcceptorQueue() 563 } 564 } 565 if _, err := chain.InsertChain(canonblocks[tt.commitBlock:]); err != nil { 566 t.Fatalf("Failed to import canonical chain tail: %v", err) 567 } 568 569 // Pull the plug on the database, simulating a hard crash 570 db.Close() 571 572 // Start a new blockchain back up and see where the repair leads us 573 db, err = rawdb.NewLevelDBDatabase(datadir, 0, 0, "", false) 574 if err != nil { 575 t.Fatalf("Failed to reopen persistent database: %v", err) 576 } 577 defer db.Close() 578 579 newChain, err := NewBlockChain(db, config, params.TestChainConfig, engine, vm.Config{}, lastAcceptedHash) 580 if err != nil { 581 t.Fatalf("Failed to recreate chain: %v", err) 582 } 583 defer newChain.Stop() 584 585 // Iterate over all the remaining blocks and ensure there are no gaps 586 verifyNoGaps(t, newChain, true, canonblocks) 587 verifyNoGaps(t, newChain, false, sideblocks) 588 verifyCutoff(t, newChain, true, canonblocks, tt.expCanonicalBlocks) 589 verifyCutoff(t, newChain, false, sideblocks, tt.expSidechainBlocks) 590 591 if head := newChain.CurrentHeader(); head.Number.Uint64() != tt.expHeadBlock { 592 t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadBlock) 593 } 594 if head := newChain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock { 595 t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock) 596 } 597 }