github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/state_synchronization/indexer/indexer_core_test.go (about)

     1  package indexer
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/rs/zerolog"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/mock"
    14  	mocks "github.com/stretchr/testify/mock"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/onflow/flow-go/fvm/storage/derived"
    18  	"github.com/onflow/flow-go/ledger"
    19  	"github.com/onflow/flow-go/ledger/common/convert"
    20  	"github.com/onflow/flow-go/ledger/common/pathfinder"
    21  	"github.com/onflow/flow-go/ledger/complete"
    22  	"github.com/onflow/flow-go/model/flow"
    23  	"github.com/onflow/flow-go/module/executiondatasync/execution_data"
    24  	"github.com/onflow/flow-go/module/mempool/stdmap"
    25  	"github.com/onflow/flow-go/module/metrics"
    26  	synctest "github.com/onflow/flow-go/module/state_synchronization/requester/unittest"
    27  	"github.com/onflow/flow-go/storage"
    28  	storagemock "github.com/onflow/flow-go/storage/mock"
    29  	pebbleStorage "github.com/onflow/flow-go/storage/pebble"
    30  	"github.com/onflow/flow-go/utils/unittest"
    31  )
    32  
    33  type indexCoreTest struct {
    34  	t                *testing.T
    35  	indexer          *IndexerCore
    36  	registers        *storagemock.RegisterIndex
    37  	events           *storagemock.Events
    38  	collection       *flow.Collection
    39  	collections      *storagemock.Collections
    40  	transactions     *storagemock.Transactions
    41  	results          *storagemock.LightTransactionResults
    42  	headers          *storagemock.Headers
    43  	ctx              context.Context
    44  	blocks           []*flow.Block
    45  	data             *execution_data.BlockExecutionDataEntity
    46  	lastHeightStore  func(t *testing.T) uint64
    47  	firstHeightStore func(t *testing.T) uint64
    48  	registersStore   func(t *testing.T, entries flow.RegisterEntries, height uint64) error
    49  	eventsStore      func(t *testing.T, ID flow.Identifier, events []flow.EventsList) error
    50  	registersGet     func(t *testing.T, IDs flow.RegisterID, height uint64) (flow.RegisterValue, error)
    51  }
    52  
    53  func newIndexCoreTest(
    54  	t *testing.T,
    55  	blocks []*flow.Block,
    56  	exeData *execution_data.BlockExecutionDataEntity,
    57  ) *indexCoreTest {
    58  
    59  	collection := unittest.CollectionFixture(0)
    60  
    61  	return &indexCoreTest{
    62  		t:            t,
    63  		registers:    storagemock.NewRegisterIndex(t),
    64  		events:       storagemock.NewEvents(t),
    65  		collection:   &collection,
    66  		results:      storagemock.NewLightTransactionResults(t),
    67  		collections:  storagemock.NewCollections(t),
    68  		transactions: storagemock.NewTransactions(t),
    69  		blocks:       blocks,
    70  		ctx:          context.Background(),
    71  		data:         exeData,
    72  		headers:      newBlockHeadersStorage(blocks).(*storagemock.Headers), // convert it back to mock type for tests,
    73  	}
    74  }
    75  
    76  func (i *indexCoreTest) useDefaultBlockByHeight() *indexCoreTest {
    77  	i.headers.
    78  		On("BlockIDByHeight", mocks.AnythingOfType("uint64")).
    79  		Return(func(height uint64) (flow.Identifier, error) {
    80  			for _, b := range i.blocks {
    81  				if b.Header.Height == height {
    82  					return b.ID(), nil
    83  				}
    84  			}
    85  			return flow.ZeroID, fmt.Errorf("not found")
    86  		})
    87  
    88  	i.headers.
    89  		On("ByHeight", mocks.AnythingOfType("uint64")).
    90  		Return(func(height uint64) (*flow.Header, error) {
    91  			for _, b := range i.blocks {
    92  				if b.Header.Height == height {
    93  					return b.Header, nil
    94  				}
    95  			}
    96  			return nil, fmt.Errorf("not found")
    97  		})
    98  
    99  	return i
   100  }
   101  
   102  func (i *indexCoreTest) setLastHeight(f func(t *testing.T) uint64) *indexCoreTest {
   103  	i.registers.
   104  		On("LatestHeight").
   105  		Return(func() uint64 {
   106  			return f(i.t)
   107  		})
   108  	return i
   109  }
   110  
   111  func (i *indexCoreTest) useDefaultHeights() *indexCoreTest {
   112  	i.registers.
   113  		On("FirstHeight").
   114  		Return(func() uint64 {
   115  			return i.blocks[0].Header.Height
   116  		})
   117  	i.registers.
   118  		On("LatestHeight").
   119  		Return(func() uint64 {
   120  			return i.blocks[len(i.blocks)-1].Header.Height
   121  		})
   122  	return i
   123  }
   124  
   125  func (i *indexCoreTest) setStoreRegisters(f func(t *testing.T, entries flow.RegisterEntries, height uint64) error) *indexCoreTest {
   126  	i.registers.
   127  		On("Store", mock.AnythingOfType("flow.RegisterEntries"), mock.AnythingOfType("uint64")).
   128  		Return(func(entries flow.RegisterEntries, height uint64) error {
   129  			return f(i.t, entries, height)
   130  		}).Once()
   131  	return i
   132  }
   133  
   134  func (i *indexCoreTest) setStoreEvents(f func(*testing.T, flow.Identifier, []flow.EventsList) error) *indexCoreTest {
   135  	i.events.
   136  		On("BatchStore", mock.AnythingOfType("flow.Identifier"), mock.AnythingOfType("[]flow.EventsList"), mock.Anything).
   137  		Return(func(blockID flow.Identifier, events []flow.EventsList, batch storage.BatchStorage) error {
   138  			require.NotNil(i.t, batch)
   139  			return f(i.t, blockID, events)
   140  		})
   141  	return i
   142  }
   143  
   144  func (i *indexCoreTest) setStoreTransactionResults(f func(*testing.T, flow.Identifier, []flow.LightTransactionResult) error) *indexCoreTest {
   145  	i.results.
   146  		On("BatchStore", mock.AnythingOfType("flow.Identifier"), mock.AnythingOfType("[]flow.LightTransactionResult"), mock.Anything).
   147  		Return(func(blockID flow.Identifier, results []flow.LightTransactionResult, batch storage.BatchStorage) error {
   148  			require.NotNil(i.t, batch)
   149  			return f(i.t, blockID, results)
   150  		})
   151  	return i
   152  }
   153  
   154  func (i *indexCoreTest) setGetRegisters(f func(t *testing.T, ID flow.RegisterID, height uint64) (flow.RegisterValue, error)) *indexCoreTest {
   155  	i.registers.
   156  		On("Get", mock.AnythingOfType("flow.RegisterID"), mock.AnythingOfType("uint64")).
   157  		Return(func(IDs flow.RegisterID, height uint64) (flow.RegisterValue, error) {
   158  			return f(i.t, IDs, height)
   159  		})
   160  	return i
   161  }
   162  
   163  func (i *indexCoreTest) useDefaultStorageMocks() *indexCoreTest {
   164  
   165  	i.collections.On("StoreLightAndIndexByTransaction", mock.AnythingOfType("*flow.LightCollection")).Return(nil).Maybe()
   166  	i.transactions.On("Store", mock.AnythingOfType("*flow.TransactionBody")).Return(nil).Maybe()
   167  
   168  	return i
   169  }
   170  
   171  func (i *indexCoreTest) useDefaultEvents() *indexCoreTest {
   172  	i.events.
   173  		On("BatchStore", mock.AnythingOfType("flow.Identifier"), mock.AnythingOfType("[]flow.EventsList"), mock.Anything).
   174  		Return(nil)
   175  	return i
   176  }
   177  
   178  func (i *indexCoreTest) useDefaultTransactionResults() *indexCoreTest {
   179  	i.results.
   180  		On("BatchStore", mock.AnythingOfType("flow.Identifier"), mock.AnythingOfType("[]flow.LightTransactionResult"), mock.Anything).
   181  		Return(nil)
   182  	return i
   183  }
   184  
   185  func (i *indexCoreTest) initIndexer() *indexCoreTest {
   186  	db, dbDir := unittest.TempBadgerDB(i.t)
   187  	i.t.Cleanup(func() {
   188  		require.NoError(i.t, db.Close())
   189  		require.NoError(i.t, os.RemoveAll(dbDir))
   190  	})
   191  
   192  	i.useDefaultHeights()
   193  
   194  	collectionsToMarkFinalized, err := stdmap.NewTimes(100)
   195  	require.NoError(i.t, err)
   196  	collectionsToMarkExecuted, err := stdmap.NewTimes(100)
   197  	require.NoError(i.t, err)
   198  	blocksToMarkExecuted, err := stdmap.NewTimes(100)
   199  	require.NoError(i.t, err)
   200  
   201  	log := zerolog.New(os.Stdout)
   202  	blocks := storagemock.NewBlocks(i.t)
   203  
   204  	collectionExecutedMetric, err := NewCollectionExecutedMetricImpl(
   205  		log,
   206  		metrics.NewNoopCollector(),
   207  		collectionsToMarkFinalized,
   208  		collectionsToMarkExecuted,
   209  		blocksToMarkExecuted,
   210  		i.collections,
   211  		blocks,
   212  	)
   213  	require.NoError(i.t, err)
   214  
   215  	derivedChainData, err := derived.NewDerivedChainData(derived.DefaultDerivedDataCacheSize)
   216  	require.NoError(i.t, err)
   217  
   218  	indexer, err := New(
   219  		log,
   220  		metrics.NewNoopCollector(),
   221  		db,
   222  		i.registers,
   223  		i.headers,
   224  		i.events,
   225  		i.collections,
   226  		i.transactions,
   227  		i.results,
   228  		flow.Testnet.Chain(),
   229  		derivedChainData,
   230  		collectionExecutedMetric,
   231  	)
   232  	require.NoError(i.t, err)
   233  	i.indexer = indexer
   234  	return i
   235  }
   236  
   237  func (i *indexCoreTest) runIndexBlockData() error {
   238  	i.initIndexer()
   239  	return i.indexer.IndexBlockData(i.data)
   240  }
   241  
   242  func (i *indexCoreTest) runGetRegister(ID flow.RegisterID, height uint64) (flow.RegisterValue, error) {
   243  	i.initIndexer()
   244  	return i.indexer.RegisterValue(ID, height)
   245  }
   246  
   247  func TestExecutionState_IndexBlockData(t *testing.T) {
   248  	blocks := unittest.BlockchainFixture(5)
   249  	block := blocks[len(blocks)-1]
   250  	collection := unittest.CollectionFixture(0)
   251  
   252  	// this test makes sure the index block data is correctly calling store register with the
   253  	// same entries we create as a block execution data test, and correctly converts the registers
   254  	t.Run("Index Single Chunk and Single Register", func(t *testing.T) {
   255  		trie := trieUpdateFixture(t)
   256  		ed := &execution_data.BlockExecutionData{
   257  			BlockID: block.ID(),
   258  			ChunkExecutionDatas: []*execution_data.ChunkExecutionData{
   259  				{
   260  					Collection: &collection,
   261  					TrieUpdate: trie,
   262  				},
   263  			},
   264  		}
   265  		execData := execution_data.NewBlockExecutionDataEntity(block.ID(), ed)
   266  
   267  		err := newIndexCoreTest(t, blocks, execData).
   268  			initIndexer().
   269  			useDefaultEvents().
   270  			useDefaultTransactionResults().
   271  			// make sure update registers match in length and are same as block data ledger payloads
   272  			setStoreRegisters(func(t *testing.T, entries flow.RegisterEntries, height uint64) error {
   273  				assert.Equal(t, height, block.Header.Height)
   274  				assert.Len(t, trie.Payloads, entries.Len())
   275  
   276  				// make sure all the registers from the execution data have been stored as well the value matches
   277  				trieRegistersPayloadComparer(t, trie.Payloads, entries)
   278  				return nil
   279  			}).
   280  			runIndexBlockData()
   281  
   282  		assert.NoError(t, err)
   283  	})
   284  
   285  	// this test makes sure that if we have multiple trie updates in a single block data
   286  	// and some of those trie updates are for same register but have different values,
   287  	// we only update that register once with the latest value, so this makes sure merging of
   288  	// registers is done correctly.
   289  	t.Run("Index Multiple Chunks and Merge Same Register Updates", func(t *testing.T) {
   290  		tries := []*ledger.TrieUpdate{trieUpdateFixture(t), trieUpdateFixture(t)}
   291  		// make sure we have two register updates that are updating the same value, so we can check
   292  		// if the value from the second update is being persisted instead of first
   293  		tries[1].Paths[0] = tries[0].Paths[0]
   294  		testValue := tries[1].Payloads[0]
   295  		key, err := testValue.Key()
   296  		require.NoError(t, err)
   297  		testRegisterID, err := convert.LedgerKeyToRegisterID(key)
   298  		require.NoError(t, err)
   299  
   300  		ed := &execution_data.BlockExecutionData{
   301  			BlockID: block.ID(),
   302  			ChunkExecutionDatas: []*execution_data.ChunkExecutionData{
   303  				{
   304  					Collection: &collection,
   305  					TrieUpdate: tries[0],
   306  				},
   307  				{
   308  					Collection: &collection,
   309  					TrieUpdate: tries[1],
   310  				},
   311  			},
   312  		}
   313  		execData := execution_data.NewBlockExecutionDataEntity(block.ID(), ed)
   314  
   315  		testRegisterFound := false
   316  		err = newIndexCoreTest(t, blocks, execData).
   317  			initIndexer().
   318  			useDefaultEvents().
   319  			useDefaultStorageMocks().
   320  			useDefaultTransactionResults().
   321  			// make sure update registers match in length and are same as block data ledger payloads
   322  			setStoreRegisters(func(t *testing.T, entries flow.RegisterEntries, height uint64) error {
   323  				for _, entry := range entries {
   324  					if entry.Key.String() == testRegisterID.String() {
   325  						testRegisterFound = true
   326  						assert.True(t, testValue.Value().Equals(entry.Value))
   327  					}
   328  				}
   329  				// we should make sure the register updates are equal to both payloads' length -1 since we don't
   330  				// duplicate the same register
   331  				assert.Equal(t, len(tries[0].Payloads)+len(tries[1].Payloads)-1, len(entries))
   332  				return nil
   333  			}).
   334  			runIndexBlockData()
   335  
   336  		assert.NoError(t, err)
   337  		assert.True(t, testRegisterFound)
   338  	})
   339  
   340  	t.Run("Index Events", func(t *testing.T) {
   341  		expectedEvents := unittest.EventsFixture(20)
   342  		ed := &execution_data.BlockExecutionData{
   343  			BlockID: block.ID(),
   344  			ChunkExecutionDatas: []*execution_data.ChunkExecutionData{
   345  				// split events into 2 chunks
   346  				{
   347  					Collection: &collection,
   348  					Events:     expectedEvents[:10],
   349  				},
   350  				{
   351  					Collection: &collection,
   352  					Events:     expectedEvents[10:],
   353  				},
   354  			},
   355  		}
   356  		execData := execution_data.NewBlockExecutionDataEntity(block.ID(), ed)
   357  
   358  		err := newIndexCoreTest(t, blocks, execData).
   359  			initIndexer().
   360  			useDefaultStorageMocks().
   361  			// make sure all events are stored at once in order
   362  			setStoreEvents(func(t *testing.T, actualBlockID flow.Identifier, actualEvents []flow.EventsList) error {
   363  				assert.Equal(t, block.ID(), actualBlockID)
   364  				require.Len(t, actualEvents, 1)
   365  				require.Len(t, actualEvents[0], len(expectedEvents))
   366  				for i, expected := range expectedEvents {
   367  					assert.Equal(t, expected, actualEvents[0][i])
   368  				}
   369  				return nil
   370  			}).
   371  			// make sure an empty set of transaction results were stored
   372  			setStoreTransactionResults(func(t *testing.T, actualBlockID flow.Identifier, actualResults []flow.LightTransactionResult) error {
   373  				assert.Equal(t, block.ID(), actualBlockID)
   374  				require.Len(t, actualResults, 0)
   375  				return nil
   376  			}).
   377  			// make sure an empty set of register entries was stored
   378  			setStoreRegisters(func(t *testing.T, entries flow.RegisterEntries, height uint64) error {
   379  				assert.Equal(t, height, block.Header.Height)
   380  				assert.Equal(t, 0, entries.Len())
   381  				return nil
   382  			}).
   383  			runIndexBlockData()
   384  
   385  		assert.NoError(t, err)
   386  	})
   387  
   388  	t.Run("Index Tx Results", func(t *testing.T) {
   389  		expectedResults := unittest.LightTransactionResultsFixture(20)
   390  		ed := &execution_data.BlockExecutionData{
   391  			BlockID: block.ID(),
   392  			ChunkExecutionDatas: []*execution_data.ChunkExecutionData{
   393  				// split events into 2 chunks
   394  				{
   395  					Collection:         &collection,
   396  					TransactionResults: expectedResults[:10],
   397  				},
   398  				{
   399  					Collection:         &collection,
   400  					TransactionResults: expectedResults[10:],
   401  				},
   402  			},
   403  		}
   404  		execData := execution_data.NewBlockExecutionDataEntity(block.ID(), ed)
   405  
   406  		err := newIndexCoreTest(t, blocks, execData).
   407  			initIndexer().
   408  			useDefaultStorageMocks().
   409  			// make sure an empty set of events were stored
   410  			setStoreEvents(func(t *testing.T, actualBlockID flow.Identifier, actualEvents []flow.EventsList) error {
   411  				assert.Equal(t, block.ID(), actualBlockID)
   412  				require.Len(t, actualEvents, 1)
   413  				require.Len(t, actualEvents[0], 0)
   414  				return nil
   415  			}).
   416  			// make sure all results are stored at once in order
   417  			setStoreTransactionResults(func(t *testing.T, actualBlockID flow.Identifier, actualResults []flow.LightTransactionResult) error {
   418  				assert.Equal(t, block.ID(), actualBlockID)
   419  				require.Len(t, actualResults, len(expectedResults))
   420  				for i, expected := range expectedResults {
   421  					assert.Equal(t, expected, actualResults[i])
   422  				}
   423  				return nil
   424  			}).
   425  			// make sure an empty set of register entries was stored
   426  			setStoreRegisters(func(t *testing.T, entries flow.RegisterEntries, height uint64) error {
   427  				assert.Equal(t, height, block.Header.Height)
   428  				assert.Equal(t, 0, entries.Len())
   429  				return nil
   430  			}).
   431  			runIndexBlockData()
   432  
   433  		assert.NoError(t, err)
   434  	})
   435  
   436  	t.Run("Index Collections", func(t *testing.T) {
   437  		expectedCollections := unittest.CollectionListFixture(2)
   438  		ed := &execution_data.BlockExecutionData{
   439  			BlockID: block.ID(),
   440  			ChunkExecutionDatas: []*execution_data.ChunkExecutionData{
   441  				{Collection: expectedCollections[0]},
   442  				{Collection: expectedCollections[1]},
   443  			},
   444  		}
   445  		execData := execution_data.NewBlockExecutionDataEntity(block.ID(), ed)
   446  		err := newIndexCoreTest(t, blocks, execData).
   447  			initIndexer().
   448  			useDefaultStorageMocks().
   449  			// make sure an empty set of events were stored
   450  			setStoreEvents(func(t *testing.T, actualBlockID flow.Identifier, actualEvents []flow.EventsList) error {
   451  				assert.Equal(t, block.ID(), actualBlockID)
   452  				require.Len(t, actualEvents, 1)
   453  				require.Len(t, actualEvents[0], 0)
   454  				return nil
   455  			}).
   456  			// make sure an empty set of transaction results were stored
   457  			setStoreTransactionResults(func(t *testing.T, actualBlockID flow.Identifier, actualResults []flow.LightTransactionResult) error {
   458  				assert.Equal(t, block.ID(), actualBlockID)
   459  				require.Len(t, actualResults, 0)
   460  				return nil
   461  			}).
   462  			// make sure an empty set of register entries was stored
   463  			setStoreRegisters(func(t *testing.T, entries flow.RegisterEntries, height uint64) error {
   464  				assert.Equal(t, height, block.Header.Height)
   465  				assert.Equal(t, 0, entries.Len())
   466  				return nil
   467  			}).
   468  			runIndexBlockData()
   469  
   470  		assert.NoError(t, err)
   471  	})
   472  
   473  	t.Run("Index AllTheThings", func(t *testing.T) {
   474  		expectedEvents := unittest.EventsFixture(20)
   475  		expectedResults := unittest.LightTransactionResultsFixture(20)
   476  		expectedCollections := unittest.CollectionListFixture(2)
   477  		expectedTries := []*ledger.TrieUpdate{trieUpdateFixture(t), trieUpdateFixture(t)}
   478  		expectedPayloads := make([]*ledger.Payload, 0)
   479  		for _, trie := range expectedTries {
   480  			expectedPayloads = append(expectedPayloads, trie.Payloads...)
   481  		}
   482  
   483  		ed := &execution_data.BlockExecutionData{
   484  			BlockID: block.ID(),
   485  			ChunkExecutionDatas: []*execution_data.ChunkExecutionData{
   486  				{
   487  					Collection:         expectedCollections[0],
   488  					Events:             expectedEvents[:10],
   489  					TransactionResults: expectedResults[:10],
   490  					TrieUpdate:         expectedTries[0],
   491  				},
   492  				{
   493  					Collection:         expectedCollections[1],
   494  					TransactionResults: expectedResults[10:],
   495  					Events:             expectedEvents[10:],
   496  					TrieUpdate:         expectedTries[1],
   497  				},
   498  			},
   499  		}
   500  		execData := execution_data.NewBlockExecutionDataEntity(block.ID(), ed)
   501  		err := newIndexCoreTest(t, blocks, execData).
   502  			initIndexer().
   503  			useDefaultStorageMocks().
   504  			// make sure all events are stored at once in order
   505  			setStoreEvents(func(t *testing.T, actualBlockID flow.Identifier, actualEvents []flow.EventsList) error {
   506  				assert.Equal(t, block.ID(), actualBlockID)
   507  				require.Len(t, actualEvents, 1)
   508  				require.Len(t, actualEvents[0], len(expectedEvents))
   509  				for i, expected := range expectedEvents {
   510  					assert.Equal(t, expected, actualEvents[0][i])
   511  				}
   512  				return nil
   513  			}).
   514  			// make sure all results are stored at once in order
   515  			setStoreTransactionResults(func(t *testing.T, actualBlockID flow.Identifier, actualResults []flow.LightTransactionResult) error {
   516  				assert.Equal(t, block.ID(), actualBlockID)
   517  				require.Len(t, actualResults, len(expectedResults))
   518  				for i, expected := range expectedResults {
   519  					assert.Equal(t, expected, actualResults[i])
   520  				}
   521  				return nil
   522  			}).
   523  			// make sure update registers match in length and are same as block data ledger payloads
   524  			setStoreRegisters(func(t *testing.T, entries flow.RegisterEntries, actualHeight uint64) error {
   525  				assert.Equal(t, actualHeight, block.Header.Height)
   526  				assert.Equal(t, entries.Len(), len(expectedPayloads))
   527  
   528  				// make sure all the registers from the execution data have been stored as well the value matches
   529  				trieRegistersPayloadComparer(t, expectedPayloads, entries)
   530  				return nil
   531  			}).
   532  			runIndexBlockData()
   533  
   534  		assert.NoError(t, err)
   535  	})
   536  
   537  	// this test makes sure we get correct error when we try to index block that is not
   538  	// within the range of indexed heights.
   539  	t.Run("Invalid Heights", func(t *testing.T) {
   540  		last := blocks[len(blocks)-1]
   541  		ed := &execution_data.BlockExecutionData{
   542  			BlockID: last.Header.ID(),
   543  		}
   544  		execData := execution_data.NewBlockExecutionDataEntity(last.ID(), ed)
   545  		latestHeight := blocks[len(blocks)-3].Header.Height
   546  
   547  		err := newIndexCoreTest(t, blocks, execData).
   548  			// return a height one smaller than the latest block in storage
   549  			setLastHeight(func(t *testing.T) uint64 {
   550  				return latestHeight
   551  			}).
   552  			runIndexBlockData()
   553  
   554  		assert.EqualError(t, err, fmt.Sprintf("must index block data with the next height %d, but got %d", latestHeight+1, last.Header.Height))
   555  	})
   556  
   557  	// this test makes sure that if a block we try to index is not found in block storage
   558  	// we get correct error.
   559  	t.Run("Unknown block ID", func(t *testing.T) {
   560  		unknownBlock := unittest.BlockFixture()
   561  		ed := &execution_data.BlockExecutionData{
   562  			BlockID: unknownBlock.Header.ID(),
   563  		}
   564  		execData := execution_data.NewBlockExecutionDataEntity(unknownBlock.Header.ID(), ed)
   565  
   566  		err := newIndexCoreTest(t, blocks, execData).runIndexBlockData()
   567  
   568  		assert.True(t, errors.Is(err, storage.ErrNotFound))
   569  	})
   570  
   571  }
   572  
   573  func TestExecutionState_RegisterValues(t *testing.T) {
   574  	t.Run("Get value for single register", func(t *testing.T) {
   575  		blocks := unittest.BlockchainFixture(5)
   576  		height := blocks[1].Header.Height
   577  		id := flow.RegisterID{
   578  			Owner: "1",
   579  			Key:   "2",
   580  		}
   581  		val := flow.RegisterValue("0x1")
   582  
   583  		values, err := newIndexCoreTest(t, blocks, nil).
   584  			initIndexer().
   585  			setGetRegisters(func(t *testing.T, ID flow.RegisterID, height uint64) (flow.RegisterValue, error) {
   586  				return val, nil
   587  			}).
   588  			runGetRegister(id, height)
   589  
   590  		assert.NoError(t, err)
   591  		assert.Equal(t, values, val)
   592  	})
   593  }
   594  
   595  func newBlockHeadersStorage(blocks []*flow.Block) storage.Headers {
   596  	blocksByID := make(map[flow.Identifier]*flow.Block, 0)
   597  	for _, b := range blocks {
   598  		blocksByID[b.ID()] = b
   599  	}
   600  
   601  	return synctest.MockBlockHeaderStorage(synctest.WithByID(blocksByID))
   602  }
   603  
   604  func trieUpdateWithPayloadsFixture(payloads []*ledger.Payload) *ledger.TrieUpdate {
   605  	keys := make([]ledger.Key, 0)
   606  	values := make([]ledger.Value, 0)
   607  	for _, payload := range payloads {
   608  		key, _ := payload.Key()
   609  		keys = append(keys, key)
   610  		values = append(values, payload.Value())
   611  	}
   612  
   613  	update, _ := ledger.NewUpdate(ledger.DummyState, keys, values)
   614  	trie, _ := pathfinder.UpdateToTrieUpdate(update, complete.DefaultPathFinderVersion)
   615  	return trie
   616  }
   617  
   618  func trieUpdateFixture(t *testing.T) *ledger.TrieUpdate {
   619  	return trieUpdateWithPayloadsFixture(
   620  		[]*ledger.Payload{
   621  			ledgerPayloadFixture(t),
   622  			ledgerPayloadFixture(t),
   623  			ledgerPayloadFixture(t),
   624  			ledgerPayloadFixture(t),
   625  		})
   626  }
   627  
   628  func ledgerPayloadFixture(t *testing.T) *ledger.Payload {
   629  	owner := unittest.RandomAddressFixture()
   630  	key := make([]byte, 8)
   631  	_, err := rand.Read(key)
   632  	require.NoError(t, err)
   633  	val := make([]byte, 8)
   634  	_, err = rand.Read(key)
   635  	require.NoError(t, err)
   636  	return ledgerPayloadWithValuesFixture(owner.String(), fmt.Sprintf("%x", key), val)
   637  }
   638  
   639  func ledgerPayloadWithValuesFixture(owner string, key string, value []byte) *ledger.Payload {
   640  	k := ledger.Key{
   641  		KeyParts: []ledger.KeyPart{
   642  			{
   643  				Type:  ledger.KeyPartOwner,
   644  				Value: []byte(owner),
   645  			},
   646  			{
   647  				Type:  ledger.KeyPartKey,
   648  				Value: []byte(key),
   649  			},
   650  		},
   651  	}
   652  
   653  	return ledger.NewPayload(k, value)
   654  }
   655  
   656  // trieRegistersPayloadComparer checks that trie payloads and register payloads are same, used for testing.
   657  func trieRegistersPayloadComparer(t *testing.T, triePayloads []*ledger.Payload, registerPayloads flow.RegisterEntries) {
   658  	assert.Equal(t, len(triePayloads), len(registerPayloads.Values()), "registers length should equal")
   659  
   660  	// crate a lookup map that matches flow register ID to index in the payloads slice
   661  	payloadRegID := make(map[flow.RegisterID]int)
   662  	for i, p := range triePayloads {
   663  		k, _ := p.Key()
   664  		regKey, _ := convert.LedgerKeyToRegisterID(k)
   665  		payloadRegID[regKey] = i
   666  	}
   667  
   668  	for _, entry := range registerPayloads {
   669  		index, ok := payloadRegID[entry.Key]
   670  		assert.True(t, ok, fmt.Sprintf("register entry not found for key %s", entry.Key.String()))
   671  		val := triePayloads[index].Value()
   672  		assert.True(t, val.Equals(entry.Value), fmt.Sprintf("payload values not same %s - %s", val, entry.Value))
   673  	}
   674  }
   675  
   676  func TestIndexerIntegration_StoreAndGet(t *testing.T) {
   677  	regOwnerAddress := unittest.RandomAddressFixture()
   678  	regOwner := string(regOwnerAddress.Bytes())
   679  	regKey := "code"
   680  	registerID := flow.NewRegisterID(regOwnerAddress, regKey)
   681  
   682  	db, dbDir := unittest.TempBadgerDB(t)
   683  	t.Cleanup(func() {
   684  		require.NoError(t, os.RemoveAll(dbDir))
   685  	})
   686  
   687  	logger := zerolog.Nop()
   688  	metrics := metrics.NewNoopCollector()
   689  
   690  	derivedChainData, err := derived.NewDerivedChainData(derived.DefaultDerivedDataCacheSize)
   691  	require.NoError(t, err)
   692  
   693  	// this test makes sure index values for a single register are correctly updated and always last value is returned
   694  	t.Run("Single Index Value Changes", func(t *testing.T) {
   695  		pebbleStorage.RunWithRegistersStorageAtInitialHeights(t, 0, 0, func(registers *pebbleStorage.Registers) {
   696  			index, err := New(
   697  				logger,
   698  				metrics,
   699  				db,
   700  				registers,
   701  				nil,
   702  				nil,
   703  				nil,
   704  				nil,
   705  				nil,
   706  				flow.Testnet.Chain(),
   707  				derivedChainData,
   708  				nil,
   709  			)
   710  			require.NoError(t, err)
   711  
   712  			values := [][]byte{[]byte("1"), []byte("1"), []byte("2"), []byte("3"), []byte("4")}
   713  			for i, val := range values {
   714  				testDesc := fmt.Sprintf("test iteration number %d failed with test value %s", i, val)
   715  				height := uint64(i + 1)
   716  				err := storeRegisterWithValue(index, height, regOwner, regKey, val)
   717  				require.NoError(t, err)
   718  
   719  				results, err := index.RegisterValue(registerID, height)
   720  				require.NoError(t, err, testDesc)
   721  				assert.Equal(t, val, results)
   722  			}
   723  		})
   724  	})
   725  
   726  	// this test makes sure if a register is not found the value returned is nil and without an error in order for this to be
   727  	// up to the specification script executor requires
   728  	t.Run("Missing Register", func(t *testing.T) {
   729  		pebbleStorage.RunWithRegistersStorageAtInitialHeights(t, 0, 0, func(registers *pebbleStorage.Registers) {
   730  			index, err := New(
   731  				logger,
   732  				metrics,
   733  				db,
   734  				registers,
   735  				nil,
   736  				nil,
   737  				nil,
   738  				nil,
   739  				nil,
   740  				flow.Testnet.Chain(),
   741  				derivedChainData,
   742  				nil,
   743  			)
   744  			require.NoError(t, err)
   745  
   746  			value, err := index.RegisterValue(registerID, 0)
   747  			require.Nil(t, value)
   748  			assert.NoError(t, err)
   749  		})
   750  	})
   751  
   752  	// this test makes sure that even if indexed values for a specific register are requested with higher height
   753  	// the correct highest height indexed value is returned.
   754  	// e.g. we index A{h(1) -> X}, A{h(2) -> Y}, when we request h(4) we get value Y
   755  	t.Run("Single Index Value At Later Heights", func(t *testing.T) {
   756  		pebbleStorage.RunWithRegistersStorageAtInitialHeights(t, 0, 0, func(registers *pebbleStorage.Registers) {
   757  			index, err := New(
   758  				logger,
   759  				metrics,
   760  				db,
   761  				registers,
   762  				nil,
   763  				nil,
   764  				nil,
   765  				nil,
   766  				nil,
   767  				flow.Testnet.Chain(),
   768  				derivedChainData,
   769  				nil,
   770  			)
   771  			require.NoError(t, err)
   772  
   773  			storeValues := [][]byte{[]byte("1"), []byte("2")}
   774  
   775  			require.NoError(t, storeRegisterWithValue(index, 1, regOwner, regKey, storeValues[0]))
   776  
   777  			require.NoError(t, index.indexRegisters(nil, 2))
   778  
   779  			value, err := index.RegisterValue(registerID, uint64(2))
   780  			require.Nil(t, err)
   781  			assert.Equal(t, storeValues[0], value)
   782  
   783  			require.NoError(t, index.indexRegisters(nil, 3))
   784  
   785  			err = storeRegisterWithValue(index, 4, regOwner, regKey, storeValues[1])
   786  			require.NoError(t, err)
   787  
   788  			value, err = index.RegisterValue(registerID, uint64(4))
   789  			require.Nil(t, err)
   790  			assert.Equal(t, storeValues[1], value)
   791  
   792  			value, err = index.RegisterValue(registerID, uint64(3))
   793  			require.Nil(t, err)
   794  			assert.Equal(t, storeValues[0], value)
   795  		})
   796  	})
   797  
   798  	// this test makes sure we correctly handle weird payloads
   799  	t.Run("Empty and Nil Payloads", func(t *testing.T) {
   800  		pebbleStorage.RunWithRegistersStorageAtInitialHeights(t, 0, 0, func(registers *pebbleStorage.Registers) {
   801  			index, err := New(
   802  				logger,
   803  				metrics,
   804  				db,
   805  				registers,
   806  				nil,
   807  				nil,
   808  				nil,
   809  				nil,
   810  				nil,
   811  				flow.Testnet.Chain(),
   812  				derivedChainData,
   813  				nil,
   814  			)
   815  			require.NoError(t, err)
   816  
   817  			require.NoError(t, index.indexRegisters(map[ledger.Path]*ledger.Payload{}, 1))
   818  			require.NoError(t, index.indexRegisters(map[ledger.Path]*ledger.Payload{}, 1))
   819  			require.NoError(t, index.indexRegisters(nil, 2))
   820  		})
   821  	})
   822  }
   823  
   824  // helper to store register at height and increment index range
   825  func storeRegisterWithValue(indexer *IndexerCore, height uint64, owner string, key string, value []byte) error {
   826  	payload := ledgerPayloadWithValuesFixture(owner, key, value)
   827  	return indexer.indexRegisters(map[ledger.Path]*ledger.Payload{ledger.DummyPath: payload}, height)
   828  }