Skip to content

Check data integrity for EVM events#529

Merged
devbugging merged 14 commits into
mainfrom
check-evm-events-integrity
Sep 13, 2024
Merged

Check data integrity for EVM events#529
devbugging merged 14 commits into
mainfrom
check-evm-events-integrity

Conversation

@m-Peter
Copy link
Copy Markdown
Collaborator

@m-Peter m-Peter commented Sep 6, 2024

Description


For contributor use:

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

Summary by CodeRabbit

  • New Features

    • Enhanced validation for EVM block events to ensure all referenced transactions are accounted for.
    • Improved functionality for creating transactions from encoded data.
    • Introduced a new method for fetching block events with improved error handling.
  • Bug Fixes

    • Added tests to validate behavior when blocks reference missing transactions, ensuring robustness.
  • Refactor

    • Improved clarity in function signatures and naming conventions for better code readability.
    • Made function fields in the mock client struct accessible from outside the package.

@m-Peter m-Peter added this to the Flow-EVM-M2 milestone Sep 6, 2024
@m-Peter m-Peter self-assigned this Sep 6, 2024
@coderabbitai

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

Comment thread models/events.go Outdated
return nil, fmt.Errorf("EVM block can not be nil if transactions are present, Flow block: %d", events.Height)
}

if e.block != nil && len(e.transactions) > 0 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at this point if block is nil should be separate check and return error

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, we have the assumptions that we can have at most one EVM Block, so the block can also be nil, and we should check against that.

Comment thread models/events.go Outdated
}

if e.block != nil && len(e.transactions) > 0 {
txHashes := evmTypes.TransactionHashes([]gethCommon.Hash{})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then after here I don't think you need to check transactions length... because you should do that even for empty slice of txs... so it should match empty root

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the check for transactions length in: 1e18078

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 659eb51 and 1e18078.

Files selected for processing (1)
  • models/events.go (2 hunks)
Files skipped from review as they are similar to previous changes (1)
  • models/events.go

@m-Peter m-Peter force-pushed the check-evm-events-integrity branch from 90612d1 to e7b7901 Compare September 6, 2024 14:18
Comment thread services/ingestion/subscriber.go Outdated
Comment on lines +151 to +173
blkEvents := flow.BlockEvents{
BlockID: blockEvents.BlockID,
Height: blockEvents.Height,
BlockTimestamp: blockEvents.BlockTimestamp,
}
for _, eventFilter := range r.blocksFilter().EventTypes {
recoveredEvents, err := r.client.GetEventsForHeightRange(
ctx,
eventFilter,
blockEvents.Height,
blockEvents.Height,
)
if err != nil {
events <- models.NewBlockEventsError(err)
return
}
for _, blockEvent := range recoveredEvents {
blkEvents.Events = append(blkEvents.Events, blockEvent.Events...)
}
}

events <- models.NewBlockEvents(blkEvents)
return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please extract this into a method

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍
Updated in e177ebf

Comment thread services/ingestion/subscriber.go Outdated
events <- models.NewBlockEventsError(err)
return
}
for _, blockEvent := range recoveredEvents {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should only get back one right, because range is 1, it would be better to check length and then just use that 1 item instead of a for loop, this would be another safety check.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍
Updated in e177ebf

}

evts := models.NewBlockEvents(blockEvents)
if evts.Err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should test this in the subscriber_test I think ti shouldn't be hard

