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

Support constructing and submitting V5 transactions #1931

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open

Conversation

jsdw
Copy link
Collaborator

@jsdw jsdw commented Feb 24, 2025

This PR adds support for constructing v5 bare and general extrinsics, and updates some of the surrounding terminology to match that of polkadot-sdk More concretely:

  • Add support for VerifySignature transaction extension (this is how a signature is passed in a V5 general extrinsic)
  • Make alterations to allow this to be populated as needed, and for the correct signer payload to be constructed (in V5, we always hash the payload, we ignore any extensions prior to/including VerifySignature when constructing the payload, and we inject signature into this payload just prior to submitting).
  • Simplify setting mortality; no longer need to provide the "from" block details, just the number of blocks to be valid from.
  • Change RefineParams into Params and change interface to inject_* to align with how we now inject signature.
  • add versioned methods to create extrinsics; create_v4_partial, create_v5_partial, create_v4_partial_offline, create_v5_partial_offline, create_v4_unsigned, create_v5_bare. Add similar to subxt_core (only having versioned methods at that level).
  • Signer methods take T::AccountId rather than T::Address now because this is what V5 requires, and only AccountId was ever useful in v4 anyway. SignerT::address is thus not needed and removed. (See https://github.com/paritytech/polkadot-sdk/blob/cf52a0d960a17b4d023bb7c9351e159c60227c85/docs/sdk/src/reference_docs/extrinsic_encoding.rs#L100)
  • Renames: create_partial_signed => create_partial, SignedExtension => TransactionExtension, extra => value, additional => implicit, PartialExtrinsic => PartialTransaction, SubmittableExtrinsic => SubmittableTransaction
  • Add tests to create and then decode extrinsics (via frame_decode) to help ensure things are working.

Todo:

  • Use frame-decode to check that we can decode constructed transactions properly
  • Address vs AccountId; do we need to allow both or support v4 construction as a fallback?

@jsdw jsdw requested a review from a team as a code owner February 24, 2025 15:49
@jsdw jsdw marked this pull request as draft February 24, 2025 15:50
@jsdw jsdw marked this pull request as ready for review February 25, 2025 17:58
@jsdw jsdw changed the title Support constructing V5 transactions Support constructing abd submitting V5 transactions Feb 25, 2025
@jsdw jsdw changed the title Support constructing abd submitting V5 transactions Support constructing and submitting V5 transactions Feb 25, 2025
/// `None` means the tx will be immortal.
mortality: Option<Mortality<T::Hash>>,
/// `None` means the tx will be immortal, else it's mortal for N blocks (if possible).
mortality: Option<u64>,
Copy link
Member

Choose a reason for hiding this comment

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

I guess this is related to changes of RefineParams stuff, I like that the API is much easier to use block number instead of both block hash and block number

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I messed with everything a bunch and then realised I had to undo a load of stuff, but wanted to keep not needing the block hash too :)

/// Set the signature. This happens after we have constructed the extrinsic params,
/// and so is defined here rather than on the params, below. We need to use `&dyn Any`
/// to keep this trait object safe, but can downcast in the impls.
fn inject_signature(&mut self, _account_id: &dyn Any, _signature: &dyn Any) {}
Copy link
Member

@niklasad1 niklasad1 Mar 7, 2025

Choose a reason for hiding this comment

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

aye, I see the downcast/upcast annoyance now

IIRC, these needs to be downcasted to T::AccountId and T::Signature in the impl maybe add a big
warning if account_id and signature doesn't have the correct types it will panic

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yup exactly; the impl has access to T: Config and the types come from a client with the same config, so all should work, but yeah let's make the warnings way clearer here!

) -> Result<Transaction<T>, Error> {
// 1. Validate this call against the current node metadata if the call comes
// with a hash allowing us to do so.
validate(call, metadata)?;

// 2. Encode extrinsic
// 3. Encode extrinsic
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
// 3. Encode extrinsic
// 2. Encode extrinsic

revert, looks like it was correct before

//
// Either could make sense, but we keep the oldest to prioritize backward compat with
// older tooling.
version: *e
Copy link
Member

Choose a reason for hiding this comment

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

does this mean in practice that we will probably just use transaction v4 if we assume both formats are supported by the node we connected to?

Copy link
Collaborator Author

@jsdw jsdw Mar 7, 2025

Choose a reason for hiding this comment

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

Yup; we pick the lowest version for now because we know it'll work!

(for instance, right now you can submit v5 extrinsics but the VerifySignature extension doesn't appear to be used yet anywhere (at least on Substrate dev nodes and iirc polkadot), which means you couldn't sign them, so they would fail anyways)

///
/// Note: if not provided, the default account nonce will be set to 0 and the default mortality will be _immortal_.
/// This is because this method runs offline, and so is unable to fetch the data needed for more appropriate values.
pub fn create_partial_signed_offline<Call>(
pub fn create_partial_offline<Call>(
Copy link
Member

@niklasad1 niklasad1 Mar 7, 2025

Choose a reason for hiding this comment

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

create_partial_signed_offline removed?

I guess one needs to call sign explicitly on it now?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's just calleed create_partial_offline now :)

The "signed" bit felt confusing because the thing being created is partial but it's definitely not signed yet!

Copy link
Member

@niklasad1 niklasad1 left a comment

Choose a reason for hiding this comment

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

Nice, looks way cleaner than I had mind.

Perhaps just some extra clarification on the method that exposes the dyn Any stuff.

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