github.com/ethereum/go-ethereum@v1.16.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/filepath" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/ethereum/go-ethereum/consensus" 33 "github.com/ethereum/go-ethereum/consensus/ethash" 34 "github.com/ethereum/go-ethereum/core/rawdb" 35 "github.com/ethereum/go-ethereum/core/types" 36 "github.com/ethereum/go-ethereum/ethdb" 37 "github.com/ethereum/go-ethereum/ethdb/pebble" 38 "github.com/ethereum/go-ethereum/params" 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 ethdb.Database 58 genDb ethdb.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 := filepath.Join(datadir, "ancient") 67 68 pdb, err := pebble.New(datadir, 0, 0, "", false) 69 if err != nil { 70 t.Fatalf("Failed to create persistent key-value database: %v", err) 71 } 72 db, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: ancient}) 73 if err != nil { 74 t.Fatalf("Failed to create persistent freezer 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, gspec, engine, DefaultConfig().WithStateScheme(basic.scheme).WithNoAsyncFlush(true)) 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 && basic.scheme == rawdb.HashScheme { 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 basic.scheme == rawdb.HashScheme { 152 if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) { 153 t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot()) 154 } 155 } 156 157 // Check the snapshot, ensure it's integrated 158 if basic.scheme == rawdb.HashScheme { 159 if err := chain.snaps.Verify(block.Root()); err != nil { 160 t.Errorf("The disk layer is not integrated %v", err) 161 } 162 } 163 } 164 165 //nolint:unused 166 func (basic *snapshotTestBasic) dump() string { 167 buffer := new(strings.Builder) 168 169 fmt.Fprint(buffer, "Chain:\n G") 170 for i := 0; i < basic.chainBlocks; i++ { 171 fmt.Fprintf(buffer, "->C%d", i+1) 172 } 173 fmt.Fprint(buffer, " (HEAD)\n\n") 174 175 fmt.Fprintf(buffer, "Commit: G") 176 if basic.commitBlock > 0 { 177 fmt.Fprintf(buffer, ", C%d", basic.commitBlock) 178 } 179 fmt.Fprint(buffer, "\n") 180 181 fmt.Fprintf(buffer, "Snapshot: G") 182 if basic.snapshotBlock > 0 { 183 fmt.Fprintf(buffer, ", C%d", basic.snapshotBlock) 184 } 185 fmt.Fprint(buffer, "\n") 186 187 //if crash { 188 // fmt.Fprintf(buffer, "\nCRASH\n\n") 189 //} else { 190 // fmt.Fprintf(buffer, "\nSetHead(%d)\n\n", basic.setHead) 191 //} 192 fmt.Fprintf(buffer, "------------------------------\n\n") 193 194 fmt.Fprint(buffer, "Expected in leveldb:\n G") 195 for i := 0; i < basic.expCanonicalBlocks; i++ { 196 fmt.Fprintf(buffer, "->C%d", i+1) 197 } 198 fmt.Fprintf(buffer, "\n\n") 199 fmt.Fprintf(buffer, "Expected head header : C%d\n", basic.expHeadHeader) 200 fmt.Fprintf(buffer, "Expected head fast block: C%d\n", basic.expHeadFastBlock) 201 if basic.expHeadBlock == 0 { 202 fmt.Fprintf(buffer, "Expected head block : G\n") 203 } else { 204 fmt.Fprintf(buffer, "Expected head block : C%d\n", basic.expHeadBlock) 205 } 206 if basic.expSnapshotBottom == 0 { 207 fmt.Fprintf(buffer, "Expected snapshot disk : G\n") 208 } else { 209 fmt.Fprintf(buffer, "Expected snapshot disk : C%d\n", basic.expSnapshotBottom) 210 } 211 return buffer.String() 212 } 213 214 func (basic *snapshotTestBasic) teardown() { 215 basic.db.Close() 216 basic.genDb.Close() 217 os.RemoveAll(basic.datadir) 218 os.RemoveAll(basic.ancient) 219 } 220 221 // snapshotTest is a test case type for normal snapshot recovery. 222 // It can be used for testing that restart Geth normally. 223 type snapshotTest struct { 224 snapshotTestBasic 225 } 226 227 func (snaptest *snapshotTest) test(t *testing.T) { 228 // It's hard to follow the test case, visualize the input 229 // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) 230 // fmt.Println(snaptest.dump()) 231 chain, blocks := snaptest.prepare(t) 232 233 // Restart the chain normally 234 chain.Stop() 235 newchain, err := NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, DefaultConfig().WithStateScheme(snaptest.scheme)) 236 if err != nil { 237 t.Fatalf("Failed to recreate chain: %v", err) 238 } 239 defer newchain.Stop() 240 241 snaptest.verify(t, newchain, blocks) 242 } 243 244 // crashSnapshotTest is a test case type for irregular snapshot recovery. 245 // It can be used for testing that restart Geth after the crash. 246 type crashSnapshotTest struct { 247 snapshotTestBasic 248 } 249 250 func (snaptest *crashSnapshotTest) test(t *testing.T) { 251 // It's hard to follow the test case, visualize the input 252 // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) 253 // fmt.Println(snaptest.dump()) 254 chain, blocks := snaptest.prepare(t) 255 256 // Pull the plug on the database, simulating a hard crash 257 db := chain.db 258 db.Close() 259 chain.stopWithoutSaving() 260 chain.triedb.Close() 261 262 // Start a new blockchain back up and see where the repair leads us 263 pdb, err := pebble.New(snaptest.datadir, 0, 0, "", false) 264 if err != nil { 265 t.Fatalf("Failed to create persistent key-value database: %v", err) 266 } 267 newdb, err := rawdb.Open(pdb, rawdb.OpenOptions{Ancient: snaptest.ancient}) 268 if err != nil { 269 t.Fatalf("Failed to create persistent freezer database: %v", err) 270 } 271 defer newdb.Close() 272 273 // The interesting thing is: instead of starting the blockchain after 274 // the crash, we do restart twice here: one after the crash and one 275 // after the normal stop. It's used to ensure the broken snapshot 276 // can be detected all the time. 277 newchain, err := NewBlockChain(newdb, snaptest.gspec, snaptest.engine, DefaultConfig().WithStateScheme(snaptest.scheme)) 278 if err != nil { 279 t.Fatalf("Failed to recreate chain: %v", err) 280 } 281 newchain.Stop() 282 283 newchain, err = NewBlockChain(newdb, snaptest.gspec, snaptest.engine, DefaultConfig().WithStateScheme(snaptest.scheme)) 284 if err != nil { 285 t.Fatalf("Failed to recreate chain: %v", err) 286 } 287 defer newchain.Stop() 288 289 snaptest.verify(t, newchain, blocks) 290 } 291 292 // gappedSnapshotTest is a test type used to test this scenario: 293 // - have a complete snapshot 294 // - restart without enabling the snapshot 295 // - insert a few blocks 296 // - restart with enabling the snapshot again 297 type gappedSnapshotTest struct { 298 snapshotTestBasic 299 gapped int // Number of blocks to insert without enabling snapshot 300 } 301 302 func (snaptest *gappedSnapshotTest) test(t *testing.T) { 303 // It's hard to follow the test case, visualize the input 304 // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) 305 // fmt.Println(snaptest.dump()) 306 chain, blocks := snaptest.prepare(t) 307 308 // Insert blocks without enabling snapshot if gapping is required. 309 chain.Stop() 310 gappedBlocks, _ := GenerateChain(snaptest.gspec.Config, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.gapped, func(i int, b *BlockGen) {}) 311 312 // Insert a few more blocks without enabling snapshot 313 var options = &BlockChainConfig{ 314 TrieCleanLimit: 256, 315 TrieDirtyLimit: 256, 316 TrieTimeLimit: 5 * time.Minute, 317 SnapshotLimit: 0, 318 StateScheme: snaptest.scheme, 319 TxLookupLimit: -1, 320 } 321 newchain, err := NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, options) 322 if err != nil { 323 t.Fatalf("Failed to recreate chain: %v", err) 324 } 325 newchain.InsertChain(gappedBlocks) 326 newchain.Stop() 327 328 // Restart the chain with enabling the snapshot 329 options = DefaultConfig().WithStateScheme(snaptest.scheme) 330 newchain, err = NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, options) 331 if err != nil { 332 t.Fatalf("Failed to recreate chain: %v", err) 333 } 334 defer newchain.Stop() 335 336 snaptest.verify(t, newchain, blocks) 337 } 338 339 // setHeadSnapshotTest is the test type used to test this scenario: 340 // - have a complete snapshot 341 // - set the head to a lower point 342 // - restart 343 type setHeadSnapshotTest struct { 344 snapshotTestBasic 345 setHead uint64 // Block number to set head back to 346 } 347 348 func (snaptest *setHeadSnapshotTest) test(t *testing.T) { 349 // It's hard to follow the test case, visualize the input 350 // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) 351 // fmt.Println(snaptest.dump()) 352 chain, blocks := snaptest.prepare(t) 353 354 // Rewind the chain if setHead operation is required. 355 chain.SetHead(snaptest.setHead) 356 chain.Stop() 357 358 newchain, err := NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, DefaultConfig().WithStateScheme(snaptest.scheme)) 359 if err != nil { 360 t.Fatalf("Failed to recreate chain: %v", err) 361 } 362 defer newchain.Stop() 363 364 snaptest.verify(t, newchain, blocks) 365 } 366 367 // wipeCrashSnapshotTest is the test type used to test this scenario: 368 // - have a complete snapshot 369 // - restart, insert more blocks without enabling the snapshot 370 // - restart again with enabling the snapshot 371 // - crash 372 type wipeCrashSnapshotTest struct { 373 snapshotTestBasic 374 newBlocks int 375 } 376 377 func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { 378 // It's hard to follow the test case, visualize the input 379 // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) 380 // fmt.Println(snaptest.dump()) 381 chain, blocks := snaptest.prepare(t) 382 383 // Firstly, stop the chain properly, with all snapshot journal 384 // and state committed. 385 chain.Stop() 386 387 config := &BlockChainConfig{ 388 TrieCleanLimit: 256, 389 TrieDirtyLimit: 256, 390 TrieTimeLimit: 5 * time.Minute, 391 SnapshotLimit: 0, 392 StateScheme: snaptest.scheme, 393 TxLookupLimit: -1, 394 } 395 newchain, err := NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, config) 396 if err != nil { 397 t.Fatalf("Failed to recreate chain: %v", err) 398 } 399 newBlocks, _ := GenerateChain(snaptest.gspec.Config, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.newBlocks, func(i int, b *BlockGen) {}) 400 newchain.InsertChain(newBlocks) 401 newchain.Stop() 402 403 // Restart the chain, the wiper should start working 404 config = &BlockChainConfig{ 405 TrieCleanLimit: 256, 406 TrieDirtyLimit: 256, 407 TrieTimeLimit: 5 * time.Minute, 408 SnapshotLimit: 256, 409 SnapshotWait: false, // Don't wait rebuild 410 StateScheme: snaptest.scheme, 411 TxLookupLimit: -1, 412 } 413 tmp, err := NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, config) 414 if err != nil { 415 t.Fatalf("Failed to recreate chain: %v", err) 416 } 417 418 // Simulate the blockchain crash. 419 tmp.triedb.Close() 420 tmp.stopWithoutSaving() 421 422 newchain, err = NewBlockChain(snaptest.db, snaptest.gspec, snaptest.engine, DefaultConfig().WithStateScheme(snaptest.scheme)) 423 if err != nil { 424 t.Fatalf("Failed to recreate chain: %v", err) 425 } 426 snaptest.verify(t, newchain, blocks) 427 newchain.Stop() 428 } 429 430 // Tests a Geth restart with valid snapshot. Before the shutdown, all snapshot 431 // journal will be persisted correctly. In this case no snapshot recovery is 432 // required. 433 func TestRestartWithNewSnapshot(t *testing.T) { 434 // Chain: 435 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 436 // 437 // Commit: G 438 // Snapshot: G 439 // 440 // SetHead(0) 441 // 442 // ------------------------------ 443 // 444 // Expected in leveldb: 445 // G->C1->C2->C3->C4->C5->C6->C7->C8 446 // 447 // Expected head header : C8 448 // Expected head fast block: C8 449 // Expected head block : C8 450 // Expected snapshot disk : G 451 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 452 test := &snapshotTest{ 453 snapshotTestBasic{ 454 scheme: scheme, 455 chainBlocks: 8, 456 snapshotBlock: 0, 457 commitBlock: 0, 458 expCanonicalBlocks: 8, 459 expHeadHeader: 8, 460 expHeadFastBlock: 8, 461 expHeadBlock: 8, 462 expSnapshotBottom: 0, // Initial disk layer built from genesis 463 }, 464 } 465 test.test(t) 466 test.teardown() 467 } 468 } 469 470 // Tests a Geth was crashed and restarts with a broken snapshot. In this case the 471 // chain head should be rewound to the point with available state. And also the 472 // new head should must be lower than disk layer. But there is no committed point 473 // so the chain should be rewound to genesis and the disk layer should be left 474 // for recovery. 475 func TestNoCommitCrashWithNewSnapshot(t *testing.T) { 476 // Chain: 477 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 478 // 479 // Commit: G 480 // Snapshot: G, C4 481 // 482 // CRASH 483 // 484 // ------------------------------ 485 // 486 // Expected in leveldb: 487 // G->C1->C2->C3->C4->C5->C6->C7->C8 488 // 489 // Expected head header : C8 490 // Expected head fast block: C8 491 // Expected head block : G 492 // Expected snapshot disk : C4 493 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 494 test := &crashSnapshotTest{ 495 snapshotTestBasic{ 496 scheme: scheme, 497 chainBlocks: 8, 498 snapshotBlock: 4, 499 commitBlock: 0, 500 expCanonicalBlocks: 8, 501 expHeadHeader: 8, 502 expHeadFastBlock: 8, 503 expHeadBlock: 0, 504 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 505 }, 506 } 507 test.test(t) 508 test.teardown() 509 } 510 } 511 512 // Tests a Geth was crashed and restarts with a broken snapshot. In this case the 513 // chain head should be rewound to the point with available state. And also the 514 // new head should must be lower than disk layer. But there is only a low committed 515 // point so the chain should be rewound to committed point and the disk layer 516 // should be left for recovery. 517 func TestLowCommitCrashWithNewSnapshot(t *testing.T) { 518 // Chain: 519 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 520 // 521 // Commit: G, C2 522 // Snapshot: G, C4 523 // 524 // CRASH 525 // 526 // ------------------------------ 527 // 528 // Expected in leveldb: 529 // G->C1->C2->C3->C4->C5->C6->C7->C8 530 // 531 // Expected head header : C8 532 // Expected head fast block: C8 533 // Expected head block : C2 534 // Expected snapshot disk : C4 535 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 536 test := &crashSnapshotTest{ 537 snapshotTestBasic{ 538 scheme: scheme, 539 chainBlocks: 8, 540 snapshotBlock: 4, 541 commitBlock: 2, 542 expCanonicalBlocks: 8, 543 expHeadHeader: 8, 544 expHeadFastBlock: 8, 545 expHeadBlock: 2, 546 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 547 }, 548 } 549 test.test(t) 550 test.teardown() 551 } 552 } 553 554 // Tests a Geth was crashed and restarts with a broken snapshot. In this case 555 // the chain head should be rewound to the point with available state. And also 556 // the new head should must be lower than disk layer. But there is only a high 557 // committed point so the chain should be rewound to genesis and the disk layer 558 // should be left for recovery. 559 func TestHighCommitCrashWithNewSnapshot(t *testing.T) { 560 // Chain: 561 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 562 // 563 // Commit: G, C6 564 // Snapshot: G, C4 565 // 566 // CRASH 567 // 568 // ------------------------------ 569 // 570 // Expected in leveldb: 571 // G->C1->C2->C3->C4->C5->C6->C7->C8 572 // 573 // Expected head header : C8 574 // Expected head fast block: C8 575 // Expected head block : G (Hash mode), C6 (Path mode) 576 // Expected snapshot disk : C4 (Hash mode) 577 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 578 expHead := uint64(0) 579 if scheme == rawdb.PathScheme { 580 // The pathdb database makes sure that snapshot and trie are consistent, 581 // so only the last two blocks are reverted in case of a crash. 582 expHead = uint64(6) 583 } 584 test := &crashSnapshotTest{ 585 snapshotTestBasic{ 586 scheme: scheme, 587 chainBlocks: 8, 588 snapshotBlock: 4, 589 commitBlock: 6, 590 expCanonicalBlocks: 8, 591 expHeadHeader: 8, 592 expHeadFastBlock: 8, 593 expHeadBlock: expHead, 594 expSnapshotBottom: 4, // Last committed disk layer, wait recovery 595 }, 596 } 597 test.test(t) 598 test.teardown() 599 } 600 } 601 602 // Tests a Geth was running with snapshot enabled. Then restarts without 603 // enabling snapshot and after that re-enable the snapshot again. In this 604 // case the snapshot should be rebuilt with latest chain head. 605 func TestGappedNewSnapshot(t *testing.T) { 606 // Chain: 607 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 608 // 609 // Commit: G 610 // Snapshot: G 611 // 612 // SetHead(0) 613 // 614 // ------------------------------ 615 // 616 // Expected in leveldb: 617 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 618 // 619 // Expected head header : C10 620 // Expected head fast block: C10 621 // Expected head block : C10 622 // Expected snapshot disk : C10 623 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 624 test := &gappedSnapshotTest{ 625 snapshotTestBasic: snapshotTestBasic{ 626 scheme: scheme, 627 chainBlocks: 8, 628 snapshotBlock: 0, 629 commitBlock: 0, 630 expCanonicalBlocks: 10, 631 expHeadHeader: 10, 632 expHeadFastBlock: 10, 633 expHeadBlock: 10, 634 expSnapshotBottom: 10, // Rebuilt snapshot from the latest HEAD 635 }, 636 gapped: 2, 637 } 638 test.test(t) 639 test.teardown() 640 } 641 } 642 643 // Tests the Geth was running with snapshot enabled and resetHead is applied. 644 // In this case the head is rewound to the target(with state available). After 645 // that the chain is restarted and the original disk layer is kept. 646 func TestSetHeadWithNewSnapshot(t *testing.T) { 647 // Chain: 648 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 649 // 650 // Commit: G 651 // Snapshot: G 652 // 653 // SetHead(4) 654 // 655 // ------------------------------ 656 // 657 // Expected in leveldb: 658 // G->C1->C2->C3->C4 659 // 660 // Expected head header : C4 661 // Expected head fast block: C4 662 // Expected head block : C4 663 // Expected snapshot disk : G 664 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 665 test := &setHeadSnapshotTest{ 666 snapshotTestBasic: snapshotTestBasic{ 667 scheme: scheme, 668 chainBlocks: 8, 669 snapshotBlock: 0, 670 commitBlock: 0, 671 expCanonicalBlocks: 4, 672 expHeadHeader: 4, 673 expHeadFastBlock: 4, 674 expHeadBlock: 4, 675 expSnapshotBottom: 0, // The initial disk layer is built from the genesis 676 }, 677 setHead: 4, 678 } 679 test.test(t) 680 test.teardown() 681 } 682 } 683 684 // Tests the Geth was running with a complete snapshot and then imports a few 685 // more new blocks on top without enabling the snapshot. After the restart, 686 // crash happens. Check everything is ok after the restart. 687 func TestRecoverSnapshotFromWipingCrash(t *testing.T) { 688 // Chain: 689 // G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD) 690 // 691 // Commit: G 692 // Snapshot: G 693 // 694 // SetHead(0) 695 // 696 // ------------------------------ 697 // 698 // Expected in leveldb: 699 // G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10 700 // 701 // Expected head header : C10 702 // Expected head fast block: C10 703 // Expected head block : C8 704 // Expected snapshot disk : C10 705 for _, scheme := range []string{rawdb.HashScheme, rawdb.PathScheme} { 706 test := &wipeCrashSnapshotTest{ 707 snapshotTestBasic: snapshotTestBasic{ 708 scheme: scheme, 709 chainBlocks: 8, 710 snapshotBlock: 4, 711 commitBlock: 0, 712 expCanonicalBlocks: 10, 713 expHeadHeader: 10, 714 expHeadFastBlock: 10, 715 expHeadBlock: 10, 716 expSnapshotBottom: 10, 717 }, 718 newBlocks: 2, 719 } 720 test.test(t) 721 test.teardown() 722 } 723 }