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

Add standalone support for RPC Spec V2 chainHead #762

Closed
wants to merge 13 commits into from
Closed

Conversation

lexnv
Copy link
Collaborator

@lexnv lexnv commented Jan 9, 2023

This PR adds standalone support for the RPC V2 chainHead family of functions into subxt.
The API is extending the chainHead_follow subscription via a custom high level wrapper for better ergonomics.

All the chainHead subscriptions and methods are parsed internally to return concrete object types.
The new API constructed on top of the block's API is exercised in the chainhead_subscription example.

To keep future compatibility with #760, the subscription events are moved into the new file rpc/types.rs.
The work of this PR was started on the https://github.com/paritytech/subxt/tree/lexnv/rpc_spec_v2_poc branch.

API Example

let mut follow_sub = api.blocks().subscribe_chainhead_finalized(true).await?;

    // Handle all subscriptions from the `chainHead_follow`.
    while let Some(block) = follow_sub.next().await {
        let block = block?;

        let body = block.body().await?;
        println!("[hash={:?}] body={:?}", block.hash(), body);

        let header = block.header().await?;
        println!("[hash={:?}] header={:?}", block.hash(), header);

        let call_params = AccountKeyring::Alice.to_account_id().encode();
        let call = block
            .call("AccountNonceApi_account_nonce".into(), Some(&call_params))
            .await?;
        println!("[hash={:?}] call={:?}", block.hash(), call);

        println!("Events:");
        let events = block.events().await?;
        for event in events.iter() {
            let event = event?;
            let pallet = event.pallet_name();
            let variant = event.variant_name();
            println!("    {pallet}::{variant} event");
        }

        // Unpin the block as last step.
        block.unpin().await?;
    }

Integration Notes

Metadata and the runtime version of the node can be obtained using the chainHead API, although it is not user friendly, nor ergonomic:

  • a subscription must be created with the runtime flag set (true)
  • a custom runtime API call "Metadata_metadata" must be made to obtain the metadata
  • runtime version must be obtained from the first event reported by the subscription.

Considering that metadata and runtime version must be available when creating the subxt client, the complete integration with chainHead makes an extensive overhead in terms of complexity and code to maintain.
This could in part be mitigated by the archive family of functions, which would achieve the same result without needing to create a subscription.

The current API allows users to query at arbitrary blocks (body / header / storage), this will make difficult the integration with the chainHead API without modifying subxt. Although, subxt as it is could rely on the archive methods instead to not alter the current API.

Part of #732.

Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
@lexnv lexnv self-assigned this Jan 9, 2023
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
@jsdw
Copy link
Collaborator

jsdw commented Jan 10, 2023

This looks great, and gives us a lot to think about!

Some slightly out of order thoughts I have offhand:

  1. I don't want us to have any new RPC-V2 functions living next to the existing ones in our higher level APIs (Users shouldn't care how the details are obtained and I don't want people having two options to get blocks for instance).
  2. That said, exposing the new RPC API functions that we'll make use of at the RPC layer I think makes sense to give us something to start working with. (though we'll want to make clear that they aren't a part of the stable subxt interface yet, so I think maybe keeping the word "unstable" in them makes sense). They could possibly be hidden behind a feature flag. I'd lean towards also just mirroring the RPC names and not prefixing "subscribe" etc (it's clear in the return type anyway).
  3. One thing that is made more clear to me here is that we can double down on the "block centric" sort of API. That is; accessing storage, the "call" stuff, events, extrinsic details etc are all functions that you can call once you obtain a block (given that you have a subscription ID to use). Any .at(hash) style methods imply calling into the "archive_" methods to obtain details.

So, I guess I'd propose the following:

  1. Split the PR up. Let's focus on merging the RPC methods and types such that all of the functionality of the chain head API is exposed at the RPC level. This will provide us a base to build future things on (while giving us time to think about what those APIs will look like).
  2. In the mean time, we can start shaping the current API a little more towards what the V2 methods expose. ie, we already expose extrinsics and events on the blocks API; let's provide some entry point into getting storage things too, in a way that is compatible with the V2 API when we eventually use it.
  3. Let's have a chat about the archive_ RPC methods and see where we are there (ie what would we need to add so that you could get hold of old storage/blocks etc in subxt using the .at(hash) style entry points).

And later, we can either:

  • Swap out the existing APIs (which are hopefully close by now to how the new RPC wants to work) to use the new RPC methods etc after enough time has passed that they are probably available in most places, or
  • Optionally swap them out behind a feature flag (where we only expose one set of APIs, but the feature flag can make use of V2 methods). We can work on this before it's necessarily stabilised.

What do you think?

@lexnv lexnv mentioned this pull request Jan 10, 2023
@jsdw
Copy link
Collaborator

jsdw commented Jan 11, 2023

Given the above I'll close this, but I'm sure that this work will be very useful to refer to as we do get to building this API out :)

@jsdw jsdw closed this Jan 11, 2023
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.

2 participants