github.com/core-coin/go-core/v2@v2.1.9/core/blockchain_snapshot_test.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core 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  	"os"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/core-coin/go-core/v2/consensus/cryptore"
    32  
    33  	"github.com/core-coin/go-core/v2/core/rawdb"
    34  	"github.com/core-coin/go-core/v2/core/vm"
    35  	"github.com/core-coin/go-core/v2/params"
    36  )
    37  
    38  // snapshotTest is a test case for snapshot recovery. It can be used for
    39  // simulating these scenarios:
    40  // (i)   Gocore restarts normally with valid legacy snapshot
    41  // (ii)  Gocore restarts normally with valid new-format snapshot
    42  // (iii) Gocore restarts after the crash, with broken legacy snapshot
    43  // (iv)  Gocore restarts after the crash, with broken new-format snapshot
    44  // (v)   Gocore restarts normally, but it's requested to be rewound to a lower point via SetHead
    45  // (vi)  Gocore restarts normally with a stale snapshot
    46  type snapshotTest struct {
    47  	legacy       bool   // Flag whether the loaded snapshot is in legacy format
    48  	crash        bool   // Flag whether the Gocore restarts from the previous crash
    49  	restartCrash int    // Number of blocks to insert after the normal stop, then the crash happens
    50  	gapped       int    // Number of blocks to insert without enabling snapshot
    51  	setHead      uint64 // Block number to set head back to
    52  
    53  	chainBlocks   int    // Number of blocks to generate for the canonical chain
    54  	snapshotBlock uint64 // Block number of the relevant snapshot disk layer
    55  	commitBlock   uint64 // Block number for which to commit the state to disk
    56  
    57  	expCanonicalBlocks int    // Number of canonical blocks expected to remain in the database (excl. genesis)
    58  	expHeadHeader      uint64 // Block number of the expected head header
    59  	expHeadFastBlock   uint64 // Block number of the expected head fast sync block
    60  	expHeadBlock       uint64 // Block number of the expected head full block
    61  	expSnapshotBottom  uint64 // The block height corresponding to the snapshot disk layer
    62  }
    63  
    64  func (tt *snapshotTest) dump() string {
    65  	buffer := new(strings.Builder)
    66  
    67  	fmt.Fprint(buffer, "Chain:\n  G")
    68  	for i := 0; i < tt.chainBlocks; i++ {
    69  		fmt.Fprintf(buffer, "->C%d", i+1)
    70  	}
    71  	fmt.Fprint(buffer, " (HEAD)\n\n")
    72  
    73  	fmt.Fprintf(buffer, "Commit:   G")
    74  	if tt.commitBlock > 0 {
    75  		fmt.Fprintf(buffer, ", C%d", tt.commitBlock)
    76  	}
    77  	fmt.Fprint(buffer, "\n")
    78  
    79  	fmt.Fprintf(buffer, "Snapshot: G")
    80  	if tt.snapshotBlock > 0 {
    81  		fmt.Fprintf(buffer, ", C%d", tt.snapshotBlock)
    82  	}
    83  	fmt.Fprint(buffer, "\n")
    84  
    85  	if tt.crash {
    86  		fmt.Fprintf(buffer, "\nCRASH\n\n")
    87  	} else {
    88  		fmt.Fprintf(buffer, "\nSetHead(%d)\n\n", tt.setHead)
    89  	}
    90  	fmt.Fprintf(buffer, "------------------------------\n\n")
    91  
    92  	fmt.Fprint(buffer, "Expected in leveldb:\n  G")
    93  	for i := 0; i < tt.expCanonicalBlocks; i++ {
    94  		fmt.Fprintf(buffer, "->C%d", i+1)
    95  	}
    96  	fmt.Fprintf(buffer, "\n\n")
    97  	fmt.Fprintf(buffer, "Expected head header    : C%d\n", tt.expHeadHeader)
    98  	fmt.Fprintf(buffer, "Expected head fast block: C%d\n", tt.expHeadFastBlock)
    99  	if tt.expHeadBlock == 0 {
   100  		fmt.Fprintf(buffer, "Expected head block     : G\n")
   101  	} else {
   102  		fmt.Fprintf(buffer, "Expected head block     : C%d\n", tt.expHeadBlock)
   103  	}
   104  	if tt.expSnapshotBottom == 0 {
   105  		fmt.Fprintf(buffer, "Expected snapshot disk  : G\n")
   106  	} else {
   107  		fmt.Fprintf(buffer, "Expected snapshot disk  : C%d\n", tt.expSnapshotBottom)
   108  	}
   109  	return buffer.String()
   110  }
   111  
   112  // Tests a Gocore restart with valid snapshot. Before the shutdown, all snapshot
   113  // journal will be persisted correctly. In this case no snapshot recovery is
   114  // required.
   115  func TestRestartWithNewSnapshot(t *testing.T) {
   116  	// Chain:
   117  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   118  	//
   119  	// Commit:   G
   120  	// Snapshot: G
   121  	//
   122  	// SetHead(0)
   123  	//
   124  	// ------------------------------
   125  	//
   126  	// Expected in leveldb:
   127  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   128  	//
   129  	// Expected head header    : C8
   130  	// Expected head fast block: C8
   131  	// Expected head block     : C8
   132  	// Expected snapshot disk  : G
   133  	testSnapshot(t, &snapshotTest{
   134  		legacy:             false,
   135  		crash:              false,
   136  		gapped:             0,
   137  		setHead:            0,
   138  		chainBlocks:        8,
   139  		snapshotBlock:      0,
   140  		commitBlock:        0,
   141  		expCanonicalBlocks: 8,
   142  		expHeadHeader:      8,
   143  		expHeadFastBlock:   8,
   144  		expHeadBlock:       8,
   145  		expSnapshotBottom:  0, // Initial disk layer built from genesis
   146  	})
   147  }
   148  
   149  // Tests a Gocore restart with valid but "legacy" snapshot. Before the shutdown,
   150  // all snapshot journal will be persisted correctly. In this case no snapshot
   151  // recovery is required.
   152  func TestRestartWithLegacySnapshot(t *testing.T) {
   153  	// Chain:
   154  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   155  	//
   156  	// Commit:   G
   157  	// Snapshot: G
   158  	//
   159  	// SetHead(0)
   160  	//
   161  	// ------------------------------
   162  	//
   163  	// Expected in leveldb:
   164  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   165  	//
   166  	// Expected head header    : C8
   167  	// Expected head fast block: C8
   168  	// Expected head block     : C8
   169  	// Expected snapshot disk  : G
   170  	testSnapshot(t, &snapshotTest{
   171  		legacy:             true,
   172  		crash:              false,
   173  		gapped:             0,
   174  		setHead:            0,
   175  		chainBlocks:        8,
   176  		snapshotBlock:      0,
   177  		commitBlock:        0,
   178  		expCanonicalBlocks: 8,
   179  		expHeadHeader:      8,
   180  		expHeadFastBlock:   8,
   181  		expHeadBlock:       8,
   182  		expSnapshotBottom:  0, // Initial disk layer built from genesis
   183  	})
   184  }
   185  
   186  // Tests a Gocore was crashed and restarts with a broken snapshot. In this case the
   187  // chain head should be rewound to the point with available state. And also the
   188  // new head should must be lower than disk layer. But there is no committed point
   189  // so the chain should be rewound to genesis and the disk layer should be left
   190  // for recovery.
   191  func TestNoCommitCrashWithNewSnapshot(t *testing.T) {
   192  	// Chain:
   193  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   194  	//
   195  	// Commit:   G
   196  	// Snapshot: G, C4
   197  	//
   198  	// CRASH
   199  	//
   200  	// ------------------------------
   201  	//
   202  	// Expected in leveldb:
   203  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   204  	//
   205  	// Expected head header    : C8
   206  	// Expected head fast block: C8
   207  	// Expected head block     : G
   208  	// Expected snapshot disk  : C4
   209  	testSnapshot(t, &snapshotTest{
   210  		legacy:             false,
   211  		crash:              true,
   212  		gapped:             0,
   213  		setHead:            0,
   214  		chainBlocks:        8,
   215  		snapshotBlock:      4,
   216  		commitBlock:        0,
   217  		expCanonicalBlocks: 8,
   218  		expHeadHeader:      8,
   219  		expHeadFastBlock:   8,
   220  		expHeadBlock:       0,
   221  		expSnapshotBottom:  4, // Last committed disk layer, wait recovery
   222  	})
   223  }
   224  
   225  // Tests a Gocore was crashed and restarts with a broken snapshot. In this case the
   226  // chain head should be rewound to the point with available state. And also the
   227  // new head should must be lower than disk layer. But there is only a low committed
   228  // point so the chain should be rewound to committed point and the disk layer
   229  // should be left for recovery.
   230  func TestLowCommitCrashWithNewSnapshot(t *testing.T) {
   231  	// Chain:
   232  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   233  	//
   234  	// Commit:   G, C2
   235  	// Snapshot: G, C4
   236  	//
   237  	// CRASH
   238  	//
   239  	// ------------------------------
   240  	//
   241  	// Expected in leveldb:
   242  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   243  	//
   244  	// Expected head header    : C8
   245  	// Expected head fast block: C8
   246  	// Expected head block     : C2
   247  	// Expected snapshot disk  : C4
   248  	testSnapshot(t, &snapshotTest{
   249  		legacy:             false,
   250  		crash:              true,
   251  		gapped:             0,
   252  		setHead:            0,
   253  		chainBlocks:        8,
   254  		snapshotBlock:      4,
   255  		commitBlock:        2,
   256  		expCanonicalBlocks: 8,
   257  		expHeadHeader:      8,
   258  		expHeadFastBlock:   8,
   259  		expHeadBlock:       2,
   260  		expSnapshotBottom:  4, // Last committed disk layer, wait recovery
   261  	})
   262  }
   263  
   264  // Tests a Gocore was crashed and restarts with a broken snapshot. In this case
   265  // the chain head should be rewound to the point with available state. And also
   266  // the new head should must be lower than disk layer. But there is only a high
   267  // committed point so the chain should be rewound to genesis and the disk layer
   268  // should be left for recovery.
   269  func TestHighCommitCrashWithNewSnapshot(t *testing.T) {
   270  	// Chain:
   271  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   272  	//
   273  	// Commit:   G, C6
   274  	// Snapshot: G, C4
   275  	//
   276  	// CRASH
   277  	//
   278  	// ------------------------------
   279  	//
   280  	// Expected in leveldb:
   281  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   282  	//
   283  	// Expected head header    : C8
   284  	// Expected head fast block: C8
   285  	// Expected head block     : G
   286  	// Expected snapshot disk  : C4
   287  	testSnapshot(t, &snapshotTest{
   288  		legacy:             false,
   289  		crash:              true,
   290  		gapped:             0,
   291  		setHead:            0,
   292  		chainBlocks:        8,
   293  		snapshotBlock:      4,
   294  		commitBlock:        6,
   295  		expCanonicalBlocks: 8,
   296  		expHeadHeader:      8,
   297  		expHeadFastBlock:   8,
   298  		expHeadBlock:       0,
   299  		expSnapshotBottom:  4, // Last committed disk layer, wait recovery
   300  	})
   301  }
   302  
   303  // Tests a Gocore was crashed and restarts with a broken and "legacy format"
   304  // snapshot. In this case the entire legacy snapshot should be discared
   305  // and rebuild from the new chain head. The new head here refers to the
   306  // genesis because there is no committed point.
   307  func TestNoCommitCrashWithLegacySnapshot(t *testing.T) {
   308  	// Chain:
   309  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   310  	//
   311  	// Commit:   G
   312  	// Snapshot: G, C4
   313  	//
   314  	// CRASH
   315  	//
   316  	// ------------------------------
   317  	//
   318  	// Expected in leveldb:
   319  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   320  	//
   321  	// Expected head header    : C8
   322  	// Expected head fast block: C8
   323  	// Expected head block     : G
   324  	// Expected snapshot disk  : G
   325  	testSnapshot(t, &snapshotTest{
   326  		legacy:             true,
   327  		crash:              true,
   328  		gapped:             0,
   329  		setHead:            0,
   330  		chainBlocks:        8,
   331  		snapshotBlock:      4,
   332  		commitBlock:        0,
   333  		expCanonicalBlocks: 8,
   334  		expHeadHeader:      8,
   335  		expHeadFastBlock:   8,
   336  		expHeadBlock:       0,
   337  		expSnapshotBottom:  0, // Rebuilt snapshot from the latest HEAD(genesis)
   338  	})
   339  }
   340  
   341  // Tests a Gocore was crashed and restarts with a broken and "legacy format"
   342  // snapshot. In this case the entire legacy snapshot should be discared
   343  // and rebuild from the new chain head. The new head here refers to the
   344  // block-2 because it's committed into the disk.
   345  func TestLowCommitCrashWithLegacySnapshot(t *testing.T) {
   346  	// Chain:
   347  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   348  	//
   349  	// Commit:   G, C2
   350  	// Snapshot: G, C4
   351  	//
   352  	// CRASH
   353  	//
   354  	// ------------------------------
   355  	//
   356  	// Expected in leveldb:
   357  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   358  	//
   359  	// Expected head header    : C8
   360  	// Expected head fast block: C8
   361  	// Expected head block     : C2
   362  	// Expected snapshot disk  : C2
   363  	testSnapshot(t, &snapshotTest{
   364  		legacy:             true,
   365  		crash:              true,
   366  		gapped:             0,
   367  		setHead:            0,
   368  		chainBlocks:        8,
   369  		snapshotBlock:      4,
   370  		commitBlock:        2,
   371  		expCanonicalBlocks: 8,
   372  		expHeadHeader:      8,
   373  		expHeadFastBlock:   8,
   374  		expHeadBlock:       2,
   375  		expSnapshotBottom:  2, // Rebuilt snapshot from the latest HEAD
   376  	})
   377  }
   378  
   379  // Tests a Gocore was crashed and restarts with a broken and "legacy format"
   380  // snapshot. In this case the entire legacy snapshot should be discared
   381  // and rebuild from the new chain head.
   382  //
   383  // The new head here refers to the the genesis, the reason is:
   384  //   - the state of block-6 is committed into the disk
   385  //   - the legacy disk layer of block-4 is committed into the disk
   386  //   - the head is rewound the genesis in order to find an available
   387  //     state lower than disk layer
   388  func TestHighCommitCrashWithLegacySnapshot(t *testing.T) {
   389  	// Chain:
   390  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   391  	//
   392  	// Commit:   G, C6
   393  	// Snapshot: G, C4
   394  	//
   395  	// CRASH
   396  	//
   397  	// ------------------------------
   398  	//
   399  	// Expected in leveldb:
   400  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   401  	//
   402  	// Expected head header    : C8
   403  	// Expected head fast block: C8
   404  	// Expected head block     : G
   405  	// Expected snapshot disk  : G
   406  	testSnapshot(t, &snapshotTest{
   407  		legacy:             true,
   408  		crash:              true,
   409  		gapped:             0,
   410  		setHead:            0,
   411  		chainBlocks:        8,
   412  		snapshotBlock:      4,
   413  		commitBlock:        6,
   414  		expCanonicalBlocks: 8,
   415  		expHeadHeader:      8,
   416  		expHeadFastBlock:   8,
   417  		expHeadBlock:       0,
   418  		expSnapshotBottom:  0, // Rebuilt snapshot from the latest HEAD(genesis)
   419  	})
   420  }
   421  
   422  // Tests a Gocore was running with snapshot enabled. Then restarts without
   423  // enabling snapshot and after that re-enable the snapshot again. In this
   424  // case the snapshot should be rebuilt with latest chain head.
   425  func TestGappedNewSnapshot(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->C9->C10
   438  	//
   439  	// Expected head header    : C10
   440  	// Expected head fast block: C10
   441  	// Expected head block     : C10
   442  	// Expected snapshot disk  : C10
   443  	testSnapshot(t, &snapshotTest{
   444  		legacy:             false,
   445  		crash:              false,
   446  		gapped:             2,
   447  		setHead:            0,
   448  		chainBlocks:        8,
   449  		snapshotBlock:      0,
   450  		commitBlock:        0,
   451  		expCanonicalBlocks: 10,
   452  		expHeadHeader:      10,
   453  		expHeadFastBlock:   10,
   454  		expHeadBlock:       10,
   455  		expSnapshotBottom:  10, // Rebuilt snapshot from the latest HEAD
   456  	})
   457  }
   458  
   459  // Tests a Gocore was running with leagcy snapshot enabled. Then restarts
   460  // without enabling snapshot and after that re-enable the snapshot again.
   461  // In this case the snapshot should be rebuilt with latest chain head.
   462  func TestGappedLegacySnapshot(t *testing.T) {
   463  	// Chain:
   464  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   465  	//
   466  	// Commit:   G
   467  	// Snapshot: G
   468  	//
   469  	// SetHead(0)
   470  	//
   471  	// ------------------------------
   472  	//
   473  	// Expected in leveldb:
   474  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10
   475  	//
   476  	// Expected head header    : C10
   477  	// Expected head fast block: C10
   478  	// Expected head block     : C10
   479  	// Expected snapshot disk  : C10
   480  	testSnapshot(t, &snapshotTest{
   481  		legacy:             true,
   482  		crash:              false,
   483  		gapped:             2,
   484  		setHead:            0,
   485  		chainBlocks:        8,
   486  		snapshotBlock:      0,
   487  		commitBlock:        0,
   488  		expCanonicalBlocks: 10,
   489  		expHeadHeader:      10,
   490  		expHeadFastBlock:   10,
   491  		expHeadBlock:       10,
   492  		expSnapshotBottom:  10, // Rebuilt snapshot from the latest HEAD
   493  	})
   494  }
   495  
   496  // Tests the Gocore was running with snapshot enabled and resetHead is applied.
   497  // In this case the head is rewound to the target(with state available). After
   498  // that the chain is restarted and the original disk layer is kept.
   499  func TestSetHeadWithNewSnapshot(t *testing.T) {
   500  	// Chain:
   501  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   502  	//
   503  	// Commit:   G
   504  	// Snapshot: G
   505  	//
   506  	// SetHead(4)
   507  	//
   508  	// ------------------------------
   509  	//
   510  	// Expected in leveldb:
   511  	//   G->C1->C2->C3->C4
   512  	//
   513  	// Expected head header    : C4
   514  	// Expected head fast block: C4
   515  	// Expected head block     : C4
   516  	// Expected snapshot disk  : G
   517  	testSnapshot(t, &snapshotTest{
   518  		legacy:             false,
   519  		crash:              false,
   520  		gapped:             0,
   521  		setHead:            4,
   522  		chainBlocks:        8,
   523  		snapshotBlock:      0,
   524  		commitBlock:        0,
   525  		expCanonicalBlocks: 4,
   526  		expHeadHeader:      4,
   527  		expHeadFastBlock:   4,
   528  		expHeadBlock:       4,
   529  		expSnapshotBottom:  0, // The initial disk layer is built from the genesis
   530  	})
   531  }
   532  
   533  // Tests the Gocore was running with snapshot(legacy-format) enabled and resetHead
   534  // is applied. In this case the head is rewound to the target(with state available).
   535  // After that the chain is restarted and the original disk layer is kept.
   536  func TestSetHeadWithLegacySnapshot(t *testing.T) {
   537  	// Chain:
   538  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   539  	//
   540  	// Commit:   G
   541  	// Snapshot: G
   542  	//
   543  	// SetHead(4)
   544  	//
   545  	// ------------------------------
   546  	//
   547  	// Expected in leveldb:
   548  	//   G->C1->C2->C3->C4
   549  	//
   550  	// Expected head header    : C4
   551  	// Expected head fast block: C4
   552  	// Expected head block     : C4
   553  	// Expected snapshot disk  : G
   554  	testSnapshot(t, &snapshotTest{
   555  		legacy:             true,
   556  		crash:              false,
   557  		gapped:             0,
   558  		setHead:            4,
   559  		chainBlocks:        8,
   560  		snapshotBlock:      0,
   561  		commitBlock:        0,
   562  		expCanonicalBlocks: 4,
   563  		expHeadHeader:      4,
   564  		expHeadFastBlock:   4,
   565  		expHeadBlock:       4,
   566  		expSnapshotBottom:  0, // The initial disk layer is built from the genesis
   567  	})
   568  }
   569  
   570  // Tests the Gocore was running with snapshot(legacy-format) enabled and upgrades
   571  // the disk layer journal(journal generator) to latest format. After that the Gocore
   572  // is restarted from a crash. In this case Gocore will find the new-format disk layer
   573  // journal but with legacy-format diff journal(the new-format is never committed),
   574  // and the invalid diff journal is expected to be dropped.
   575  func TestRecoverSnapshotFromCrashWithLegacyDiffJournal(t *testing.T) {
   576  	// Chain:
   577  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   578  	//
   579  	// Commit:   G
   580  	// Snapshot: G
   581  	//
   582  	// SetHead(0)
   583  	//
   584  	// ------------------------------
   585  	//
   586  	// Expected in leveldb:
   587  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10
   588  	//
   589  	// Expected head header    : C10
   590  	// Expected head fast block: C10
   591  	// Expected head block     : C8
   592  	// Expected snapshot disk  : C10
   593  	testSnapshot(t, &snapshotTest{
   594  		legacy:             true,
   595  		crash:              false,
   596  		restartCrash:       2,
   597  		gapped:             0,
   598  		setHead:            0,
   599  		chainBlocks:        8,
   600  		snapshotBlock:      0,
   601  		commitBlock:        0,
   602  		expCanonicalBlocks: 10,
   603  		expHeadHeader:      10,
   604  		expHeadFastBlock:   10,
   605  		expHeadBlock:       8,  // The persisted state in the first running
   606  		expSnapshotBottom:  10, // The persisted disk layer in the second running
   607  	})
   608  }
   609  
   610  func testSnapshot(t *testing.T, tt *snapshotTest) {
   611  	// It's hard to follow the test case, visualize the input
   612  	// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
   613  	// fmt.Println(tt.dump())
   614  
   615  	// Create a temporary persistent database
   616  	datadir, err := ioutil.TempDir("", "")
   617  	if err != nil {
   618  		t.Fatalf("Failed to create temporary datadir: %v", err)
   619  	}
   620  	os.RemoveAll(datadir)
   621  
   622  	db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "")
   623  	if err != nil {
   624  		t.Fatalf("Failed to create persistent database: %v", err)
   625  	}
   626  	defer db.Close() // Might double close, should be fine
   627  
   628  	// Initialize a fresh chain
   629  	var (
   630  		genesis = new(Genesis).MustCommit(db)
   631  		engine  = cryptore.NewFullFaker()
   632  		gendb   = rawdb.NewMemoryDatabase()
   633  
   634  		// Snapshot is enabled, the first snapshot is created from the Genesis.
   635  		// The snapshot memory allowance is 256MB, it means no snapshot flush
   636  		// will happen during the block insertion.
   637  		cacheConfig = defaultCacheConfig
   638  	)
   639  	chain, err := NewBlockChain(db, cacheConfig, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   640  	if err != nil {
   641  		t.Fatalf("Failed to create chain: %v", err)
   642  	}
   643  	blocks, _ := GenerateChain(params.MainnetChainConfig, genesis, engine, gendb, tt.chainBlocks, func(i int, b *BlockGen) {})
   644  
   645  	// Insert the blocks with configured settings.
   646  	var breakpoints []uint64
   647  	if tt.commitBlock > tt.snapshotBlock {
   648  		breakpoints = append(breakpoints, tt.snapshotBlock, tt.commitBlock)
   649  	} else {
   650  		breakpoints = append(breakpoints, tt.commitBlock, tt.snapshotBlock)
   651  	}
   652  	var startPoint uint64
   653  	for _, point := range breakpoints {
   654  		if _, err := chain.InsertChain(blocks[startPoint:point]); err != nil {
   655  			t.Fatalf("Failed to import canonical chain start: %v", err)
   656  		}
   657  		startPoint = point
   658  
   659  		if tt.commitBlock > 0 && tt.commitBlock == point {
   660  			chain.stateCache.TrieDB().Commit(blocks[point-1].Root(), true, nil)
   661  		}
   662  		if tt.snapshotBlock > 0 && tt.snapshotBlock == point {
   663  			if tt.legacy {
   664  				// Here we commit the snapshot disk root to simulate
   665  				// committing the legacy snapshot.
   666  				rawdb.WriteSnapshotRoot(db, blocks[point-1].Root())
   667  			} else {
   668  				chain.snaps.Cap(blocks[point-1].Root(), 0)
   669  				diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root()
   670  				if !bytes.Equal(diskRoot.Bytes(), blockRoot.Bytes()) {
   671  					t.Fatalf("Failed to flush disk layer change, want %x, got %x", blockRoot, diskRoot)
   672  				}
   673  			}
   674  		}
   675  	}
   676  	if _, err := chain.InsertChain(blocks[startPoint:]); err != nil {
   677  		t.Fatalf("Failed to import canonical chain tail: %v", err)
   678  	}
   679  	// Set the flag for writing legacy journal if necessary
   680  	if tt.legacy {
   681  		chain.writeLegacyJournal = true
   682  	}
   683  	// Pull the plug on the database, simulating a hard crash
   684  	if tt.crash {
   685  		db.Close()
   686  
   687  		// Start a new blockchain back up and see where the repair leads us
   688  		db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "")
   689  		if err != nil {
   690  			t.Fatalf("Failed to reopen persistent database: %v", err)
   691  		}
   692  		defer db.Close()
   693  
   694  		// The interesting thing is: instead of start the blockchain after
   695  		// the crash, we do restart twice here: one after the crash and one
   696  		// after the normal stop. It's used to ensure the broken snapshot
   697  		// can be detected all the time.
   698  		chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   699  		if err != nil {
   700  			t.Fatalf("Failed to recreate chain: %v", err)
   701  		}
   702  		chain.Stop()
   703  
   704  		chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   705  		if err != nil {
   706  			t.Fatalf("Failed to recreate chain: %v", err)
   707  		}
   708  		defer chain.Stop()
   709  	} else if tt.gapped > 0 {
   710  		// Insert blocks without enabling snapshot if gapping is required.
   711  		chain.Stop()
   712  		gappedBlocks, _ := GenerateChain(params.MainnetChainConfig, blocks[len(blocks)-1], engine, gendb, tt.gapped, func(i int, b *BlockGen) {})
   713  
   714  		// Insert a few more blocks without enabling snapshot
   715  		var cacheConfig = &CacheConfig{
   716  			TrieCleanLimit: 256,
   717  			TrieDirtyLimit: 256,
   718  			TrieTimeLimit:  5 * time.Minute,
   719  			SnapshotLimit:  0,
   720  		}
   721  		chain, err = NewBlockChain(db, cacheConfig, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   722  		if err != nil {
   723  			t.Fatalf("Failed to recreate chain: %v", err)
   724  		}
   725  		chain.InsertChain(gappedBlocks)
   726  		chain.Stop()
   727  
   728  		chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   729  		if err != nil {
   730  			t.Fatalf("Failed to recreate chain: %v", err)
   731  		}
   732  		defer chain.Stop()
   733  	} else if tt.setHead != 0 {
   734  		// Rewind the chain if setHead operation is required.
   735  		chain.SetHead(tt.setHead)
   736  		chain.Stop()
   737  
   738  		chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   739  		if err != nil {
   740  			t.Fatalf("Failed to recreate chain: %v", err)
   741  		}
   742  		defer chain.Stop()
   743  	} else if tt.restartCrash != 0 {
   744  		// Firstly, stop the chain properly, with all snapshot journal
   745  		// and state committed.
   746  		chain.Stop()
   747  
   748  		// Restart chain, forcibly flush the disk layer journal with new format
   749  		newBlocks, _ := GenerateChain(params.MainnetChainConfig, blocks[len(blocks)-1], engine, gendb, tt.restartCrash, func(i int, b *BlockGen) {})
   750  		chain, err = NewBlockChain(db, cacheConfig, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   751  		if err != nil {
   752  			t.Fatalf("Failed to recreate chain: %v", err)
   753  		}
   754  		chain.InsertChain(newBlocks)
   755  		chain.Snapshot().Cap(newBlocks[len(newBlocks)-1].Root(), 0)
   756  
   757  		// Simulate the blockchain crash
   758  		// Don't call chain.Stop here, so that no snapshot
   759  		// journal and latest state will be committed
   760  
   761  		// Restart the chain after the crash
   762  		chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   763  		if err != nil {
   764  			t.Fatalf("Failed to recreate chain: %v", err)
   765  		}
   766  		defer chain.Stop()
   767  	} else {
   768  		chain.Stop()
   769  
   770  		// Restart the chain normally
   771  		chain, err = NewBlockChain(db, nil, params.MainnetChainConfig, engine, vm.Config{}, nil, nil)
   772  		if err != nil {
   773  			t.Fatalf("Failed to recreate chain: %v", err)
   774  		}
   775  		defer chain.Stop()
   776  	}
   777  
   778  	// Iterate over all the remaining blocks and ensure there are no gaps
   779  	verifyNoGaps(t, chain, true, blocks)
   780  	verifyCutoff(t, chain, true, blocks, tt.expCanonicalBlocks)
   781  
   782  	if head := chain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader {
   783  		t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader)
   784  	}
   785  	if head := chain.CurrentFastBlock(); head.NumberU64() != tt.expHeadFastBlock {
   786  		t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadFastBlock)
   787  	}
   788  	if head := chain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock {
   789  		t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock)
   790  	}
   791  	// Check the disk layer, ensure they are matched
   792  	block := chain.GetBlockByNumber(tt.expSnapshotBottom)
   793  	if block == nil {
   794  		t.Errorf("The correspnding block[%d] of snapshot disk layer is missing", tt.expSnapshotBottom)
   795  	} else if !bytes.Equal(chain.snaps.DiskRoot().Bytes(), block.Root().Bytes()) {
   796  		t.Errorf("The snapshot disk layer root is incorrect, want %x, get %x", block.Root(), chain.snaps.DiskRoot())
   797  	}
   798  }