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