coderabbitai[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 7f1382b and db68721.

Files selected for processing (2)
  • models/events.go (2 hunks)
  • models/events_test.go (2 hunks)
Files skipped from review as they are similar to previous changes (2)
  • models/events.go
  • models/events_test.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between db68721 and ccd1601.

Files selected for processing (3)
  • services/ingestion/subscriber.go (2 hunks)
  • services/ingestion/subscriber_test.go (2 hunks)
  • services/testutils/mock_client.go (3 hunks)
Additional comments not posted (6)
services/testutils/mock_client.go (6)

14-17: Approved: Exported function fields in MockClient.

The renaming of function fields to start with uppercase letters correctly exports them, allowing for external access. This is useful for testing and extending the functionality of MockClient.


21-21: Approved: Updated method implementation.

The method GetBlockHeaderByHeight correctly uses the newly exported function field name, maintaining consistency with the structural changes in the MockClient.


25-25: Approved: Updated method implementation.

The method GetLatestBlockHeader correctly uses the newly exported function field name, ensuring consistency with the structural changes in the MockClient.


29-29: Approved: Updated method implementation.

The method GetNodeVersionInfo correctly uses the newly exported function field name, aligning with the structural changes in the MockClient.


38-38: Approved: Updated method implementation.

The method SubscribeEventsByBlockHeight correctly uses the newly exported function field name, ensuring consistency with the structural changes in the MockClient.


Line range hint 41-63: Approved: Updated initialization in SetupClientForRange.

The function SetupClientForRange has been correctly updated to initialize the MockClient struct with the newly renamed function fields. This ensures that the mock client can be set up correctly with the updated function signatures.

Comment on lines +265 to +299
func (r *RPCSubscriber) fetchBlockEvents(
ctx context.Context,
blockEvents flow.BlockEvents,
) models.BlockEvents {
blkEvents := flow.BlockEvents{
BlockID: blockEvents.BlockID,
Height: blockEvents.Height,
BlockTimestamp: blockEvents.BlockTimestamp,
}
for _, eventType := range r.blocksFilter().EventTypes {
recoveredEvents, err := r.client.GetEventsForHeightRange(
ctx,
eventType,
blockEvents.Height,
blockEvents.Height,
)
if err != nil {
return models.NewBlockEventsError(err)
}

if len(recoveredEvents) > 1 {
return models.NewBlockEventsError(
fmt.Errorf(
"received unexpected Flow block events for height: %d",
blockEvents.Height,
),
)
}

if len(recoveredEvents) == 0 {
models.NewBlockEvents(blkEvents)
}

blkEvents.Events = append(blkEvents.Events, recoveredEvents[0].Events...)
}

return models.NewBlockEvents(blkEvents)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-implemented error handling in fetchBlockEvents, consider enhancing logging and handling of no-event scenarios.

The fetchBlockEvents method is a robust addition to the RPCSubscriber class, enhancing error handling and data integrity during event fetching. The method's checks for errors and unexpected multiple events are well-placed.

Suggestions:

  • Consider adding logging for successful event retrieval to improve traceability and debugging.
  • Handle cases where no events are retrieved more explicitly, perhaps by logging or returning a specific error.

Would you like a code snippet to implement these logging enhancements?

@m-Peter m-Peter force-pushed the check-evm-events-integrity branch from ccd1601 to cad404e Compare September 9, 2024 09:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between ccd1601 and cad404e.

Files selected for processing (3)
  • services/ingestion/subscriber.go (2 hunks)
  • services/ingestion/subscriber_test.go (2 hunks)
  • services/testutils/mock_client.go (3 hunks)
Files skipped from review as they are similar to previous changes (1)
  • services/testutils/mock_client.go

Comment on lines +149 to +158
evts := models.NewBlockEvents(blockEvents)
if evts.Err != nil {
r.logger.Warn().Err(err).Msgf(
"failed to parse EVM block events for Flow height: %d, retrying with gRPC API...",
blockEvents.Height,
)
events <- r.fetchBlockEvents(ctx, blockEvents)
} else {
events <- models.NewBlockEvents(blockEvents)
}

This comment was marked as outdated.

Comment on lines +265 to +299
func (r *RPCSubscriber) fetchBlockEvents(
ctx context.Context,
blockEvents flow.BlockEvents,
) models.BlockEvents {
blkEvents := flow.BlockEvents{
BlockID: blockEvents.BlockID,
Height: blockEvents.Height,
BlockTimestamp: blockEvents.BlockTimestamp,
}
for _, eventType := range r.blocksFilter().EventTypes {
recoveredEvents, err := r.client.GetEventsForHeightRange(
ctx,
eventType,
blockEvents.Height,
blockEvents.Height,
)
if err != nil {
return models.NewBlockEventsError(err)
}

if len(recoveredEvents) > 1 {
return models.NewBlockEventsError(
fmt.Errorf(
"received multiple Flow block events for height: %d",
blockEvents.Height,
),
)
}

if len(recoveredEvents) == 0 {
return models.NewBlockEventsError(
fmt.Errorf(
"received empty Flow block events for height: %d",
blockEvents.Height,
),
)
}

blkEvents.Events = append(blkEvents.Events, recoveredEvents[0].Events...)
}

return models.NewBlockEvents(blkEvents)
}

This comment was marked as outdated.

Comment on lines +68 to +207
func Test_SubscribingWithRetryOnError(t *testing.T) {
endHeight := uint64(10)
sporkClients := []access.Client{}
currentClient := testutils.SetupClientForRange(1, endHeight)

cadenceHeight := uint64(5)
txCount := 10
hashes := make([]gethCommon.Hash, txCount)
flowEvents := make([]flow.Event, 0)

// generate txs
for i := 0; i < txCount; i++ {
cdcEvent, txEvent, tx, _, err := newTransaction(cadenceHeight)
require.NoError(t, err)
hashes[i] = tx.Hash()
flowEvent := flow.Event{
Type: string(txEvent.Etype),
Value: cdcEvent,
}
flowEvents = append(flowEvents, flowEvent)
}

evmTxEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: flowEvents,
}

// generate single block
cdcEvent, evmBlock, blockEvent, err := newBlock(cadenceHeight, hashes)
require.NoError(t, err)
flowEvent := flow.Event{
Type: string(blockEvent.Etype),
Value: cdcEvent,
}
evmBlockEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: []flow.Event{flowEvent},
}

currentClient.On(
"GetEventsForHeightRange",
mock.AnythingOfType("context.backgroundCtx"),
"A.b6763b4399a888c8.EVM.BlockExecuted",
uint64(cadenceHeight),
uint64(cadenceHeight),
).Return([]flow.BlockEvents{evmBlockEvents}, nil).Once()

currentClient.On(
"GetEventsForHeightRange",
mock.AnythingOfType("context.backgroundCtx"),
"A.b6763b4399a888c8.EVM.TransactionExecuted",
uint64(cadenceHeight),
uint64(cadenceHeight),
).Return([]flow.BlockEvents{evmTxEvents}, nil).Once()

currentClient.SubscribeEventsByBlockHeightFunc = func(
ctx context.Context,
startHeight uint64,
filter flow.EventFilter,
opts ...access.SubscribeOption,
) (<-chan flow.BlockEvents, <-chan error, error) {
events := make(chan flow.BlockEvents)
errors := make(chan error)

blockEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: evmTxEvents.Events,
}

// generate single block
cdcEvent, _, blockEvent, err := newBlock(cadenceHeight, hashes[:txCount-2])
require.NoError(t, err)
flowEvent := flow.Event{
Type: string(blockEvent.Etype),
Value: cdcEvent,
}
blockEvents.Events = append(blockEvents.Events, flowEvent)

go func() {
defer close(events)

for i := startHeight; i <= endHeight; i++ {
if i == cadenceHeight {
events <- blockEvents
} else {
events <- flow.BlockEvents{
Height: i,
}
}
}
}()

return events, errors, nil
}

