diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index d1d2eff7524ac2792edabca07ee97939e3b0d957..1bbf2b53636a0b7962f0c548810b91f03569bf33 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -177,10 +177,10 @@ func (b *SimulatedBackend) Rollback() {
 }
 
 func (b *SimulatedBackend) emptyPendingBlock() {
-	blocks, receipts, _ := core.GenerateChain(b.config, b.prependBlock, ethash.NewFaker(), b.database.RwKV(), 1, func(int, *core.BlockGen) {}, false /* intermediateHashes */)
-	b.pendingBlock = blocks[0]
-	b.pendingReceipts = receipts[0]
-	b.pendingHeader = b.pendingBlock.Header()
+	chain, _ := core.GenerateChain(b.config, b.prependBlock, ethash.NewFaker(), b.database.RwKV(), 1, func(int, *core.BlockGen) {}, false /* intermediateHashes */)
+	b.pendingBlock = chain.Blocks[0]
+	b.pendingReceipts = chain.Receipts[0]
+	b.pendingHeader = chain.Headers[0]
 	b.gasPool = new(core.GasPool).AddGas(b.pendingHeader.GasLimit)
 	b.pendingReader = state.NewPlainStateReader(b.database)
 	b.pendingState = state.New(b.pendingReader)
@@ -657,7 +657,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx types.Transac
 		return err
 	}
 	//fmt.Printf("==== Start producing block %d\n", (b.prependBlock.NumberU64() + 1))
