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 }