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