client, err := requester.NewCrossSporkClient(
currentClient,
sporkClients,
zerolog.Nop(),
flowGo.Previewnet,
)
require.NoError(t, err)

subscriber := NewRPCSubscriber(client, 100, flowGo.Previewnet, zerolog.Nop())

events := subscriber.Subscribe(context.Background(), 1)

var prevHeight uint64

for ev := range events {
if prevHeight == endHeight {
require.ErrorIs(t, ev.Err, errs.ErrDisconnected)
break
}

require.NoError(t, ev.Err)

// this makes sure all the event heights are sequential
eventHeight := ev.Events.CadenceHeight()
require.Equal(t, prevHeight+1, eventHeight)
prevHeight = eventHeight

if eventHeight == cadenceHeight {
assert.Equal(t, evmBlock, ev.Events.Block())
for i := 0; i < txCount; i++ {
tx := ev.Events.Transactions()[i]
assert.Equal(t, hashes[i], tx.Hash())
}
}
}

// this makes sure we indexed all the events
require.Equal(t, uint64(endHeight), prevHeight)
}

This comment was marked as outdated.

Comment on lines +209 to +348
func Test_SubscribingWithRetryOnErrorMultipleBlocks(t *testing.T) {
endHeight := uint64(10)
sporkClients := []access.Client{}
currentClient := testutils.SetupClientForRange(1, endHeight)

cadenceHeight := uint64(5)
txCount := 10
hashes := make([]gethCommon.Hash, txCount)
flowEvents := make([]flow.Event, 0)

// generate txs
for i := 0; i < txCount; i++ {
cdcEvent, txEvent, tx, _, err := newTransaction(cadenceHeight)
require.NoError(t, err)
hashes[i] = tx.Hash()
flowEvent := flow.Event{
Type: string(txEvent.Etype),
Value: cdcEvent,
}
flowEvents = append(flowEvents, flowEvent)
}

evmTxEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: flowEvents,
}

