Skip to content

Conversation

@gui1117
Copy link
Contributor

@gui1117 gui1117 commented Jan 3, 2025

This PR enhance UncheckedExtrinsic type with a new optional generic: ExtensionOtherVersion.
This generic defaults to InvalidVersion meaning there is not other version than the regular version 0. This is the same behavior as before this PR.

New feature

To use this new feature, you can use the new types TxExtLineAtVers and MultiVersion to define a transaction extension with multiple version:

pub type TransactionExtensionV0 = ();
pub type TransactionExtensionV4 = ();
pub type TransactionExtensionV7 = ();

pub type OtherVersions = MultiVersion<
	TxExtLineAtVers<4, TransactionExtensionV4>;
	TxExtLineAtVers<7, TransactionExtensionV7>;
>;

pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<
	AccountId,
	RuntimeCall,
	UintAuthorityId,
	TransactionExtensionV0, // The version 0, same as before
	OtherVersions, // The other versions.
>;

Breaking change

The types Preamble, CheckedExtrinsic and ExtrinsicFormat also have this new optional generic. Their type definition also have changed a bit, the General variant was 2 fields, the version and the extension, it is now only one field, the extension, and the version can be retrieve by calling extension.version()

The type inference for those types may fail because of this PR, to update the code, write some partial type: UncheckedExtrinsic<_, _, _, _>, Preamble<_, _, _>, ExtrinsicFormat<_, _> and CheckedExtrinsic<_, _, _>`.

Alternative implementation

This PR breaks the types a bit, I think it is very minimal and fine, but if this is annoying we can still keep the old types and write new types such as UncheckedExtrinsicV2, CheckedExtrinsicV2 etc..

@gui1117 gui1117 marked this pull request as ready for review January 8, 2025 09:44
@gui1117 gui1117 requested a review from a team as a code owner January 8, 2025 09:44
@gui1117 gui1117 added the T1-FRAME This PR/Issue is related to core FRAME, the framework. label Jan 8, 2025
Copy link
Contributor

@franciscoaguirre franciscoaguirre 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

}

#[test]
fn dispatch_of_invalid_extrinsic_fails() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a comment explaining why this is invalid?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I changed the inner comments to make it more clear. Also the previous inner comment was wrong.

Fixed: 0071ee2

///
/// This trait is part of [`VersTxExtLine`]. It is defined independently to allow implementation to
/// rely only on it without bounding the whole trait [`VersTxExtLine`].
pub trait VersTxExtLineWeight<Call: Dispatchable> {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the Line in the name?

Copy link
Contributor Author

@gui1117 gui1117 Jan 9, 2026

Choose a reason for hiding this comment

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

It is short for pipeline. I couldn't find good names for this. The main issue is that we often conflate a singular transaction extension, and a pipeline of transaction extension. They are both represented in a single trait TransactionExtension where the associated const IDENTIFIER is only meaningful for the singular transaction extension, and the associated function fn metadata() -> Vec<TransactionExtensionMetadata> has a type signatured designed for the pipeline, and the automatic implementation is wrong for the pipeline and must be override.

So for those new types I tried to explicitly make a difference by adding this Line in the name, which means it is only meaningful for a pipeline.
The version we give here is only a version for the pipeline, not an individual transaction extension.

I added the reason for the name in the doc: 0071ee2

Copy link
Contributor

@carlosala carlosala left a comment

Choose a reason for hiding this comment

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

It looks good!

signature_ty: ir.signature_ty,
extra_ty: ir.extra_ty,
signed_extensions: ir.extensions.into_iter().map(Into::into).collect(),
signed_extensions: ir.extensions_v0.into_iter().map(Into::into).collect(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Just passing by, checking this change was made, and we were not adding lots of wrong extensions!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes and for metadata v16, if a user still want to use a signed transaction instead of a general transaction, the version for the the extensions is 0.

Comment on lines 1505 to 1508
/// The transaction extensions version 0 attached to this `Extrinsic`.
// We could remove this associated type and let user retrieve it from
// `TransactionExtensionsVersions`.
type TransactionExtensionsV0;
Copy link
Member

Choose a reason for hiding this comment

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

Yeah we should probably merge.

Copy link
Contributor Author

@gui1117 gui1117 Jan 22, 2026

Choose a reason for hiding this comment

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

yes I now removed TransactionExtensionsV0: a49294c

type TransactionExtensionsV0;

/// All version of transaction extensions attached to this `Extrinsic`.
type TransactionExtensionsVersions;
Copy link
Member

Choose a reason for hiding this comment

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

Naming could be improved, because these are the transaction extensions and not just their versions?

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 will use TransactionExtensionPipelines I think

Comment on lines +426 to +430
// NOTE: We avoid adding `ExtensionOtherVersions` in the type parameter type info
// because they break tools like subxt that hard-coded the number of type
// parameters. Also in general type parameters are not useful, information related
// to transaction extension are available in the metadata like for v16 the fields
// `transaction_extensions_by_version` and `transaction_extensions`.
Copy link
Member

Choose a reason for hiding this comment

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

What is subxt using internally to build/verify the extrinsic? The dedicated extrinsic metadata or just this type somehow extracted from the metadata?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines 246 to 247
/// The transaction extensions in the order they appear in the extrinsic for the version 0.
pub extensions_v0: Vec<TransactionExtensionMetadataIR<T>>,
Copy link
Member

Choose a reason for hiding this comment

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

IMO we should remove this and have the invariant that 0 always exists in extensions_by_version.

Copy link
Contributor Author

@gui1117 gui1117 Jan 22, 2026

Choose a reason for hiding this comment

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

yes I now removed TransactionExtensionsV0: a9942a8

Comment on lines 246 to 247
/// The transaction extensions in the order they appear in the extrinsic for the version 0.
pub extensions_v0: Vec<TransactionExtensionMetadataIR<T>>,
Copy link
Member

Choose a reason for hiding this comment

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

Yeah I would remove this and always assume that extensions_by_version contains 0.

Copy link
Contributor Author

@gui1117 gui1117 Jan 22, 2026

Choose a reason for hiding this comment

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

yes I now removed TransactionExtensionsV0: a9942a8

Comment on lines 65 to 66
// The type cannot be instantiated so this method is never called.
Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0)))
Copy link
Member

Choose a reason for hiding this comment

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

Then just use unimplemented!()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok fixed here e071249


/// A type implements [`DecodeWithVersion`] where inner decoding is implementing
/// [`codec::DecodeWithMemTracking`].
pub trait DecodeWithVersionWithMemTracking: DecodeWithVersion {}
Copy link
Member

Choose a reason for hiding this comment

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

Yeah why do we need this?

/// This defines multiple version of a transaction extensions pipeline.
///
/// (type name is short for versioned (Vers) transaction (Tx) Extension (Ext) pipeline (Line)).
pub trait VersTxExtLine<Call: Dispatchable>:
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need 5 different traits? Can this not just be one trait?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For VersTxExtLineWeight, we could merge it in, I avoided because it required to change some bounds here and there that weren't bound before. But also it looks ok to force them as I don't think anybody use the genericity offered here. https://github.com/paritytech/polkadot-sdk/compare/gui-transaction-extension-multiple-version...gui-transaction-extension-multiple-version-remove-tx-weight?expand=1

For VersTxExtLineVersion, I don't think we can merge it in. It it required to derive Encode for many types such as Preamble but VersTxExtLine is generic over the call, and Preamble doesn't have a call.
Then the question is why VersTxExtLine is generic over the call? IIRC because TransactionExtension is generic over the call. Why is TransactionExtension generic over the call? actually I think for no reason. Usually the transaction extensions are defined generically over the runtime config, which defines the call. So TransactionExtension could be not generic and instead have an associated type name Call but this is a bit late. Or Maybe I miss something.

For DecodeWithVersion, same issue as VersTxExtLineVersion but for implementing Decode on Preamble.

(Note that maybe both issue could be fixed with having Preamble use a phantom data for the call. not sure).

For DecodeWithVersionWithMemTracking, for consistency and hopefully less errors, but also I agree we can merge into DecodeWithVersion.

/// ```
#[allow(private_interfaces)]
#[derive(PartialEq, Eq, Clone, Debug, TypeInfo)]
pub enum MultiVersion<
Copy link
Member

