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

feat: Support block and header versions gql #1848

Merged
merged 25 commits into from
May 2, 2024

Conversation

bvrooman
Copy link
Contributor

@bvrooman bvrooman commented Apr 19, 2024

Related issues:

This PR adds a version field to the Block and BlockHeader entities returned by the GraphQL API.

Screenshot 2024-04-19 at 9 31 42 PM

Additionally, the Block and BlockHeader client types are updated to include the version as well.

Checklist

  • Breaking changes are clearly marked as such in the PR description and changelog
  • New behavior is reflected in tests
  • The specification matches the implemented behavior (link update PR if changes are needed)

Before requesting review

  • I have reviewed the code myself
  • I have created follow-up issues caused by this PR and linked them here

After merging, notify other teams

[Add or remove entries as needed]

@bvrooman bvrooman self-assigned this Apr 20, 2024
@bvrooman bvrooman marked this pull request as ready for review April 20, 2024 01:30

#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct Version {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved this out of chain.rs and into the parent level schema.rs for reuse in other submodules.

@bvrooman bvrooman requested a review from a team April 20, 2024 01:36
@bvrooman
Copy link
Contributor Author

bvrooman commented Apr 20, 2024

This PR updates the relevant snapshot tests. However, it does not appear that we have tests for all resources against the GQL server-level API or client API (integration or mock GQL), meaning I did not add automated tests that verify the version returned by the GQL server . We have some high level tests for specific actions and resources like TXs but it could be worth having more tests for GQL responses (as long as this responsibility lies with fuel-core and not an indexer).

Copy link
Member

@MitchTurner MitchTurner left a comment

Choose a reason for hiding this comment

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

Looking good. Can you add a test to tests/tests/blocks.rs that checks the version?

edit: I just saw your last comment. Yeah. I think tests/tests/blocks.rs is the right place for that coverage

@bvrooman
Copy link
Contributor Author

As I was working on the client side testing, I discovered something that may require us to pivot slightly on our current approach:

When using the pattern SomeTypeVersion, defined something like this:
Screenshot 2024-04-21 at 11 04 45 PM

The compiler actually doesn't like this:

error: This type is already used in another variant
  --> crates/fuel-core/src/schema/block.rs:93:8
   |
93 |     V2(Version),
   |        ^^^^^^^

We can't use this pattern to define multiple versions as discriminated variants (it's as if the variant types themselves are the discriminant here).

Next, I tried using something like:

#[derive(SimpleObject)]
pub struct BlockVersion(pub Version);

But this still isn't allowed because tuple types aren't supported, and all values must be named in async_graphql.

So instead, I will just use the Version object directly. We lose the advantages strong typing, but using Version directly allows us to actually create a future-facing pattern.

@bvrooman
Copy link
Contributor Author

Looking good. Can you add a test to tests/tests/blocks.rs that checks the version?

edit: I just saw your last comment. Yeah. I think tests/tests/blocks.rs is the right place for that coverage

This is a bit tricky: The version isn't actually a field on the client Block type; rather, the version is used to instruct the client on how to construct the client Block type. This means that we cannot simply test for the version number. Real unit tests would require having other versions of the Block, which would allow us to test things like:

  • different constructions of Block (i.e. version 1, version 2, etc.)
  • unsupported version (i.e. server returns a version not supported by the client)

Because we don't have these, I created some contrived versions and tests here: https://github.com/FuelLabs/fuel-core/pull/1852/files#diff-b43da8aec184b70c3e3ac74b3f156eb2142bf651b724ccfb3d7dc9e6d7d70b01R120. This is just for reference, but should illustrate the kinds of tests we can have in the future, and why we can't really test for that now.

CHANGELOG.md Outdated Show resolved Hide resolved
#[derive(cynic::QueryFragment, Clone, Debug)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct Block {
pub version: Version,
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have a separate enum with all supported versions for ConsensusParameters(and other parameters). This strategy forces us to cover all possible values during compilation time, and I think it is still good to continue using it.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use enum at the async-graphql server level and at the cynic client level. This gives us exhaustive variant checks.

@xgreenx xgreenx marked this pull request as draft April 24, 2024 13:13
@bvrooman bvrooman marked this pull request as ready for review April 24, 2024 13:32
@bvrooman bvrooman marked this pull request as draft April 24, 2024 13:33
@bvrooman bvrooman marked this pull request as ready for review April 25, 2024 03:38
Copy link
Collaborator

@xgreenx xgreenx 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=)

Comment on lines 39 to 46
pub struct Version(u8);

#[Object]
impl Version {
async fn value(&self) -> scalars::U8 {
self.0.into()
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can remove this one=)

@MitchTurner
Copy link
Member

This is a bit tricky: The version isn't actually a field on the client Block type; rather, the version is used to instruct the client on how to construct the client Block type. This means that we cannot simply test for the version number. Real unit tests would require having other versions of the Block, which would allow us to test things like:

Okay. That makes sense. I feel like someone mentioned creating something like

    #[cfg(feature = "test_helpers")]
    VTest(VTestBlock)

variant that we could use to show upgradability.

Maybe I misheard. But I'm kinda impartial to something like that. On one hand it would be a decent tracer bullet to prove that we have upgradability, but it also doesn't really capture any desired behavior. Obviously we could get rid of it when we add a real version, but we could also just add the test once we add thet variant.

🤷

@bvrooman
Copy link
Contributor Author

bvrooman commented Apr 25, 2024

This is a bit tricky: The version isn't actually a field on the client Block type; rather, the version is used to instruct the client on how to construct the client Block type. This means that we cannot simply test for the version number. Real unit tests would require having other versions of the Block, which would allow us to test things like:

Okay. That makes sense. I feel like someone mentioned creating something like

