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 }