github.com/theQRL/go-zond@v0.1.1/core/blockchain_snapshot_test.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum 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 "math/big" 26 "os" 27 "path" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/theQRL/go-zond/consensus" 33 "github.com/theQRL/go-zond/consensus/ethash" 34 "github.com/theQRL/go-zond/core/rawdb" 35 "github.com/theQRL/go-zond/core/types" 36 "github.com/theQRL/go-zond/core/vm" 37 "github.com/theQRL/go-zond/params" 38 "github.com/theQRL/go-zond/zonddb" 39 ) 40 41 // snapshotTestBasic wraps the common testing fields in the snapshot tests. 42 type snapshotTestBasic struct { 43 scheme string // Disk scheme used for storing trie nodes 44 chainBlocks int // Number of blocks to generate for the canonical chain 45 snapshotBlock uint64 // Block number of the relevant snapshot disk layer 46 commitBlock uint64 // Block number for which to commit the state to disk 47 48 expCanonicalBlocks int // Number of canonical blocks expected to remain in the database (excl. genesis) 49 expHeadHeader uint64 // Block number of the expected head header 50 expHeadFastBlock uint64 // Block number of the expected head fast sync block 51 expHeadBlock uint64 // Block number of the expected head full block 52 expSnapshotBottom uint64 // The block height corresponding to the snapshot disk layer 53 54 // share fields, set in runtime 55 datadir string 56 ancient string 57 db zonddb.Database 58 genDb zonddb.Database 59 engine consensus.Engine 60 gspec *Genesis 61 } 62 63 func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Block) { 64 // Create a temporary persistent database 65 datadir := t.TempDir() 66 ancient := path.Join(datadir, "ancient") 67 68 db, err := rawdb.Open(rawdb.OpenOptions{ 69 Directory: datadir, 70 AncientsDirectory: ancient, 71 Ephemeral: true, 72 }) 73 if err != nil { 74 t.Fatalf("Failed to create persistent database: %v", err) 75 } 76 // Initialize a fresh chain 77 var ( 78 gspec = &Genesis{ 79 BaseFee: big.NewInt(params.InitialBaseFee), 80 Config: params.AllEthashProtocolChanges, 81 } 82 engine = ethash.NewFullFaker() 83 ) 84 chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(basic.scheme), gspec, nil, engine, vm.Config{}, nil, nil) 85 if err != nil { 86 t.Fatalf("Failed to create chain: %v", err) 87 } 88 genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, basic.chainBlocks, func(i int, b *BlockGen) {}) 89 90 // Insert the blocks with configured settings. 91 var breakpoints []uint64 92 if basic.commitBlock > basic.snapshotBlock { 93 breakpoints = append(breakpoints, basic.snapshotBlock, basic.commitBlock) 94 } else { 95 breakpoints = append(breakpoints, basic.commitBlock, basic.snapshotBlock) 96 } 97 var startPoint uint64 98 for _, point := range breakpoints { 99 if _, err := chain.InsertChain(blocks[startPoint:point]); err != nil { 100 t.Fatalf("Failed to import canonical chain start: %v", err) 101 } 102 startPoint = point 103 104 if basic.commitBlock > 0 && basic.commitBlock == point { 105 chain.TrieDB().Commit(blocks[point-1].Root(), false) 106 } 107 if basic.snapshotBlock > 0 && basic.snapshotBlock == point { 108 // Flushing the entire snap tree into the disk, the 109 // relevant (a) snapshot root and (b) snapshot generator 110 // will be persisted atomically. 111 chain.snaps.Cap(blocks[point-1].Root(), 0) 112 diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root() 113 if !bytes.Equal(diskRoot.Bytes(), blockRoot.Bytes()) { 114 t.Fatalf("Failed to flush disk layer change, want %x, got %x", blockRoot, diskRoot) 115 } 116 } 117 } 118 if _, err := chain.InsertChain(blocks[startPoint:]); err != nil { 119 t.Fatalf("Failed to import canonical chain tail: %v", err) 120 } 121 122 // Set runtime fields 123 basic.datadir = datadir 124 basic.ancient = ancient 125 basic.db = db 126 basic.genDb = genDb 127 basic.engine = engine 128 basic.gspec = gspec 129 return chain, blocks 130 } 131 132 func (basic *snapshotTestBasic) verify(t *testing.T, chain *BlockChain, blocks []*types.Block) { 133 // Iterate over all the remaining blocks and ensure there are no gaps 134 verifyNoGaps(t, chain, true, blocks) 135 verifyCutoff(t, chain, true, blocks, basic.expCanonicalBlocks) 136 137 if head := chain.CurrentHeader(); head.Number.Uint64() != basic.expHeadHeader { 138 t.Errorf("Head header mismatch: have %d, want %d", head.Number, basic.expHeadHeader) 139 } 140 if head := chain.CurrentSnapBlock(); head.Number.Uint64() != basic.expHeadFastBlock { 141 t.Errorf("Head fast block mismatch: have %d, want %d", head.Number, basic.expHeadFastBlock) 142 } 143 if head := chain.CurrentBlock(); head.Number.Uint64() != basic.expHeadBlock { 144 t.Errorf("Head block mismatch: have %d, want %d", head.Number, basic.expHeadBlock) 145 } 146 147 // Check the disk layer, ensure they are matched 148 block := chain.GetBlockByNumber(basic.expSnapshotBottom) 149 if block == nil { 150 t.Errorf("The corresponding block[%d] of snapshot disk layer is missing", basic.expSnapshotBottom) 151 } else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { 152 t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) 153 } 154 155 // Check the snapshot, ensure it's integrated 156 if err := chain.snaps.Verify(block.Root()); err != nil { 157 t.Errorf("The disk layer is not integrated %v", err) 158 } 159 } 160 161 //nolint:unused 162 func (basic *snapshotTestBasic) dump() string { 163 buffer := new(strings.Builder) 164 165 fmt.Fprint(buffer, "Chain:\n G") 166 for i := 0; i < basic.chainBlocks; i++ { 167 fmt.Fprintf(buffer, "->C%d", i+1) 168 } 169 fmt.Fprint(buffer, " (HEAD)\n\n") 170 171 fmt.Fprintf(buffer, "Commit: G") 172 if basic.commitBlock > 0 { 173 fmt.Fprintf(buffer, ", C%d", basic.commitBlock) 174 } 175 fmt.Fprint(buffer, "\n") 176 177 fmt.Fprintf(buffer, "Snapshot: G") 178 if basic.snapshotBlock > 0 { 179 fmt.Fprintf(buffer, ", C%d", basic.snapshotBlock) 180 } 181 fmt.Fprint(buffer, "\n") 182 183 //if crash { 184 // fmt.Fprintf(buffer, "\nCRASH\n\n") 185 //} else { 186 // fmt.Fprintf(buffer, "\nSetHead(%d)\n\n", basic.setHead) 187 //} 188 fmt.Fprintf(buffer, "------------------------------\n\n") 189 190 fmt.Fprint(buffer, "Expected in leveldb:\n G") 191 for i := 0; i < basic.expCanonicalBlocks; i++ { 192 fmt.Fprintf(buffer, "->C%d", i+1) 193 } 194 fmt.Fprintf(buffer, "\n\n") 195 fmt.Fprintf(buffer, "Expected head header : C%d\n", basic.expHeadHeader) 196 fmt.Fprintf(buffer, "Expected head fast block: C%d\n", basic.expHeadFastBlock) 197 if basic.expHeadBlock == 0 { 198 fmt.Fprintf(buffer, "Expected head block : G\n") 199 } else { 200 fmt.Fprintf(buffer, "Expected head block : C%d\n", basic.expHeadBlock) 201 } 202 if basic.expSnapshotBottom == 0 { 203 fmt.Fprintf(buffer, "Expected snapshot disk : G\n") 204 } else { 205 fmt.Fprintf(buffer, "Expected snapshot disk : C%d\n", basic.expSnapshotBottom) 206 } 207 return buffer.String() 208 } 209 210 func (basic *snapshotTestBasic) teardown() { 211 basic.db.Close() 212 basic.genDb.Close() 213 os.RemoveAll(basic.datadir) 214 os.RemoveAll(basic.ancient) 215 } 216 217 // snapshotTest is a test case type for normal snapshot recovery. 218 // It can be used for testing that restart Geth normally. 219 type snapshotTest struct { 220 snapshotTestBasic 221 } 222 223 func (snaptest *snapshotTest) test(t *testing.T) { 224 // It's hard to follow the test case, visualize the input 225 // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 226 // fmt.Println(tt.dump()) 227 chain, blocks := snaptest.prepare(t) 228 229 // Restart the chain normally 230 chain.Stop() 231 newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 232 if err != nil { 233 t.Fatalf("Failed to recreate chain: %v", err) 234 } 235 defer newchain.Stop() 236 237 snaptest.verify(t, newchain, blocks) 238 } 239 240 // crashSnapshotTest is a test case type for irregular snapshot recovery. 241 // It can be used for testing that restart Geth after the crash. 242 type crashSnapshotTest struct { 243 snapshotTestBasic 244 } 245 246 func (snaptest *crashSnapshotTest) test(t *testing.T) { 247 // It's hard to follow the test case, visualize the input 248 // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 249 // fmt.Println(tt.dump()) 250 chain, blocks := snaptest.prepare(t) 251 252 // Pull the plug on the database, simulating a hard crash 253 db := chain.db 254 db.Close() 255 chain.stopWithoutSaving() 256 chain.triedb.Close() 257 258 // Start a new blockchain back up and see where the repair leads us 259 newdb, err := rawdb.Open(rawdb.OpenOptions{ 260 Directory: snaptest.datadir, 261 AncientsDirectory: snaptest.ancient, 262 Ephemeral: true, 263 }) 264 if err != nil { 265 t.Fatalf("Failed to reopen persistent database: %v", err) 266 } 267 defer newdb.Close() 268 269 // The interesting thing is: instead of starting the blockchain after 270 // the crash, we do restart twice here: one after the crash and one 271 // after the normal stop. It's used to ensure the broken snapshot 272 // can be detected all the time. 273 newchain, err := NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 274 if err != nil { 275 t.Fatalf("Failed to recreate chain: %v", err) 276 } 277 newchain.Stop() 278 279 newchain, err = NewBlockChain(newdb, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 280 if err != nil { 281 t.Fatalf("Failed to recreate chain: %v", err) 282 } 283 defer newchain.Stop() 284 285 snaptest.verify(t, newchain, blocks) 286 } 287 288 // gappedSnapshotTest is a test type used to test this scenario: 289 // - have a complete snapshot 290 // - restart without enabling the snapshot 291 // - insert a few blocks 292 // - restart with enabling the snapshot again 293 type gappedSnapshotTest struct { 294 snapshotTestBasic 295 gapped int // Number of blocks to insert without enabling snapshot 296 } 297 298 func (snaptest *gappedSnapshotTest) test(t *testing.T) { 299 // It's hard to follow the test case, visualize the input 300 // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 301 // fmt.Println(tt.dump()) 302 chain, blocks := snaptest.prepare(t) 303 304 // Insert blocks without enabling snapshot if gapping is required. 305 chain.Stop() 306 gappedBlocks, _ := GenerateChain(snaptest.gspec.Config, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.gapped, func(i int, b *BlockGen) {}) 307 308 // Insert a few more blocks without enabling snapshot 309 var cacheConfig = &CacheConfig{ 310 TrieCleanLimit: 256, 311 TrieDirtyLimit: 256, 312 TrieTimeLimit: 5 * time.Minute, 313 SnapshotLimit: 0, 314 StateScheme: snaptest.scheme, 315 } 316 newchain, err := NewBlockChain(snaptest.db, cacheConfig, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 317 if err != nil { 318 t.Fatalf("Failed to recreate chain: %v", err) 319 } 320 newchain.InsertChain(gappedBlocks) 321 newchain.Stop() 322 323 // Restart the chain with enabling the snapshot 324 newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 325 if err != nil { 326 t.Fatalf("Failed to recreate chain: %v", err) 327 } 328 defer newchain.Stop() 329 330 snaptest.verify(t, newchain, blocks) 331 } 332 333 // setHeadSnapshotTest is the test type used to test this scenario: 334 // - have a complete snapshot 335 // - set the head to a lower point 336 // - restart 337 type setHeadSnapshotTest struct { 338 snapshotTestBasic 339 setHead uint64 // Block number to set head back to 340 } 341 342 func (snaptest *setHeadSnapshotTest) test(t *testing.T) { 343 // It's hard to follow the test case, visualize the input 344 // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 345 // fmt.Println(tt.dump()) 346 chain, blocks := snaptest.prepare(t) 347 348 // Rewind the chain if setHead operation is required. 349 chain.SetHead(snaptest.setHead) 350 chain.Stop() 351 352 newchain, err := NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 353 if err != nil { 354 t.Fatalf("Failed to recreate chain: %v", err) 355 } 356 defer newchain.Stop() 357 358 snaptest.verify(t, newchain, blocks) 359 } 360 361 // wipeCrashSnapshotTest is the test type used to test this scenario: 362 // - have a complete snapshot 363 // - restart, insert more blocks without enabling the snapshot 364 // - restart again with enabling the snapshot 365 // - crash 366 type wipeCrashSnapshotTest struct { 367 snapshotTestBasic 368 newBlocks int 369 } 370 371 func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { 372 // It's hard to follow the test case, visualize the input 373 // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) 374 // fmt.Println(tt.dump()) 375 chain, blocks := snaptest.prepare(t) 376 377 // Firstly, stop the chain properly, with all snapshot journal 378 // and state committed. 379 chain.Stop() 380 381 config := &CacheConfig{ 382 TrieCleanLimit: 256, 383 TrieDirtyLimit: 256, 384 TrieTimeLimit: 5 * time.Minute, 385 SnapshotLimit: 0, 386 StateScheme: snaptest.scheme, 387 } 388 newchain, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 389 if err != nil { 390 t.Fatalf("Failed to recreate chain: %v", err) 391 } 392 newBlocks, _ := GenerateChain(snaptest.gspec.Config, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.newBlocks, func(i int, b *BlockGen) {}) 393 newchain.InsertChain(newBlocks) 394 newchain.Stop() 395 396 // Restart the chain, the wiper should start working 397 config = &CacheConfig{ 398 TrieCleanLimit: 256, 399 TrieDirtyLimit: 256, 400 TrieTimeLimit: 5 * time.Minute, 401 SnapshotLimit: 256, 402 SnapshotWait: false, // Don't wait rebuild 403 StateScheme: snaptest.scheme, 404 } 405 tmp, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 406 if err != nil { 407 t.Fatalf("Failed to recreate chain: %v", err) 408 } 409 410 // Simulate the blockchain crash. 411 tmp.triedb.Close() 412 tmp.stopWithoutSaving() 413 414 newchain, err = NewBlockChain(snaptest.db, DefaultCacheConfigWithScheme(snaptest.scheme), snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) 415 if err != nil { 416 t.Fatalf("Failed to recreate chain: %v", err) 417 } 418 snaptest.verify(t, newchain, blocks) 419 newchain.Stop() 420 } 421 422 // Tests a Geth restart with valid snapshot. Before the shutdown, all snapshot 423 // journal will be persisted correctly. In this case no snapshot recovery is 424 // required. 425 func TestRestartWithNewSnapshot(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 438 // 439 // Expected head header : C8 440 // Expected head fast block: C8 441 // Expected head block : C8 442 // Expected snapshot disk : G 443 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 444 test := &snapshotTest{ 445 snapshotTestBasic{ 446 scheme: scheme, 447 chainBlocks: 8, 448 snapshotBlock: 0, 449 commitBlock: 0, 450 expCanonicalBlocks: 8, 451 expHeadHeader: 8, 452 expHeadFastBlock: 8, 453 expHeadBlock: 8, 454 expSnapshotBottom: 0, // Initial disk layer built from genesis 455 }, 456 } 457 test.test(t) 458 test.teardown() 459 } 460 } 461 462 // Tests a Geth was crashed and restarts with a broken snapshot. In this case the 463 // chain head should be rewound to the point with available state. And also the 464 // new head should must be lower than disk layer. But there is no committed point 465 // so the chain should be rewound to genesis and the disk layer should be left 466 // for recovery. 467 func TestNoCommitCrashWithNewSnapshot(t *testing.T) { 468 // Chain: 469 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 470 // 471 // Commit: G 472 // Snapshot: G, C4 473 // 474 // CRASH 475 // 476 // ------------------------------ 477 // 478 // Expected in leveldb: 479 // G->C1->C2->C3->C4->C5->C6->C7->C8 480 // 481 // Expected head header : C8 482 // Expected head fast block: C8 483 // Expected head block : G 484 // Expected snapshot disk : C4 485 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 486 test := &crashSnapshotTest{ 487 snapshotTestBasic{ 488 scheme: scheme, 489 chainBlocks: 8, 490 snapshotBlock: 4, 491 commitBlock: 0, 492 expCanonicalBlocks: 8, 493 expHeadHeader: 8, 494 expHeadFastBlock: 8, 495 expHeadBlock: 0, 496 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 497 }, 498 } 499 test.test(t) 500 test.teardown() 501 } 502 } 503 504 // Tests a Geth was crashed and restarts with a broken snapshot. In this case the 505 // chain head should be rewound to the point with available state. And also the 506 // new head should must be lower than disk layer. But there is only a low committed 507 // point so the chain should be rewound to committed point and the disk layer 508 // should be left for recovery. 509 func TestLowCommitCrashWithNewSnapshot(t *testing.T) { 510 // Chain: 511 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 512 // 513 // Commit: G, C2 514 // Snapshot: G, C4 515 // 516 // CRASH 517 // 518 // ------------------------------ 519 // 520 // Expected in leveldb: 521 // G->C1->C2->C3->C4->C5->C6->C7->C8 522 // 523 // Expected head header : C8 524 // Expected head fast block: C8 525 // Expected head block : C2 526 // Expected snapshot disk : C4 527 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 528 test := &crashSnapshotTest{ 529 snapshotTestBasic{ 530 scheme: scheme, 531 chainBlocks: 8, 532 snapshotBlock: 4, 533 commitBlock: 2, 534 expCanonicalBlocks: 8, 535 expHeadHeader: 8, 536 expHeadFastBlock: 8, 537 expHeadBlock: 2, 538 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 539 }, 540 } 541 test.test(t) 542 test.teardown() 543 } 544 } 545 546 // Tests a Geth was crashed and restarts with a broken snapshot. In this case 547 // the chain head should be rewound to the point with available state. And also 548 // the new head should must be lower than disk layer. But there is only a high 549 // committed point so the chain should be rewound to genesis and the disk layer 550 // should be left for recovery. 551 func TestHighCommitCrashWithNewSnapshot(t *testing.T) { 552 // Chain: 553 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 554 // 555 // Commit: G, C6 556 // Snapshot: G, C4 557 // 558 // CRASH 559 // 560 // ------------------------------ 561 // 562 // Expected in leveldb: 563 // G->C1->C2->C3->C4->C5->C6->C7->C8 564 // 565 // Expected head header : C8 566 // Expected head fast block: C8 567 // Expected head block : G 568 // Expected snapshot disk : C4 569 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 570 expHead := uint64(0) 571 if scheme == rawdb.PathScheme { 572 expHead = uint64(4) 573 } 574 test := &crashSnapshotTest{ 575 snapshotTestBasic{ 576 scheme: scheme, 577 chainBlocks: 8, 578 snapshotBlock: 4, 579 commitBlock: 6, 580 expCanonicalBlocks: 8, 581 expHeadHeader: 8, 582 expHeadFastBlock: 8, 583 expHeadBlock: expHead, 584 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 585 }, 586 } 587 test.test(t) 588 test.teardown() 589 } 590 } 591 592 // Tests a Geth was running with snapshot enabled. Then restarts without 593 // enabling snapshot and after that re-enable the snapshot again. In this 594 // case the snapshot should be rebuilt with latest chain head. 595 func TestGappedNewSnapshot(t *testing.T) { 596 // Chain: 597 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 598 // 599 // Commit: G 600 // Snapshot: G 601 // 602 // SetHead(0) 603 // 604 // ------------------------------ 605 // 606 // Expected in leveldb: 607 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 608 // 609 // Expected head header : C10 610 // Expected head fast block: C10 611 // Expected head block : C10 612 // Expected snapshot disk : C10 613 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 614 test := &gappedSnapshotTest{ 615 snapshotTestBasic: snapshotTestBasic{ 616 scheme: scheme, 617 chainBlocks: 8, 618 snapshotBlock: 0, 619 commitBlock: 0, 620 expCanonicalBlocks: 10, 621 expHeadHeader: 10, 622 expHeadFastBlock: 10, 623 expHeadBlock: 10, 624 expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD 625 }, 626 gapped: 2, 627 } 628 test.test(t) 629 test.teardown() 630 } 631 } 632 633 // Tests the Geth was running with snapshot enabled and resetHead is applied. 634 // In this case the head is rewound to the target(with state available). After 635 // that the chain is restarted and the original disk layer is kept. 636 func TestSetHeadWithNewSnapshot(t *testing.T) { 637 // Chain: 638 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 639 // 640 // Commit: G 641 // Snapshot: G 642 // 643 // SetHead(4) 644 // 645 // ------------------------------ 646 // 647 // Expected in leveldb: 648 // G->C1->C2->C3->C4 649 // 650 // Expected head header : C4 651 // Expected head fast block: C4 652 // Expected head block : C4 653 // Expected snapshot disk : G 654 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 655 test := &setHeadSnapshotTest{ 656 snapshotTestBasic: snapshotTestBasic{ 657 scheme: scheme, 658 chainBlocks: 8, 659 snapshotBlock: 0, 660 commitBlock: 0, 661 expCanonicalBlocks: 4, 662 expHeadHeader: 4, 663 expHeadFastBlock: 4, 664 expHeadBlock: 4, 665 expSnapshotBottom: 0, // The initial disk layer is built from the genesis 666 }, 667 setHead: 4, 668 } 669 test.test(t) 670 test.teardown() 671 } 672 } 673 674 // Tests the Geth was running with a complete snapshot and then imports a few 675 // more new blocks on top without enabling the snapshot. After the restart, 676 // crash happens. Check everything is ok after the restart. 677 func TestRecoverSnapshotFromWipingCrash(t *testing.T) { 678 // Chain: 679 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 680 // 681 // Commit: G 682 // Snapshot: G 683 // 684 // SetHead(0) 685 // 686 // ------------------------------ 687 // 688 // Expected in leveldb: 689 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 690 // 691 // Expected head header : C10 692 // Expected head fast block: C10 693 // Expected head block : C8 694 // Expected snapshot disk : C10 695 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 696 test := &wipeCrashSnapshotTest{ 697 snapshotTestBasic: snapshotTestBasic{ 698 scheme: scheme, 699 chainBlocks: 8, 700 snapshotBlock: 4, 701 commitBlock: 0, 702 expCanonicalBlocks: 10, 703 expHeadHeader: 10, 704 expHeadFastBlock: 10, 705 expHeadBlock: 10, 706 expSnapshotBottom: 10, 707 }, 708 newBlocks: 2, 709 } 710 test.test(t) 711 test.teardown() 712 } 713 }