From e00031e481c381d0622e2e366247fadd20ec1ac5 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:44:20 +0300 Subject: [PATCH] Metadata V15: Enrich extrinsic type info for decoding (#14123) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * metadata-ir: Add extrinsic type info to decode address, call, sig Signed-off-by: Alexandru Vasile * frame-metadata: Point to unreleased branch Signed-off-by: Alexandru Vasile * metadata-ir: Include addrees, call, signature in V15 conversion Signed-off-by: Alexandru Vasile * metadata-ir: Include extra ty Signed-off-by: Alexandru Vasile * construct_runtime: Extract address,call,sig,extra ty from tx type Signed-off-by: Alexandru Vasile * frame/tests: Check metadata populates xt types correctly Signed-off-by: Alexandru Vasile * metadata-ir/tests: Add extra fields on ExtrinsicMetadataIR Signed-off-by: Alexandru Vasile * primitives/traits: Expand the `Extrinsic::SignaturePayload` Signed-off-by: Alexandru Vasile * primitives: Adjust to new `Extrinsic` associated types Signed-off-by: Alexandru Vasile * frame/metadata: Simplify metadata generation Signed-off-by: Alexandru Vasile * frame/example: Adjust to new interface Signed-off-by: Alexandru Vasile * frame/tests: Adjust `extrinsic_metadata_ir_types` Signed-off-by: Alexandru Vasile * Revert the additional Extrinsic' associated types Signed-off-by: Alexandru Vasile * primitives: Add `SignaturePayload` marker trait Signed-off-by: Alexandru Vasile * primitives: Implement SignaturePayload for empty tuple Signed-off-by: Alexandru Vasile * Adjust to new SignaturePayload trait Signed-off-by: Alexandru Vasile * tests: Adjust `extrinsic_metadata_ir_types` to new interface Signed-off-by: Alexandru Vasile * frame/support: Adjust pallet test Signed-off-by: Alexandru Vasile * frame: Add Extrinsic length prefix to the metadata Signed-off-by: Alexandru Vasile * primitives: Populate `ExtrinsicMetadataIR` with `len_ty` Signed-off-by: Alexandru Vasile * Update primitives/runtime/src/traits.rs Co-authored-by: Bastian Köcher * Apply cargo fmt Signed-off-by: Alexandru Vasile * v15: Remove len type of the extrinsic Signed-off-by: Alexandru Vasile * cargo: Update frame-metadata Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile Co-authored-by: Bastian Köcher Co-authored-by: parity-processbot <> --- .../src/construct_runtime/expand/metadata.rs | 20 ++++++++++++- frame/support/src/traits/misc.rs | 8 +++-- frame/support/test/tests/pallet.rs | 28 ++++++++++++++++-- primitives/metadata-ir/src/lib.rs | 4 +++ primitives/metadata-ir/src/types.rs | 14 +++++++++ primitives/metadata-ir/src/v15.rs | 9 +++--- .../src/generic/unchecked_extrinsic.rs | 21 ++++++++++---- primitives/runtime/src/testing.rs | 19 +++++++++--- primitives/runtime/src/traits.rs | 29 +++++++++++++++++-- 9 files changed, 131 insertions(+), 21 deletions(-) diff --git a/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/frame/support/procedural/src/construct_runtime/expand/metadata.rs index 3448ba84ec20b..7b99ecf6ad273 100644 --- a/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -96,11 +96,29 @@ pub fn expand_runtime_metadata( // `Deref` needs a reference for resolving the function call. let rt = #runtime; + let ty = #scrate::scale_info::meta_type::<#extrinsic>(); + let address_ty = #scrate::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress + >(); + let call_ty = #scrate::scale_info::meta_type::< + <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call + >(); + let signature_ty = #scrate::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature + >(); + let extra_ty = #scrate::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra + >(); + #scrate::metadata_ir::MetadataIR { pallets: #scrate::sp_std::vec![ #(#pallets),* ], extrinsic: #scrate::metadata_ir::ExtrinsicMetadataIR { - ty: #scrate::scale_info::meta_type::<#extrinsic>(), + ty, version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, + address_ty, + call_ty, + signature_ty, + extra_ty, signed_extensions: < < #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index a6f8c46d63951..85eb7b47e26da 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -893,7 +893,8 @@ pub trait ExtrinsicCall: sp_runtime::traits::Extrinsic { #[cfg(feature = "std")] impl ExtrinsicCall for sp_runtime::testing::TestXt where - Call: codec::Codec + Sync + Send, + Call: codec::Codec + Sync + Send + TypeInfo, + Extra: TypeInfo, { fn call(&self) -> &Self::Call { &self.call @@ -903,7 +904,10 @@ where impl ExtrinsicCall for sp_runtime::generic::UncheckedExtrinsic where - Extra: sp_runtime::traits::SignedExtension, + Address: TypeInfo, + Call: TypeInfo, + Signature: TypeInfo, + Extra: sp_runtime::traits::SignedExtension + TypeInfo, { fn call(&self) -> &Self::Call { &self.function diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index d0efcb3b5c18f..b7a7e2a767a6e 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -36,7 +36,10 @@ use sp_io::{ hashing::{blake2_128, twox_128, twox_64}, TestExternalities, }; -use sp_runtime::{DispatchError, ModuleError}; +use sp_runtime::{ + traits::{Extrinsic as ExtrinsicT, SignaturePayload as SignaturePayloadT}, + DispatchError, ModuleError, +}; parameter_types! { /// Used to control if the storage version should be updated. @@ -1700,6 +1703,28 @@ fn metadata_ir_pallet_runtime_docs() { assert_eq!(pallet.docs, expected); } +#[test] +fn extrinsic_metadata_ir_types() { + let ir = Runtime::metadata_ir().extrinsic; + + assert_eq!(meta_type::<<::SignaturePayload as SignaturePayloadT>::SignatureAddress>(), ir.address_ty); + assert_eq!(meta_type::(), ir.address_ty); + + assert_eq!(meta_type::<::Call>(), ir.call_ty); + assert_eq!(meta_type::(), ir.call_ty); + + assert_eq!( + meta_type::< + <::SignaturePayload as SignaturePayloadT>::Signature, + >(), + ir.signature_ty + ); + assert_eq!(meta_type::<()>(), ir.signature_ty); + + assert_eq!(meta_type::<<::SignaturePayload as SignaturePayloadT>::SignatureExtra>(), ir.extra_ty); + assert_eq!(meta_type::>(), ir.extra_ty); +} + #[test] fn test_pallet_runtime_docs() { let docs = crate::pallet::Pallet::::pallet_documentation_metadata(); @@ -1713,7 +1738,6 @@ fn test_pallet_info_access() { assert_eq!(::name(), "System"); assert_eq!(::name(), "Example"); assert_eq!(::name(), "Example2"); - assert_eq!(::index(), 0); assert_eq!(::index(), 1); assert_eq!(::index(), 2); diff --git a/primitives/metadata-ir/src/lib.rs b/primitives/metadata-ir/src/lib.rs index a932585a75ca9..9dc03f964a09e 100644 --- a/primitives/metadata-ir/src/lib.rs +++ b/primitives/metadata-ir/src/lib.rs @@ -81,6 +81,10 @@ mod test { extrinsic: ExtrinsicMetadataIR { ty: meta_type::<()>(), version: 0, + address_ty: meta_type::<()>(), + call_ty: meta_type::<()>(), + signature_ty: meta_type::<()>(), + extra_ty: meta_type::<()>(), signed_extensions: vec![], }, ty: meta_type::<()>(), diff --git a/primitives/metadata-ir/src/types.rs b/primitives/metadata-ir/src/types.rs index 76d9385cd3cbf..b107d20a8e2bf 100644 --- a/primitives/metadata-ir/src/types.rs +++ b/primitives/metadata-ir/src/types.rs @@ -155,9 +155,19 @@ impl IntoPortable for PalletMetadataIR { #[derive(Clone, PartialEq, Eq, Encode, Debug)] pub struct ExtrinsicMetadataIR { /// The type of the extrinsic. + /// + /// Note: Field used for metadata V14 only. pub ty: T::Type, /// Extrinsic version. pub version: u8, + /// The type of the address that signes the extrinsic + pub address_ty: T::Type, + /// The type of the outermost Call enum. + pub call_ty: T::Type, + /// The type of the extrinsic's signature. + pub signature_ty: T::Type, + /// The type of the outermost Extra enum. + pub extra_ty: T::Type, /// The signed extensions in the order they appear in the extrinsic. pub signed_extensions: Vec>, } @@ -169,6 +179,10 @@ impl IntoPortable for ExtrinsicMetadataIR { ExtrinsicMetadataIR { ty: registry.register_type(&self.ty), version: self.version, + address_ty: registry.register_type(&self.address_ty), + call_ty: registry.register_type(&self.call_ty), + signature_ty: registry.register_type(&self.signature_ty), + extra_ty: registry.register_type(&self.extra_ty), signed_extensions: registry.map_into_portable(self.signed_extensions), } } diff --git a/primitives/metadata-ir/src/v15.rs b/primitives/metadata-ir/src/v15.rs index 814a5ae98372f..a942eb73223b2 100644 --- a/primitives/metadata-ir/src/v15.rs +++ b/primitives/metadata-ir/src/v15.rs @@ -101,12 +101,11 @@ impl From for ExtrinsicMetadata { fn from(ir: ExtrinsicMetadataIR) -> Self { ExtrinsicMetadata { version: ir.version, + address_ty: ir.address_ty, + call_ty: ir.call_ty, + signature_ty: ir.signature_ty, + extra_ty: ir.extra_ty, signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), - // Note: These fields are populated by complementary PR: https://github.com/paritytech/substrate/pull/14123. - address_ty: ir.ty, - call_ty: ir.ty, - signature_ty: ir.ty, - extra_ty: ir.ty, } } } diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index b9d7b9eb1b6c8..0b1cd2b54290e 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -21,7 +21,7 @@ use crate::{ generic::CheckedExtrinsic, traits::{ self, Checkable, Extrinsic, ExtrinsicMetadata, IdentifyAccount, MaybeDisplay, Member, - SignedExtension, + SignaturePayload, SignedExtension, }, transaction_validity::{InvalidTransaction, TransactionValidityError}, OpaqueExtrinsic, @@ -40,6 +40,9 @@ use sp_std::{fmt, prelude::*}; /// the decoding fails. const EXTRINSIC_FORMAT_VERSION: u8 = 4; +/// The `SingaturePayload` of `UncheckedExtrinsic`. +type UncheckedSignaturePayload = (Address, Signature, Extra); + /// A extrinsic right from the external world. This is unchecked and so /// can contain a signature. #[derive(PartialEq, Eq, Clone)] @@ -50,11 +53,19 @@ where /// The signature, address, number of extrinsics have come before from /// the same signer and an era describing the longevity of this transaction, /// if this is a signed extrinsic. - pub signature: Option<(Address, Signature, Extra)>, + pub signature: Option>, /// The function that should be called. pub function: Call, } +impl SignaturePayload + for UncheckedSignaturePayload +{ + type SignatureAddress = Address; + type Signature = Signature; + type SignatureExtra = Extra; +} + /// Manual [`TypeInfo`] implementation because of custom encoding. The data is a valid encoded /// `Vec`, but requires some logic to extract the signature and payload. /// @@ -103,12 +114,12 @@ impl } } -impl Extrinsic - for UncheckedExtrinsic +impl + Extrinsic for UncheckedExtrinsic { type Call = Call; - type SignaturePayload = (Address, Signature, Extra); + type SignaturePayload = UncheckedSignaturePayload; fn is_signed(&self) -> Option { Some(self.signature.is_some()) diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index 6d02e23094f90..abf4d81634691 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -23,7 +23,7 @@ use crate::{ scale_info::TypeInfo, traits::{ self, Applyable, BlakeTwo256, Checkable, DispatchInfoOf, Dispatchable, OpaqueKeys, - PostDispatchInfoOf, SignedExtension, ValidateUnsigned, + PostDispatchInfoOf, SignaturePayload, SignedExtension, ValidateUnsigned, }, transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, ApplyExtrinsicResultWithInfo, KeyTypeId, @@ -279,6 +279,15 @@ where } } +/// The signature payload of a `TestXt`. +type TxSingaturePayload = (u64, Extra); + +impl SignaturePayload for TxSingaturePayload { + type SignatureAddress = u64; + type Signature = (); + type SignatureExtra = Extra; +} + /// Test transaction, tuple of (sender, call, signed_extra) /// with index only used if sender is some. /// @@ -286,7 +295,7 @@ where #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)] pub struct TestXt { /// Signature of the extrinsic. - pub signature: Option<(u64, Extra)>, + pub signature: Option>, /// Call of the extrinsic. pub call: Call, } @@ -331,9 +340,11 @@ impl Checkable for TestXt traits::Extrinsic for TestXt { +impl traits::Extrinsic + for TestXt +{ type Call = Call; - type SignaturePayload = (u64, Extra); + type SignaturePayload = TxSingaturePayload; fn is_signed(&self) -> Option { Some(self.signature.is_some()) diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 9c09bad21d9bc..3aa27234fbce2 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -1242,14 +1242,14 @@ pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerialize + Debug + 'st /// Something that acts like an `Extrinsic`. pub trait Extrinsic: Sized { /// The function call. - type Call; + type Call: TypeInfo; /// The payload we carry for signed extrinsics. /// /// Usually it will contain a `Signature` and /// may include some additional data that are specific to signed /// extrinsics. - type SignaturePayload; + type SignaturePayload: SignaturePayload; /// Is this `Extrinsic` signed? /// If no information are available about signed/unsigned, `None` should be returned. @@ -1268,6 +1268,31 @@ pub trait Extrinsic: Sized { } } +/// Something that acts like a [`SignaturePayload`](Extrinsic::SignaturePayload) of an +/// [`Extrinsic`]. +pub trait SignaturePayload { + /// The type of the address that signed the extrinsic. + /// + /// Particular to a signed extrinsic. + type SignatureAddress: TypeInfo; + + /// The signature type of the extrinsic. + /// + /// Particular to a signed extrinsic. + type Signature: TypeInfo; + + /// The additional data that is specific to the signed extrinsic. + /// + /// Particular to a signed extrinsic. + type SignatureExtra: TypeInfo; +} + +impl SignaturePayload for () { + type SignatureAddress = (); + type Signature = (); + type SignatureExtra = (); +} + /// Implementor is an [`Extrinsic`] and provides metadata about this extrinsic. pub trait ExtrinsicMetadata { /// The format version of the `Extrinsic`.