    #[cfg(feature = "test_helpers")]
    VTest(VTestBlock)

variant that we could use to show upgradability.

Maybe I misheard. But I'm kinda impartial to something like that. On one hand it would be a decent tracer bullet to prove that we have upgradability, but it also doesn't really capture any desired behavior. Obviously we could get rid of it when we add a real version, but we could also just add the test once we add thet variant.

🤷

I like the idea. I hacked together some "test" blocks in another branch to ensure the functionality, which is how I discovered that our previous approach (using Union) didn't actually work. So there's definitely value there.

Adding a test block variant will require adding the supporting test code. This means adding test code to all the match statements, places requiring destructuring of the block into parts (transactions, etc), and other areas touched. The cost of this is added maintenance and code clutter. And ultimately, this may be fine. I can understand reasons to do it and not to do it.

I'd like to try it locally with a cleaner version of my above mentioned branch, including using the proper compiler flags. I will see how it sits and if it still makes sense in practice.

@bvrooman
Copy link
Contributor Author

bvrooman commented Apr 28, 2024

After spending some time on adding the Test variant, I think there are practical reasons that make this infeasible.

For one, this kind of test requires supporting the Test variant at both the server level and client level. Whenever an object is added to the server API, it must be included in the SDL. We don't have the ability to have separate SDLs based on what is and isn't hidden behind the test compiler flag (i.e., #[cfg(test)]). We can simply choose to always include the Test variant in the SDL, but now we are leaking test code into production. We also cannot use #[cfg(test)] at the client level at this point, because the SDL doesn't distinguish it as test code, and the compiler complains that the variant is not covered. We could omit using the #[cfg(test)] flag, but again, this would require us to leak test code into production.

For two, supporting this variant introduces test code that requires maintenance. There are a few places that require us to retrieve details about the Block, such as the header and transactions. To support this, we need to fill out the Test block with almost the same data and transformations as a regular V1 block. The maintenance cost may not be justified.

See this commit for details.

In the end, I recommend against codifying this test.

This reverts commit 90d3829.
@xgreenx xgreenx added the breaking A breaking api change label Apr 29, 2024
Copy link
Member

@MitchTurner MitchTurner left a comment

Choose a reason for hiding this comment

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

LGTM

@bvrooman bvrooman merged commit fd68745 into master May 2, 2024
33 checks passed
@bvrooman bvrooman deleted the bvrooman/feat/support-block-and-header-versions-gql branch May 2, 2024 22:16
@xgreenx xgreenx mentioned this pull request May 29, 2024
xgreenx added a commit that referenced this pull request May 29, 2024
## Version v0.27.0

### Added
- [#1898](#1898): Enforce
increasing of the `Executor::VERSION` on each release.

### Changed

- [#1906](#1906): Makes
`cli::snapshot::Command` members public such that clients can create and
execute snapshot commands programmatically. This enables snapshot
execution in external programs, such as the regenesis test suite.
- [#1891](#1891): Regenesis
now preserves `FuelBlockMerkleData` and `FuelBlockMerkleMetadata` in the
off-chain table. These tables are checked when querying message proofs.
- [#1886](#1886): Use ref to
`Block` in validation code
- [#1876](#1876): Updated
benchmark to include the worst scenario for `CROO` opcode. Also include
consensus parameters in bench output.
- [#1879](#1879): Return the
old behaviour for the `discovery_works` test.
- [#1848](#1848): Added
`version` field to the `Block` and `BlockHeader` GraphQL entities. Added
corresponding `version` field to the `Block` and `BlockHeader` client
types in `fuel-core-client`.
- [#1873](#1873): Separate
dry runs from block production in executor code, remove `ExecutionKind`
and `ExecutionType`, remove `thread_block_transaction` concept, remove
`PartialBlockComponent` type, refactor away `inner` functions.
- [#1900](#1900): Update the
root README as `fuel-core run` no longer has `--chain` as an option. It
has been replaced by `--snapshot`.

#### Breaking

- [#1894](#1894): Use testnet
configuration for local testnet.
- [#1894](#1894): Removed
support for helm chart.
- [#1910](#1910): `fuel-vm`
upgraded to `0.50.0`. More information in the
[changelog](https://github.com/FuelLabs/fuel-vm/releases/tag/v0.50.0).

## What's Changed
* feat: Support block and header versions gql by @bvrooman in
#1848
* Updated `croo` opcode benchmark to depend on the contract size by
@xgreenx in #1876
* Return the old behaviour for the `discovery_works` test by @xgreenx in
#1879
* Weekly `cargo update` by @github-actions in
#1880
* Separate production from dry runs in executor & Cleanup all execution
paths :) by @MitchTurner in
#1873
* Use ref instead of owned `Block` in validation by @MitchTurner in
#1886
* Weekly `cargo update` by @github-actions in
#1893
* ci: fix typos programmatically by @sdankel in
#1890
* feat: Preserve message proofs post-regenesis by @bvrooman in
#1891
* chore: update README fuel-core run options by @K1-R1 in
#1900
* Weekly `cargo update` by @github-actions in
#1903
* chore: Make snapshot command members pub accessible by @bvrooman in
#1906
* Use testnet configuration for local testnet by @xgreenx in
#1894
* Enforce increasing of the `Executor::VERSION` on each release by
@xgreenx in #1898
* Bumped the version of the `fuel-vm` to `0.50.0` by @xgreenx in
#1910

## New Contributors
* @K1-R1 made their first contribution in
#1900

**Full Changelog**:
v0.26.0...v0.27.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking A breaking api change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support versioning of Block and BlockHeader in the GraphQL API
4 participants