From dd3e760174d118168e0930b18764caa289766df1 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Wed, 15 Nov 2023 15:37:14 -0800 Subject: [PATCH 1/5] add storehouse loader --- cmd/execution_builder.go | 2 +- engine/execution/ingestion/engine_test.go | 2 +- .../{loader.go => unexecuted_loader.go} | 18 ++-- ...ader_test.go => unexecuted_loader_test.go} | 0 .../ingestion/loader/unfinalized_loader.go | 88 +++++++++++++++++++ engine/testutil/nodes.go | 2 +- 6 files changed, 100 insertions(+), 12 deletions(-) rename engine/execution/ingestion/loader/{loader.go => unexecuted_loader.go} (92%) rename engine/execution/ingestion/loader/{loader_test.go => unexecuted_loader_test.go} (100%) create mode 100644 engine/execution/ingestion/loader/unfinalized_loader.go diff --git a/cmd/execution_builder.go b/cmd/execution_builder.go index 9f0fe5114f3..a4551358df8 100644 --- a/cmd/execution_builder.go +++ b/cmd/execution_builder.go @@ -868,7 +868,7 @@ func (exeNode *ExecutionNode) LoadIngestionEngine( } fetcher := fetcher.NewCollectionFetcher(node.Logger, exeNode.collectionRequester, node.State, exeNode.exeConf.onflowOnlyLNs) - loader := loader.NewLoader(node.Logger, node.State, node.Storage.Headers, exeNode.executionState) + loader := loader.NewUnexecutedLoader(node.Logger, node.State, node.Storage.Headers, exeNode.executionState) exeNode.ingestionEng, err = ingestion.New( exeNode.ingestionUnit, diff --git a/engine/execution/ingestion/engine_test.go b/engine/execution/ingestion/engine_test.go index 7edd78a8351..c458d2783ba 100644 --- a/engine/execution/ingestion/engine_test.go +++ b/engine/execution/ingestion/engine_test.go @@ -112,7 +112,7 @@ func runWithEngine(t *testing.T, f func(testingContext)) { uploadMgr := uploader.NewManager(trace.NewNoopTracer()) fetcher := mocks.NewMockFetcher() - loader := loader.NewLoader(log, protocolState, headers, executionState) + loader := loader.NewUnexecutedLoader(log, protocolState, headers, executionState) engine, err = New( unit, diff --git a/engine/execution/ingestion/loader/loader.go b/engine/execution/ingestion/loader/unexecuted_loader.go similarity index 92% rename from engine/execution/ingestion/loader/loader.go rename to engine/execution/ingestion/loader/unexecuted_loader.go index 7d34d7ea666..d09c876c7e6 100644 --- a/engine/execution/ingestion/loader/loader.go +++ b/engine/execution/ingestion/loader/unexecuted_loader.go @@ -13,20 +13,20 @@ import ( "github.com/onflow/flow-go/utils/logging" ) -type Loader struct { +type UnexecutedLoader struct { log zerolog.Logger state protocol.State headers storage.Headers // see comments on getHeaderByHeight for why we need it execState state.ExecutionState } -func NewLoader( +func NewUnexecutedLoader( log zerolog.Logger, state protocol.State, headers storage.Headers, execState state.ExecutionState, -) *Loader { - return &Loader{ +) *UnexecutedLoader { + return &UnexecutedLoader{ log: log.With().Str("component", "ingestion_engine_block_loader").Logger(), state: state, headers: headers, @@ -34,7 +34,7 @@ func NewLoader( } } -func (e *Loader) LoadUnexecuted(ctx context.Context) ([]flow.Identifier, error) { +func (e *UnexecutedLoader) LoadUnexecuted(ctx context.Context) ([]flow.Identifier, error) { // saving an executed block is currently not transactional, so it's possible // the block is marked as executed but the receipt might not be saved during a crash. // in order to mitigate this problem, we always re-execute the last executed and finalized @@ -104,7 +104,7 @@ func (e *Loader) LoadUnexecuted(ctx context.Context) ([]flow.Identifier, error) return blockIDs, nil } -func (e *Loader) unexecutedBlocks(ctx context.Context) ( +func (e *UnexecutedLoader) unexecutedBlocks(ctx context.Context) ( finalized []flow.Identifier, pending []flow.Identifier, err error, @@ -126,7 +126,7 @@ func (e *Loader) unexecutedBlocks(ctx context.Context) ( return finalized, pending, nil } -func (e *Loader) finalizedUnexecutedBlocks(ctx context.Context, finalized protocol.Snapshot) ( +func (e *UnexecutedLoader) finalizedUnexecutedBlocks(ctx context.Context, finalized protocol.Snapshot) ( []flow.Identifier, error, ) { @@ -196,7 +196,7 @@ func (e *Loader) finalizedUnexecutedBlocks(ctx context.Context, finalized protoc return unexecuted, nil } -func (e *Loader) pendingUnexecutedBlocks(ctx context.Context, finalized protocol.Snapshot) ( +func (e *UnexecutedLoader) pendingUnexecutedBlocks(ctx context.Context, finalized protocol.Snapshot) ( []flow.Identifier, error, ) { @@ -224,7 +224,7 @@ func (e *Loader) pendingUnexecutedBlocks(ctx context.Context, finalized protocol // if the EN is dynamically bootstrapped, the finalized blocks at height range: // [ sealedRoot.Height, finalizedRoot.Height - 1] can not be retrieved from // protocol state, but only from headers -func (e *Loader) getHeaderByHeight(height uint64) (*flow.Header, error) { +func (e *UnexecutedLoader) getHeaderByHeight(height uint64) (*flow.Header, error) { // we don't use protocol state because for dynamic boostrapped execution node // the last executed and sealed block is below the finalized root block return e.headers.ByHeight(height) diff --git a/engine/execution/ingestion/loader/loader_test.go b/engine/execution/ingestion/loader/unexecuted_loader_test.go similarity index 100% rename from engine/execution/ingestion/loader/loader_test.go rename to engine/execution/ingestion/loader/unexecuted_loader_test.go diff --git a/engine/execution/ingestion/loader/unfinalized_loader.go b/engine/execution/ingestion/loader/unfinalized_loader.go new file mode 100644 index 00000000000..c8d22d28471 --- /dev/null +++ b/engine/execution/ingestion/loader/unfinalized_loader.go @@ -0,0 +1,88 @@ +package loader + +import ( + "context" + "fmt" + + "github.com/rs/zerolog" + + "github.com/onflow/flow-go/engine/execution/state" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/storage" +) + +type UnfinalizedLoader struct { + log zerolog.Logger + state protocol.State + headers storage.Headers // see comments on getHeaderByHeight for why we need it + execState state.ExecutionState +} + +func NewUnfinalizedLoader( + log zerolog.Logger, + state protocol.State, + headers storage.Headers, + execState state.ExecutionState, +) *UnfinalizedLoader { + return &UnfinalizedLoader{ + log: log.With().Str("component", "ingestion_engine_block_loader").Logger(), + state: state, + headers: headers, + execState: execState, + } +} + +func (e *UnfinalizedLoader) LoadUnexecuted(ctx context.Context) ([]flow.Identifier, error) { + lastExecuted := e.execState.GetHighestFinalizedExecuted() + + // get finalized height + finalized := e.state.Final() + final, err := finalized.Head() + if err != nil { + return nil, fmt.Errorf("could not get finalized block: %w", err) + } + + // TODO: dynamically bootstrapped execution node will reload blocks from + unexecutedFinalized := make([]flow.Identifier, 0) + + // starting from the first unexecuted block, go through each unexecuted and finalized block + // reload its block to execution queues + // loading finalized blocks + for height := lastExecuted + 1; height <= final.Height; height++ { + header, err := e.getHeaderByHeight(height) + if err != nil { + return nil, fmt.Errorf("could not get header at height: %v, %w", height, err) + } + + unexecutedFinalized = append(unexecutedFinalized, header.ID()) + } + + // loaded all pending blocks + pendings, err := finalized.Descendants() + if err != nil { + return nil, fmt.Errorf("could not get descendants of finalized block: %w", err) + } + + unexecuted := append(unexecutedFinalized, pendings...) + + e.log.Info(). + Uint64("last_finalized", final.Height). + Uint64("last_finalized_executed", lastExecuted). + // Uint64("sealed_root_height", rootBlock.Height). + // Hex("sealed_root_id", logging.Entity(rootBlock)). + Int("total_finalized_unexecuted", len(unexecutedFinalized)). + Int("total_unexecuted", len(unexecuted)). + Msgf("finalized unexecuted blocks") + + return unexecuted, nil +} + +// if the EN is dynamically bootstrapped, the finalized blocks at height range: +// [ sealedRoot.Height, finalizedRoot.Height - 1] can not be retrieved from +// protocol state, but only from headers +func (e *UnfinalizedLoader) getHeaderByHeight(height uint64) (*flow.Header, error) { + // we don't use protocol state because for dynamic boostrapped execution node + // the last executed and sealed block is below the finalized root block + return e.headers.ByHeight(height) +} diff --git a/engine/testutil/nodes.go b/engine/testutil/nodes.go index c25f0e04a24..7ff2faf0a20 100644 --- a/engine/testutil/nodes.go +++ b/engine/testutil/nodes.go @@ -712,7 +712,7 @@ func ExecutionNode(t *testing.T, hub *stub.Hub, identity *flow.Identity, identit ) fetcher := exeFetcher.NewCollectionFetcher(node.Log, requestEngine, node.State, false) - loader := loader.NewLoader(node.Log, node.State, node.Headers, execState) + loader := loader.NewUnexecutedLoader(node.Log, node.State, node.Headers, execState) rootHead, rootQC := getRoot(t, &node) ingestionEngine, err := ingestion.New( unit, From 03fd33c012d7544c6d85c0c2f60cf4535f1f780b Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Thu, 16 Nov 2023 11:49:42 -0800 Subject: [PATCH 2/5] add loaders --- .../loader/unexecuted_loader_test.go | 16 +++--- .../ingestion/loader/unfinalized_loader.go | 4 +- .../loader/unfinalized_loader_test.go | 52 +++++++++++++++++++ .../state/mock/finalized_execution_state.go | 39 ++++++++++++++ engine/execution/state/state.go | 5 ++ 5 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 engine/execution/ingestion/loader/unfinalized_loader_test.go create mode 100644 engine/execution/state/mock/finalized_execution_state.go diff --git a/engine/execution/ingestion/loader/unexecuted_loader_test.go b/engine/execution/ingestion/loader/unexecuted_loader_test.go index 5b61d155e8c..571412d02c0 100644 --- a/engine/execution/ingestion/loader/unexecuted_loader_test.go +++ b/engine/execution/ingestion/loader/unexecuted_loader_test.go @@ -20,7 +20,7 @@ import ( "github.com/onflow/flow-go/utils/unittest/mocks" ) -var _ ingestion.BlockLoader = (*loader.Loader)(nil) +var _ ingestion.BlockLoader = (*loader.UnexecutedLoader)(nil) // ExecutionState is a mocked version of execution state that // simulates some of its behavior for testing purpose @@ -93,7 +93,7 @@ func TestLoadingUnexecutedBlocks(t *testing.T) { headers := storage.NewMockHeaders(ctrl) headers.EXPECT().ByBlockID(genesis.ID()).Return(genesis.Header, nil) log := unittest.Logger() - loader := loader.NewLoader(log, ps, headers, es) + loader := loader.NewUnexecutedLoader(log, ps, headers, es) unexecuted, err := loader.LoadUnexecuted(context.Background()) require.NoError(t, err) @@ -121,7 +121,7 @@ func TestLoadingUnexecutedBlocks(t *testing.T) { headers := storage.NewMockHeaders(ctrl) headers.EXPECT().ByBlockID(genesis.ID()).Return(genesis.Header, nil) log := unittest.Logger() - loader := loader.NewLoader(log, ps, headers, es) + loader := loader.NewUnexecutedLoader(log, ps, headers, es) unexecuted, err := loader.LoadUnexecuted(context.Background()) require.NoError(t, err) @@ -149,7 +149,7 @@ func TestLoadingUnexecutedBlocks(t *testing.T) { headers := storage.NewMockHeaders(ctrl) headers.EXPECT().ByBlockID(genesis.ID()).Return(genesis.Header, nil) log := unittest.Logger() - loader := loader.NewLoader(log, ps, headers, es) + loader := loader.NewUnexecutedLoader(log, ps, headers, es) es.ExecuteBlock(t, blockA) es.ExecuteBlock(t, blockB) @@ -182,7 +182,7 @@ func TestLoadingUnexecutedBlocks(t *testing.T) { headers := storage.NewMockHeaders(ctrl) headers.EXPECT().ByBlockID(genesis.ID()).Return(genesis.Header, nil) log := unittest.Logger() - loader := loader.NewLoader(log, ps, headers, es) + loader := loader.NewUnexecutedLoader(log, ps, headers, es) // block C is the only finalized block, index its header by its height headers.EXPECT().ByHeight(blockC.Header.Height).Return(blockC.Header, nil) @@ -219,7 +219,7 @@ func TestLoadingUnexecutedBlocks(t *testing.T) { headers := storage.NewMockHeaders(ctrl) headers.EXPECT().ByBlockID(genesis.ID()).Return(genesis.Header, nil) log := unittest.Logger() - loader := loader.NewLoader(log, ps, headers, es) + loader := loader.NewUnexecutedLoader(log, ps, headers, es) // block C is finalized, index its header by its height headers.EXPECT().ByHeight(blockC.Header.Height).Return(blockC.Header, nil) @@ -255,7 +255,7 @@ func TestLoadingUnexecutedBlocks(t *testing.T) { headers := storage.NewMockHeaders(ctrl) headers.EXPECT().ByBlockID(genesis.ID()).Return(genesis.Header, nil) log := unittest.Logger() - loader := loader.NewLoader(log, ps, headers, es) + loader := loader.NewUnexecutedLoader(log, ps, headers, es) // block A is finalized, index its header by its height headers.EXPECT().ByHeight(blockA.Header.Height).Return(blockA.Header, nil) @@ -316,7 +316,7 @@ func TestLoadingUnexecutedBlocks(t *testing.T) { headers := storage.NewMockHeaders(ctrl) headers.EXPECT().ByBlockID(genesis.ID()).Return(genesis.Header, nil) log := unittest.Logger() - loader := loader.NewLoader(log, ps, headers, es) + loader := loader.NewUnexecutedLoader(log, ps, headers, es) // block C is finalized, index its header by its height headers.EXPECT().ByHeight(blockC.Header.Height).Return(blockC.Header, nil) diff --git a/engine/execution/ingestion/loader/unfinalized_loader.go b/engine/execution/ingestion/loader/unfinalized_loader.go index c8d22d28471..68c4ade5491 100644 --- a/engine/execution/ingestion/loader/unfinalized_loader.go +++ b/engine/execution/ingestion/loader/unfinalized_loader.go @@ -16,14 +16,14 @@ type UnfinalizedLoader struct { log zerolog.Logger state protocol.State headers storage.Headers // see comments on getHeaderByHeight for why we need it - execState state.ExecutionState + execState state.FinalizedExecutionState } func NewUnfinalizedLoader( log zerolog.Logger, state protocol.State, headers storage.Headers, - execState state.ExecutionState, + execState state.FinalizedExecutionState, ) *UnfinalizedLoader { return &UnfinalizedLoader{ log: log.With().Str("component", "ingestion_engine_block_loader").Logger(), diff --git a/engine/execution/ingestion/loader/unfinalized_loader_test.go b/engine/execution/ingestion/loader/unfinalized_loader_test.go new file mode 100644 index 00000000000..3612f4a9ab0 --- /dev/null +++ b/engine/execution/ingestion/loader/unfinalized_loader_test.go @@ -0,0 +1,52 @@ +package loader_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/engine/execution/ingestion" + "github.com/onflow/flow-go/engine/execution/ingestion/loader" + stateMock "github.com/onflow/flow-go/engine/execution/state/mock" + "github.com/onflow/flow-go/model/flow" + storage "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/unittest" + "github.com/onflow/flow-go/utils/unittest/mocks" +) + +var _ ingestion.BlockLoader = (*loader.UnfinalizedLoader)(nil) + +func TestLoadingUnfinalizedBlocks(t *testing.T) { + ps := mocks.NewProtocolState() + + // Genesis <- A <- B <- C (finalized) + chain, result, seal := unittest.ChainFixture(4) + genesis, blockA, blockB, blockC := chain[0], chain[1], chain[2], chain[3] + + logChain(chain) + + require.NoError(t, ps.Bootstrap(genesis, result, seal)) + require.NoError(t, ps.Extend(blockA)) + require.NoError(t, ps.Extend(blockB)) + require.NoError(t, ps.Extend(blockC)) + require.NoError(t, ps.Finalize(blockC.ID())) + + es := new(stateMock.FinalizedExecutionState) + es.On("GetHighestFinalizedExecuted").Return(genesis.Header.Height) + headers := new(storage.Headers) + headers.On("ByHeight", blockA.Header.Height).Return(blockA.Header, nil) + headers.On("ByHeight", blockB.Header.Height).Return(blockB.Header, nil) + headers.On("ByHeight", blockC.Header.Height).Return(blockC.Header, nil) + + loader := loader.NewUnfinalizedLoader(unittest.Logger(), ps, headers, es) + + unexecuted, err := loader.LoadUnexecuted(context.Background()) + require.NoError(t, err) + + unittest.IDsEqual(t, []flow.Identifier{ + blockA.ID(), + blockB.ID(), + blockC.ID(), + }, unexecuted) +} diff --git a/engine/execution/state/mock/finalized_execution_state.go b/engine/execution/state/mock/finalized_execution_state.go new file mode 100644 index 00000000000..ae878be58e0 --- /dev/null +++ b/engine/execution/state/mock/finalized_execution_state.go @@ -0,0 +1,39 @@ +// Code generated by mockery v2.21.4. DO NOT EDIT. + +package mock + +import mock "github.com/stretchr/testify/mock" + +// FinalizedExecutionState is an autogenerated mock type for the FinalizedExecutionState type +type FinalizedExecutionState struct { + mock.Mock +} + +// GetHighestFinalizedExecuted provides a mock function with given fields: +func (_m *FinalizedExecutionState) GetHighestFinalizedExecuted() uint64 { + ret := _m.Called() + + var r0 uint64 + if rf, ok := ret.Get(0).(func() uint64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(uint64) + } + + return r0 +} + +type mockConstructorTestingTNewFinalizedExecutionState interface { + mock.TestingT + Cleanup(func()) +} + +// NewFinalizedExecutionState creates a new instance of FinalizedExecutionState. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewFinalizedExecutionState(t mockConstructorTestingTNewFinalizedExecutionState) *FinalizedExecutionState { + mock := &FinalizedExecutionState{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/engine/execution/state/state.go b/engine/execution/state/state.go index 3d996475ff9..b86a36bb015 100644 --- a/engine/execution/state/state.go +++ b/engine/execution/state/state.go @@ -46,6 +46,11 @@ type ScriptExecutionState interface { HasState(flow.StateCommitment) bool } +// FinalizedExecutionState is an interface used to access the finalized execution state +type FinalizedExecutionState interface { + GetHighestFinalizedExecuted() uint64 +} + // TODO Many operations here are should be transactional, so we need to refactor this // to store a reference to DB and compose operations and procedures rather then // just being amalgamate of proxies for single transactions operation From 17d0b61770e4ac90eba6905c8a6466d2142fe766 Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 17 Nov 2023 08:55:25 -0800 Subject: [PATCH 3/5] add comments update log --- engine/execution/ingestion/loader/unexecuted_loader.go | 3 ++- engine/execution/ingestion/loader/unfinalized_loader.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/execution/ingestion/loader/unexecuted_loader.go b/engine/execution/ingestion/loader/unexecuted_loader.go index d09c876c7e6..91029d2cb49 100644 --- a/engine/execution/ingestion/loader/unexecuted_loader.go +++ b/engine/execution/ingestion/loader/unexecuted_loader.go @@ -13,6 +13,7 @@ import ( "github.com/onflow/flow-go/utils/logging" ) +// deprecated. Storehouse is going to use unfinalized loader instead type UnexecutedLoader struct { log zerolog.Logger state protocol.State @@ -27,7 +28,7 @@ func NewUnexecutedLoader( execState state.ExecutionState, ) *UnexecutedLoader { return &UnexecutedLoader{ - log: log.With().Str("component", "ingestion_engine_block_loader").Logger(), + log: log.With().Str("component", "ingestion_engine_unexecuted_loader").Logger(), state: state, headers: headers, execState: execState, diff --git a/engine/execution/ingestion/loader/unfinalized_loader.go b/engine/execution/ingestion/loader/unfinalized_loader.go index 68c4ade5491..5fbc15f7c2a 100644 --- a/engine/execution/ingestion/loader/unfinalized_loader.go +++ b/engine/execution/ingestion/loader/unfinalized_loader.go @@ -26,7 +26,7 @@ func NewUnfinalizedLoader( execState state.FinalizedExecutionState, ) *UnfinalizedLoader { return &UnfinalizedLoader{ - log: log.With().Str("component", "ingestion_engine_block_loader").Logger(), + log: log.With().Str("component", "ingestion_engine_unfinalized_loader").Logger(), state: state, headers: headers, execState: execState, From ab72defd4d261bb5d77e0500527f37cd847dab1e Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 17 Nov 2023 13:54:33 -0800 Subject: [PATCH 4/5] add comments --- engine/execution/ingestion/loader/unexecuted_loader.go | 2 ++ engine/execution/ingestion/loader/unfinalized_loader.go | 3 +++ 2 files changed, 5 insertions(+) diff --git a/engine/execution/ingestion/loader/unexecuted_loader.go b/engine/execution/ingestion/loader/unexecuted_loader.go index 91029d2cb49..8c91aab8083 100644 --- a/engine/execution/ingestion/loader/unexecuted_loader.go +++ b/engine/execution/ingestion/loader/unexecuted_loader.go @@ -35,6 +35,8 @@ func NewUnexecutedLoader( } } +// LoadUnexecuted loads all unexecuted and validated blocks +// any error returned are exceptions func (e *UnexecutedLoader) LoadUnexecuted(ctx context.Context) ([]flow.Identifier, error) { // saving an executed block is currently not transactional, so it's possible // the block is marked as executed but the receipt might not be saved during a crash. diff --git a/engine/execution/ingestion/loader/unfinalized_loader.go b/engine/execution/ingestion/loader/unfinalized_loader.go index 5fbc15f7c2a..bcfc699074a 100644 --- a/engine/execution/ingestion/loader/unfinalized_loader.go +++ b/engine/execution/ingestion/loader/unfinalized_loader.go @@ -19,6 +19,7 @@ type UnfinalizedLoader struct { execState state.FinalizedExecutionState } +// NewUnfinalizedLoader creates a new loader that loads all unfinalized and validated blocks func NewUnfinalizedLoader( log zerolog.Logger, state protocol.State, @@ -33,6 +34,8 @@ func NewUnfinalizedLoader( } } +// LoadUnexecuted loads all unfinalized and validated blocks +// any error returned are exceptions func (e *UnfinalizedLoader) LoadUnexecuted(ctx context.Context) ([]flow.Identifier, error) { lastExecuted := e.execState.GetHighestFinalizedExecuted() From 4685e19797f9478ac6857a284beea1bcefe8952c Mon Sep 17 00:00:00 2001 From: "Leo Zhang (zhangchiqing)" Date: Fri, 17 Nov 2023 13:56:21 -0800 Subject: [PATCH 5/5] update loader --- .../ingestion/loader/unfinalized_loader_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/engine/execution/ingestion/loader/unfinalized_loader_test.go b/engine/execution/ingestion/loader/unfinalized_loader_test.go index 3612f4a9ab0..3c8b84aed40 100644 --- a/engine/execution/ingestion/loader/unfinalized_loader_test.go +++ b/engine/execution/ingestion/loader/unfinalized_loader_test.go @@ -20,9 +20,10 @@ var _ ingestion.BlockLoader = (*loader.UnfinalizedLoader)(nil) func TestLoadingUnfinalizedBlocks(t *testing.T) { ps := mocks.NewProtocolState() - // Genesis <- A <- B <- C (finalized) - chain, result, seal := unittest.ChainFixture(4) - genesis, blockA, blockB, blockC := chain[0], chain[1], chain[2], chain[3] + // Genesis <- A <- B <- C (finalized) <- D + chain, result, seal := unittest.ChainFixture(5) + genesis, blockA, blockB, blockC, blockD := + chain[0], chain[1], chain[2], chain[3], chain[4] logChain(chain) @@ -30,6 +31,7 @@ func TestLoadingUnfinalizedBlocks(t *testing.T) { require.NoError(t, ps.Extend(blockA)) require.NoError(t, ps.Extend(blockB)) require.NoError(t, ps.Extend(blockC)) + require.NoError(t, ps.Extend(blockD)) require.NoError(t, ps.Finalize(blockC.ID())) es := new(stateMock.FinalizedExecutionState) @@ -48,5 +50,6 @@ func TestLoadingUnfinalizedBlocks(t *testing.T) { blockA.ID(), blockB.ID(), blockC.ID(), + blockD.ID(), }, unexecuted) }