Choose a reason for hiding this comment

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

This entire type can just be replaced by a tuple of TxExtLineAtVers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can replace it with a tuple of something I guess but not TxExtLineAtVers. We need to implement Encode and Decode, so the resulting type is not a tuple of all possible pipelines but it is the variant for the version.

If we want to define this aggregation of versioned pipeline with a tuple I guess we need a new trait that decodes into a trait object?

trait PipelineDefinition {
  fn decode() -> Result<Box<impl VersTxExtLine>, ()>;
  fn metadata() -> Metadata;
}

Then we can define all the supported version with a tuple of PipelineDefinition.
If we want this we also need to move the type info into this PipelineDefinition because it would make the VersTxExtLine incompatible with trait object usage.

Maybe MultiVersions is ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

or we can make a tuple that defines an associated type and the associated type is just MultiVersion of each element of the tuple's associated type.

Comment on lines 1506 to 1511
// We could remove this associated type and let user retrieve it from
// `TransactionExtensionsVersions`.
type TransactionExtensionsV0;

/// All version of transaction extensions attached to this `Extrinsic`.
type TransactionExtensionsVersions;
Copy link
Member

Choose a reason for hiding this comment

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

Probably also not worth the split here?

Copy link
Contributor Author

@gui1117 gui1117 Jan 22, 2026

Choose a reason for hiding this comment

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

yes I now removed TransactionExtensionsV0: a49294c

@gui1117
Copy link
Contributor Author

gui1117 commented Jan 25, 2026

I currently renamed into:

  • VersTxExtLinePipeline : The transaction extension pipeline global trait
  • VersTxExtLineWeightPipelineWeight : The weight trait for the transaction extension pipeline (probably we should merge this into Pipeline)
  • VersTxExtLineVersionPipelineVersion : The version trait for the transaction extension pipeline
  • VersTxExtLineMetadataBuilderPipelineMetadataBuilder : Just a helper type to build the metadata, not so important IMO
  • TxExtLineAtVersPipelineAtVers : The type to easily declare a pipeline for a specific version. Maybe AtVers is a better name?
  • TransactionExtensionsVersionsTransactionExtensionPipelines : the associated type in the metadata for the definition of all transaction extension pipelines

@paritytech-workflow-stopper
Copy link

All GitHub workflows were cancelled due to failure one of the required jobs.
Failed workflow url: https://github.com/paritytech/polkadot-sdk/actions/runs/21381994474
Failed job name: test-linux-stable-no-try-runtime

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

Labels

T1-FRAME This PR/Issue is related to core FRAME, the framework. T17-primitives Changes to primitives that are not covered by any other label.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants