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