// generate single block
cdcEvent, _, blockEvent, err := newBlock(cadenceHeight, hashes)
require.NoError(t, err)
flowEvent := flow.Event{
Type: string(blockEvent.Etype),
Value: cdcEvent,
}
evmBlockEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: []flow.Event{flowEvent},
}

currentClient.On(
"GetEventsForHeightRange",
mock.AnythingOfType("context.backgroundCtx"),
"A.b6763b4399a888c8.EVM.BlockExecuted",
uint64(cadenceHeight),
uint64(cadenceHeight),
).Return([]flow.BlockEvents{evmBlockEvents, evmBlockEvents}, nil).Once()

currentClient.On(
"GetEventsForHeightRange",
mock.AnythingOfType("context.backgroundCtx"),
"A.b6763b4399a888c8.EVM.TransactionExecuted",
uint64(cadenceHeight),
uint64(cadenceHeight),
).Return([]flow.BlockEvents{evmTxEvents}, nil).Once()

currentClient.SubscribeEventsByBlockHeightFunc = func(
ctx context.Context,
startHeight uint64,
filter flow.EventFilter,
opts ...access.SubscribeOption,
) (<-chan flow.BlockEvents, <-chan error, error) {
events := make(chan flow.BlockEvents)
errors := make(chan error)

blockEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: evmTxEvents.Events,
}

// generate single block
cdcEvent, _, blockEvent, err := newBlock(cadenceHeight, hashes[:txCount-2])
require.NoError(t, err)
flowEvent := flow.Event{
Type: string(blockEvent.Etype),
Value: cdcEvent,
}
blockEvents.Events = append(blockEvents.Events, flowEvent)

go func() {
defer close(events)

for i := startHeight; i <= endHeight; i++ {
if i == cadenceHeight {
events <- blockEvents
} else {
events <- flow.BlockEvents{
Height: i,
}
}
}
}()

return events, errors, nil
}

client, err := requester.NewCrossSporkClient(
currentClient,
sporkClients,
zerolog.Nop(),
flowGo.Previewnet,
)
require.NoError(t, err)

subscriber := NewRPCSubscriber(client, 100, flowGo.Previewnet, zerolog.Nop())

events := subscriber.Subscribe(context.Background(), 1)

var prevHeight uint64

for ev := range events {
if prevHeight == endHeight {
require.ErrorIs(t, ev.Err, errs.ErrDisconnected)
break
}

if prevHeight+1 == cadenceHeight {
require.Error(t, ev.Err)
assert.ErrorContains(
t,
ev.Err,
"received multiple Flow block events for height: 5",
)
prevHeight = cadenceHeight
} else {
require.NoError(t, ev.Err)
// this makes sure all the event heights are sequential
eventHeight := ev.Events.CadenceHeight()
require.Equal(t, prevHeight+1, eventHeight)
prevHeight = eventHeight
}
}

require.Equal(t, endHeight, prevHeight)
}

This comment was marked as outdated.

Comment thread services/ingestion/subscriber_test.go Outdated
Comment on lines +350 to +475
func Test_SubscribingWithRetryOnErrorEmptyBlocks(t *testing.T) {
endHeight := uint64(10)
sporkClients := []access.Client{}
currentClient := testutils.SetupClientForRange(1, endHeight)

cadenceHeight := uint64(5)
txCount := 10
hashes := make([]gethCommon.Hash, txCount)
flowEvents := make([]flow.Event, 0)

// generate txs
for i := 0; i < txCount; i++ {
cdcEvent, txEvent, tx, _, err := newTransaction(cadenceHeight)
require.NoError(t, err)
hashes[i] = tx.Hash()
flowEvent := flow.Event{
Type: string(txEvent.Etype),
Value: cdcEvent,
}
flowEvents = append(flowEvents, flowEvent)
}

evmTxEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: flowEvents,
}

currentClient.On(
"GetEventsForHeightRange",
mock.AnythingOfType("context.backgroundCtx"),
"A.b6763b4399a888c8.EVM.BlockExecuted",
uint64(cadenceHeight),
uint64(cadenceHeight),
).Return([]flow.BlockEvents{}, nil).Once()