-	blocks, receipts, err := core.GenerateChain(b.config, b.prependBlock, ethash.NewFaker(), b.database.RwKV(), 1, func(number int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(b.config, b.prependBlock, ethash.NewFaker(), b.database.RwKV(), 1, func(number int, block *core.BlockGen) {
 		for _, tx := range b.pendingBlock.Transactions() {
 			block.AddTxWithChain(b.getHeader, b.engine, tx)
 		}
@@ -667,9 +667,9 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx types.Transac
 		return err
 	}
 	//fmt.Printf("==== End producing block %d\n", b.pendingBlock.NumberU64())
-	b.pendingBlock = blocks[0]
-	b.pendingReceipts = receipts[0]
-	b.pendingHeader = b.pendingBlock.Header()
+	b.pendingBlock = chain.Blocks[0]
+	b.pendingReceipts = chain.Receipts[0]
+	b.pendingHeader = chain.Headers[0]
 	return nil
 }
 
@@ -778,7 +778,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
 		return errors.New("could not adjust time on non-empty block")
 	}
 
-	blocks, _, err := core.GenerateChain(b.config, b.prependBlock, ethash.NewFaker(), b.database.RwKV(), 1, func(number int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(b.config, b.prependBlock, ethash.NewFaker(), b.database.RwKV(), 1, func(number int, block *core.BlockGen) {
 		for _, tx := range b.pendingBlock.Transactions() {
 			block.AddTxWithChain(b.getHeader, b.engine, tx)
 		}
@@ -787,8 +787,8 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
 	if err != nil {
 		return err
 	}
-	b.pendingBlock = blocks[0]
-	b.pendingHeader = b.pendingBlock.Header()
+	b.pendingBlock = chain.Blocks[0]
+	b.pendingHeader = chain.Headers[0]
 
 	return nil
 }
diff --git a/cmd/headers/download/downloader.go b/cmd/headers/download/downloader.go
index 73311130bb574b10af5282befbac3102e2bdfeb9..109cc449eb6705412f227082e4555103141c9bac 100644
--- a/cmd/headers/download/downloader.go
+++ b/cmd/headers/download/downloader.go
@@ -84,7 +84,15 @@ func RecvUploadMessage(ctx context.Context, sentry proto_sentry.SentryClient, ha
 	}
 }
 
-func RecvMessage(ctx context.Context, sentry proto_sentry.SentryClient, handleInboundMessage func(ctx context.Context, inreq *proto_sentry.InboundMessage, sentry proto_sentry.SentryClient) error) {
+// RecvMessage is normally run in a separate go-routine because it only exists when there a no more messages
+// to be received (end of process, or interruption, or end of test)
+// wg is used only in tests to avoid using waits, which is brittle. For non-test code wg == nil
+func RecvMessage(
+	ctx context.Context,
+	sentry proto_sentry.SentryClient,
+	handleInboundMessage func(ctx context.Context, inreq *proto_sentry.InboundMessage, sentry proto_sentry.SentryClient) error,
+	wg *sync.WaitGroup,
+) {
 	streamCtx, cancel := context.WithCancel(ctx)
 	defer cancel()
 
@@ -109,6 +117,9 @@ func RecvMessage(ctx context.Context, sentry proto_sentry.SentryClient, handleIn
 		if err = handleInboundMessage(ctx, req, sentry); err != nil {
 			log.Error("RecvMessage: Handling incoming message", "error", err)
 		}
+		if wg != nil {
+			wg.Done()
+		}
 	}
 }
 
diff --git a/cmd/headers/download/sentry_mock_test.go b/cmd/headers/download/sentry_mock_test.go
index 109be148314e653c373e5cf91f8610382b45e97a..1a0737240f898832e41604ea25c033afbbf3adb9 100644
--- a/cmd/headers/download/sentry_mock_test.go
+++ b/cmd/headers/download/sentry_mock_test.go
@@ -7,7 +7,6 @@ import (
 	"os"
 	"sync"
 	"testing"
-	"time"
 
 	"github.com/c2h5oh/datasize"
 	"github.com/holiman/uint256"
@@ -51,21 +50,15 @@ type MockSentry struct {
 	genesis      *types.Block
 	sentryClient *SentryClientDirect
 	stream       sentry.Sentry_ReceiveMessagesServer // Stream of annoucements and download responses
-	streamLock   sync.Mutex
+	streamWg     sync.WaitGroup
 	peerId       *ptypes.H512
+	receiveWg    sync.WaitGroup
 }
 
 // Stream returns stream, waiting if necessary
 func (ms *MockSentry) Stream() sentry.Sentry_ReceiveMessagesServer {
-	for {
-		ms.streamLock.Lock()
-		if ms.stream != nil {
-			ms.streamLock.Unlock()
-			return ms.stream
-		}
-		ms.streamLock.Unlock()
-		time.Sleep(time.Millisecond)
-	}
+	ms.streamWg.Wait()
+	return ms.stream
 }
 
 func (ms *MockSentry) PenalizePeer(context.Context, *sentry.PenalizePeerRequest) (*emptypb.Empty, error) {
@@ -90,9 +83,8 @@ func (ms *MockSentry) SetStatus(context.Context, *sentry.StatusData) (*emptypb.E
 	return nil, nil
 }
 func (ms *MockSentry) ReceiveMessages(_ *emptypb.Empty, stream sentry.Sentry_ReceiveMessagesServer) error {
-	ms.streamLock.Lock()
 	ms.stream = stream
-	ms.streamLock.Unlock()
+	ms.streamWg.Done()
 	<-ms.ctx.Done()
 	return nil
 }
@@ -225,7 +217,8 @@ func mock(t *testing.T) *MockSentry {
 		t.Fatal(err)
 	}
 	mock.peerId = gointerfaces.ConvertBytesToH512([]byte("12345"))
-	go RecvMessage(mock.ctx, mock.sentryClient, mock.downloader.HandleInboundMessage)
+	mock.streamWg.Add(1)
+	go RecvMessage(mock.ctx, mock.sentryClient, mock.downloader.HandleInboundMessage, &mock.receiveWg)
 	t.Cleanup(func() {
 		mock.cancel()
 		txPool.Stop()
@@ -242,7 +235,7 @@ func TestHeaderStep(t *testing.T) {
 	log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
 	m := mock(t)
 
-	blocks, _, err := core.GenerateChain(m.chainConfig, m.genesis, m.engine, m.db, 100, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(m.chainConfig, m.genesis, m.engine, m.db, 100, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 	}, false /* intemediateHashes */)
 	if err != nil {
@@ -250,28 +243,27 @@ func TestHeaderStep(t *testing.T) {
 	}
 	// Send NewBlock message
 	b, err := rlp.EncodeToBytes(&eth.NewBlockPacket{
-		Block: blocks[len(blocks)-1],
+		Block: chain.TopBlock,
 		TD:    big.NewInt(1), // This is ignored anyway
 	})
 	require.NoError(t, err)
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_NewBlock, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
 	// Send all the headers
-	headers := make([]*types.Header, len(blocks))
-	for i, block := range blocks {
-		headers[i] = block.Header()
-	}
 	b, err = rlp.EncodeToBytes(&eth.BlockHeadersPacket66{
 		RequestId:          1,
-		BlockHeadersPacket: headers,
+		BlockHeadersPacket: chain.Headers,
 	})
 	require.NoError(t, err)
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_BlockHeaders, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
+	m.receiveWg.Wait() // Wait for all messages to be processed before we proceeed
 
 	notifier := &remotedbserver.Events{}
 	initialCycle := true
-	highestSeenHeader := uint64(blocks[len(blocks)-1].NumberU64())
+	highestSeenHeader := uint64(chain.TopBlock.NumberU64())
 	if err := stages.StageLoopStep(m.ctx, m.db, m.sync, highestSeenHeader, m.chainConfig, notifier, initialCycle, nil); err != nil {
 		t.Fatal(err)
 	}
@@ -281,7 +273,7 @@ func TestReorg(t *testing.T) {
 	log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
 	m := mock(t)
 
-	blocks, _, err := core.GenerateChain(m.chainConfig, m.genesis, m.engine, m.db, 10, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(m.chainConfig, m.genesis, m.engine, m.db, 10, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 	}, false /* intemediateHashes */)
 	if err != nil {
@@ -289,52 +281,51 @@ func TestReorg(t *testing.T) {
 	}
 	// Send NewBlock message
 	b, err := rlp.EncodeToBytes(&eth.NewBlockPacket{
-		Block: blocks[len(blocks)-1],
+		Block: chain.TopBlock,
 		TD:    big.NewInt(1), // This is ignored anyway
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_NewBlock, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
 
 	// Send all the headers
-	headers := make([]*types.Header, len(blocks))
-	for i, block := range blocks {
-		headers[i] = block.Header()
-	}
 	b, err = rlp.EncodeToBytes(&eth.BlockHeadersPacket66{
 		RequestId:          1,
-		BlockHeadersPacket: headers,
+		BlockHeadersPacket: chain.Headers,
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_BlockHeaders, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
+	m.receiveWg.Wait() // Wait for all messages to be processed before we proceeed
 
 	notifier := &remotedbserver.Events{}
 	initialCycle := true
-	highestSeenHeader := uint64(blocks[len(blocks)-1].NumberU64())
+	highestSeenHeader := uint64(chain.TopBlock.NumberU64())
 	if err := stages.StageLoopStep(m.ctx, m.db, m.sync, highestSeenHeader, m.chainConfig, notifier, initialCycle, nil); err != nil {
 		t.Fatal(err)
 	}
 
 	// Now generate three competing branches, one short and two longer ones
-	short, _, err := core.GenerateChain(m.chainConfig, blocks[len(blocks)-1], m.engine, m.db, 2, func(i int, b *core.BlockGen) {
+	short, err := core.GenerateChain(m.chainConfig, chain.TopBlock, m.engine, m.db, 2, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 	}, false /* intemediateHashes */)
 	if err != nil {
 		t.Fatalf("generate short fork: %v", err)
 	}
-	long1, _, err := core.GenerateChain(m.chainConfig, blocks[len(blocks)-1], m.engine, m.db, 10, func(i int, b *core.BlockGen) {
+	long1, err := core.GenerateChain(m.chainConfig, chain.TopBlock, m.engine, m.db, 10, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{2}) // Need to make headers different from short branch
 	}, false /* intemediateHashes */)
 	if err != nil {
 		t.Fatalf("generate short fork: %v", err)
 	}
 	// Second long chain needs to be slightly shorter than the first long chain
-	long2, _, err := core.GenerateChain(m.chainConfig, blocks[len(blocks)-1], m.engine, m.db, 9, func(i int, b *core.BlockGen) {
+	long2, err := core.GenerateChain(m.chainConfig, chain.TopBlock, m.engine, m.db, 9, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{3}) // Need to make headers different from short branch and another long branch
 	}, false /* intemediateHashes */)
 	if err != nil {
@@ -343,31 +334,30 @@ func TestReorg(t *testing.T) {
 
 	// Send NewBlock message for short branch
 	b, err = rlp.EncodeToBytes(&eth.NewBlockPacket{
-		Block: short[len(short)-1],
+		Block: short.TopBlock,
 		TD:    big.NewInt(1), // This is ignored anyway
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_NewBlock, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
 
 	// Send headers of the short branch
-	headers = make([]*types.Header, len(short))
-	for i, block := range short {
-		headers[i] = block.Header()
-	}
 	b, err = rlp.EncodeToBytes(&eth.BlockHeadersPacket66{
 		RequestId:          2,
-		BlockHeadersPacket: headers,
+		BlockHeadersPacket: short.Headers,
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_BlockHeaders, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
+	m.receiveWg.Wait() // Wait for all messages to be processed before we proceeed
 
-	highestSeenHeader = uint64(short[len(short)-1].NumberU64())
+	highestSeenHeader = uint64(short.TopBlock.NumberU64())
 	initialCycle = false
 	if err := stages.StageLoopStep(m.ctx, m.db, m.sync, highestSeenHeader, m.chainConfig, notifier, initialCycle, nil); err != nil {
 		t.Fatal(err)
@@ -375,53 +365,48 @@ func TestReorg(t *testing.T) {
 
 	// Send NewBlock message for long1 branch
 	b, err = rlp.EncodeToBytes(&eth.NewBlockPacket{
-		Block: long1[len(long1)-1],
+		Block: long1.TopBlock,
 		TD:    big.NewInt(1), // This is ignored anyway
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_NewBlock, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
 
 	// Send headers of the long2 branch
-	headers = make([]*types.Header, len(long2))
-	for i, block := range long2 {
-		headers[i] = block.Header()
-	}
 	b, err = rlp.EncodeToBytes(&eth.BlockHeadersPacket66{
 		RequestId:          3,
-		BlockHeadersPacket: headers,
+		BlockHeadersPacket: long2.Headers,
 	})
 	if err != nil {
 		t.Fatal(err)
 	}
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_BlockHeaders, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
 
 	// Send headers of the long1 branch
-	headers = make([]*types.Header, len(long1))
-	for i, block := range long1 {
-		headers[i] = block.Header()
-	}
 	b, err = rlp.EncodeToBytes(&eth.BlockHeadersPacket66{
 		RequestId:          4,
-		BlockHeadersPacket: headers,
+		BlockHeadersPacket: long1.Headers,
 	})
 	require.NoError(t, err)
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_BlockHeaders, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
+	m.receiveWg.Wait() // Wait for all messages to be processed before we proceeed
 
-	time.Sleep(100 * time.Millisecond)
 	// This is unwind step
-	highestSeenHeader = uint64(long1[len(long1)-1].NumberU64())
+	highestSeenHeader = uint64(long1.TopBlock.NumberU64())
 	if err := stages.StageLoopStep(m.ctx, m.db, m.sync, highestSeenHeader, m.chainConfig, notifier, initialCycle, nil); err != nil {
 		t.Fatal(err)
 	}
 
 	// another short chain
 	// Now generate three competing branches, one short and two longer ones
-	short2, _, err := core.GenerateChain(m.chainConfig, long1[len(long1)-1], m.engine, m.db, 2, func(i int, b *core.BlockGen) {
+	short2, err := core.GenerateChain(m.chainConfig, long1.TopBlock, m.engine, m.db, 2, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 	}, false /* intemediateHashes */)
 	if err != nil {
@@ -430,27 +415,26 @@ func TestReorg(t *testing.T) {
 
 	// Send NewBlock message for short branch
 	b, err = rlp.EncodeToBytes(&eth.NewBlockPacket{
-		Block: short2[len(short2)-1],
+		Block: short2.TopBlock,
 		TD:    big.NewInt(1), // This is ignored anyway
 	})
 	require.NoError(t, err)
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_NewBlock, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
 
 	// Send headers of the short branch
-	headers = make([]*types.Header, len(short2))
-	for i, block := range short2 {
-		headers[i] = block.Header()
-	}
 	b, err = rlp.EncodeToBytes(&eth.BlockHeadersPacket66{
 		RequestId:          5,
-		BlockHeadersPacket: headers,
+		BlockHeadersPacket: short2.Headers,
 	})
 	require.NoError(t, err)
+	m.receiveWg.Add(1)
 	err = m.Stream().Send(&sentry.InboundMessage{Id: sentry.MessageId_BlockHeaders, Data: b, PeerId: m.peerId})
 	require.NoError(t, err)
+	m.receiveWg.Wait() // Wait for all messages to be processed before we proceeed
 
-	highestSeenHeader = uint64(short2[len(short2)-1].NumberU64())
+	highestSeenHeader = uint64(short2.TopBlock.NumberU64())
 	initialCycle = false
 	if err := stages.StageLoopStep(m.ctx, m.db, m.sync, highestSeenHeader, m.chainConfig, notifier, initialCycle, nil); err != nil {
 		t.Fatal(err)
diff --git a/cmd/pics/state.go b/cmd/pics/state.go
index e423c4f41d57e49febd9dd2bab609bead5d18d80..f4ab3f2cabd904071eb5bf07360af0d3a9cdc626 100644
--- a/cmd/pics/state.go
+++ b/cmd/pics/state.go
@@ -303,7 +303,7 @@ func initialState1() error {
 
 	var tokenContract *contracts.Token
 	// We generate the blocks without plainstant because it's not supported in core.GenerateChain
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 8, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 8, func(i int, block *core.BlockGen) {
 		var (
 			tx  types.Transaction
 			txs []types.Transaction
@@ -430,7 +430,7 @@ func initialState1() error {
 	// BLOCK 1
 	snapshotDB = db.MemCopy()
 
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[0], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[0], true /* rootCheck */); err != nil {
 		return err
 	}
 
@@ -441,7 +441,7 @@ func initialState1() error {
 
 	// BLOCK 2
 	snapshotDB = db.MemCopy()
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[1], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[1], true /* rootCheck */); err != nil {
 		return err
 	}
 
@@ -453,7 +453,7 @@ func initialState1() error {
 	// BLOCK 3
 	snapshotDB = db.MemCopy()
 
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[2], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[2], true /* rootCheck */); err != nil {
 		return err
 	}
 
@@ -465,7 +465,7 @@ func initialState1() error {
 	// BLOCK 4
 	snapshotDB = db.MemCopy()
 
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[3], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[3], true /* rootCheck */); err != nil {
 		return err
 	}
 
@@ -477,7 +477,7 @@ func initialState1() error {
 	// BLOCK 5
 	snapshotDB = db.MemCopy()
 
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[4], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[4], true /* rootCheck */); err != nil {
 		return err
 	}
 
@@ -489,7 +489,7 @@ func initialState1() error {
 	// BLOCK 6
 	snapshotDB = db.MemCopy()
 
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[5], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[5], true /* rootCheck */); err != nil {
 		return err
 	}
 
@@ -501,7 +501,7 @@ func initialState1() error {
 	// BLOCK 7
 	snapshotDB = db.MemCopy()
 
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[6], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[6], true /* rootCheck */); err != nil {
 		return err
 	}
 
@@ -516,7 +516,7 @@ func initialState1() error {
 	// BLOCK 8
 	snapshotDB = db.MemCopy()
 
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[7], true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[7], true /* rootCheck */); err != nil {
 		return err
 	}
 
diff --git a/cmd/rpcdaemon/commands/test_util.go b/cmd/rpcdaemon/commands/test_util.go
index 55bc727bb8b3b09da3c5fbca090eeb97a38fd733..0e13d05bfd270ab817f7a74eb18cceaa2231fd10 100644
--- a/cmd/rpcdaemon/commands/test_util.go
+++ b/cmd/rpcdaemon/commands/test_util.go
@@ -68,7 +68,7 @@ func createTestDb() (ethdb.Database, error) {
 
 	var tokenContract *contracts.Token
 	// We generate the blocks without plainstant because it's not supported in core.GenerateChain
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 10, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 10, func(i int, block *core.BlockGen) {
 		var (
 			tx  types.Transaction
 			txs []types.Transaction
@@ -183,7 +183,7 @@ func createTestDb() (ethdb.Database, error) {
 		return nil, err
 	}
 
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks, true /* rootCheck */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks, true /* rootCheck */); err != nil {
 		return nil, err
 	}
 
diff --git a/consensus/clique/clique_test.go b/consensus/clique/clique_test.go
index 7b4409b10e11d08f6ba0a4d65c4001b8be945d3f..929db7b9003220b8f233cf2e408bc4b295b1f3c9 100644
--- a/consensus/clique/clique_test.go
+++ b/consensus/clique/clique_test.go
@@ -61,7 +61,7 @@ func TestReimportMirroredState(t *testing.T) {
 	// Generate a batch of blocks, each properly signed
 	getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(db, hash, number) }
 
-	blocks, _, err := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
 		// The chain maker doesn't have access to a chain, so the difficulty will be
 		// lets unset (nil). Set it here to the correct value.
 		block.SetDifficulty(diffInTurn)
@@ -79,24 +79,24 @@ func TestReimportMirroredState(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generate blocks: %v", err)
 	}
-	for i, block := range blocks {
+	for i, block := range chain.Blocks {
 		header := block.Header()
 		if i > 0 {
-			header.ParentHash = blocks[i-1].Hash()
+			header.ParentHash = chain.Blocks[i-1].Hash()
 		}
 		header.Extra = make([]byte, ExtraVanity+ExtraSeal)
 		header.Difficulty = diffInTurn
 
 		sig, _ := crypto.Sign(SealHash(header).Bytes(), key)
 		copy(header.Extra[len(header.Extra)-ExtraSeal:], sig)
-		blocks[i] = block.WithSeal(header)
+		chain.Blocks[i] = block.WithSeal(header)
 	}
 
 	// Insert the first two blocks and make sure the chain is valid
 	db = ethdb.NewTestDB(t)
 	genspec.MustCommit(db)
 
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.AllCliqueProtocolChanges, &vm.Config{}, engine, blocks[:2], true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.AllCliqueProtocolChanges, &vm.Config{}, engine, chain.Blocks[:2], true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert initial blocks: %v", err)
 	}
 	if head, err1 := rawdb.ReadBlockByHashDeprecated(db, rawdb.ReadHeadHeaderHash(db)); err1 != nil {
@@ -108,7 +108,7 @@ func TestReimportMirroredState(t *testing.T) {
 	// Simulate a crash by creating a new chain on top of the database, without
 	// flushing the dirty states out. Insert the last block, triggering a sidechain
 	// reimport.
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.AllCliqueProtocolChanges, &vm.Config{}, engine, blocks[2:], true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.AllCliqueProtocolChanges, &vm.Config{}, engine, chain.Blocks[2:], true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert final block: %v", err)
 	}
 	if head, err1 := rawdb.ReadBlockByHashDeprecated(db, rawdb.ReadHeadHeaderHash(db)); err1 != nil {
diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go
index b1feaa272f82deab24acdcfc9a031e8de1fe6b63..1147a10b706118ff9c6dad93883d50f0d7ecad38 100644
--- a/consensus/clique/snapshot_test.go
+++ b/consensus/clique/snapshot_test.go
@@ -428,7 +428,7 @@ func TestClique(t *testing.T) {
 			engine.fakeDiff = true
 
 			genesisBlock, _, _ := genesis.ToBlock()
-			blocks, _, err := core.GenerateChain(&config, genesisBlock, engine, db.RwKV(), len(tt.votes), func(j int, gen *core.BlockGen) {
+			chain, err := core.GenerateChain(&config, genesisBlock, engine, db.RwKV(), 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 {
@@ -441,11 +441,11 @@ func TestClique(t *testing.T) {
 				t.Fatalf("generate blocks: %v", err)
 			}
 			// Iterate through the blocks and seal them individually
-			for j, block := range blocks {
+			for j, block := range chain.Blocks {
 				// Get the header and prepare it for signing
 				header := block.Header()
 				if j > 0 {
-					header.ParentHash = blocks[j-1].Hash()
+					header.ParentHash = chain.Blocks[j-1].Hash()
 				}
 				header.Extra = make([]byte, ExtraVanity+ExtraSeal)
 				if auths := tt.votes[j].checkpoint; auths != nil {
@@ -456,11 +456,11 @@ func TestClique(t *testing.T) {
 
 				// Generate the signature, embed it into the header and the block
 				accounts.sign(header, tt.votes[j].signer)
-				blocks[j] = block.WithSeal(header)
+				chain.Blocks[j] = block.WithSeal(header)
 			}
 			// Split the blocks up into individual import batches (cornercase testing)
 			batches := [][]*types.Block{nil}
-			for j, block := range blocks {
+			for j, block := range chain.Blocks {
 				if tt.votes[j].newbatch {
 					batches = append(batches, nil)
 				}
@@ -487,7 +487,7 @@ func TestClique(t *testing.T) {
 				return
 			}
 			// No failure was produced or requested, generate the final voting snapshot
-			head := blocks[len(blocks)-1]
+			head := chain.Blocks[len(chain.Blocks)-1]
 
 			snap, err := engine.snapshot(stagedsync.ChainReader{Cfg: config, Db: db}, head.NumberU64(), head.Hash(), nil)
 			if err != nil {
diff --git a/core/block_validator_test.go b/core/block_validator_test.go
index 4fa9216998c57672e1106bfd18f9de0329737831..32f5f0a31623b7a96f6dae13f76ee242857bd9cc 100644
--- a/core/block_validator_test.go
+++ b/core/block_validator_test.go
@@ -39,31 +39,27 @@ func TestHeaderVerification(t *testing.T) {
 		engine  = ethash.NewFaker()
 	)
 
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 8, nil, false /* intemediateHashes */)
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 8, nil, false /* intemediateHashes */)
 	if err != nil {
 		t.Fatalf("genetate chain: %v", err)
 	}
 
-	headers := make([]*types.Header, len(blocks))
-	for i, block := range blocks {
-		headers[i] = block.Header()
-	}
 	// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
-	for i := 0; i < len(blocks); i++ {
+	for i := 0; i < chain.Length; i++ {
 		for j, valid := range []bool{true, false} {
 			if valid {
 				engine := ethash.NewFaker()
-				err = engine.VerifyHeaders(stagedsync.ChainReader{Cfg: *params.TestChainConfig, Db: db}, []*types.Header{headers[i]}, []bool{true})
+				err = engine.VerifyHeaders(stagedsync.ChainReader{Cfg: *params.TestChainConfig, Db: db}, []*types.Header{chain.Headers[i]}, []bool{true})
 			} else {
-				engine := ethash.NewFakeFailer(headers[i].Number.Uint64())
-				err = engine.VerifyHeaders(stagedsync.ChainReader{Cfg: *params.TestChainConfig, Db: db}, []*types.Header{headers[i]}, []bool{true})
+				engine := ethash.NewFakeFailer(chain.Headers[i].Number.Uint64())
+				err = engine.VerifyHeaders(stagedsync.ChainReader{Cfg: *params.TestChainConfig, Db: db}, []*types.Header{chain.Headers[i]}, []bool{true})
 			}
 			if (err == nil) != valid {
 				t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, err, valid)
 			}
 		}
 		engine := ethash.NewFaker()
-		if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, blocks[i:i+1], true /* checkRoot */); err != nil {
+		if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks[i:i+1], true /* checkRoot */); err != nil {
 			t.Fatalf("test %d: error inserting the block: %v", i, err)
 		}
 
diff --git a/core/chain_makers.go b/core/chain_makers.go
index ec7c023042941f3d52d280783a3807f6391381fd..bc5f6d6b039042651803da4002c629f81515c9d1 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -192,6 +192,14 @@ func (b *BlockGen) GetReceipts() []*types.Receipt {
 
 var GenerateTrace bool
 
+type ChainPack struct {
+	Length   int
+	Headers  []*types.Header
+	Blocks   []*types.Block
+	Receipts []types.Receipts
+	TopBlock *types.Block // Convinience field to access the last block
+}
+
 // GenerateChain creates a chain of n blocks. The first block's
 // parent will be the provided parent. db is used to store
 // intermediate states and should contain the parent's state trie.
@@ -206,15 +214,15 @@ var GenerateTrace bool
 // a similar non-validating proof of work implementation.
 func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.RwKV, n int, gen func(int, *BlockGen),
 	intermediateHashes bool,
-) ([]*types.Block, []types.Receipts, error) {
+) (*ChainPack, error) {
 	if config == nil {
 		config = params.TestChainConfig
 	}
-	blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
+	headers, blocks, receipts := make([]*types.Header, n), make(types.Blocks, n), make([]types.Receipts, n)
 	chainreader := &FakeChainReader{Cfg: config, current: parent}
 	tx, errBegin := db.BeginRw(context.Background())
 	if errBegin != nil {
-		return nil, nil, errBegin
+		return nil, errBegin
 	}
 	defer tx.Rollback()
 
@@ -323,8 +331,9 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
 		ibs := state.New(stateReader)
 		block, receipt, err := genblock(i, parent, ibs, stateReader, plainStateWriter)
 		if err != nil {
-			return nil, nil, fmt.Errorf("generating block %d: %w", i, err)
+			return nil, fmt.Errorf("generating block %d: %w", i, err)
 		}
+		headers[i] = block.Header()
 		blocks[i] = block
 		receipts[i] = receipt
 		parent = block
@@ -332,7 +341,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
 
 	tx.Rollback()
 
-	return blocks, receipts, nil
+	return &ChainPack{Length: n, Headers: headers, Blocks: blocks, Receipts: receipts, TopBlock: blocks[n-1]}, nil
 }
 
 func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.IntraBlockState, engine consensus.Engine) *types.Header {
diff --git a/core/rlp_test.go b/core/rlp_test.go
index 939124e384822e8d13dadfa572e5b63bbd0c8715..5794bfcc25fb41e7509093313b8840fdf2f8d419 100644
--- a/core/rlp_test.go
+++ b/core/rlp_test.go
@@ -52,7 +52,7 @@ func getBlock(transactions int, uncles int, dataSize int) *types.Block {
 	)
 
 	// We need to generate as many blocks +1 as uncles
-	blocks, _, _ := GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), uncles+1,
+	chain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), uncles+1,
 		func(n int, b *BlockGen) {
 			if n == uncles {
 				// Add transactions and stuff on the last block
@@ -66,7 +66,7 @@ func getBlock(transactions int, uncles int, dataSize int) *types.Block {
 				}
 			}
 		}, false /* intermediateHashes */)
-	block := blocks[len(blocks)-1]
+	block := chain.TopBlock
 	return block
 }
 
diff --git a/core/state/database_test.go b/core/state/database_test.go
index 178527348def6a0b466274a52ad032d4d985e8f1..03b1eba192968d21d81025f8bd1766d56c5237f6 100644
--- a/core/state/database_test.go
+++ b/core/state/database_test.go
@@ -95,7 +95,7 @@ func TestCreate2Revive(t *testing.T) {
 	// In the third block, we cause the first child contract to selfdestruct
 	// In the forth block, we create the second child contract, and we expect it to have a "clean slate" of storage,
 	// i.e. without any storage items that "inherited" from the first child contract by mistake
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -143,7 +143,7 @@ func TestCreate2Revive(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[0], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[0], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -153,7 +153,7 @@ func TestCreate2Revive(t *testing.T) {
 	}
 
 	// BLOCK 2
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	var it *contracts.ReviveDeployEventIterator
@@ -180,7 +180,7 @@ func TestCreate2Revive(t *testing.T) {
 	}
 
 	// BLOCK 3
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[2], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[2], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	st = state.New(state.NewDbStateReader(db))
@@ -189,7 +189,7 @@ func TestCreate2Revive(t *testing.T) {
 	}
 
 	// BLOCK 4
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[3], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[3], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	it, err = revive.FilterDeployEvent(nil)
@@ -267,7 +267,7 @@ func TestCreate2Polymorth(t *testing.T) {
 	// In the third block, we cause the first child contract to selfdestruct
 	// In the forth block, we create the second child contract
 	// In the 5th block, we delete and re-create the child contract twice
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 5, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 5, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -349,7 +349,7 @@ func TestCreate2Polymorth(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[0], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[0], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -359,7 +359,7 @@ func TestCreate2Polymorth(t *testing.T) {
 	}
 
 	// BLOCK 2
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	var it *contracts.PolyDeployEventIterator
@@ -385,7 +385,7 @@ func TestCreate2Polymorth(t *testing.T) {
 	}
 
 	// BLOCK 3
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[2], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[2], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	st = state.New(state.NewDbStateReader(db))
@@ -394,7 +394,7 @@ func TestCreate2Polymorth(t *testing.T) {
 	}
 
 	// BLOCK 4
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[3], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[3], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	it, err = poly.FilterDeployEvent(nil)
@@ -419,7 +419,7 @@ func TestCreate2Polymorth(t *testing.T) {
 	}
 
 	// BLOCK 5
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[4], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[4], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	it, err = poly.FilterDeployEvent(nil)
@@ -480,7 +480,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	var err error
 
 	// Here we generate 3 blocks, two of which (the one with "Change" invocation and "Destruct" invocation will be reverted during the reorg)
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -515,7 +515,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	transactOptsLonger := bind.NewKeyedTransactor(key)
 	transactOptsLonger.GasLimit = 1000000
 
-	longerBlocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
+	longerChain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -541,7 +541,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[0:1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[0:1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -556,7 +556,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	st.GetState(contractAddress, &key0, &correctValueX)
 
 	// BLOCKS 2 + 3
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[1:], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[1:], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -566,7 +566,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	}
 
 	// REORG of block 2 and 3, and insert new (empty) BLOCK 2, 3, and 4
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerBlocks[1:4], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerChain.Blocks[1:4], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	st = state.New(state.NewDbStateReader(db))
@@ -618,7 +618,7 @@ func TestReorgOverStateChange(t *testing.T) {
 	var err error
 
 	// Here we generate 3 blocks, two of which (the one with "Change" invocation and "Destruct" invocation will be reverted during the reorg)
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -646,7 +646,7 @@ func TestReorgOverStateChange(t *testing.T) {
 	defer contractBackendLonger.Close()
 	transactOptsLonger := bind.NewKeyedTransactor(key)
 	transactOptsLonger.GasLimit = 1000000
-	longerBlocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
+	longerChain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -672,7 +672,7 @@ func TestReorgOverStateChange(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[:1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[:1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -687,12 +687,12 @@ func TestReorgOverStateChange(t *testing.T) {
 	st.GetState(contractAddress, &key0, &correctValueX)
 
 	// BLOCK 2
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[1:], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[1:], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
 	// REORG of block 2 and 3, and insert new (empty) BLOCK 2, 3, and 4
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerBlocks[1:3], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerChain.Blocks[1:3], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	st = state.New(state.NewDbStateReader(db))
@@ -766,7 +766,7 @@ func TestCreateOnExistingStorage(t *testing.T) {
 	// There is one block, and it ends up deploying Revive contract (could be any other contract, it does not really matter)
 	// On the address contractAddr, where there is a storage item in the genesis, but no contract code
 	// We expect the pre-existing storage items to be removed by the deployment
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -792,7 +792,7 @@ func TestCreateOnExistingStorage(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[:1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[:1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -890,7 +890,7 @@ func TestEip2200Gas(t *testing.T) {
 
 	// Here we generate 1 block with 2 transactions, first creates a contract with some initial values in the
 	// It activates the SSTORE pricing rules specific to EIP-2200 (istanbul)
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -924,7 +924,7 @@ func TestEip2200Gas(t *testing.T) {
 	balanceBefore := st.GetBalance(address)
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[0], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[0], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -974,7 +974,7 @@ func TestWrongIncarnation(t *testing.T) {
 	var changer *contracts.Changer
 	var err error
 
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -1006,7 +1006,7 @@ func TestWrongIncarnation(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[0], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[0], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -1030,7 +1030,7 @@ func TestWrongIncarnation(t *testing.T) {
 	}
 
 	// BLOCKS 2
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	addrHash = crypto.Keccak256(contractAddress[:])
@@ -1092,7 +1092,7 @@ func TestWrongIncarnation2(t *testing.T) {
 
 	var contractAddress common.Address
 
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -1127,7 +1127,7 @@ func TestWrongIncarnation2(t *testing.T) {
 	contractBackendLonger := backends.NewSimulatedBackendWithConfig(gspec.Alloc, gspec.Config, gspec.GasLimit)
 	transactOptsLonger := bind.NewKeyedTransactor(key)
 	transactOptsLonger.GasLimit = 1000000
-	longerBlocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
+	longerChain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 3, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -1154,12 +1154,12 @@ func TestWrongIncarnation2(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[:1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[:1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
 	// BLOCKS 2
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[1:], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[1:], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -1181,7 +1181,7 @@ func TestWrongIncarnation2(t *testing.T) {
 		t.Fatal("wrong incarnation")
 	}
 	// REORG of block 2 and 3, and insert new (empty) BLOCK 2, 3, and 4
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerBlocks[1:], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerChain.Blocks[1:], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -1352,7 +1352,7 @@ func TestRecreateAndRewind(t *testing.T) {
 	var phoenixAddress common.Address
 	var err error
 
-	blocks, _, err1 := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
+	chain, err1 := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 4, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -1414,7 +1414,7 @@ func TestRecreateAndRewind(t *testing.T) {
 	defer contractBackendLonger.Close()
 	transactOptsLonger := bind.NewKeyedTransactor(key)
 	transactOptsLonger.GasLimit = 1000000
-	longerBlocks, _, err1 := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 5, func(i int, block *core.BlockGen) {
+	longerChain, err1 := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 5, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -1469,7 +1469,7 @@ func TestRecreateAndRewind(t *testing.T) {
 	}
 
 	// BLOCKS 1 and 2
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[:2], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[:2], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -1486,7 +1486,7 @@ func TestRecreateAndRewind(t *testing.T) {
 	}
 
 	// Block 3 and 4
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks[2:], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks[2:], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
@@ -1501,7 +1501,7 @@ func TestRecreateAndRewind(t *testing.T) {
 	}
 
 	// Reorg
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerBlocks, true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, longerChain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/eth/backend.go b/eth/backend.go
index b2c1fc7cb80001ddbe46d2552a71392668b0399e..78bb151179396c92f57d917daf9f041f22436ecc 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -717,7 +717,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
 // Ethereum protocol implementation.
 func (s *Ethereum) Start() error {
 	if s.config.EnableDownloadV2 {
-		go download.RecvMessage(s.downloadV2Ctx, s.sentries[0], s.downloadServer.HandleInboundMessage)
+		go download.RecvMessage(s.downloadV2Ctx, s.sentries[0], s.downloadServer.HandleInboundMessage, nil /* waitGroup */)
 		go download.RecvUploadMessage(s.downloadV2Ctx, s.sentries[0], s.downloadServer.HandleInboundMessage)
 		go download.Loop(s.downloadV2Ctx, s.chainDB.RwKV(), s.stagedSync2, s.downloadServer, s.events, s.config.StateStream, s.waitForStageLoopStop)
 	} else {
diff --git a/eth/downloader/testchain_test.go b/eth/downloader/testchain_test.go
index ca83d3d6a59765a6a910bc0e5199eaf5aa45dfac..8dbca0ec384d98239c895893461161c58974a48c 100644
--- a/eth/downloader/testchain_test.go
+++ b/eth/downloader/testchain_test.go
@@ -170,7 +170,7 @@ func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool)
 	signer := types.MakeSigner(params.TestChainConfig, 1)
 
 	testGenesisNonce := int64(-1)
-	blocks, receipts, err := core.GenerateChain(params.TestChainConfig, tc.genesis, ethash.NewFaker(), tc.db.RwKV(), totalLen, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, tc.genesis, ethash.NewFaker(), tc.db.RwKV(), totalLen, func(i int, block *core.BlockGen) {
 		if i < existingLen || existingLen == 0 {
 			block.SetCoinbase(zeroAddress)
 
@@ -220,13 +220,13 @@ func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool)
 
 	// Convert the block-chain into a hash-chain and header/block maps
 	td := new(big.Int).Set(tc.td(parent.Hash()))
-	for i, b := range blocks[existingLen:] {
+	for i, b := range chain.Blocks[existingLen:] {
 		td = td.Add(td, b.Difficulty())
 		hash := b.Hash()
 		tc.chain = append(tc.chain, hash)
 		tc.blockm[hash] = b
 		tc.headerm[hash] = b.Header()
-		tc.receiptm[hash] = receipts[existingLen+i]
+		tc.receiptm[hash] = chain.Receipts[existingLen+i]
 		tc.tdm[hash] = new(big.Int).Set(td)
 	}
 }
diff --git a/eth/fetcher/block_fetcher_test.go b/eth/fetcher/block_fetcher_test.go
index 962bbc04c16cafb90a4c0ee54788fb9c56207ed5..662d2ca3f45e374c07d0dc92fd91787ce112686c 100644
--- a/eth/fetcher/block_fetcher_test.go
+++ b/eth/fetcher/block_fetcher_test.go
@@ -51,7 +51,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common
 	db := ethdb.NewMemDatabase()
 	defer db.Close()
 	core.GenesisBlockForTesting(db, testAddress, big.NewInt(1000000000))
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), db.RwKV(), n, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), db.RwKV(), n, func(i int, block *core.BlockGen) {
 		block.SetCoinbase(common.Address{seed})
 
 		// If the block number is multiple of 3, send a bonus transaction to the miner
@@ -75,7 +75,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common
 	hashes[len(hashes)-1] = parent.Hash()
 	blockm := make(map[common.Hash]*types.Block, n+1)
 	blockm[parent.Hash()] = parent
-	for i, b := range blocks {
+	for i, b := range chain.Blocks {
 		hashes[len(hashes)-i-2] = b.Hash()
 		blockm[b.Hash()] = b
 	}
diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go
index ea7dfd1d56cec5e6b134dafdebd67e81f5ffd422..573358c7c2dadbaddca5470d7a6b18e8b63a6e19 100644
--- a/eth/filters/filter_system_test.go
+++ b/eth/filters/filter_system_test.go
@@ -178,11 +178,11 @@ func TestBlockSubscription(t *testing.T) {
 		backend     = &testBackend{db: db}
 		api         = NewPublicFilterAPI(backend, deadline)
 		genesis     = (&core.Genesis{Config: params.TestChainConfig}).MustCommit(db)
-		chain, _, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 10, func(i int, gen *core.BlockGen) {}, false /* intermediateHashes */)
+		chain, _    = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 10, func(i int, gen *core.BlockGen) {}, false /* intermediateHashes */)
 		chainEvents = []core.ChainEvent{}
 	)
 
-	for _, blk := range chain {
+	for _, blk := range chain.Blocks {
 		chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk})
 	}
 
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index 68d3c96e9091421f0ee0400634723013e473c617..efaf8248958277574ae3d188328aaaba67906574 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -53,7 +53,7 @@ func BenchmarkFilters(b *testing.B) {
 	)
 
 	genesis := core.GenesisBlockForTesting(db, addr1, big.NewInt(1000000))
-	chain, receipts, err := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 100010, func(i int, gen *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 100010, func(i int, gen *core.BlockGen) {
 		switch i {
 		case 2403:
 			receipt := makeReceipt(addr1)
@@ -73,7 +73,7 @@ func BenchmarkFilters(b *testing.B) {
 	if err != nil {
 		b.Fatalf("generate chain: %v", err)
 	}
-	for i, block := range chain {
+	for i, block := range chain.Blocks {
 		if err := rawdb.WriteBlockDeprecated(context.Background(), db, block); err != nil {
 			panic(err)
 		}
@@ -81,7 +81,7 @@ func BenchmarkFilters(b *testing.B) {
 			panic(err)
 		}
 		rawdb.WriteHeadBlockHash(db, block.Hash())
-		if err := rawdb.WriteReceipts(db, block.NumberU64(), receipts[i]); err != nil {
+		if err := rawdb.WriteReceipts(db, block.NumberU64(), chain.Receipts[i]); err != nil {
 			panic(err)
 		}
 	}
diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go
index 81301c6e99af5a00685c12dd911385c50604728c..9d91602ca735401839def2aabaceffecf386f5b7 100644
--- a/eth/gasprice/gasprice_test.go
+++ b/eth/gasprice/gasprice_test.go
@@ -77,7 +77,7 @@ func newTestBackend(t *testing.T) *testBackend {
 	}
 
 	// Generate testing blocks
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 32, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 32, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		tx, txErr := types.SignTx(types.NewTransaction(b.TxNonce(addr), common.HexToAddress("deadbeef"), uint256.NewInt().SetUint64(100), 21000, uint256.NewInt().SetUint64(uint64(int64(i+1)*params.GWei)), nil), *signer, key)
 		if txErr != nil {
@@ -89,7 +89,7 @@ func newTestBackend(t *testing.T) *testBackend {
 		t.Error(err)
 	}
 	// Construct testing chain
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Error(err)
 	}
 	return &testBackend{db: db, cfg: params.TestChainConfig}
diff --git a/eth/handler_test.go b/eth/handler_test.go
index b9c82d62fc86303a496e9aa9163bec1561e91878..a786478cc1ca1ab4c725e9c9107d415b2354124a 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -71,11 +71,11 @@ func newTestHandlerWithBlocks(t *testing.T, blocks int) *testHandler {
 
 	headBlock := genesis
 	if blocks > 0 {
-		bs, _, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), blocks, nil, false)
-		if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), bs, true /* checkRoot */); err != nil {
+		chain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), blocks, nil, false)
+		if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 			panic(err)
 		}
-		headBlock = bs[len(bs)-1]
+		headBlock = chain.TopBlock
 	}
 	txpool := mock.NewTestTxPool()
 
diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go
index 7990aabaf1172b012ce45078168208ffba9e3a86..61533a29f776058af3fda7e1b41786cd29030654 100644
--- a/eth/protocols/eth/handler_test.go
+++ b/eth/protocols/eth/handler_test.go
@@ -73,11 +73,11 @@ func newTestBackendWithGenerator(t *testing.T, blocks int, generator func(int, *
 
 	headBlock := genesis
 	if blocks > 0 {
-		bs, _, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), blocks, generator, true)
-		if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), bs, true /* checkRoot */); err != nil {
+		chain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), blocks, generator, true)
+		if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 			panic(err)
 		}
-		headBlock = bs[len(bs)-1]
+		headBlock = chain.TopBlock
 	}
 	txconfig := core.DefaultTxPoolConfig
 	txconfig.Journal = "" // Don't litter the disk with test journals
diff --git a/tests/statedb_chain_test.go b/tests/statedb_chain_test.go
index d44061c4a99f696e80c7cdfd09bb249f3d6b5bcb..cc25a60064b81cb01942ccc68fd79e6f88192322 100644
--- a/tests/statedb_chain_test.go
+++ b/tests/statedb_chain_test.go
@@ -78,7 +78,7 @@ func TestSelfDestructReceive(t *testing.T) {
 	// effectively turning it from contract account to a non-contract account
 	// The second block is empty and is only used to force the newly created blockchain object to reload the trie
 	// from the database.
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 2, func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 
 		switch i {
@@ -112,12 +112,12 @@ func TestSelfDestructReceive(t *testing.T) {
 	}
 
 	// BLOCK 1
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[0], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[0], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 
 	// BLOCK 2
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, blocks[1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, engine, chain.Blocks[1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	// If we got this far, the newly created blockchain (with empty trie cache) loaded trie from the database
diff --git a/tests/statedb_insert_chain_transaction_test.go b/tests/statedb_insert_chain_transaction_test.go
index 5ae68b96b923b925d753c36177fb5683ebabb075..4523e82ac6682c7269643a8426fbf7919dfd3f2c 100644
--- a/tests/statedb_insert_chain_transaction_test.go
+++ b/tests/statedb_insert_chain_transaction_test.go
@@ -682,7 +682,7 @@ func genBlocks(t *testing.T, gspec *core.Genesis, txs map[int]tx) (consensus.Eng
 	contractBackend := backends.NewSimulatedBackendWithConfig(gspec.Alloc, gspec.Config, gspec.GasLimit)
 	defer contractBackend.Close()
 
-	blocks, receipts, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), len(txs), func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), len(txs), func(i int, block *core.BlockGen) {
 		var tx types.Transaction
 		var isContractCall bool
 		signer := types.LatestSignerForChainID(nil)
@@ -712,7 +712,7 @@ func genBlocks(t *testing.T, gspec *core.Genesis, txs map[int]tx) (consensus.Eng
 	if err != nil {
 		return nil, nil, nil, nil, fmt.Errorf("generate chain: %w", err)
 	}
-	return engine, db, blocks, receipts, err
+	return engine, db, chain.Blocks, chain.Receipts, err
 }
 
 type blockTx func(_ *core.BlockGen, backend bind.ContractBackend) (types.Transaction, bool)
diff --git a/turbo/stages/blockchain_test.go b/turbo/stages/blockchain_test.go
index fd40d8587228cec3d6e2b9c76da636b698f1e579..a6e93eb1589d3e2f0956e452b8d3be85fd49cc4a 100644
--- a/turbo/stages/blockchain_test.go
+++ b/turbo/stages/blockchain_test.go
@@ -62,10 +62,10 @@ func makeHeaderChain(parent *types.Header, n int, engine consensus.Engine, db et
 
 // makeBlockChain creates a deterministic chain of blocks rooted at parent.
 func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethdb.RwKV, seed int) []*types.Block {
-	blocks, _, _ := core.GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *core.BlockGen) {
+	chain, _ := core.GenerateChain(params.TestChainConfig, parent, engine, db, n, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
 	}, false /* intermediateHashes */)
-	return blocks
+	return chain.Blocks
 }
 
 // newCanonical creates a chain database, and injects a deterministic canonical
@@ -399,38 +399,30 @@ func testReorg(t *testing.T, first, second []int64, td int64, full bool) {
 	db, genesis := newCanonical(t, ethash.NewFaker(), 0, full)
 
 	// Insert an easy and a difficult chain afterwards
-	easyBlocks, _, err := core.GenerateChain(params.TestChainConfig, rawdb.ReadCurrentBlockDeprecated(db), ethash.NewFaker(), db.RwKV(), len(first), func(i int, b *core.BlockGen) {
+	easyChain, err := core.GenerateChain(params.TestChainConfig, rawdb.ReadCurrentBlockDeprecated(db), ethash.NewFaker(), db.RwKV(), len(first), func(i int, b *core.BlockGen) {
 		b.OffsetTime(first[i])
 	}, false /* intemediateHashes */)
 	if err != nil {
 		t.Fatalf("generate chain: %v", err)
 	}
-	diffBlocks, _, err := core.GenerateChain(params.TestChainConfig, rawdb.ReadCurrentBlockDeprecated(db), ethash.NewFaker(), db.RwKV(), len(second), func(i int, b *core.BlockGen) {
+	diffChain, err := core.GenerateChain(params.TestChainConfig, rawdb.ReadCurrentBlockDeprecated(db), ethash.NewFaker(), db.RwKV(), len(second), func(i int, b *core.BlockGen) {
 		b.OffsetTime(second[i])
 	}, false /* intemediateHashes */)
 	if err != nil {
 		t.Fatalf("generate chain: %v", err)
 	}
 	if full {
-		if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), easyBlocks, true /* checkRoot */); err != nil {
+		if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), easyChain.Blocks, true /* checkRoot */); err != nil {
 			t.Fatalf("failed to insert easy chain: %v", err)
 		}
-		if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), diffBlocks, true /* checkRoot */); err != nil {
+		if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, ethash.NewFaker(), diffChain.Blocks, true /* checkRoot */); err != nil {
 			t.Fatalf("failed to insert difficult chain: %v", err)
 		}
 	} else {
-		easyHeaders := make([]*types.Header, len(easyBlocks))
-		for i, block := range easyBlocks {
-			easyHeaders[i] = block.Header()
-		}
-		diffHeaders := make([]*types.Header, len(diffBlocks))
-		for i, block := range diffBlocks {
-			diffHeaders[i] = block.Header()
-		}
-		if _, _, _, err = stagedsync.InsertHeadersInStages(db, params.TestChainConfig, ethash.NewFaker(), easyHeaders); err != nil {
+		if _, _, _, err = stagedsync.InsertHeadersInStages(db, params.TestChainConfig, ethash.NewFaker(), easyChain.Headers); err != nil {
 			t.Fatalf("failed to insert easy chain: %v", err)
 		}
-		if _, _, _, err = stagedsync.InsertHeadersInStages(db, params.TestChainConfig, ethash.NewFaker(), diffHeaders); err != nil {
+		if _, _, _, err = stagedsync.InsertHeadersInStages(db, params.TestChainConfig, ethash.NewFaker(), diffChain.Headers); err != nil {
 			t.Fatalf("failed to insert difficult chain: %v", err)
 		}
 	}
@@ -553,7 +545,7 @@ func TestChainTxReorgs(t *testing.T) {
 	//  - futureAdd: transaction added after the reorg has already finished
 	var pastAdd, freshAdd, futureAdd types.Transaction
 
-	chain, _, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, gen *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, gen *core.BlockGen) {
 		switch i {
 		case 0:
 			pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, uint256.NewInt().SetUint64(1000), params.TxGas, nil, nil), *signer, key2)
@@ -574,12 +566,12 @@ func TestChainTxReorgs(t *testing.T) {
 		t.Fatalf("generate chain: %v", err)
 	}
 	// Import the chain. This runs all block validation rules.
-	if _, err1 := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain, true /* checkRoot */); err1 != nil {
+	if _, err1 := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err1 != nil {
 		t.Fatalf("failed to insert original chain: %v", err1)
 	}
 
 	// overwrite the old chain
-	chain, _, err = core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db2.RwKV(), 5, func(i int, gen *core.BlockGen) {
+	chain, err = core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db2.RwKV(), 5, func(i int, gen *core.BlockGen) {
 		switch i {
 		case 0:
 			pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, uint256.NewInt().SetUint64(1000), params.TxGas, nil, nil), *signer, key3)
@@ -600,7 +592,7 @@ func TestChainTxReorgs(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generate chain: %v", err)
 	}
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert forked chain: %v", err)
 	}
 
@@ -656,7 +648,7 @@ func TestLogReorgs(t *testing.T) {
 
 	rmLogsCh := make(chan core.RemovedLogsEvent, 10)
 	blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
-	chain, _, err := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 2, func(i int, gen *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 2, func(i int, gen *core.BlockGen) {
 		if i == 1 {
 			tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(uint256.Int), 1000000, new(uint256.Int), code), *signer, key1)
 			if err != nil {
@@ -669,11 +661,11 @@ func TestLogReorgs(t *testing.T) {
 		t.Fatalf("generate chain: %v", err)
 	}
 
-	if _, err1 := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain, true /* checkRoot */); err1 != nil {
+	if _, err1 := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err1 != nil {
 		t.Fatalf("failed to insert chain: %v", err1)
 	}
 
-	chain, _, err = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db2.RwKV(), 3, func(i int, gen *core.BlockGen) {}, false /* intemediateHashes */)
+	chain, err = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db2.RwKV(), 3, func(i int, gen *core.BlockGen) {}, false /* intemediateHashes */)
 	if err != nil {
 		t.Fatalf("generate chain: %v", err)
 	}
@@ -685,7 +677,7 @@ func TestLogReorgs(t *testing.T) {
 		}
 		close(done)
 	}()
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert forked chain: %v", err)
 	}
 	// In turbo-geth, RemoveLogsEvent is not working yet
@@ -727,7 +719,7 @@ func TestLogRebirth(t *testing.T) {
 	blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
 
 	// This chain contains a single log.
-	chain, _, chainErr := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 2, func(i int, gen *core.BlockGen) {
+	chain, chainErr := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 2, func(i int, gen *core.BlockGen) {
 		if i == 1 {
 			tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(uint256.Int), 1000000, new(uint256.Int), logCode), *signer, key1)
 			if err != nil {
@@ -741,7 +733,7 @@ func TestLogRebirth(t *testing.T) {
 	}
 	// Generate long reorg chain containing another log. Inserting the
 	// chain removes one log and adds one.
-	forkChain, _, forkChainErr := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 2, func(i int, gen *core.BlockGen) {
+	forkChain, forkChainErr := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 2, func(i int, gen *core.BlockGen) {
 		if i == 1 {
 			tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(uint256.Int), 1000000, new(uint256.Int), logCode), *signer, key1)
 			if err != nil {
@@ -757,7 +749,7 @@ func TestLogRebirth(t *testing.T) {
 	// This chain segment is rooted in the original chain, but doesn't contain any logs.
 	// When inserting it, the canonical chain switches away from forkChain and re-emits
 	// the log event for the old chain, as well as a RemovedLogsEvent for forkChain.
-	newBlocks, _, newBlocksErr := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 3, func(i int, gen *core.BlockGen) {
+	newChain, newBlocksErr := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 3, func(i int, gen *core.BlockGen) {
 		if i == 1 {
 			tx, err1 := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(uint256.Int), 1000000, new(uint256.Int), logCode), *signer, key1)
 			if err1 != nil {
@@ -770,17 +762,17 @@ func TestLogRebirth(t *testing.T) {
 		t.Fatalf("generate new blocks: %v", newBlocksErr)
 	}
 
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert chain: %v", err)
 	}
 	checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
 
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), forkChain, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), forkChain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert forked chain: %v", err)
 	}
 	checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
 
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), newBlocks[2:], true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), newChain.Blocks[2:], true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert forked chain: %v", err)
 	}
 	checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
@@ -809,7 +801,7 @@ func TestSideLogRebirth(t *testing.T) {
 	blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
 
 	// Generate main chain
-	chain, _, err := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 2, func(i int, gen *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 2, func(i int, gen *core.BlockGen) {
 		if i == 1 {
 			gen.OffsetTime(-9) // higher block difficulty
 		}
@@ -818,7 +810,7 @@ func TestSideLogRebirth(t *testing.T) {
 		t.Fatalf("generate chain: %v", err)
 	}
 	// Generate side chain with lower difficulty
-	sideChain, _, sideChainErr := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, gen *core.BlockGen) {
+	sideChain, sideChainErr := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, gen *core.BlockGen) {
 		if i == 1 {
 			tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(uint256.Int), 1000000, new(uint256.Int), logCode), *signer, key1)
 			if err != nil {
@@ -830,16 +822,16 @@ func TestSideLogRebirth(t *testing.T) {
 	if sideChainErr != nil {
 		t.Fatalf("generate side chain: %v", sideChainErr)
 	}
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert forked chain: %v", err)
 	}
 	checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), sideChain[:2], true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), sideChain.Blocks[:2], true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert forked chain: %v", err)
 	}
 	checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
 
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), sideChain[2:], true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), sideChain.Blocks[2:], true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert forked chain: %v", err)
 	}
 	checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
@@ -867,16 +859,16 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan co
 func TestCanonicalBlockRetrieval(t *testing.T) {
 	db, genesis := newCanonical(t, ethash.NewFaker(), 0, true)
 
-	chain, _, err2 := core.GenerateChain(params.AllEthashProtocolChanges, genesis, ethash.NewFaker(), db.RwKV(), 10, func(i int, gen *core.BlockGen) {}, false /* intermediateHashes */)
+	chain, err2 := core.GenerateChain(params.AllEthashProtocolChanges, genesis, ethash.NewFaker(), db.RwKV(), 10, func(i int, gen *core.BlockGen) {}, false /* intermediateHashes */)
 	if err2 != nil {
 		t.Fatalf("generate chain: %v", err2)
 	}
 
-	ok, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.AllEthashProtocolChanges, &vm.Config{}, ethash.NewFaker(), chain, true)
+	ok, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.AllEthashProtocolChanges, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true)
 	require.NoError(t, err)
 	require.True(t, ok)
 
-	for _, block := range chain {
+	for _, block := range chain.Blocks {
 		// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
 		ch, err := rawdb.ReadCanonicalHash(db, block.NumberU64())
 		require.NoError(t, err)
@@ -918,7 +910,7 @@ func TestEIP155Transition(t *testing.T) {
 		genesis = gspec.MustCommit(db)
 	)
 
-	blocks, _, chainErr := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 4, func(i int, block *core.BlockGen) {
+	chain, chainErr := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 4, func(i int, block *core.BlockGen) {
 		var (
 			tx      types.Transaction
 			err     error
@@ -963,7 +955,7 @@ func TestEIP155Transition(t *testing.T) {
 		t.Fatalf("generate chain: %v", chainErr)
 	}
 
-	if _, chainErr = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks, true /* checkRoot */); chainErr != nil {
+	if _, chainErr = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); chainErr != nil {
 		t.Fatal(chainErr)
 	}
 	block, _ := rawdb.ReadBlockByNumberDeprecated(db, 1)
@@ -978,13 +970,13 @@ func TestEIP155Transition(t *testing.T) {
 	if !block.Transactions()[1].Protected() {
 		t.Error("Expected block[3].txs[1] to be replay protected")
 	}
-	if _, chainErr = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks[4:], true /* checkRoot */); chainErr != nil {
+	if _, chainErr = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks[4:], true /* checkRoot */); chainErr != nil {
 		t.Fatal(chainErr)
 	}
 
 	// generate an invalid chain id transaction
 	config := &params.ChainConfig{ChainID: big.NewInt(2), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
-	blocks, _, chainErr = core.GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), db.RwKV(), 4, func(i int, block *core.BlockGen) {
+	chain, chainErr = core.GenerateChain(config, chain.TopBlock, ethash.NewFaker(), db.RwKV(), 4, func(i int, block *core.BlockGen) {
 		var (
 			basicTx = func(signer types.Signer) (types.Transaction, error) {
 				return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(uint256.Int), 21000, new(uint256.Int), nil), signer, key)
@@ -1001,7 +993,7 @@ func TestEIP155Transition(t *testing.T) {
 	if chainErr != nil {
 		t.Fatalf("generate blocks: %v", chainErr)
 	}
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks, true /* checkRoot */); !errors.Is(err, types.ErrInvalidChainId) {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); !errors.Is(err, types.ErrInvalidChainId) {
 		t.Errorf("expected error: %v, got %v", types.ErrInvalidChainId, err)
 	}
 }
@@ -1031,7 +1023,7 @@ func doModesTest(t *testing.T, history, preimages, receipts, txlookup bool) erro
 		genesis, _, _ = gspec.Commit(db, history)
 	)
 
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 4, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 4, func(i int, block *core.BlockGen) {
 		var (
 			tx      types.Transaction
 			err     error
@@ -1076,7 +1068,7 @@ func doModesTest(t *testing.T, history, preimages, receipts, txlookup bool) erro
 		return fmt.Errorf("generate blocks: %v", err)
 	}
 
-	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.StorageMode{History: history, Receipts: receipts, TxIndex: txlookup}, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks, true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlocksInStages(db, ethdb.StorageMode{History: history, Receipts: receipts, TxIndex: txlookup}, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 		return err
 	}
 
@@ -1174,7 +1166,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 		genesis = gspec.MustCommit(db)
 	)
 
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, block *core.BlockGen) {
 		var (
 			txn    types.Transaction
 			err    error
@@ -1197,7 +1189,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 		t.Fatalf("generate blocks: %v", err)
 	}
 	// account must exist pre eip 161
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks[0], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks[0], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	if st := state.New(state.NewPlainStateReader(db)); !st.Exist(theAddr) {
@@ -1205,7 +1197,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	}
 
 	// account needs to be deleted post eip 161
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks[1], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks[1], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	if st := state.New(state.NewPlainStateReader(db)); st.Exist(theAddr) {
@@ -1213,7 +1205,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 	}
 
 	// account mustn't be created post eip 161
-	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks[2], true /* checkRoot */); err != nil {
+	if _, err = stagedsync.InsertBlockInStages(db, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks[2], true /* checkRoot */); err != nil {
 		t.Fatal(err)
 	}
 	if st := state.New(state.NewPlainStateReader(db)); st.Exist(theAddr) {
@@ -1240,7 +1232,7 @@ func TestDoubleAccountRemoval(t *testing.T) {
 
 	var theAddr common.Address
 
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, block *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 3, func(i int, block *core.BlockGen) {
 		nonce := block.TxNonce(bankAddress)
 		switch i {
 		case 0:
@@ -1267,7 +1259,7 @@ func TestDoubleAccountRemoval(t *testing.T) {
 		t.Fatalf("generate blocks: %v", err)
 	}
 
-	_, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), blocks, true /* checkRoot */)
+	_, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */)
 	assert.NoError(t, err)
 
 	st := state.New(state.NewDbStateReader(db))
@@ -1307,15 +1299,15 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
 	diskdb := ethdb.NewTestDB(t)
 	(&core.Genesis{Config: params.TestChainConfig}).MustCommit(diskdb)
 
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) }, false /* intermediateHashes */)
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{1}) }, false /* intermediateHashes */)
 	if err != nil {
 		t.Fatalf("generate blocks: %v", err)
 	}
 
 	// Generate a bunch of fork blocks, each side forking from the canonical chain
-	forks := make([]*types.Block, len(blocks))
+	forks := make([]*types.Block, chain.Length)
 	for i := 0; i < len(forks); i++ {
-		fork, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), i+1, func(j int, b *core.BlockGen) {
+		fork, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), i+1, func(j int, b *core.BlockGen) {
 			//nolint:scopelint
 			if j == i {
 				b.SetCoinbase(common.Address{2})
@@ -1327,12 +1319,12 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
 		if err != nil {
 			t.Fatalf("generate fork %d: %v", i, err)
 		}
-		forks[i] = fork[len(fork)-1]
+		forks[i] = fork.TopBlock
 	}
 	// Import the canonical and fork chain side by side, verifying the current block
 	// and current header consistency
-	for i := 0; i < len(blocks); i++ {
-		if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, blocks[i:i+1], true /* checkRoot */); err != nil {
+	for i := 0; i < chain.Length; i++ {
+		if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks[i:i+1], true /* checkRoot */); err != nil {
 			t.Fatalf("block %d: failed to insert into chain: %v", i, err)
 		}
 
@@ -1362,13 +1354,13 @@ func TestLargeReorgTrieGC(t *testing.T) {
 	db := ethdb.NewTestDB(t)
 	genesis := (&core.Genesis{Config: params.TestChainConfig}).MustCommit(db)
 
-	shared, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64, func(i int, b *core.BlockGen) {
+	shared, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 	}, false /* intemediateHashes */)
 	if err != nil {
 		t.Fatalf("generate shared chain: %v", err)
 	}
-	original, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64+2*core.TriesInMemory, func(i int, b *core.BlockGen) {
+	original, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64+2*core.TriesInMemory, func(i int, b *core.BlockGen) {
 		if i < 64 {
 			b.SetCoinbase(common.Address{1})
 		} else {
@@ -1378,7 +1370,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
 	if err != nil {
 		t.Fatalf("generate original chain: %v", err)
 	}
-	competitor, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64+2*core.TriesInMemory+1, func(i int, b *core.BlockGen) {
+	competitor, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 64+2*core.TriesInMemory+1, func(i int, b *core.BlockGen) {
 		if i < 64 {
 			b.SetCoinbase(common.Address{1})
 		} else {
@@ -1391,20 +1383,20 @@ func TestLargeReorgTrieGC(t *testing.T) {
 	}
 
 	// Import the shared chain and the original canonical one
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, shared, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, shared.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert shared chain: %v", err)
 	}
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, original, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, original.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert original chain: %v", err)
 	}
 	// Import the competitor chain without exceeding the canonical's TD and ensure
 	// we have not processed any of the blocks (protection against malicious blocks)
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, competitor[:len(competitor)-2], true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, competitor.Blocks[:competitor.Length-2], true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert competitor chain: %v", err)
 	}
 	// Import the head of the competitor chain, triggering the reorg and ensure we
 	// successfully reprocess all the stashed away blocks.
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, competitor[len(competitor)-2:], true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, competitor.Blocks[competitor.Length-2:], true /* checkRoot */); err != nil {
 		t.Fatalf("failed to finalize competitor chain: %v", err)
 	}
 }
@@ -1557,7 +1549,7 @@ func TestLowDiffLongChain(t *testing.T) {
 
 	// We must use a pretty long chain to ensure that the fork doesn't overtake us
 	// until after at least 128 blocks post tip
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 6*core.TriesInMemory, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 6*core.TriesInMemory, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		b.OffsetTime(-9)
 	}, false /* intermediateHashes */)
@@ -1565,7 +1557,7 @@ func TestLowDiffLongChain(t *testing.T) {
 		t.Fatalf("generate blocks: %v", err)
 	}
 	// Generate fork chain, starting from an early block
-	fork, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 11+8*core.TriesInMemory, func(i int, b *core.BlockGen) {
+	fork, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 11+8*core.TriesInMemory, func(i int, b *core.BlockGen) {
 		if i < 11 {
 			b.SetCoinbase(common.Address{1})
 			b.OffsetTime(-9)
@@ -1581,17 +1573,17 @@ func TestLowDiffLongChain(t *testing.T) {
 	diskDB := ethdb.NewTestDB(t)
 	new(core.Genesis).MustCommit(diskDB)
 
-	if _, err := stagedsync.InsertBlocksInStages(diskDB, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskDB, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 
 	// And now import the fork
-	if _, err := stagedsync.InsertBlocksInStages(diskDB, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, fork, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskDB, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, fork.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 
 	head := rawdb.ReadCurrentBlockDeprecated(diskDB)
-	if got := fork[len(fork)-1].Hash(); got != head.Hash() {
+	if got := fork.TopBlock.Hash(); got != head.Hash() {
 		t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
 	}
 	// Sanity check that all the canonical numbers are present
@@ -1656,7 +1648,7 @@ func TestDeleteCreateRevert(t *testing.T) {
 		genesis = gspec.MustCommit(db)
 	)
 
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		// One transaction to AAAA
 		tx, _ := types.SignTx(types.NewTransaction(0, aa,
@@ -1675,7 +1667,7 @@ func TestDeleteCreateRevert(t *testing.T) {
 	diskdb := ethdb.NewTestDB(t)
 	gspec.MustCommit(diskdb)
 
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 }
@@ -1768,7 +1760,7 @@ func TestDeleteRecreateSlots(t *testing.T) {
 		},
 	}
 	genesis := gspec.MustCommit(db)
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		// One transaction to AA, to kill it
 		tx, _ := types.SignTx(types.NewTransaction(0, aa,
@@ -1783,7 +1775,7 @@ func TestDeleteRecreateSlots(t *testing.T) {
 		t.Fatalf("generate blocks: %v", err)
 	}
 	// Import the canonical chain
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 	statedb := state.New(state.NewDbStateReader(db))
@@ -1851,7 +1843,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
 	}
 	genesis := gspec.MustCommit(db)
 
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		// One transaction to AA, to kill it
 		tx, _ := types.SignTx(types.NewTransaction(0, aa,
@@ -1866,7 +1858,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
 		t.Fatalf("generate blocks: %v", err)
 	}
 	// Import the canonical chain
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 	statedb := state.New(state.NewDbStateReader(db))
@@ -2009,7 +2001,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
 		return tx
 	}
 
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 150, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 150, func(i int, b *core.BlockGen) {
 		var exp = new(expectation)
 		exp.blocknum = i + 1
 		exp.values = make(map[int]int)
@@ -2041,7 +2033,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
 	var asHash = func(num int) common.Hash {
 		return common.BytesToHash([]byte{byte(num)})
 	}
-	for i, block := range blocks {
+	for i, block := range chain.Blocks {
 		blockNum := i + 1
 		if _, err := stagedsync.InsertBlockInStages(db, params.TestChainConfig, &vm.Config{}, engine, block, true /* checkRoot */); err != nil {
 			t.Fatalf("block %d: failed to insert into chain: %v", i, err)
@@ -2162,7 +2154,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
 	genesis := gspec.MustCommit(db)
 	nonce := uint64(0)
 
-	blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 4, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(params.TestChainConfig, genesis, engine, db.RwKV(), 4, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		// One transaction to BB
 		tx, _ := types.SignTx(types.NewTransaction(nonce, bb,
@@ -2183,8 +2175,8 @@ func TestInitThenFailCreateContract(t *testing.T) {
 	}
 	// First block tries to create, but fails
 	{
-		block := blocks[0]
-		if _, err := stagedsync.InsertBlockInStages(diskdb, params.TestChainConfig, &vm.Config{}, engine, blocks[0], true /* checkRoot */); err != nil {
+		block := chain.Blocks[0]
+		if _, err := stagedsync.InsertBlockInStages(diskdb, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks[0], true /* checkRoot */); err != nil {
 			t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
 		}
 		statedb = state.New(state.NewPlainStateReader(diskdb))
@@ -2193,8 +2185,8 @@ func TestInitThenFailCreateContract(t *testing.T) {
 		}
 	}
 	// Import the rest of the blocks
-	for _, block := range blocks[1:] {
-		if _, err := stagedsync.InsertBlockInStages(diskdb, params.TestChainConfig, &vm.Config{}, engine, blocks[0], true /* checkRoot */); err != nil {
+	for _, block := range chain.Blocks[1:] {
+		if _, err := stagedsync.InsertBlockInStages(diskdb, params.TestChainConfig, &vm.Config{}, engine, chain.Blocks[0], true /* checkRoot */); err != nil {
 			t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
 		}
 	}
@@ -2237,7 +2229,7 @@ func TestEIP2718Transition(t *testing.T) {
 		genesis = gspec.MustCommit(db)
 	)
 
-	blocks, _, _ := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
+	chain, _ := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 1, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		gasPrice, _ := uint256.FromBig(big.NewInt(1))
 		chainID, _ := uint256.FromBig(gspec.Config.ChainID)
@@ -2266,7 +2258,7 @@ func TestEIP2718Transition(t *testing.T) {
 	diskdb := ethdb.NewTestDB(t)
 	gspec.MustCommit(diskdb)
 
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 
@@ -2327,7 +2319,7 @@ func TestEIP1559Transition(t *testing.T) {
 		signer  = types.LatestSigner(gspec.Config)
 	)
 
-	blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 501, func(i int, b *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, engine, db.RwKV(), 501, func(i int, b *core.BlockGen) {
 		if i == 500 {
 			b.SetCoinbase(common.Address{1})
 		} else {
@@ -2366,11 +2358,11 @@ func TestEIP1559Transition(t *testing.T) {
 	diskdb := ethdb.NewTestDB(t)
 	gspec.MustCommit(diskdb)
 
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 
-	block := blocks[500]
+	block := chain.Blocks[500]
 
 	// 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
 	expectedGas := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
@@ -2397,7 +2389,7 @@ func TestEIP1559Transition(t *testing.T) {
 		t.Fatalf("sender expenditure incorrect: expected %d, got %d", expected, actual)
 	}
 
-	blocks, _, err = core.GenerateChain(gspec.Config, block, engine, diskdb.RwKV(), 1, func(i int, b *core.BlockGen) {
+	chain, err = core.GenerateChain(gspec.Config, block, engine, diskdb.RwKV(), 1, func(i int, b *core.BlockGen) {
 		b.SetCoinbase(common.Address{2})
 
 		var tx types.Transaction = types.NewTransaction(0, aa, u256.Num0, 30000, new(uint256.Int).Mul(new(uint256.Int).SetUint64(5), new(uint256.Int).SetUint64(params.GWei)), nil)
@@ -2409,11 +2401,11 @@ func TestEIP1559Transition(t *testing.T) {
 		t.Fatalf("generate chain: %v", err)
 	}
 
-	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, blocks, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(diskdb, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, engine, chain.Blocks, true /* checkRoot */); err != nil {
 		t.Fatalf("failed to insert into chain: %v", err)
 	}
 
-	block = blocks[0]
+	block = chain.Blocks[0]
 	statedb = state.New(state.NewPlainStateReader(diskdb))
 	effectiveTip := block.Transactions()[0].GetPrice().Uint64() - block.BaseFee().Uint64()
 
diff --git a/turbo/stages/chain_makers_test.go b/turbo/stages/chain_makers_test.go
index 3a9e8a20e73049a7311197526065d5b75f1624a6..5ac9bf0dfeeeefdf1edbbb4e875d9cca8c8f62c6 100644
--- a/turbo/stages/chain_makers_test.go
+++ b/turbo/stages/chain_makers_test.go
@@ -65,7 +65,7 @@ func ExampleGenerateChain() {
 	// each block and adds different features to gen based on the
 	// block index.
 	signer := types.LatestSignerForChainID(nil)
-	chain, _, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 5, func(i int, gen *core.BlockGen) {
+	chain, err := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db.RwKV(), 5, func(i int, gen *core.BlockGen) {
 		switch i {
 		case 0:
 			// In block 1, addr1 sends addr2 some ether.
@@ -97,7 +97,7 @@ func ExampleGenerateChain() {
 	}
 
 	// Import the chain. This runs all block validation rules.
-	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain, true /* checkRoot */); err != nil {
+	if _, err := stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, gspec.Config, &vm.Config{}, ethash.NewFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 		fmt.Printf("insert error%v\n", err)
 		return
 	}
diff --git a/turbo/stages/genesis_test.go b/turbo/stages/genesis_test.go
index fe1b71c5c70a2f1f35d98f5abf295d212910b7e6..c733299a5748f82361f43e4288195a286e3d25dc 100644
--- a/turbo/stages/genesis_test.go
+++ b/turbo/stages/genesis_test.go
@@ -126,11 +126,11 @@ func TestSetupGenesis(t *testing.T) {
 				// Advance to block #4, past the homestead transition block of customg.
 				genesis := oldcustomg.MustCommit(db)
 
-				blocks, _, err := core.GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db.RwKV(), 4, nil, false /* intermediateHashes */)
+				chain, err := core.GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db.RwKV(), 4, nil, false /* intermediateHashes */)
 				if err != nil {
 					return nil, nil, err
 				}
-				if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, oldcustomg.Config, &vm.Config{}, ethash.NewFullFaker(), blocks, true /* checkRoot */); err != nil {
+				if _, err = stagedsync.InsertBlocksInStages(db, ethdb.DefaultStorageMode, oldcustomg.Config, &vm.Config{}, ethash.NewFullFaker(), chain.Blocks, true /* checkRoot */); err != nil {
 					return nil, nil, err
 				}
 				// This should return a compatibility error.