Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Access] Get protocol snapshot by block id and block height #4957

Conversation

AndriiDiachuk
Copy link
Contributor

@AndriiDiachuk AndriiDiachuk commented Nov 8, 2023

#4869

In this pull request are Introduced new methods GetProtocolStateSnapshotByBlockID, GetProtocolStateSnapshotByHeight that return the snapshot from the specified block if that block is finalized.

Related PR:
onflow/flow#1401
onflow/flow-emulator#501

@AndriiDiachuk AndriiDiachuk changed the title Andrii diachuk/4869 get protocol snapshot from any block endpoint [Access] Get protocol snapshot by block id and block height Nov 9, 2023
@codecov-commenter
Copy link

codecov-commenter commented Nov 9, 2023

Codecov Report

Attention: 39 lines in your changes are missing coverage. Please review.

Comparison is base (456c131) 56.31% compared to head (d2612f9) 54.20%.
Report is 30 commits behind head on master.

Files Patch % Lines
engine/protocol/handler.go 0.00% 17 Missing ⚠️
engine/access/rpc/backend/backend_network.go 83.33% 9 Missing and 2 partials ⚠️
engine/access/apiproxy/access_api_proxy.go 0.00% 8 Missing ⚠️
utils/unittest/chain_suite.go 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4957      +/-   ##
==========================================
- Coverage   56.31%   54.20%   -2.12%     
==========================================
  Files         976      786     -190     
  Lines       91752    73885   -17867     
==========================================
- Hits        51673    40047   -11626     
+ Misses      36253    31035    -5218     
+ Partials     3826     2803    -1023     
Flag Coverage Δ
unittests 54.20% <58.94%> (-2.12%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@AndriiDiachuk AndriiDiachuk marked this pull request as ready for review November 17, 2023 10:31
Copy link
Contributor

@peterargue peterargue left a comment

Choose a reason for hiding this comment

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

thanks @AndriiDiachuk! great work so far! Added a few comments.

@jordanschalm it looks like the original version GetLatestProtocolStateSnapshot uses a method that may return a snapshot from an earlier block. This would be confusing for these new methods. Is that required, or only needed for dynamic bootstrapping?

// getValidSnapshot will return a valid snapshot that has a sealing segment which
// 1. does not contain any blocks that span an epoch transition
// 2. does not contain any blocks that span an epoch phase transition
// If a snapshot does contain an invalid sealing segment query the state
// by height of each block in the segment and return a snapshot at the point
// where the transition happens.
func (b *backendNetwork) getValidSnapshot(snapshot protocol.Snapshot, blocksVisited int) (protocol.Snapshot, error) {

@jordanschalm
Copy link
Member

@jordanschalm it looks like the original version GetLatestProtocolStateSnapshot uses a method that may return a snapshot from an earlier block. This would be confusing for these new methods. Is that required, or only needed for dynamic bootstrapping?

The snapshot model doesn't have a way to encode having one part of the sealing segment being in a different epoch or phase than another. So if we return a snapshot that spans two epochs or phases, the client would not be able to correctly interpret the epoch/phase of all the blocks it contains.

For now, in this case where a specific height is requested, I suggest we return a sentinel error, rather than walking backward to find a valid snapshot. Blocks with snapshots that span one of these state transitions are very rare (<0.001% on Mainnet).

It should be easier to modify the snapshot to accommodate these cases once Dynamic Protocol State is in place. Each block contains a commitment to the corresponding protocol state, including epoch info. Then we can include a list of ProtocolStateEntrys in the snapshot.

@AndriiDiachuk
Copy link
Contributor Author

Reviewed the finalization logic. It is correct as-is, however I've added some suggestions around performance, specificity of errors, and documentation.

@jordanschalm Thanks for your suggestions. Pushed code with changes. Waiting for a new round of review.

…o AndriiDiachuk/4869-get-protocol-snapshot-from-any-block-endpoint
suite.Require().Equal(status.Errorf(codes.InvalidArgument, "failed to retrieve snapshot for block by height %v",
fmt.Errorf("%d: %v", newBlock.Header.Height, "block not finalized")).Error(),
err.Error())
suite.T().Run("failed to get a valid snapshot", func(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

What specific failure case are we testing here?

The test code implies that the failure is arising from the mocked error in AtHeight on line 473, however AtHeight function is not invoked in this code path. When I inspect the error, it is actually coming from the fact that we are failing to produce a sealing segment for the queried snapshot, because newBlock has not been finalized as far as the local state variable is concerned. This is inconsistent with the mocked BlockIDByHeight on line 476, which implies that newBlock is finalized.

Please clearly state which failure case is being tested and make sure the test implements that failure case consistently. There are many different cases we can and should test here, for example:

  • (unknown query block) ErrUnknownSnapshotReference error from snapshot.AtBlockID -> NotFound code
  • (different block finalized at height) BlockIDByHeight returns different block ID -> InvalidArgument code
  • unexpected error from snapshot.AtBlockID -> Internal code
  • unexpected error from BlockIDByHeight -> Internal code

})
}

// TestGetProtocolStateSnapshotByBlockID_NonQueryBlock tests our GetProtocolStateSnapshotByBlockID RPC endpoint
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// TestGetProtocolStateSnapshotByBlockID_NonQueryBlock tests our GetProtocolStateSnapshotByBlockID RPC endpoint
// TestGetProtocolStateSnapshotByBlockID_UnknownQueryBlock tests our GetProtocolStateSnapshotByBlockID RPC endpoint

snapshotBytes, err := backend.GetProtocolStateSnapshotByBlockID(ctx, newBlock.ID())
suite.Require().Nil(snapshotBytes)
suite.Require().Error(err)
suite.Require().ErrorAs(err, &expectedError)
Copy link
Member

Choose a reason for hiding this comment

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

expectedError has the interface type error, not a concrete type that implements error, so you could pass in any error-typed value as the first argument here, and the assertion would pass.

status.Errorf does not support unwrapping errors as far as I can tell, so we unfortunately cannot use the built-in error checking functions here. (As a side not, if we could, we would likely want errors.Is, not errors.As, as we want to compare value equality, not type equality.)

Suggested change
suite.Require().ErrorAs(err, &expectedError)

Copy link
Member

@jordanschalm jordanschalm left a comment

Choose a reason for hiding this comment

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

Looks good to me aside from the last couple of comments. @peterargue I ended up reviewing the full PR, not just the finalization logic.

Thank you for the excellent test coverage of the new RPC methods.

Copy link
Contributor

@peterargue peterargue left a comment

Choose a reason for hiding this comment

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

thanks @AndriiDiachuk! Excellent documentation.

Added a couple small comments, but looks good.

Copy link
Contributor

@peterargue peterargue left a comment

Choose a reason for hiding this comment

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

thanks @AndriiDiachuk! Excellent documentation.

Added a couple small comments, but looks good.

@peterargue peterargue enabled auto-merge December 11, 2023 19:14
@peterargue peterargue added this pull request to the merge queue Dec 11, 2023
Merged via the queue into onflow:master with commit aaa8e4e Dec 11, 2023
peterargue added a commit that referenced this pull request Dec 12, 2023
…otocol-snapshot-from-any-block-endpoint

[Access] Get protocol snapshot by block id and block height
peterargue added a commit that referenced this pull request Dec 12, 2023
…otocol-snapshot-from-any-block-endpoint

[Access] Get protocol snapshot by block id and block height
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants