github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/core/blockchain_snapshot_test.go (about)

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