currentClient.On(
"GetEventsForHeightRange",
mock.AnythingOfType("context.backgroundCtx"),
"A.b6763b4399a888c8.EVM.TransactionExecuted",
uint64(cadenceHeight),
uint64(cadenceHeight),
).Return([]flow.BlockEvents{evmTxEvents}, nil).Once()

currentClient.SubscribeEventsByBlockHeightFunc = func(
ctx context.Context,
startHeight uint64,
filter flow.EventFilter,
opts ...access.SubscribeOption,
) (<-chan flow.BlockEvents, <-chan error, error) {
events := make(chan flow.BlockEvents)
errors := make(chan error)

blockEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: evmTxEvents.Events,
}

// generate single block
cdcEvent, _, blockEvent, err := newBlock(cadenceHeight, hashes[:txCount-2])
require.NoError(t, err)
flowEvent := flow.Event{
Type: string(blockEvent.Etype),
Value: cdcEvent,
}
blockEvents.Events = append(blockEvents.Events, flowEvent)

go func() {
defer close(events)

for i := startHeight; i <= endHeight; i++ {
if i == cadenceHeight {
events <- blockEvents
} else {
events <- flow.BlockEvents{
Height: i,
}
}
}
}()

return events, errors, nil
}

client, err := requester.NewCrossSporkClient(
currentClient,
sporkClients,
zerolog.Nop(),
flowGo.Previewnet,
)
require.NoError(t, err)

subscriber := NewRPCSubscriber(client, 100, flowGo.Previewnet, zerolog.Nop())

events := subscriber.Subscribe(context.Background(), 1)

var prevHeight uint64

for ev := range events {
if prevHeight == endHeight {
require.ErrorIs(t, ev.Err, errs.ErrDisconnected)
break
}

if prevHeight+1 == cadenceHeight {
require.Error(t, ev.Err)
assert.ErrorContains(
t,
ev.Err,
"received empty Flow block events for height: 5",
)
prevHeight = cadenceHeight
} else {
require.NoError(t, ev.Err)
// this makes sure all the event heights are sequential
eventHeight := ev.Events.CadenceHeight()
require.Equal(t, prevHeight+1, eventHeight)
prevHeight = eventHeight
}
}

require.Equal(t, endHeight, prevHeight)
}

This comment was marked as outdated.

Comment thread services/ingestion/subscriber.go Outdated
}
}

func (r *RPCSubscriber) fetchBlockEvents(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment explaining what this function does and how it is used for backup

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point 👍
Added in 76d544c

"failed to parse EVM block events for Flow height: %d, retrying with gRPC API...",
blockEvents.Height,
)
events <- r.fetchBlockEvents(ctx, blockEvents)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a commnet something like:

// call an alternative grpc request to fetch events in case the event streaming failed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 76d544c

Comment thread services/ingestion/subscriber_test.go Outdated
Comment on lines +79 to +112
for i := 0; i < txCount; i++ {
cdcEvent, txEvent, tx, _, err := newTransaction(cadenceHeight)
require.NoError(t, err)
hashes[i] = tx.Hash()
flowEvent := flow.Event{
Type: string(txEvent.Etype),
Value: cdcEvent,
}
flowEvents = append(flowEvents, flowEvent)
}

evmTxEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: flowEvents,
}

// generate single block
cdcEvent, evmBlock, blockEvent, err := newBlock(cadenceHeight, hashes)
require.NoError(t, err)
flowEvent := flow.Event{
Type: string(blockEvent.Etype),
Value: cdcEvent,
}
evmBlockEvents := flow.BlockEvents{
BlockID: flow.Identifier{0x1},
Height: cadenceHeight,
BlockTimestamp: time.Now(),
Events: []flow.Event{flowEvent},
}

currentClient.On(
"GetEventsForHeightRange",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we extract all this setup section into a helper so all tests don't have to implement it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point 👍 It was a lot of duplicated code.
Updated in 3c9ead3

require.Equal(t, uint64(endHeight), prevHeight)
}

func Test_SubscribingWithRetryOnErrorMultipleBlocks(t *testing.T) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add above each test a one-liner comment explaining what it tests

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 3c9ead3

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Outside diff range and nitpick comments (3)
services/ingestion/subscriber.go (3)

149-158: Integration of fetchBlockEvents enhances error handling.

The modifications to the Subscribe method effectively integrate the new fetchBlockEvents method, enhancing the robustness of event handling. The method's comprehensive handling of disconnection errors and context cancellations is commendable.

