github.com/MetalBlockchain/subnet-evm@v0.4.9/core/blockchain_repair_test.go (about)

     1  // (c) 2019-2021, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2020 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  // Tests that abnormal program termination (i.e.crash) and restart doesn't leave
    28  // the database in some strange state with gaps in the chain, nor with block data
    29  // dangling in the future.
    30  
    31  package core
    32  
    33  import (
    34  	"math/big"
    35  	"testing"
    36  
    37  	"github.com/MetalBlockchain/subnet-evm/consensus/dummy"
    38  	"github.com/MetalBlockchain/subnet-evm/core/rawdb"
    39  	"github.com/MetalBlockchain/subnet-evm/core/types"
    40  	"github.com/MetalBlockchain/subnet-evm/core/vm"
    41  	"github.com/MetalBlockchain/subnet-evm/params"
    42  	"github.com/ethereum/go-ethereum/common"
    43  )
    44  
    45  // rewindTest is a test case for chain rollback upon user request.
    46  type rewindTest struct {
    47  	canonicalBlocks int    // Number of blocks to generate for the canonical chain (heavier)
    48  	sidechainBlocks int    // Number of blocks to generate for the side chain (lighter)
    49  	commitBlock     uint64 // Block number for which to commit the state to disk
    50  
    51  	expCanonicalBlocks int    // Number of canonical blocks expected to remain in the database (excl. genesis)
    52  	expSidechainBlocks int    // Number of sidechain blocks expected to remain in the database (excl. genesis)
    53  	expHeadBlock       uint64 // Block number of the expected head full block
    54  }
    55  
    56  // Tests a recovery for a short canonical chain where a recent block was already
    57  // committed to disk and then the process crashed. In this case we expect the full
    58  // chain to be rolled back to the committed block, but the chain data itself left
    59  // in the database for replaying.
    60  func TestShortRepair(t *testing.T)              { testShortRepair(t, false) }
    61  func TestShortRepairWithSnapshots(t *testing.T) { testShortRepair(t, true) }
    62  
    63  func testShortRepair(t *testing.T, snapshots bool) {
    64  	// Chain:
    65  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
    66  	//
    67  	// Commit: G, C4
    68  	//
    69  	// CRASH
    70  	//
    71  	// ------------------------------
    72  	//
    73  	// Expected in leveldb:
    74  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
    75  	//
    76  	// Expected head block     : C4 (C0 with no snapshots)
    77  	rt := &rewindTest{
    78  		canonicalBlocks:    8,
    79  		sidechainBlocks:    0,
    80  		commitBlock:        4,
    81  		expCanonicalBlocks: 8,
    82  		expSidechainBlocks: 0,
    83  		expHeadBlock:       0,
    84  	}
    85  	if snapshots {
    86  		rt.expHeadBlock = 4
    87  	}
    88  	testRepair(t, rt, snapshots)
    89  }
    90  
    91  // Tests a recovery for a short canonical chain and a shorter side chain, where a
    92  // recent block was already committed to disk and then the process crashed. In this
    93  // test scenario the side chain is below the committed block. In this case we expect
    94  // the canonical chain to be rolled back to the committed block, but the chain data
    95  // itself left in the database for replaying.
    96  func TestShortOldForkedRepair(t *testing.T)              { testShortOldForkedRepair(t, false) }
    97  func TestShortOldForkedRepairWithSnapshots(t *testing.T) { testShortOldForkedRepair(t, true) }
    98  
    99  func testShortOldForkedRepair(t *testing.T, snapshots bool) {
   100  	// Chain:
   101  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   102  	//   └->S1->S2->S3
   103  	//
   104  	// Commit: G, C4
   105  	//
   106  	// CRASH
   107  	//
   108  	// ------------------------------
   109  	//
   110  	// Expected in leveldb:
   111  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   112  	//   └->S1->S2->S3
   113  	//
   114  	// Expected head block     : C4 (C0 with no snapshots)
   115  	rt := &rewindTest{
   116  		canonicalBlocks:    8,
   117  		sidechainBlocks:    3,
   118  		commitBlock:        4,
   119  		expCanonicalBlocks: 8,
   120  		expSidechainBlocks: 3,
   121  		expHeadBlock:       0,
   122  	}
   123  	if snapshots {
   124  		rt.expHeadBlock = 4
   125  	}
   126  	testRepair(t, rt, snapshots)
   127  }
   128  
   129  // Tests a recovery for a short canonical chain and a shorter side chain, where a
   130  // recent block was already committed to disk and then the process crashed. In this
   131  // test scenario the side chain reaches above the committed block. In this case we
   132  // expect the canonical chain to be rolled back to the committed block, but the
   133  // chain data itself left in the database for replaying.
   134  func TestShortNewlyForkedRepair(t *testing.T)              { testShortNewlyForkedRepair(t, false) }
   135  func TestShortNewlyForkedRepairWithSnapshots(t *testing.T) { testShortNewlyForkedRepair(t, true) }
   136  
   137  func testShortNewlyForkedRepair(t *testing.T, snapshots bool) {
   138  	// Chain:
   139  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   140  	//   └->S1->S2->S3->S4->S5->S6
   141  	//
   142  	// Commit: G, C4
   143  	//
   144  	// CRASH
   145  	//
   146  	// ------------------------------
   147  	//
   148  	// Expected in leveldb:
   149  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   150  	//   └->S1->S2->S3->S4->S5->S6
   151  	//
   152  	// Expected head block     : C4 (C0 with no snapshots)
   153  	rt := &rewindTest{
   154  		canonicalBlocks:    8,
   155  		sidechainBlocks:    6,
   156  		commitBlock:        4,
   157  		expCanonicalBlocks: 8,
   158  		expSidechainBlocks: 6,
   159  		expHeadBlock:       0,
   160  	}
   161  	if snapshots {
   162  		rt.expHeadBlock = 4
   163  	}
   164  	testRepair(t, rt, snapshots)
   165  }
   166  
   167  // Tests a recovery for a short canonical chain and a longer side chain, where a
   168  // recent block was already committed to disk and then the process crashed. In this
   169  // case we expect the canonical chain to be rolled back to the committed block, but
   170  // the chain data itself left in the database for replaying.
   171  func TestShortReorgedRepair(t *testing.T)              { testShortReorgedRepair(t, false) }
   172  func TestShortReorgedRepairWithSnapshots(t *testing.T) { testShortReorgedRepair(t, true) }
   173  
   174  func testShortReorgedRepair(t *testing.T, snapshots bool) {
   175  	// Chain:
   176  	//   G->C1->C2->C3->C4->C5->C6->C7->C8 (HEAD)
   177  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
   178  	//
   179  	// Commit: G, C4
   180  	//
   181  	// CRASH
   182  	//
   183  	// ------------------------------
   184  	//
   185  	// Expected in leveldb:
   186  	//   G->C1->C2->C3->C4->C5->C6->C7->C8
   187  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10
   188  	//
   189  	// Expected head block     : C4 (C0 with no snapshots)
   190  	rt := &rewindTest{
   191  		canonicalBlocks:    8,
   192  		sidechainBlocks:    10,
   193  		commitBlock:        4,
   194  		expCanonicalBlocks: 8,
   195  		expSidechainBlocks: 10,
   196  		expHeadBlock:       0,
   197  	}
   198  	if snapshots {
   199  		rt.expHeadBlock = 4
   200  	}
   201  	testRepair(t, rt, snapshots)
   202  }
   203  
   204  // Tests a recovery for a long canonical chain where a recent block was already
   205  // committed to disk and then the process crashed. In this case we expect the chain
   206  // to be rolled back to the committed block, but the chain data itself left in the
   207  // database for replaying.
   208  func TestLongShallowRepair(t *testing.T)              { testLongShallowRepair(t, false) }
   209  func TestLongShallowRepairWithSnapshots(t *testing.T) { testLongShallowRepair(t, true) }
   210  
   211  func testLongShallowRepair(t *testing.T, snapshots bool) {
   212  	// Chain:
   213  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
   214  	//
   215  	// Commit: G, C4
   216  	//
   217  	// CRASH
   218  	//
   219  	// ------------------------------
   220  	//
   221  	// Expected in leveldb:
   222  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
   223  	//
   224  	// Expected head block     : C4 (C0 with no snapshots)
   225  	rt := &rewindTest{
   226  		canonicalBlocks:    18,
   227  		sidechainBlocks:    0,
   228  		commitBlock:        4,
   229  		expCanonicalBlocks: 18,
   230  		expSidechainBlocks: 0,
   231  		expHeadBlock:       0,
   232  	}
   233  	if snapshots {
   234  		rt.expHeadBlock = 4
   235  	}
   236  	testRepair(t, rt, snapshots)
   237  }
   238  
   239  // Tests a recovery for a long canonical chain where a recent block was already committed
   240  // to disk and then the process crashed. In this case we expect the chain to be rolled
   241  // back to the committed block, but the chain data itself left in the database for replaying.
   242  func TestLongDeepRepair(t *testing.T)              { testLongDeepRepair(t, false) }
   243  func TestLongDeepRepairWithSnapshots(t *testing.T) { testLongDeepRepair(t, true) }
   244  
   245  func testLongDeepRepair(t *testing.T, snapshots bool) {
   246  	// Chain:
   247  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
   248  	//
   249  	// Commit: G, C4
   250  	//
   251  	// CRASH
   252  	//
   253  	// ------------------------------
   254  	//
   255  	// Expected in leveldb: none
   256  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
   257  	//
   258  	// Expected head block     : C4 (C0 with no snapshots)
   259  	rt := &rewindTest{
   260  		canonicalBlocks:    24,
   261  		sidechainBlocks:    0,
   262  		commitBlock:        4,
   263  		expCanonicalBlocks: 24,
   264  		expSidechainBlocks: 0,
   265  		expHeadBlock:       0,
   266  	}
   267  	if snapshots {
   268  		rt.expHeadBlock = 4
   269  	}
   270  	testRepair(t, rt, snapshots)
   271  }
   272  
   273  // Tests a recovery for a long canonical chain with a shorter side chain, where a recent
   274  // block was already committed to disk and then the process crashed. In this test scenario
   275  // the side chain is below the committed block. In this case we expect the chain to be
   276  // rolled back to the committed block, but the chain data itself left in the database
   277  // for replaying.
   278  func TestLongOldForkedShallowRepair(t *testing.T) {
   279  	testLongOldForkedShallowRepair(t, false)
   280  }
   281  func TestLongOldForkedShallowRepairWithSnapshots(t *testing.T) {
   282  	testLongOldForkedShallowRepair(t, true)
   283  }
   284  
   285  func testLongOldForkedShallowRepair(t *testing.T, snapshots bool) {
   286  	// Chain:
   287  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
   288  	//   └->S1->S2->S3
   289  	//
   290  	// Commit: G, C4
   291  	//
   292  	// CRASH
   293  	//
   294  	// ------------------------------
   295  	//
   296  	// Expected in leveldb:
   297  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
   298  	//   └->S1->S2->S3
   299  	//
   300  	// Expected head block     : C4 (C0 with no snapshots)
   301  	rt := &rewindTest{
   302  		canonicalBlocks:    18,
   303  		sidechainBlocks:    3,
   304  		commitBlock:        4,
   305  		expCanonicalBlocks: 18,
   306  		expSidechainBlocks: 3,
   307  		expHeadBlock:       0,
   308  	}
   309  	if snapshots {
   310  		rt.expHeadBlock = 4
   311  	}
   312  	testRepair(t, rt, snapshots)
   313  }
   314  
   315  // Tests a recovery for a long canonical chain a shorter side chain, where a recent block
   316  // was already committed to disk and then the process crashed. In this test scenario the side
   317  // chain is below the committed block. In this case we expect the canonical chain to be
   318  // rolled back to the committed block, but the chain data itself left in the database for replaying.
   319  func TestLongOldForkedDeepRepair(t *testing.T)              { testLongOldForkedDeepRepair(t, false) }
   320  func TestLongOldForkedDeepRepairWithSnapshots(t *testing.T) { testLongOldForkedDeepRepair(t, true) }
   321  
   322  func testLongOldForkedDeepRepair(t *testing.T, snapshots bool) {
   323  	// Chain:
   324  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
   325  	//   └->S1->S2->S3
   326  	//
   327  	// Commit: G, C4
   328  	//
   329  	// CRASH
   330  	//
   331  	// ------------------------------
   332  	//
   333  	// Expected in leveldb:
   334  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
   335  	//   └->S1->S2->S3
   336  	//
   337  	// Expected head block     : C4 (C0 with no snapshots)
   338  	rt := &rewindTest{
   339  		canonicalBlocks:    24,
   340  		sidechainBlocks:    3,
   341  		commitBlock:        4,
   342  		expCanonicalBlocks: 24,
   343  		expSidechainBlocks: 3,
   344  		expHeadBlock:       0,
   345  	}
   346  	if snapshots {
   347  		rt.expHeadBlock = 4
   348  	}
   349  	testRepair(t, rt, snapshots)
   350  }
   351  
   352  // Tests a recovery for a long canonical chain with a shorter side chain, where a recent
   353  // block was already committed to disk and then the process crashed. In this test scenario
   354  // the side chain is above the committed block. In this case we expect the chain to be
   355  // rolled back to the committed block, but the chain data itself left in the database for replaying.
   356  func TestLongNewerForkedShallowRepair(t *testing.T) {
   357  	testLongNewerForkedShallowRepair(t, false)
   358  }
   359  func TestLongNewerForkedShallowRepairWithSnapshots(t *testing.T) {
   360  	testLongNewerForkedShallowRepair(t, true)
   361  }
   362  
   363  func testLongNewerForkedShallowRepair(t *testing.T, snapshots bool) {
   364  	// Chain:
   365  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
   366  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
   367  	//
   368  	// Commit: G, C4
   369  	//
   370  	// CRASH
   371  	//
   372  	// ------------------------------
   373  	//
   374  	// Expected in leveldb:
   375  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
   376  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
   377  	//
   378  	// Expected head block     : C4 (C0 with no snapshots)
   379  	rt := &rewindTest{
   380  		canonicalBlocks:    18,
   381  		sidechainBlocks:    12,
   382  		commitBlock:        4,
   383  		expCanonicalBlocks: 18,
   384  		expSidechainBlocks: 12,
   385  		expHeadBlock:       0,
   386  	}
   387  	if snapshots {
   388  		rt.expHeadBlock = 4
   389  	}
   390  	testRepair(t, rt, snapshots)
   391  }
   392  
   393  // Tests a recovery for a long canonical chain with a shorter side chain, where a recent block
   394  // was already committed to disk and then the process crashed. In this test scenario the side
   395  // chain is above the committed block. In this case we expect the canonical chain to be rolled
   396  // back to the committed block, but the chain data itself left in the database for replaying.
   397  func TestLongNewerForkedDeepRepair(t *testing.T)              { testLongNewerForkedDeepRepair(t, false) }
   398  func TestLongNewerForkedDeepRepairWithSnapshots(t *testing.T) { testLongNewerForkedDeepRepair(t, true) }
   399  
   400  func testLongNewerForkedDeepRepair(t *testing.T, snapshots bool) {
   401  	// Chain:
   402  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
   403  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
   404  	//
   405  	// Commit: G, C4
   406  	//
   407  	// CRASH
   408  	//
   409  	// ------------------------------
   410  	//
   411  	// Expected in leveldb:
   412  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
   413  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12
   414  	//
   415  	// Expected head block     : C4 (C0 with no snapshots)
   416  	rt := &rewindTest{
   417  		canonicalBlocks:    24,
   418  		sidechainBlocks:    12,
   419  		commitBlock:        4,
   420  		expCanonicalBlocks: 24,
   421  		expSidechainBlocks: 12,
   422  		expHeadBlock:       0,
   423  	}
   424  	if snapshots {
   425  		rt.expHeadBlock = 4
   426  	}
   427  	testRepair(t, rt, snapshots)
   428  }
   429  
   430  // Tests a recovery for a long canonical chain with a longer side chain, where a recent block
   431  // was already committed to disk and then the process crashed. In this case we expect the chain to be
   432  // rolled back to the committed block, but the chain data itself left in the database for replaying.
   433  func TestLongReorgedShallowRepair(t *testing.T)              { testLongReorgedShallowRepair(t, false) }
   434  func TestLongReorgedShallowRepairWithSnapshots(t *testing.T) { testLongReorgedShallowRepair(t, true) }
   435  
   436  func testLongReorgedShallowRepair(t *testing.T, snapshots bool) {
   437  	// Chain:
   438  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18 (HEAD)
   439  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
   440  	//
   441  	// Commit: G, C4
   442  	//
   443  	// CRASH
   444  	//
   445  	// ------------------------------
   446  	//
   447  	// Expected in leveldb:
   448  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18
   449  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
   450  	//
   451  	// Expected head block     : C4 (C0 with no snapshots)
   452  	rt := &rewindTest{
   453  		canonicalBlocks:    18,
   454  		sidechainBlocks:    26,
   455  		commitBlock:        4,
   456  		expCanonicalBlocks: 18,
   457  		expSidechainBlocks: 26,
   458  		expHeadBlock:       0,
   459  	}
   460  	if snapshots {
   461  		rt.expHeadBlock = 4
   462  	}
   463  	testRepair(t, rt, snapshots)
   464  }
   465  
   466  // Tests a recovery for a long canonical chain with a longer side chain, where a recent block
   467  // was already committed to disk and then the process crashed. In this case we expect the canonical
   468  // chains to be rolled back to the committed block, but the chain data itself left in the database
   469  // for replaying.
   470  func TestLongReorgedDeepRepair(t *testing.T)              { testLongReorgedDeepRepair(t, false) }
   471  func TestLongReorgedDeepRepairWithSnapshots(t *testing.T) { testLongReorgedDeepRepair(t, true) }
   472  
   473  func testLongReorgedDeepRepair(t *testing.T, snapshots bool) {
   474  	// Chain:
   475  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24 (HEAD)
   476  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
   477  	//
   478  	// Commit: G, C4
   479  	//
   480  	// CRASH
   481  	//
   482  	// ------------------------------
   483  	//
   484  	// Expected in leveldb:
   485  	//   G->C1->C2->C3->C4->C5->C6->C7->C8->C9->C10->C11->C12->C13->C14->C15->C16->C17->C18->C19->C20->C21->C22->C23->C24
   486  	//   └->S1->S2->S3->S4->S5->S6->S7->S8->S9->S10->S11->S12->S13->S14->S15->S16->S17->S18->S19->S20->S21->S22->S23->S24->S25->S26
   487  	//
   488  	// Expected head block     : C4 (C0 with no snapshots)
   489  	rt := &rewindTest{
   490  		canonicalBlocks:    24,
   491  		sidechainBlocks:    26,
   492  		commitBlock:        4,
   493  		expCanonicalBlocks: 24,
   494  		expSidechainBlocks: 26,
   495  		expHeadBlock:       0,
   496  	}
   497  	if snapshots {
   498  		rt.expHeadBlock = 4
   499  	}
   500  	testRepair(t, rt, snapshots)
   501  }
   502  
   503  func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
   504  	// It's hard to follow the test case, visualize the input
   505  	//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
   506  	// fmt.Println(tt.dump(true))
   507  
   508  	// Create a temporary persistent database
   509  	datadir := t.TempDir()
   510  
   511  	db, err := rawdb.NewLevelDBDatabase(datadir, 0, 0, "", false)
   512  	if err != nil {
   513  		t.Fatalf("Failed to create persistent database: %v", err)
   514  	}
   515  	defer db.Close() // Might double close, should be fine
   516  
   517  	// Initialize a fresh chain
   518  	var (
   519  		genesis = (&Genesis{Config: params.TestChainConfig, BaseFee: big.NewInt(params.TestInitialBaseFee)}).MustCommit(db)
   520  		engine  = dummy.NewFullFaker()
   521  		config  = &CacheConfig{
   522  			TrieCleanLimit: 256,
   523  			TrieDirtyLimit: 256,
   524  			SnapshotLimit:  0, // Disable snapshot by default
   525  		}
   526  	)
   527  	defer engine.Close()
   528  	if snapshots {
   529  		config.SnapshotLimit = 256
   530  	}
   531  	chain, err := NewBlockChain(db, config, params.TestChainConfig, engine, vm.Config{}, common.Hash{})
   532  	if err != nil {
   533  		t.Fatalf("Failed to create chain: %v", err)
   534  	}
   535  	lastAcceptedHash := chain.GetBlockByNumber(0).Hash()
   536  
   537  	// If sidechain blocks are needed, make a light chain and import it
   538  	var sideblocks types.Blocks
   539  	if tt.sidechainBlocks > 0 {
   540  		sideblocks, _, _ = GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, 10, func(i int, b *BlockGen) {
   541  			b.SetCoinbase(common.Address{0x01})
   542  		})
   543  		if _, err := chain.InsertChain(sideblocks); err != nil {
   544  			t.Fatalf("Failed to import side chain: %v", err)
   545  		}
   546  	}
   547  	canonblocks, _, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, 10, func(i int, b *BlockGen) {
   548  		b.SetCoinbase(common.Address{0x02})
   549  		b.SetDifficulty(big.NewInt(1000000))
   550  	})
   551  	if _, err := chain.InsertChain(canonblocks[:tt.commitBlock]); err != nil {
   552  		t.Fatalf("Failed to import canonical chain start: %v", err)
   553  	}
   554  	if tt.commitBlock > 0 {
   555  		if snapshots {
   556  			for i := uint64(0); i < tt.commitBlock; i++ {
   557  				if err := chain.Accept(canonblocks[i]); err != nil {
   558  					t.Fatalf("Failed to accept block %v: %v", i, err)
   559  				}
   560  				lastAcceptedHash = canonblocks[i].Hash()
   561  			}
   562  			chain.DrainAcceptorQueue()
   563  		}
   564  	}
   565  	if _, err := chain.InsertChain(canonblocks[tt.commitBlock:]); err != nil {
   566  		t.Fatalf("Failed to import canonical chain tail: %v", err)
   567  	}
   568  
   569  	// Pull the plug on the database, simulating a hard crash
   570  	db.Close()
   571  
   572  	// Start a new blockchain back up and see where the repair leads us
   573  	db, err = rawdb.NewLevelDBDatabase(datadir, 0, 0, "", false)
   574  	if err != nil {
   575  		t.Fatalf("Failed to reopen persistent database: %v", err)
   576  	}
   577  	defer db.Close()
   578  
   579  	newChain, err := NewBlockChain(db, config, params.TestChainConfig, engine, vm.Config{}, lastAcceptedHash)
   580  	if err != nil {
   581  		t.Fatalf("Failed to recreate chain: %v", err)
   582  	}
   583  	defer newChain.Stop()
   584  
   585  	// Iterate over all the remaining blocks and ensure there are no gaps
   586  	verifyNoGaps(t, newChain, true, canonblocks)
   587  	verifyNoGaps(t, newChain, false, sideblocks)
   588  	verifyCutoff(t, newChain, true, canonblocks, tt.expCanonicalBlocks)
   589  	verifyCutoff(t, newChain, false, sideblocks, tt.expSidechainBlocks)
   590  
   591  	if head := newChain.CurrentHeader(); head.Number.Uint64() != tt.expHeadBlock {
   592  		t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadBlock)
   593  	}
   594  	if head := newChain.CurrentBlock(); head.NumberU64() != tt.expHeadBlock {
   595  		t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), tt.expHeadBlock)
   596  	}
   597  }