diff --git a/fvm/environment/block_info.go b/fvm/environment/block_info.go index 928859c21f2..ce803d22479 100644 --- a/fvm/environment/block_info.go +++ b/fvm/environment/block_info.go @@ -143,7 +143,7 @@ func (info *blockInfo) GetBlockAtHeight( } if height+uint64(flow.DefaultTransactionExpiry) < info.blockHeader.Height { - return runtime.Block{}, false, errors.NewBlockHeightOutOfRangeError(height) + return runtime.Block{}, false, nil } header, err := info.blocks.ByHeightFrom(height, info.blockHeader) diff --git a/fvm/environment/block_info_test.go b/fvm/environment/block_info_test.go new file mode 100644 index 00000000000..bc38cd2055a --- /dev/null +++ b/fvm/environment/block_info_test.go @@ -0,0 +1,69 @@ +package environment_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/cmd/util/ledger/util" + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/fvm/tracing" + "github.com/onflow/flow-go/model/flow" + storageErr "github.com/onflow/flow-go/storage" + "github.com/onflow/flow-go/utils/unittest" +) + +func TestBlockInfo(t *testing.T) { + tracer := tracing.NewMockTracerSpan() + meter := &util.NopMeter{} + blocks := &mockBlocks{ + blocks: make(map[uint64]*flow.Header), + } + height := uint64(flow.DefaultTransactionExpiry) + header := unittest.BlockHeaderWithHeight(height) + + bi := environment.NewBlockInfo(tracer, meter, header, blocks) + + // verify the current block exists + blocks.Add(header) + b, exists, err := bi.GetBlockAtHeight(height) + require.NoError(t, err) + require.True(t, exists) + require.Equal(t, header.Height, b.Height) + + // verify blocks that do not exist + b, exists, err = bi.GetBlockAtHeight(height + 1) + require.NoError(t, err) + require.False(t, exists) + + // verify that the block at the height before the lowest accepted height exists + lowestAcceptedHeight := height - flow.DefaultTransactionExpiry + lowestHeader := unittest.BlockHeaderWithHeight(lowestAcceptedHeight) + blocks.Add(lowestHeader) + b, exists, err = bi.GetBlockAtHeight(lowestAcceptedHeight) + require.NoError(t, err) + require.True(t, exists) + require.Equal(t, lowestHeader.Height, b.Height) + + // verify that the block at the height before the lowest accepted height does not exist + _, exists, err = bi.GetBlockAtHeight(lowestAcceptedHeight - 1) + require.NoError(t, err) + require.False(t, exists) +} + +type mockBlocks struct { + blocks map[uint64]*flow.Header +} + +func (m *mockBlocks) ByHeightFrom(height uint64, header *flow.Header) (*flow.Header, error) { + h, ok := m.blocks[height] + if !ok { + return nil, fmt.Errorf("block does not exist: %w", storageErr.ErrNotFound) + } + return h, nil +} + +func (m *mockBlocks) Add(h *flow.Header) { + m.blocks[h.Height] = h +} diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 18716549e8a..ca48c8aca29 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -532,6 +532,10 @@ func BlockHeaderWithParentFixture(parent *flow.Header) *flow.Header { } } +func BlockHeaderWithHeight(height uint64) *flow.Header { + return BlockHeaderFixture(WithHeaderHeight(height)) +} + func BlockHeaderWithParentWithSoRFixture(parent *flow.Header, source []byte) *flow.Header { height := parent.Height + 1 view := parent.View + 1 + uint64(rand.Intn(10)) // Intn returns [0, n)