Suggestion:

  • Add more detailed comments explaining the error handling logic within the Subscribe method to improve maintainability and clarity for future developers.

265-299: Well-implemented fetchBlockEvents method, consider enhancing logging and handling of no-event scenarios.

The fetchBlockEvents method is a robust addition to the RPCSubscriber class, enhancing error handling and data integrity during event fetching. The method's checks for errors and unexpected multiple events are well-placed.

Suggestions:

  • Consider adding logging for successful event retrieval to improve traceability and debugging.
  • Handle cases where no events are retrieved more explicitly, perhaps by logging or returning a specific error.

285-293: Unexpected multiple events check is crucial, consider logging the error.

The check for more than one event at lines 285-293 is crucial for ensuring data integrity.

Consider logging this error for better traceability.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between cad404e and 4c1c822.

Files selected for processing (1)
  • services/ingestion/subscriber.go (2 hunks)

@m-Peter m-Peter force-pushed the check-evm-events-integrity branch from 4c1c822 to 2b1ee91 Compare September 11, 2024 08:56
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Outside diff range and nitpick comments (1)
services/ingestion/subscriber.go (1)

265-299: Robust fallback mechanism for fetching block events.

The new fetchBlockEvents method provides a reliable way to fetch block events using the gRPC API as a fallback when the primary method fails. It constructs the necessary BlockEvents object, iterates through the defined event types, and retrieves the events for the specified block height.

The error handling is appropriate, with the method returning a BlockEventsError if an error occurs during event fetching. The check for more than one event is a good safeguard to ensure data integrity.

Suggestion:

  • Consider adding more detailed logging statements within the fetchBlockEvents method to provide better visibility into the fetching process and any errors that may occur. This can aid in debugging and monitoring.
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 4c1c822 and 2b1ee91.

Files selected for processing (6)
  • models/events.go (2 hunks)
  • models/events_test.go (2 hunks)
  • services/ingestion/engine_test.go (3 hunks)
  • services/ingestion/subscriber.go (2 hunks)
  • services/ingestion/subscriber_test.go (2 hunks)
  • services/testutils/mock_client.go (3 hunks)
Files skipped from review as they are similar to previous changes (2)
  • models/events.go
  • services/ingestion/engine_test.go
Additional comments not posted (14)
services/testutils/mock_client.go (6)

14-17: LGTM!

The changes to export the function fields by renaming them from lowercase to uppercase are approved. This enhances the usability of MockClient by allowing external packages to access these function fields directly.


21-21: LGTM!

The change to use the new exported name GetBlockHeaderByHeightFunc for the function field is approved. It is consistent with the renaming of the function field.


25-25: LGTM!

The change to use the new exported name GetLatestBlockHeaderFunc for the function field is approved. It is consistent with the renaming of the function field.


29-29: LGTM!

The change to use the new exported name GetNodeVersionInfoFunc for the function field is approved. It is consistent with the renaming of the function field.


38-38: LGTM!

The change to use the new exported name SubscribeEventsByBlockHeightFunc for the function field is approved. It is consistent with the renaming of the function field.


Line range hint 41-84: LGTM!

The changes to initialize the MockClient struct with the newly renamed function fields are approved. They are consistent with the renaming of the function fields in the MockClient struct.

models/events_test.go (4)

64-155: The past review comment is still valid and applicable to the current code segment. Skipping generating a similar comment.


Line range hint 157-268: LGTM!

The test function Test_EventDecoding is well-constructed and effectively tests the event decoding logic. It covers various aspects such as transaction hashes, block hash, cumulative gas used, transaction index, and log index.

The code changes are approved.


269-276: There are no changes to the newTransaction function. Skipping generating a comment.


277-280: LGTM!

The changes to the newBlock function signature and the assignment of transaction hashes to the TransactionHashes field improve the clarity and readability of the code. The new naming convention is consistently applied throughout the function.

The code changes are approved.

services/ingestion/subscriber.go (1)

149-158: Robust error handling and fallback mechanism.

The modifications to the subscribe method enhance its resilience by introducing a fallback mechanism to fetch block events using the gRPC API when parsing EVM block events fails. This ensures that the subscriber can continue processing events even if the primary method encounters issues.

The changes are well-integrated, and the logging of the error provides visibility into any issues that occur.

services/ingestion/subscriber_test.go (3)

68-207: ****


209-348: ****


350-475: ****

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants