diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go index 4f9222d092..1a39557108 100644 --- a/consensus/clique/snapshot_test.go +++ b/consensus/clique/snapshot_test.go @@ -19,6 +19,7 @@ package clique import ( "bytes" "crypto/ecdsa" + "fmt" "math/big" "sort" "testing" @@ -95,17 +96,19 @@ type testerVote struct { newbatch bool } +type cliqueTest struct { + epoch uint64 + signers []string + votes []testerVote + results []string + failure error +} + // Tests that Clique signer voting is evaluated correctly for various simple and // complex scenarios, as well as that a few special corner cases fail correctly. func TestClique(t *testing.T) { // Define the various voting scenarios to test - tests := []struct { - epoch uint64 - signers []string - votes []testerVote - results []string - failure error - }{ + tests := []cliqueTest{ { // Single signer, no votes cast signers: []string{"A"}, @@ -377,129 +380,129 @@ func TestClique(t *testing.T) { failure: errRecentlySigned, }, } + // Run through the scenarios and test them for i, tt := range tests { - // Create the account pool and generate the initial set of signers - accounts := newTesterAccountPool() + t.Run(fmt.Sprint(i), tt.run) + } +} - signers := make([]common.Address, len(tt.signers)) - for j, signer := range tt.signers { - signers[j] = accounts.address(signer) - } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } - } - } - // Create the genesis block with the initial set of signers - genesis := &core.Genesis{ - ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), - BaseFee: big.NewInt(params.InitialBaseFee), - } - for j, signer := range signers { - copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) - } +func (tt *cliqueTest) run(t *testing.T) { + // Create the account pool and generate the initial set of signers + accounts := newTesterAccountPool() - // Assemble a chain of headers from the cast votes - config := *params.TestChainConfig - config.Clique = ¶ms.CliqueConfig{ - Period: 1, - Epoch: tt.epoch, + signers := make([]common.Address, len(tt.signers)) + for j, signer := range tt.signers { + signers[j] = accounts.address(signer) + } + for j := 0; j < len(signers); j++ { + for k := j + 1; k < len(signers); k++ { + if bytes.Compare(signers[j][:], signers[k][:]) > 0 { + signers[j], signers[k] = signers[k], signers[j] + } } - genesis.Config = &config + } + // Create the genesis block with the initial set of signers + genesis := &core.Genesis{ + ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), + BaseFee: big.NewInt(params.InitialBaseFee), + } + for j, signer := range signers { + copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) + } - engine := New(config.Clique, rawdb.NewMemoryDatabase()) - engine.fakeDiff = true + // Assemble a chain of headers from the cast votes + config := *params.TestChainConfig + config.Clique = ¶ms.CliqueConfig{ + Period: 1, + Epoch: tt.epoch, + } + genesis.Config = &config - _, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, len(tt.votes), func(j int, gen *core.BlockGen) { - // Cast the vote contained in this block - gen.SetCoinbase(accounts.address(tt.votes[j].voted)) - if tt.votes[j].auth { - var nonce types.BlockNonce - copy(nonce[:], nonceAuthVote) - gen.SetNonce(nonce) - } - }) - // Iterate through the blocks and seal them individually - for j, block := range blocks { - // Get the header and prepare it for signing - header := block.Header() - if j > 0 { - header.ParentHash = blocks[j-1].Hash() - } - header.Extra = make([]byte, extraVanity+extraSeal) - if auths := tt.votes[j].checkpoint; auths != nil { - header.Extra = make([]byte, extraVanity+len(auths)*common.AddressLength+extraSeal) - accounts.checkpoint(header, auths) - } - header.Difficulty = diffInTurn // Ignored, we just need a valid number + engine := New(config.Clique, rawdb.NewMemoryDatabase()) + engine.fakeDiff = true - // Generate the signature, embed it into the header and the block - accounts.sign(header, tt.votes[j].signer) - blocks[j] = block.WithSeal(header) - } - // Split the blocks up into individual import batches (cornercase testing) - batches := [][]*types.Block{nil} - for j, block := range blocks { - if tt.votes[j].newbatch { - batches = append(batches, nil) - } - batches[len(batches)-1] = append(batches[len(batches)-1], block) - } - // Pass all the headers through clique and ensure tallying succeeds - chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil, nil) - if err != nil { - t.Errorf("test %d: failed to create test chain: %v", i, err) - continue - } - failed := false - for j := 0; j < len(batches)-1; j++ { - if k, err := chain.InsertChain(batches[j]); err != nil { - t.Errorf("test %d: failed to import batch %d, block %d: %v", i, j, k, err) - failed = true - break - } - } - if failed { - continue + _, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, len(tt.votes), func(j int, gen *core.BlockGen) { + // Cast the vote contained in this block + gen.SetCoinbase(accounts.address(tt.votes[j].voted)) + if tt.votes[j].auth { + var nonce types.BlockNonce + copy(nonce[:], nonceAuthVote) + gen.SetNonce(nonce) } - if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure { - t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) + }) + // Iterate through the blocks and seal them individually + for j, block := range blocks { + // Get the header and prepare it for signing + header := block.Header() + if j > 0 { + header.ParentHash = blocks[j-1].Hash() } - if tt.failure != nil { - continue + header.Extra = make([]byte, extraVanity+extraSeal) + if auths := tt.votes[j].checkpoint; auths != nil { + header.Extra = make([]byte, extraVanity+len(auths)*common.AddressLength+extraSeal) + accounts.checkpoint(header, auths) } - // No failure was produced or requested, generate the final voting snapshot - head := blocks[len(blocks)-1] + header.Difficulty = diffInTurn // Ignored, we just need a valid number - snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil) - if err != nil { - t.Errorf("test %d: failed to retrieve voting snapshot: %v", i, err) - continue + // Generate the signature, embed it into the header and the block + accounts.sign(header, tt.votes[j].signer) + blocks[j] = block.WithSeal(header) + } + // Split the blocks up into individual import batches (cornercase testing) + batches := [][]*types.Block{nil} + for j, block := range blocks { + if tt.votes[j].newbatch { + batches = append(batches, nil) } - // Verify the final list of signers against the expected ones - signers = make([]common.Address, len(tt.results)) - for j, signer := range tt.results { - signers[j] = accounts.address(signer) + batches[len(batches)-1] = append(batches[len(batches)-1], block) + } + // Pass all the headers through clique and ensure tallying succeeds + chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("failed to create test chain: %v", err) + } + defer chain.Stop() + + for j := 0; j < len(batches)-1; j++ { + if k, err := chain.InsertChain(batches[j]); err != nil { + t.Fatalf("failed to import batch %d, block %d: %v", j, k, err) + break } - for j := 0; j < len(signers); j++ { - for k := j + 1; k < len(signers); k++ { - if bytes.Compare(signers[j][:], signers[k][:]) > 0 { - signers[j], signers[k] = signers[k], signers[j] - } + } + if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure { + t.Errorf("failure mismatch: have %v, want %v", err, tt.failure) + } + if tt.failure != nil { + return + } + + // No failure was produced or requested, generate the final voting snapshot + head := blocks[len(blocks)-1] + + snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil) + if err != nil { + t.Fatalf("failed to retrieve voting snapshot: %v", err) + } + // Verify the final list of signers against the expected ones + signers = make([]common.Address, len(tt.results)) + for j, signer := range tt.results { + signers[j] = accounts.address(signer) + } + for j := 0; j < len(signers); j++ { + for k := j + 1; k < len(signers); k++ { + if bytes.Compare(signers[j][:], signers[k][:]) > 0 { + signers[j], signers[k] = signers[k], signers[j] } } - result := snap.signers() - if len(result) != len(signers) { - t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers) - continue - } - for j := 0; j < len(result); j++ { - if !bytes.Equal(result[j][:], signers[j][:]) { - t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j]) - } + } + result := snap.signers() + if len(result) != len(signers) { + t.Fatalf("signers mismatch: have %x, want %x", result, signers) + } + for j := 0; j < len(result); j++ { + if !bytes.Equal(result[j][:], signers[j][:]) { + t.Fatalf("signer %d: signer mismatch: have %x, want %x", j, result[j], signers[j]) } } } diff --git a/core/blockchain.go b/core/blockchain.go index 2821fedb0f..9766f28eda 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -856,9 +856,13 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) { headBlockGauge.Update(int64(block.NumberU64())) } -// Stop stops the blockchain service. If any imports are currently in progress -// it will abort them using the procInterrupt. -func (bc *BlockChain) Stop() { +// stop stops the blockchain service. If any imports are currently in progress +// it will abort them using the procInterrupt. This method stops all running +// goroutines, but does not do all the post-stop work of persisting data. +// OBS! It is generally recommended to use the Stop method! +// This method has been exposed to allow tests to stop the blockchain while simulating +// a crash. +func (bc *BlockChain) stopWithoutSaving() { if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { return } @@ -878,6 +882,12 @@ func (bc *BlockChain) Stop() { // returned. bc.chainmu.Close() bc.wg.Wait() +} + +// Stop stops the blockchain service. If any imports are currently in progress +// it will abort them using the procInterrupt. +func (bc *BlockChain) Stop() { + bc.stopWithoutSaving() // Ensure that the entirety of the state snapshot is journalled to disk. var snapBase common.Hash diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go index 6e61f89c3b..1b3f1b7187 100644 --- a/core/blockchain_repair_test.go +++ b/core/blockchain_repair_test.go @@ -1826,6 +1826,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) { } // Pull the plug on the database, simulating a hard crash db.Close() + chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) @@ -1940,6 +1941,7 @@ func TestIssue23496(t *testing.T) { // Pull the plug on the database, simulating a hard crash db.Close() + chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false) diff --git a/core/blockchain_sethead_test.go b/core/blockchain_sethead_test.go index 1eb588d02f..1750cb4e63 100644 --- a/core/blockchain_sethead_test.go +++ b/core/blockchain_sethead_test.go @@ -1984,6 +1984,8 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { if err != nil { t.Fatalf("Failed to create chain: %v", err) } + defer chain.Stop() + // If sidechain blocks are needed, make a light chain and import it var sideblocks types.Blocks if tt.sidechainBlocks > 0 { diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go index 1b38ad51e9..e55431c914 100644 --- a/core/blockchain_snapshot_test.go +++ b/core/blockchain_snapshot_test.go @@ -247,6 +247,7 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) { // Pull the plug on the database, simulating a hard crash db := chain.db db.Close() + chain.stopWithoutSaving() // Start a new blockchain back up and see where the repair leads us newdb, err := rawdb.NewLevelDBDatabaseWithFreezer(snaptest.datadir, 0, 0, snaptest.datadir, "", false) @@ -388,15 +389,19 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { SnapshotLimit: 256, SnapshotWait: false, // Don't wait rebuild } - _, err = NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) + tmp, err := NewBlockChain(snaptest.db, config, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } + // Simulate the blockchain crash. + tmp.stopWithoutSaving() + newchain, err = NewBlockChain(snaptest.db, nil, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } + defer newchain.Stop() snaptest.verify(t, newchain, blocks) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index aba50c8627..418844cdf5 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -681,7 +681,7 @@ func TestHeadersInsertNonceError(t *testing.T) { testInsertNonceError(t, false) func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) } func testInsertNonceError(t *testing.T, full bool) { - for i := 1; i < 25 && !t.Failed(); i++ { + doTest := func(i int) { // Create a pristine chain and database genDb, _, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) if err != nil { @@ -730,6 +730,9 @@ func testInsertNonceError(t *testing.T, full bool) { } } } + for i := 1; i < 25 && !t.Failed(); i++ { + doTest(i) + } } // Tests that fast importing a block chain produces the same chain data as the @@ -1639,6 +1642,8 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + for i := 0; i < len(blocks); i++ { if _, err := chain.InsertChain(blocks[i : i+1]); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", i, err) @@ -1681,6 +1686,8 @@ func TestTrieForkGC(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + for i := 0; i < len(blocks); i++ { if _, err := chain.InsertChain(blocks[i : i+1]); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", i, err) @@ -1717,6 +1724,8 @@ func TestLargeReorgTrieGC(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if _, err := chain.InsertChain(shared); err != nil { t.Fatalf("failed to insert shared chain: %v", err) } @@ -1896,6 +1905,8 @@ func TestLowDiffLongChain(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.stopWithoutSaving() + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -1955,6 +1966,8 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + // Activate the transition since genesis if required if mergePoint == 0 { merger.ReachTTD() @@ -2092,6 +2105,7 @@ func testInsertKnownChainData(t *testing.T, typ string) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() var ( inserter func(blocks []*types.Block, receipts []types.Receipts) error @@ -2242,6 +2256,8 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + var ( inserter func(blocks []*types.Block, receipts []types.Receipts) error asserter func(t *testing.T, block *types.Block) @@ -2394,6 +2410,8 @@ func TestReorgToShorterRemovesCanonMapping(t *testing.T) { if err != nil { t.Fatal(err) } + defer chain.Stop() + if n, err := chain.InsertChain(canonblocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -2430,6 +2448,8 @@ func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) { if err != nil { t.Fatal(err) } + defer chain.Stop() + // Convert into headers canonHeaders := make([]*types.Header, len(canonblocks)) for i, block := range canonblocks { @@ -2629,6 +2649,8 @@ func TestSkipStaleTxIndicesInSnapSync(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + headers := make([]*types.Header, len(blocks)) for i, block := range blocks { headers[i] = block.Header() @@ -2769,6 +2791,8 @@ func TestSideImportPrunedBlocks(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -2857,6 +2881,8 @@ func TestDeleteCreateRevert(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -2966,6 +2992,8 @@ func TestDeleteRecreateSlots(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -3042,6 +3070,8 @@ func TestDeleteRecreateAccount(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -3163,7 +3193,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { e.exist = false e.values = nil } - t.Logf("block %d; adding destruct\n", e.blocknum) + //t.Logf("block %d; adding destruct\n", e.blocknum) return tx } var newResurrect = func(e *expectation, b *BlockGen) *types.Transaction { @@ -3174,7 +3204,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { e.exist = true e.values = map[int]int{3: e.blocknum + 1, 4: 4} } - t.Logf("block %d; adding resurrect\n", e.blocknum) + //t.Logf("block %d; adding resurrect\n", e.blocknum) return tx } @@ -3211,6 +3241,8 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + var asHash = func(num int) common.Hash { return common.BytesToHash([]byte{byte(num)}) } @@ -3340,6 +3372,8 @@ func TestInitThenFailCreateContract(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + statedb, _ := chain.State() if got, exp := statedb.GetBalance(aa), big.NewInt(100000); got.Cmp(exp) != 0 { t.Fatalf("Genesis err, got %v exp %v", got, exp) @@ -3420,6 +3454,8 @@ func TestEIP2718Transition(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -3506,6 +3542,8 @@ func TestEIP1559Transition(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -3608,6 +3646,8 @@ func TestSetCanonical(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() + if n, err := chain.InsertChain(canon); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -3758,6 +3798,7 @@ func TestCanonicalHashMarker(t *testing.T) { } } } + chain.Stop() } } @@ -3961,6 +4002,7 @@ func TestTxIndexer(t *testing.T) { chain.indexBlocks(rawdb.ReadTxIndexTail(db), 128, make(chan struct{})) verify(db, 0) + chain.Stop() db.Close() os.RemoveAll(frdir) } diff --git a/core/dao_test.go b/core/dao_test.go index 44405447de..632eafe4d5 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -75,7 +75,6 @@ func TestDAOForkRangeExtradata(t *testing.T) { for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ { // Create a pro-fork block, and try to feed into the no-fork chain bc, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) - defer bc.Stop() blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) for j := 0; j < len(blocks)/2; j++ { @@ -87,6 +86,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil { t.Fatalf("failed to commit contra-fork head for expansion: %v", err) } + bc.Stop() blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) if _, err := conBc.InsertChain(blocks); err == nil { t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0]) @@ -98,7 +98,6 @@ func TestDAOForkRangeExtradata(t *testing.T) { } // Create a no-fork block, and try to feed into the pro-fork chain bc, _ = NewBlockChain(rawdb.NewMemoryDatabase(), nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) - defer bc.Stop() blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) for j := 0; j < len(blocks)/2; j++ { @@ -110,6 +109,7 @@ func TestDAOForkRangeExtradata(t *testing.T) { if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil { t.Fatalf("failed to commit pro-fork head for expansion: %v", err) } + bc.Stop() blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) if _, err := proBc.InsertChain(blocks); err == nil { t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0]) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index c98a48ea47..e195145b73 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -92,12 +92,21 @@ func TestEth2AssembleBlock(t *testing.T) { blockParams := beacon.PayloadAttributesV1{ Timestamp: blocks[9].Time() + 5, } - execData, err := assembleBlock(api, blocks[9].Hash(), &blockParams) - if err != nil { - t.Fatalf("error producing block, err=%v", err) - } - if len(execData.Transactions) != 1 { - t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) + // This test is a bit time-sensitive, the miner needs to pick up on the + // txs in the pool. Therefore, we retry once if it fails on the first attempt. + var testErr error + for retries := 2; retries > 0; retries-- { + if execData, err := assembleBlock(api, blocks[9].Hash(), &blockParams); err != nil { + t.Fatalf("error producing block, err=%v", err) + } else if have, want := len(execData.Transactions), 1; have != want { + testErr = fmt.Errorf("invalid number of transactions, have %d want %d", have, want) + } else { + testErr = nil + break + } + } + if testErr != nil { + t.Fatal(testErr) } } @@ -113,12 +122,21 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { blockParams := beacon.PayloadAttributesV1{ Timestamp: blocks[8].Time() + 5, } - execData, err := assembleBlock(api, blocks[8].Hash(), &blockParams) - if err != nil { - t.Fatalf("error producing block, err=%v", err) - } - if len(execData.Transactions) != blocks[9].Transactions().Len() { - t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) + // This test is a bit time-sensitive, the miner needs to pick up on the + // txs in the pool. Therefore, we retry once if it fails on the first attempt. + var testErr error + for retries := 2; retries > 0; retries-- { + if execData, err := assembleBlock(api, blocks[8].Hash(), &blockParams); err != nil { + t.Fatalf("error producing block, err=%v", err) + } else if have, want := len(execData.Transactions), blocks[9].Transactions().Len(); have != want { + testErr = fmt.Errorf("invalid number of transactions, have %d want %d", have, want) + } else { + testErr = nil + break + } + } + if testErr != nil { + t.Fatal(testErr) } } diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index deece7f461..b54874d688 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -62,7 +62,7 @@ func TestFeeHistory(t *testing.T) { oracle := NewOracle(backend, config) first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) - + backend.teardown() expReward := c.expCount if len(c.percent) == 0 { expReward = 0 diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index a4399661fc..a987d46458 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -113,6 +113,12 @@ func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve return nil } +func (b *testBackend) teardown() { + b.chain.Stop() +} + +// newTestBackend creates a test backend. OBS: don't forget to invoke tearDown +// after use, otherwise the blockchain instance will mem-leak via goroutines. func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBackend { var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") @@ -198,6 +204,7 @@ func TestSuggestTipCap(t *testing.T) { // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G got, err := oracle.SuggestTipCap(context.Background()) + backend.teardown() if err != nil { t.Fatalf("Failed to retrieve recommended gas price: %v", err) } diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 346813ae2c..adf65d33fb 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -63,6 +63,8 @@ type testBackend struct { relHook func() // Hook is invoked when the requested state is released } +// testBackend creates a new test backend. OBS: After test is done, teardown must be +// invoked in order to release associated resources. func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend { backend := &testBackend{ chainConfig: gspec.Config, @@ -137,6 +139,11 @@ func (b *testBackend) ChainDb() ethdb.Database { return b.chaindb } +// teardown releases the associated resources. +func (b *testBackend) teardown() { + b.chain.Stop() +} + func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error) { statedb, err := b.chain.StateAt(block.Root()) if err != nil { @@ -198,13 +205,15 @@ func TestTraceCall(t *testing.T) { } genBlocks := 10 signer := types.HomesteadSigner{} - api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) - })) + }) + defer backend.teardown() + api := NewAPI(backend) var testSuite = []struct { blockNumber rpc.BlockNumber call ethapi.TransactionArgs @@ -330,14 +339,16 @@ func TestTraceTransaction(t *testing.T) { } target := common.Hash{} signer := types.HomesteadSigner{} - api := NewAPI(newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { + backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) target = tx.Hash() - })) + }) + defer backend.chain.Stop() + api := NewAPI(backend) result, err := api.TraceTransaction(context.Background(), target, nil) if err != nil { t.Errorf("Failed to trace transaction %v", err) @@ -371,13 +382,15 @@ func TestTraceBlock(t *testing.T) { } genBlocks := 10 signer := types.HomesteadSigner{} - api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) - })) + }) + defer backend.chain.Stop() + api := NewAPI(backend) var testSuite = []struct { blockNumber rpc.BlockNumber @@ -449,13 +462,15 @@ func TestTracingWithOverrides(t *testing.T) { } genBlocks := 10 signer := types.HomesteadSigner{} - api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { + backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) - })) + }) + defer backend.chain.Stop() + api := NewAPI(backend) randomAccounts := newAccounts(3) type res struct { Gas int diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go index 0e2f7e57b6..da0118887b 100644 --- a/ethclient/gethclient/gethclient_test.go +++ b/ethclient/gethclient/gethclient_test.go @@ -106,10 +106,6 @@ func TestGethClient(t *testing.T) { name string test func(t *testing.T) }{ - { - "TestAccessList", - func(t *testing.T) { testAccessList(t, client) }, - }, { "TestGetProof", func(t *testing.T) { testGetProof(t, client) }, @@ -132,8 +128,15 @@ func TestGethClient(t *testing.T) { "TestCallContract", func(t *testing.T) { testCallContract(t, client) }, }, + // The testaccesslist is a bit time-sensitive: the newTestBackend imports + // one block. The `testAcessList` fails if the miner has not yet created a + // new pending-block after the import event. + // Hence: this test should be last, execute the tests serially. + { + "TestAccessList", + func(t *testing.T) { testAccessList(t, client) }, + }, } - t.Parallel() for _, tt := range tests { t.Run(tt.name, tt.test) }