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