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