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  }