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

     1  // (c) 2020-2021, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package core
     5  
     6  import (
     7  	"fmt"
     8  	"math/big"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/MetalBlockchain/subnet-evm/consensus/dummy"
    14  	"github.com/MetalBlockchain/subnet-evm/core/rawdb"
    15  	"github.com/MetalBlockchain/subnet-evm/core/state"
    16  	"github.com/MetalBlockchain/subnet-evm/core/state/pruner"
    17  	"github.com/MetalBlockchain/subnet-evm/core/types"
    18  	"github.com/MetalBlockchain/subnet-evm/core/vm"
    19  	"github.com/MetalBlockchain/subnet-evm/ethdb"
    20  	"github.com/MetalBlockchain/subnet-evm/params"
    21  	"github.com/ethereum/go-ethereum/common"
    22  	"github.com/ethereum/go-ethereum/crypto"
    23  	"github.com/fsnotify/fsnotify"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  var (
    29  	archiveConfig = &CacheConfig{
    30  		TrieCleanLimit:        256,
    31  		TrieDirtyLimit:        256,
    32  		TrieDirtyCommitTarget: 20,
    33  		Pruning:               false, // Archive mode
    34  		SnapshotLimit:         256,
    35  		AcceptorQueueLimit:    64,
    36  	}
    37  
    38  	pruningConfig = &CacheConfig{
    39  		TrieCleanLimit:        256,
    40  		TrieDirtyLimit:        256,
    41  		TrieDirtyCommitTarget: 20,
    42  		Pruning:               true, // Enable pruning
    43  		CommitInterval:        4096,
    44  		SnapshotLimit:         256,
    45  		AcceptorQueueLimit:    64,
    46  	}
    47  )
    48  
    49  func createBlockChain(
    50  	db ethdb.Database,
    51  	cacheConfig *CacheConfig,
    52  	chainConfig *params.ChainConfig,
    53  	lastAcceptedHash common.Hash,
    54  ) (*BlockChain, error) {
    55  	// Import the chain. This runs all block validation rules.
    56  	blockchain, err := NewBlockChain(
    57  		db,
    58  		cacheConfig,
    59  		chainConfig,
    60  		dummy.NewCoinbaseFaker(),
    61  		vm.Config{},
    62  		lastAcceptedHash,
    63  	)
    64  	return blockchain, err
    65  }
    66  
    67  func TestArchiveBlockChain(t *testing.T) {
    68  	createArchiveBlockChain := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
    69  		return createBlockChain(db, archiveConfig, chainConfig, lastAcceptedHash)
    70  	}
    71  	for _, tt := range tests {
    72  		t.Run(tt.Name, func(t *testing.T) {
    73  			tt.testFunc(t, createArchiveBlockChain)
    74  		})
    75  	}
    76  }
    77  
    78  // awaitWatcherEventsSubside waits for at least one event on [watcher] and then waits
    79  // for at least [subsideTimeout] before returning
    80  func awaitWatcherEventsSubside(watcher *fsnotify.Watcher, subsideTimeout time.Duration) {
    81  	done := make(chan struct{})
    82  
    83  	go func() {
    84  		defer func() {
    85  			close(done)
    86  		}()
    87  
    88  		select {
    89  		case <-watcher.Events:
    90  		case <-watcher.Errors:
    91  			return
    92  		}
    93  
    94  		for {
    95  			select {
    96  			case <-watcher.Events:
    97  			case <-watcher.Errors:
    98  				return
    99  			case <-time.After(subsideTimeout):
   100  				return
   101  			}
   102  		}
   103  	}()
   104  	<-done
   105  }
   106  
   107  func TestTrieCleanJournal(t *testing.T) {
   108  	t.Skip("FLAKY")
   109  	require := require.New(t)
   110  	assert := assert.New(t)
   111  
   112  	trieCleanJournal := t.TempDir()
   113  	trieCleanJournalWatcher, err := fsnotify.NewWatcher()
   114  	require.NoError(err)
   115  	defer func() {
   116  		assert.NoError(trieCleanJournalWatcher.Close())
   117  	}()
   118  	require.NoError(trieCleanJournalWatcher.Add(trieCleanJournal))
   119  
   120  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   121  		config := *archiveConfig
   122  		config.TrieCleanJournal = trieCleanJournal
   123  		config.TrieCleanRejournal = 100 * time.Millisecond
   124  		return createBlockChain(db, &config, chainConfig, lastAcceptedHash)
   125  	}
   126  
   127  	var (
   128  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   129  		key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   130  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   131  		addr2   = crypto.PubkeyToAddress(key2.PublicKey)
   132  		// We use two separate databases since GenerateChain commits the state roots to its underlying
   133  		// database.
   134  		genDB   = rawdb.NewMemoryDatabase()
   135  		chainDB = rawdb.NewMemoryDatabase()
   136  	)
   137  
   138  	// Ensure that key1 has some funds in the genesis block.
   139  	genesisBalance := big.NewInt(1000000)
   140  	gspec := &Genesis{
   141  		Config: &params.ChainConfig{HomesteadBlock: new(big.Int)},
   142  		Alloc:  GenesisAlloc{addr1: {Balance: genesisBalance}},
   143  	}
   144  	genesis := gspec.MustCommit(genDB)
   145  	_ = gspec.MustCommit(chainDB)
   146  
   147  	blockchain, err := create(chainDB, gspec.Config, common.Hash{})
   148  	require.NoError(err)
   149  	defer blockchain.Stop()
   150  
   151  	// This call generates a chain of 3 blocks.
   152  	signer := types.HomesteadSigner{}
   153  	// Generate chain of blocks using [genDB] instead of [chainDB] to avoid writing
   154  	// to the BlockChain's database while generating blocks.
   155  	chain, _, err := GenerateChain(gspec.Config, genesis, blockchain.engine, genDB, 3, 10, func(i int, gen *BlockGen) {
   156  		tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
   157  		gen.AddTx(tx)
   158  	})
   159  	require.NoError(err)
   160  
   161  	// Insert and accept the generated chain
   162  	_, err = blockchain.InsertChain(chain)
   163  	require.NoError(err)
   164  
   165  	for _, block := range chain {
   166  		require.NoError(blockchain.Accept(block))
   167  	}
   168  	blockchain.DrainAcceptorQueue()
   169  
   170  	awaitWatcherEventsSubside(trieCleanJournalWatcher, time.Second)
   171  	// Assert that a new file is created in the trie clean journal
   172  	dirEntries, err := os.ReadDir(trieCleanJournal)
   173  	require.NoError(err)
   174  	require.NotEmpty(dirEntries)
   175  }
   176  
   177  func TestArchiveBlockChainSnapsDisabled(t *testing.T) {
   178  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   179  		return createBlockChain(
   180  			db,
   181  			&CacheConfig{
   182  				TrieCleanLimit:        256,
   183  				TrieDirtyLimit:        256,
   184  				TrieDirtyCommitTarget: 20,
   185  				Pruning:               false, // Archive mode
   186  				SnapshotLimit:         0,     // Disable snapshots
   187  				AcceptorQueueLimit:    64,
   188  			},
   189  			chainConfig,
   190  			lastAcceptedHash,
   191  		)
   192  	}
   193  	for _, tt := range tests {
   194  		t.Run(tt.Name, func(t *testing.T) {
   195  			tt.testFunc(t, create)
   196  		})
   197  	}
   198  }
   199  
   200  func TestPruningBlockChain(t *testing.T) {
   201  	createPruningBlockChain := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   202  		return createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash)
   203  	}
   204  	for _, tt := range tests {
   205  		t.Run(tt.Name, func(t *testing.T) {
   206  			tt.testFunc(t, createPruningBlockChain)
   207  		})
   208  	}
   209  }
   210  
   211  func TestPruningBlockChainSnapsDisabled(t *testing.T) {
   212  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   213  		return createBlockChain(
   214  			db,
   215  			&CacheConfig{
   216  				TrieCleanLimit:        256,
   217  				TrieDirtyLimit:        256,
   218  				TrieDirtyCommitTarget: 20,
   219  				Pruning:               true, // Enable pruning
   220  				CommitInterval:        4096,
   221  				SnapshotLimit:         0, // Disable snapshots
   222  				AcceptorQueueLimit:    64,
   223  			},
   224  			chainConfig,
   225  			lastAcceptedHash,
   226  		)
   227  	}
   228  	for _, tt := range tests {
   229  		t.Run(tt.Name, func(t *testing.T) {
   230  			tt.testFunc(t, create)
   231  		})
   232  	}
   233  }
   234  
   235  type wrappedStateManager struct {
   236  	TrieWriter
   237  }
   238  
   239  func (w *wrappedStateManager) Shutdown() error { return nil }
   240  
   241  func TestPruningBlockChainUngracefulShutdown(t *testing.T) {
   242  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   243  		blockchain, err := createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash)
   244  		if err != nil {
   245  			return nil, err
   246  		}
   247  
   248  		// Overwrite state manager, so that Shutdown is not called.
   249  		// This tests to ensure that the state manager handles an ungraceful shutdown correctly.
   250  		blockchain.stateManager = &wrappedStateManager{TrieWriter: blockchain.stateManager}
   251  		return blockchain, err
   252  	}
   253  	for _, tt := range tests {
   254  		t.Run(tt.Name, func(t *testing.T) {
   255  			tt.testFunc(t, create)
   256  		})
   257  	}
   258  }
   259  
   260  func TestPruningBlockChainUngracefulShutdownSnapsDisabled(t *testing.T) {
   261  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   262  		blockchain, err := createBlockChain(
   263  			db,
   264  			&CacheConfig{
   265  				TrieCleanLimit:        256,
   266  				TrieDirtyLimit:        256,
   267  				TrieDirtyCommitTarget: 20,
   268  				Pruning:               true, // Enable pruning
   269  				CommitInterval:        4096,
   270  				SnapshotLimit:         0, // Disable snapshots
   271  				AcceptorQueueLimit:    64,
   272  			},
   273  			chainConfig,
   274  			lastAcceptedHash,
   275  		)
   276  		if err != nil {
   277  			return nil, err
   278  		}
   279  
   280  		// Overwrite state manager, so that Shutdown is not called.
   281  		// This tests to ensure that the state manager handles an ungraceful shutdown correctly.
   282  		blockchain.stateManager = &wrappedStateManager{TrieWriter: blockchain.stateManager}
   283  		return blockchain, err
   284  	}
   285  	for _, tt := range tests {
   286  		t.Run(tt.Name, func(t *testing.T) {
   287  			tt.testFunc(t, create)
   288  		})
   289  	}
   290  }
   291  
   292  func TestEnableSnapshots(t *testing.T) {
   293  	// Set snapshots to be disabled the first time, and then enable them on the restart
   294  	snapLimit := 0
   295  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   296  		// Import the chain. This runs all block validation rules.
   297  		blockchain, err := createBlockChain(
   298  			db,
   299  			&CacheConfig{
   300  				TrieCleanLimit:        256,
   301  				TrieDirtyLimit:        256,
   302  				TrieDirtyCommitTarget: 20,
   303  				Pruning:               true, // Enable pruning
   304  				CommitInterval:        4096,
   305  				SnapshotLimit:         snapLimit,
   306  				AcceptorQueueLimit:    64,
   307  			},
   308  			chainConfig,
   309  			lastAcceptedHash,
   310  		)
   311  		if err != nil {
   312  			return nil, err
   313  		}
   314  		snapLimit = 256
   315  
   316  		return blockchain, err
   317  	}
   318  	for _, tt := range tests {
   319  		t.Run(tt.Name, func(t *testing.T) {
   320  			tt.testFunc(t, create)
   321  		})
   322  	}
   323  }
   324  
   325  func TestCorruptSnapshots(t *testing.T) {
   326  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   327  		// Delete the snapshot block hash and state root to ensure that if we die in between writing a snapshot
   328  		// diff layer to disk at any point, we can still recover on restart.
   329  		rawdb.DeleteSnapshotBlockHash(db)
   330  		rawdb.DeleteSnapshotRoot(db)
   331  
   332  		return createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash)
   333  	}
   334  	for _, tt := range tests {
   335  		t.Run(tt.Name, func(t *testing.T) {
   336  			tt.testFunc(t, create)
   337  		})
   338  	}
   339  }
   340  
   341  func TestBlockChainOfflinePruningUngracefulShutdown(t *testing.T) {
   342  	create := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   343  		// Import the chain. This runs all block validation rules.
   344  		blockchain, err := createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash)
   345  		if err != nil {
   346  			return nil, err
   347  		}
   348  
   349  		// Overwrite state manager, so that Shutdown is not called.
   350  		// This tests to ensure that the state manager handles an ungraceful shutdown correctly.
   351  		blockchain.stateManager = &wrappedStateManager{TrieWriter: blockchain.stateManager}
   352  
   353  		if lastAcceptedHash == (common.Hash{}) {
   354  			return blockchain, nil
   355  		}
   356  
   357  		targetRoot := blockchain.LastAcceptedBlock().Root()
   358  		if targetRoot == blockchain.Genesis().Root() {
   359  			return blockchain, nil
   360  		}
   361  
   362  		tempDir := t.TempDir()
   363  		if err := blockchain.CleanBlockRootsAboveLastAccepted(); err != nil {
   364  			return nil, err
   365  		}
   366  		pruner, err := pruner.NewPruner(db, tempDir, 256)
   367  		if err != nil {
   368  			return nil, fmt.Errorf("offline pruning failed (%s, %d): %w", tempDir, 256, err)
   369  		}
   370  
   371  		if err := pruner.Prune(targetRoot); err != nil {
   372  			return nil, fmt.Errorf("failed to prune blockchain with target root: %s due to: %w", targetRoot, err)
   373  		}
   374  		// Re-initialize the blockchain after pruning
   375  		return createBlockChain(db, pruningConfig, chainConfig, lastAcceptedHash)
   376  	}
   377  	for _, tt := range tests {
   378  		tst := tt
   379  		t.Run(tt.Name, func(t *testing.T) {
   380  			t.Parallel()
   381  			tst.testFunc(t, create)
   382  		})
   383  	}
   384  }
   385  
   386  func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) {
   387  	var (
   388  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   389  		key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   390  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   391  		addr2   = crypto.PubkeyToAddress(key2.PublicKey)
   392  		// We use two separate databases since GenerateChain commits the state roots to its underlying
   393  		// database.
   394  		genDB            = rawdb.NewMemoryDatabase()
   395  		chainDB          = rawdb.NewMemoryDatabase()
   396  		lastAcceptedHash common.Hash
   397  	)
   398  
   399  	// Ensure that key1 has some funds in the genesis block.
   400  	genesisBalance := big.NewInt(1000000)
   401  	gspec := &Genesis{
   402  		Config: &params.ChainConfig{HomesteadBlock: new(big.Int)},
   403  		Alloc:  GenesisAlloc{addr1: {Balance: genesisBalance}},
   404  	}
   405  	genesis := gspec.MustCommit(genDB)
   406  	_ = gspec.MustCommit(chainDB)
   407  
   408  	blockchain, err := createBlockChain(chainDB, pruningConfig, gspec.Config, lastAcceptedHash)
   409  	if err != nil {
   410  		t.Fatal(err)
   411  	}
   412  	defer blockchain.Stop()
   413  
   414  	// This call generates a chain of 3 blocks.
   415  	signer := types.HomesteadSigner{}
   416  	// Generate chain of blocks using [genDB] instead of [chainDB] to avoid writing
   417  	// to the BlockChain's database while generating blocks.
   418  	chain, _, err := GenerateChain(gspec.Config, genesis, blockchain.engine, genDB, 10, 10, func(i int, gen *BlockGen) {
   419  		tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
   420  		gen.AddTx(tx)
   421  	})
   422  	if err != nil {
   423  		t.Fatal(err)
   424  	}
   425  
   426  	if _, err := blockchain.InsertChain(chain); err != nil {
   427  		t.Fatal(err)
   428  	}
   429  	for _, block := range chain {
   430  		if err := blockchain.Accept(block); err != nil {
   431  			t.Fatal(err)
   432  		}
   433  	}
   434  	blockchain.DrainAcceptorQueue()
   435  
   436  	lastAcceptedHash = blockchain.LastConsensusAcceptedBlock().Hash()
   437  	blockchain.Stop()
   438  
   439  	blockchain, err = createBlockChain(chainDB, pruningConfig, gspec.Config, lastAcceptedHash)
   440  	if err != nil {
   441  		t.Fatal(err)
   442  	}
   443  
   444  	// Confirm that the node does not have the state for intermediate nodes (exclude the last accepted block)
   445  	for _, block := range chain[:len(chain)-1] {
   446  		if blockchain.HasState(block.Root()) {
   447  			t.Fatalf("Expected blockchain to be missing state for intermediate block %d with pruning enabled", block.NumberU64())
   448  		}
   449  	}
   450  	blockchain.Stop()
   451  
   452  	startHeight := uint64(1)
   453  	// Create a node in archival mode and re-populate the trie history.
   454  	blockchain, err = createBlockChain(
   455  		chainDB,
   456  		&CacheConfig{
   457  			TrieCleanLimit:                  256,
   458  			TrieDirtyLimit:                  256,
   459  			TrieDirtyCommitTarget:           20,
   460  			Pruning:                         false, // Archive mode
   461  			SnapshotLimit:                   256,
   462  			PopulateMissingTries:            &startHeight, // Starting point for re-populating.
   463  			PopulateMissingTriesParallelism: parallelism,
   464  			AcceptorQueueLimit:              64,
   465  		},
   466  		gspec.Config,
   467  		lastAcceptedHash,
   468  	)
   469  	if err != nil {
   470  		t.Fatal(err)
   471  	}
   472  
   473  	for _, block := range chain {
   474  		if !blockchain.HasState(block.Root()) {
   475  			t.Fatalf("failed to re-generate state for block %d", block.NumberU64())
   476  		}
   477  	}
   478  }
   479  
   480  func TestRepopulateMissingTries(t *testing.T) {
   481  	// Test with different levels of parallelism as a regression test.
   482  	for _, parallelism := range []int{1, 2, 4, 1024} {
   483  		testRepopulateMissingTriesParallel(t, parallelism)
   484  	}
   485  }
   486  
   487  func TestUngracefulAsyncShutdown(t *testing.T) {
   488  	var (
   489  		create = func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   490  			blockchain, err := createBlockChain(db, &CacheConfig{
   491  				TrieCleanLimit:        256,
   492  				TrieDirtyLimit:        256,
   493  				TrieDirtyCommitTarget: 20,
   494  				Pruning:               true,
   495  				CommitInterval:        4096,
   496  				SnapshotLimit:         256,
   497  				SkipSnapshotRebuild:   true, // Ensure the test errors if snapshot initialization fails
   498  				AcceptorQueueLimit:    1000, // ensure channel doesn't block
   499  			}, chainConfig, lastAcceptedHash)
   500  			if err != nil {
   501  				return nil, err
   502  			}
   503  			return blockchain, nil
   504  		}
   505  
   506  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   507  		key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   508  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   509  		addr2   = crypto.PubkeyToAddress(key2.PublicKey)
   510  		// We use two separate databases since GenerateChain commits the state roots to its underlying
   511  		// database.
   512  		genDB   = rawdb.NewMemoryDatabase()
   513  		chainDB = rawdb.NewMemoryDatabase()
   514  	)
   515  
   516  	// Ensure that key1 has some funds in the genesis block.
   517  	genesisBalance := big.NewInt(1000000)
   518  	gspec := &Genesis{
   519  		Config: &params.ChainConfig{HomesteadBlock: new(big.Int)},
   520  		Alloc:  GenesisAlloc{addr1: {Balance: genesisBalance}},
   521  	}
   522  	genesis := gspec.MustCommit(genDB)
   523  	_ = gspec.MustCommit(chainDB)
   524  
   525  	blockchain, err := create(chainDB, gspec.Config, common.Hash{})
   526  	if err != nil {
   527  		t.Fatal(err)
   528  	}
   529  	defer blockchain.Stop()
   530  
   531  	// This call generates a chain of 10 blocks.
   532  	signer := types.HomesteadSigner{}
   533  	// Generate chain of blocks using [genDB] instead of [chainDB] to avoid writing
   534  	// to the BlockChain's database while generating blocks.
   535  	chain, _, err := GenerateChain(gspec.Config, genesis, blockchain.engine, genDB, 10, 10, func(i int, gen *BlockGen) {
   536  		tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
   537  		gen.AddTx(tx)
   538  	})
   539  	if err != nil {
   540  		t.Fatal(err)
   541  	}
   542  
   543  	// Insert three blocks into the chain and accept only the first block.
   544  	if _, err := blockchain.InsertChain(chain); err != nil {
   545  		t.Fatal(err)
   546  	}
   547  
   548  	foundTxs := []common.Hash{}
   549  	missingTxs := []common.Hash{}
   550  	for i, block := range chain {
   551  		if err := blockchain.Accept(block); err != nil {
   552  			t.Fatal(err)
   553  		}
   554  
   555  		if i == 3 {
   556  			// At height 3, kill the async accepted block processor to force an
   557  			// ungraceful recovery
   558  			blockchain.stopAcceptor()
   559  			blockchain.acceptorQueue = nil
   560  		}
   561  
   562  		if i <= 3 {
   563  			// If <= height 3, all txs should be accessible on lookup
   564  			for _, tx := range block.Transactions() {
   565  				foundTxs = append(foundTxs, tx.Hash())
   566  			}
   567  		} else {
   568  			// If > 3, all txs should be accessible on lookup
   569  			for _, tx := range block.Transactions() {
   570  				missingTxs = append(missingTxs, tx.Hash())
   571  			}
   572  		}
   573  	}
   574  
   575  	// After inserting all blocks, we should confirm that txs added after the
   576  	// async worker shutdown cannot be found.
   577  	for _, tx := range foundTxs {
   578  		txLookup := blockchain.GetTransactionLookup(tx)
   579  		if txLookup == nil {
   580  			t.Fatalf("missing transaction: %v", tx)
   581  		}
   582  	}
   583  	for _, tx := range missingTxs {
   584  		txLookup := blockchain.GetTransactionLookup(tx)
   585  		if txLookup != nil {
   586  			t.Fatalf("transaction should be missing: %v", tx)
   587  		}
   588  	}
   589  
   590  	// check the state of the last accepted block
   591  	checkState := func(sdb *state.StateDB) error {
   592  		nonce := sdb.GetNonce(addr1)
   593  		if nonce != 10 {
   594  			return fmt.Errorf("expected nonce addr1: 10, found nonce: %d", nonce)
   595  		}
   596  		transferredFunds := big.NewInt(100000)
   597  		balance1 := sdb.GetBalance(addr1)
   598  		expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds)
   599  		if balance1.Cmp(expectedBalance1) != 0 {
   600  			return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1)
   601  		}
   602  
   603  		balance2 := sdb.GetBalance(addr2)
   604  		expectedBalance2 := transferredFunds
   605  		if balance2.Cmp(expectedBalance2) != 0 {
   606  			return fmt.Errorf("expected addr2 balance: %d, found balance: %d", expectedBalance2, balance2)
   607  		}
   608  
   609  		nonce = sdb.GetNonce(addr2)
   610  		if nonce != 0 {
   611  			return fmt.Errorf("expected addr2 nonce: 0, found nonce: %d", nonce)
   612  		}
   613  		return nil
   614  	}
   615  
   616  	_, newChain, restartedChain := checkBlockChainState(t, blockchain, gspec, chainDB, create, checkState)
   617  
   618  	allTxs := append(foundTxs, missingTxs...)
   619  	for _, bc := range []*BlockChain{newChain, restartedChain} {
   620  		// We should confirm that snapshots were properly initialized
   621  		if bc.snaps == nil {
   622  			t.Fatal("snapshot initialization failed")
   623  		}
   624  
   625  		// We should confirm all transactions can now be queried
   626  		for _, tx := range allTxs {
   627  			txLookup := bc.GetTransactionLookup(tx)
   628  			if txLookup == nil {
   629  				t.Fatalf("missing transaction: %v", tx)
   630  			}
   631  		}
   632  	}
   633  }
   634  
   635  // TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted
   636  // correctly in case reorg is called.
   637  func TestCanonicalHashMarker(t *testing.T) {
   638  	var cases = []struct {
   639  		forkA int
   640  		forkB int
   641  	}{
   642  		// ForkA: 10 blocks
   643  		// ForkB: 1 blocks
   644  		//
   645  		// reorged:
   646  		//      markers [2, 10] should be deleted
   647  		//      markers [1] should be updated
   648  		{10, 1},
   649  
   650  		// ForkA: 10 blocks
   651  		// ForkB: 2 blocks
   652  		//
   653  		// reorged:
   654  		//      markers [3, 10] should be deleted
   655  		//      markers [1, 2] should be updated
   656  		{10, 2},
   657  
   658  		// ForkA: 10 blocks
   659  		// ForkB: 10 blocks
   660  		//
   661  		// reorged:
   662  		//      markers [1, 10] should be updated
   663  		{10, 10},
   664  
   665  		// ForkA: 10 blocks
   666  		// ForkB: 11 blocks
   667  		//
   668  		// reorged:
   669  		//      markers [1, 11] should be updated
   670  		{10, 11},
   671  	}
   672  	for _, c := range cases {
   673  		var (
   674  			db    = rawdb.NewMemoryDatabase()
   675  			gspec = &Genesis{
   676  				Config:  params.TestChainConfig,
   677  				Alloc:   GenesisAlloc{},
   678  				BaseFee: big.NewInt(params.TestInitialBaseFee),
   679  			}
   680  			genesis = gspec.MustCommit(db)
   681  			engine  = dummy.NewCoinbaseFaker()
   682  		)
   683  		forkA, _, err := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkA, 10, func(i int, gen *BlockGen) {})
   684  		if err != nil {
   685  			t.Fatal(err)
   686  		}
   687  		forkB, _, err := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkB, 10, func(i int, gen *BlockGen) {})
   688  		if err != nil {
   689  			t.Fatal(err)
   690  		}
   691  
   692  		// Initialize test chain
   693  		diskdb := rawdb.NewMemoryDatabase()
   694  		gspec.MustCommit(diskdb)
   695  		chain, err := NewBlockChain(diskdb, DefaultCacheConfig, params.TestChainConfig, engine, vm.Config{}, common.Hash{})
   696  		if err != nil {
   697  			t.Fatalf("failed to create tester chain: %v", err)
   698  		}
   699  		// Insert forkA and forkB, the canonical should on forkA still
   700  		if n, err := chain.InsertChain(forkA); err != nil {
   701  			t.Fatalf("block %d: failed to insert into chain: %v", n, err)
   702  		}
   703  		if n, err := chain.InsertChain(forkB); err != nil {
   704  			t.Fatalf("block %d: failed to insert into chain: %v", n, err)
   705  		}
   706  
   707  		verify := func(head *types.Block) {
   708  			if chain.CurrentBlock().Hash() != head.Hash() {
   709  				t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash())
   710  			}
   711  			if chain.CurrentHeader().Hash() != head.Hash() {
   712  				t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash())
   713  			}
   714  			if !chain.HasState(head.Root()) {
   715  				t.Fatalf("Lost block state %v %x", head.Number(), head.Hash())
   716  			}
   717  		}
   718  
   719  		// Switch canonical chain to forkB if necessary
   720  		if len(forkA) < len(forkB) {
   721  			verify(forkB[len(forkB)-1])
   722  		} else {
   723  			verify(forkA[len(forkA)-1])
   724  			if err := chain.SetPreference(forkB[len(forkB)-1]); err != nil {
   725  				t.Fatal(err)
   726  			}
   727  			verify(forkB[len(forkB)-1])
   728  		}
   729  
   730  		// Ensure all hash markers are updated correctly
   731  		for i := 0; i < len(forkB); i++ {
   732  			block := forkB[i]
   733  			hash := chain.GetCanonicalHash(block.NumberU64())
   734  			if hash != block.Hash() {
   735  				t.Fatalf("Unexpected canonical hash %d", block.NumberU64())
   736  			}
   737  		}
   738  		if c.forkA > c.forkB {
   739  			for i := uint64(c.forkB) + 1; i <= uint64(c.forkA); i++ {
   740  				hash := chain.GetCanonicalHash(i)
   741  				if hash != (common.Hash{}) {
   742  					t.Fatalf("Unexpected canonical hash %d", i)
   743  				}
   744  			}
   745  		}
   746  	}
   747  }
   748  
   749  func TestTransactionIndices(t *testing.T) {
   750  	// Configure and generate a sample block chain
   751  	require := require.New(t)
   752  	var (
   753  		gendb   = rawdb.NewMemoryDatabase()
   754  		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
   755  		key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
   756  		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
   757  		addr2   = crypto.PubkeyToAddress(key2.PublicKey)
   758  		funds   = big.NewInt(10000000000000)
   759  		gspec   = &Genesis{
   760  			Config: &params.ChainConfig{HomesteadBlock: new(big.Int)},
   761  			Alloc:  GenesisAlloc{addr1: {Balance: funds}},
   762  		}
   763  		genesis = gspec.MustCommit(gendb)
   764  		signer  = types.LatestSigner(gspec.Config)
   765  	)
   766  	height := uint64(128)
   767  	blocks, _, err := GenerateChain(gspec.Config, genesis, dummy.NewFaker(), gendb, int(height), 10, func(i int, block *BlockGen) {
   768  		tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
   769  		require.NoError(err)
   770  		block.AddTx(tx)
   771  	})
   772  	require.NoError(err)
   773  
   774  	blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFaker(), gendb, 10, 10, nil)
   775  	require.NoError(err)
   776  
   777  	check := func(tail *uint64, chain *BlockChain) {
   778  		stored := rawdb.ReadTxIndexTail(chain.db)
   779  		require.EqualValues(tail, stored)
   780  
   781  		if tail == nil {
   782  			return
   783  		}
   784  		for i := *tail; i <= chain.CurrentBlock().NumberU64(); i++ {
   785  			block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
   786  			if block.Transactions().Len() == 0 {
   787  				continue
   788  			}
   789  			for _, tx := range block.Transactions() {
   790  				index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash())
   791  				require.NotNilf(index, "Miss transaction indices, number %d hash %s", i, tx.Hash().Hex())
   792  			}
   793  		}
   794  
   795  		for i := uint64(0); i < *tail; i++ {
   796  			block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
   797  			if block.Transactions().Len() == 0 {
   798  				continue
   799  			}
   800  			for _, tx := range block.Transactions() {
   801  				index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash())
   802  				require.Nilf(index, "Transaction indices should be deleted, number %d hash %s", i, tx.Hash().Hex())
   803  			}
   804  		}
   805  	}
   806  
   807  	conf := &CacheConfig{
   808  		TrieCleanLimit:        256,
   809  		TrieDirtyLimit:        256,
   810  		TrieDirtyCommitTarget: 20,
   811  		Pruning:               true,
   812  		CommitInterval:        4096,
   813  		SnapshotLimit:         256,
   814  		SkipSnapshotRebuild:   true, // Ensure the test errors if snapshot initialization fails
   815  		AcceptorQueueLimit:    64,
   816  	}
   817  
   818  	// Init block chain and check all needed indices has been indexed.
   819  	chainDB := rawdb.NewMemoryDatabase()
   820  	gspec.MustCommit(chainDB)
   821  
   822  	chain, err := createBlockChain(chainDB, conf, gspec.Config, common.Hash{})
   823  	require.NoError(err)
   824  
   825  	_, err = chain.InsertChain(blocks)
   826  	require.NoError(err)
   827  
   828  	for _, block := range blocks {
   829  		err := chain.Accept(block)
   830  		require.NoError(err)
   831  	}
   832  	chain.DrainAcceptorQueue()
   833  
   834  	chain.Stop()
   835  	check(nil, chain) // check all indices has been indexed
   836  
   837  	lastAcceptedHash := chain.CurrentHeader().Hash()
   838  
   839  	// Reconstruct a block chain which only reserves limited tx indices
   840  	// 128 blocks were previously indexed. Now we add a new block at each test step.
   841  	limit := []uint64{130 /* 129 + 1 reserve all */, 64 /* drop stale */, 32 /* shorten history */}
   842  	tails := []uint64{0 /* reserve all */, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */}
   843  	for i, l := range limit {
   844  		conf.TxLookupLimit = l
   845  
   846  		chain, err := createBlockChain(chainDB, conf, gspec.Config, lastAcceptedHash)
   847  		require.NoError(err)
   848  
   849  		newBlks := blocks2[i : i+1]
   850  		_, err = chain.InsertChain(newBlks) // Feed chain a higher block to trigger indices updater.
   851  		require.NoError(err)
   852  
   853  		err = chain.Accept(newBlks[0]) // Accept the block to trigger indices updater.
   854  		require.NoError(err)
   855  
   856  		chain.DrainAcceptorQueue()
   857  		time.Sleep(50 * time.Millisecond) // Wait for indices initialisation
   858  
   859  		chain.Stop()
   860  		check(&tails[i], chain)
   861  
   862  		lastAcceptedHash = chain.CurrentHeader().Hash()
   863  	}
   864  }
   865  
   866  func TestTxLookupBlockChain(t *testing.T) {
   867  	cacheConf := &CacheConfig{
   868  		TrieCleanLimit:        256,
   869  		TrieDirtyLimit:        256,
   870  		TrieDirtyCommitTarget: 20,
   871  		Pruning:               true,
   872  		CommitInterval:        4096,
   873  		SnapshotLimit:         256,
   874  		SkipSnapshotRebuild:   true, // Ensure the test errors if snapshot initialization fails
   875  		AcceptorQueueLimit:    64,   // ensure channel doesn't block
   876  		TxLookupLimit:         5,
   877  	}
   878  	createTxLookupBlockChain := func(db ethdb.Database, chainConfig *params.ChainConfig, lastAcceptedHash common.Hash) (*BlockChain, error) {
   879  		return createBlockChain(db, cacheConf, chainConfig, lastAcceptedHash)
   880  	}
   881  	for _, tt := range tests {
   882  		t.Run(tt.Name, func(t *testing.T) {
   883  			tt.testFunc(t, createTxLookupBlockChain)
   884  		})
   885  	}
   886  }