diff --git a/Cargo.lock b/Cargo.lock index d4185103f8c96..f55180f9ca942 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2413,9 +2413,9 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" dependencies = [ "cfg-if", "parity-scale-codec", @@ -2525,6 +2525,7 @@ dependencies = [ "rustversion", "scale-info", "serde", + "sp-api", "sp-arithmetic", "sp-core", "sp-io", @@ -10156,8 +10157,10 @@ dependencies = [ "hash-db", "log", "parity-scale-codec", + "scale-info", "sp-api-proc-macro", "sp-core", + "sp-metadata-ir", "sp-runtime", "sp-state-machine", "sp-std", @@ -10172,6 +10175,7 @@ name = "sp-api-proc-macro" version = "4.0.0-dev" dependencies = [ "Inflector", + "assert_matches", "blake2", "expander", "proc-macro-crate", @@ -10591,6 +10595,16 @@ dependencies = [ "zstd", ] +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std", +] + [[package]] name = "sp-mmr-primitives" version = "4.0.0-dev" @@ -10842,6 +10856,7 @@ name = "sp-test-primitives" version = "2.0.0" dependencies = [ "parity-scale-codec", + "scale-info", "serde", "sp-application-crypto", "sp-core", diff --git a/Cargo.toml b/Cargo.toml index de562ad79e47e..cae8976cca4a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -202,6 +202,7 @@ members = [ "primitives/keystore", "primitives/maybe-compressed-blob", "primitives/merkle-mountain-range", + "primitives/metadata-ir", "primitives/npos-elections", "primitives/npos-elections/fuzzer", "primitives/offchain", diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 820658372afd9..7792fdf015ba5 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.136", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -frame-metadata = { version = "15.0.0", default-features = false, features = ["v14"] } +frame-metadata = { version = "15.1.0", default-features = false, features = ["v14", "v15-unstable"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" } @@ -74,7 +74,7 @@ runtime-benchmarks = [] try-runtime = [] # By default some types have documentation, `no-metadata-docs` allows to reduce the documentation # in the metadata. -no-metadata-docs = ["frame-support-procedural/no-metadata-docs"] +no-metadata-docs = ["frame-support-procedural/no-metadata-docs", "sp-api/no-metadata-docs"] # By default some types have documentation, `full-metadata-docs` allows to add documentation to # more types in the metadata. full-metadata-docs = ["scale-info/docs"] diff --git a/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/frame/support/procedural/src/construct_runtime/expand/metadata.rs index ba6a621af7523..81fc93ba3c9ef 100644 --- a/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -77,6 +77,24 @@ pub fn expand_runtime_metadata( quote! { impl #runtime { fn metadata_ir() -> #scrate::metadata_ir::MetadataIR { + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + // + // `Deref` needs a reference for resolving the function call. + let rt = #runtime; + #scrate::metadata_ir::MetadataIR { pallets: #scrate::sp_std::vec![ #(#pallets),* ], extrinsic: #scrate::metadata_ir::ExtrinsicMetadataIR { @@ -95,7 +113,8 @@ pub fn expand_runtime_metadata( }) .collect(), }, - ty: #scrate::scale_info::meta_type::<#runtime>() + ty: #scrate::scale_info::meta_type::<#runtime>(), + apis: (&rt).runtime_metadata(), } } diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index a0d414154bfcc..c5a48a98063a4 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -296,6 +296,31 @@ fn construct_runtime_final_expansion( type RuntimeBlock = #block; } + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::sp_std::vec::Vec<#scrate::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + #outer_event #outer_origin diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index ac897fef0d872..036bd93464b9f 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -50,6 +50,7 @@ pub use paste; pub use scale_info; #[cfg(feature = "std")] pub use serde; +pub use sp_api::metadata_ir; pub use sp_core::{OpaqueMetadata, Void}; #[doc(hidden)] pub use sp_core_hashing_proc_macro; @@ -80,7 +81,6 @@ pub mod error; pub mod crypto; pub mod dispatch_context; pub mod instances; -pub mod metadata_ir; pub mod migrations; pub mod traits; pub mod weights; diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 90ef243eed6c6..ad4589c734fde 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.136", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../../primitives/arithmetic" } sp-io = { version = "7.0.0", path = "../../../primitives/io", default-features = false } sp-state-machine = { version = "0.13.0", optional = true, path = "../../../primitives/state-machine" } @@ -47,6 +48,7 @@ std = [ "sp-state-machine", "sp-arithmetic/std", "sp-version/std", + "sp-api/std", ] try-runtime = ["frame-support/try-runtime"] # WARNING: diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 11f82c4c0d266..f1dc97223322f 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -1671,7 +1671,7 @@ fn metadata_at_version() { #[test] fn metadata_versions() { - assert_eq!(vec![LATEST_METADATA_VERSION], Runtime::metadata_versions()); + assert_eq!(vec![LATEST_METADATA_VERSION, u32::MAX], Runtime::metadata_versions()); } #[test] diff --git a/frame/support/test/tests/runtime_metadata.rs b/frame/support/test/tests/runtime_metadata.rs new file mode 100644 index 0000000000000..8c04c785a35af --- /dev/null +++ b/frame/support/test/tests/runtime_metadata.rs @@ -0,0 +1,222 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support::{ + metadata_ir::{ + RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, + }, + traits::ConstU32, +}; +use scale_info::{form::MetaForm, meta_type}; +use sp_runtime::traits::Block as BlockT; + +pub type BlockNumber = u64; +pub type Index = u64; +pub type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; +pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; + +impl frame_system::Config for Runtime { + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u32; + type RuntimeCall = RuntimeCall; + type Hash = sp_runtime::testing::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU32<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system, + } +); + +sp_api::decl_runtime_apis! { + /// ApiWithCustomVersion trait documentation + /// + /// Documentation on multiline. + pub trait Api { + fn test(data: u64); + /// something_with_block. + fn something_with_block(block: Block) -> Block; + fn function_with_two_args(data: u64, block: Block); + fn same_name(); + fn wild_card(_: u32); + } +} + +sp_api::impl_runtime_apis! { + impl self::Api for Runtime { + fn test(_data: u64) { + unimplemented!() + } + + fn something_with_block(_: Block) -> Block { + unimplemented!() + } + + fn function_with_two_args(_: u64, _: Block) { + unimplemented!() + } + + fn same_name() {} + + fn wild_card(_: u32) {} + } + + impl sp_api::Core for Runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + fn execute_block(_: Block) { + unimplemented!() + } + fn initialize_block(_: &::Header) { + unimplemented!() + } + } +} + +#[test] +fn runtime_metadata() { + fn maybe_docs(doc: Vec<&'static str>) -> Vec<&'static str> { + if cfg!(feature = "no-metadata-docs") { + vec![] + } else { + doc + } + } + + let expected_runtime_metadata = vec![ + RuntimeApiMetadataIR { + name: "Api", + methods: vec![ + RuntimeApiMethodMetadataIR { + name: "test", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "data", + ty: meta_type::(), + }], + output: meta_type::<()>(), + docs: vec![], + }, + RuntimeApiMethodMetadataIR { + name: "something_with_block", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "block", + ty: meta_type::(), + }], + output: meta_type::(), + docs: maybe_docs(vec![" something_with_block."]), + }, + RuntimeApiMethodMetadataIR { + name: "function_with_two_args", + inputs: vec![ + RuntimeApiMethodParamMetadataIR:: { + name: "data", + ty: meta_type::(), + }, + RuntimeApiMethodParamMetadataIR:: { + name: "block", + ty: meta_type::(), + }, + ], + output: meta_type::<()>(), + docs: vec![], + }, + RuntimeApiMethodMetadataIR { + name: "same_name", + inputs: vec![], + output: meta_type::<()>(), + docs: vec![], + }, + RuntimeApiMethodMetadataIR { + name: "wild_card", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "_", + ty: meta_type::(), + }], + output: meta_type::<()>(), + docs: vec![], + }, + ], + docs: maybe_docs(vec![ + " ApiWithCustomVersion trait documentation", + "", + " Documentation on multiline.", + ]), + }, + RuntimeApiMetadataIR { + name: "Core", + methods: vec![ + RuntimeApiMethodMetadataIR { + name: "version", + inputs: vec![], + output: meta_type::(), + docs: maybe_docs(vec![" Returns the version of the runtime."]), + }, + RuntimeApiMethodMetadataIR { + name: "execute_block", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "block", + ty: meta_type::(), + }], + output: meta_type::<()>(), + docs: maybe_docs(vec![" Execute the given block."]), + }, + RuntimeApiMethodMetadataIR { + name: "initialize_block", + inputs: vec![RuntimeApiMethodParamMetadataIR:: { + name: "header", + ty: meta_type::<&::Header>(), + }], + output: meta_type::<()>(), + docs: maybe_docs(vec![" Initialize a block with the given header."]), + }, + ], + docs: maybe_docs(vec![ + " The `Core` runtime api that every Substrate runtime needs to implement.", + ]), + }, + ]; + + let rt = Runtime; + let runtime_metadata = (&rt).runtime_metadata(); + assert_eq!(runtime_metadata, expected_runtime_metadata); +} diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index ae1b3294c281f..55e3993466a6d 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -23,7 +23,8 @@ sp-state-machine = { version = "0.13.0", default-features = false, optional = tr sp-trie = { version = "7.0.0", default-features = false, optional = true, path = "../trie" } hash-db = { version = "0.16.0", optional = true } thiserror = { version = "1.0.30", optional = true } - +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-metadata-ir = { version = "0.1.0", default-features = false, path = "../metadata-ir" } log = { version = "0.4.17", default-features = false } [dev-dependencies] @@ -42,6 +43,8 @@ std = [ "hash-db", "thiserror", "log/std", + "scale-info/std", + "sp-metadata-ir/std", ] # Special feature to disable logging completly. # @@ -51,3 +54,5 @@ std = [ # # This sets the max logging level to `off` for `log`. disable-logging = ["log/max_level_off"] +# Do not report the documentation in the metadata. +no-metadata-docs = [] diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index ba7c6312042c9..1ba62cc53d5aa 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -24,6 +24,9 @@ proc-macro-crate = "1.1.3" expander = "1.0.0" Inflector = "0.11.4" +[dev-dependencies] +assert_matches = "1.3.0" + # Required for the doc tests [features] default = ["std"] diff --git a/primitives/api/proc-macro/src/decl_runtime_apis.rs b/primitives/api/proc-macro/src/decl_runtime_apis.rs index 3c3056d34487b..43e1e7969c5d1 100644 --- a/primitives/api/proc-macro/src/decl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/decl_runtime_apis.rs @@ -15,16 +15,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::utils::{ - extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side, generate_crate_access, - generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, - replace_wild_card_parameter_names, return_type_extract_type, versioned_trait_name, - AllowSelfRefInParameters, -}; - -use crate::common::{ - API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE, - RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES, +use crate::{ + common::{ + API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE, + RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES, + }, + runtime_metadata::generate_decl_runtime_metadata, + utils::{ + extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side, + generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version, + prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type, + versioned_trait_name, AllowSelfRefInParameters, + }, }; use proc_macro2::{Span, TokenStream}; @@ -219,6 +221,7 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { let mut decl = decl.clone(); let decl_span = decl.span(); extend_generics_with_block(&mut decl.generics); + let metadata = generate_decl_runtime_metadata(&decl); let mod_name = generate_runtime_mod_name_for_trait(&decl.ident); let found_attributes = remove_supported_attributes(&mut decl.attrs); let api_version = @@ -304,6 +307,8 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> Result { pub use #versioned_ident as #main_api_ident; + #metadata + pub #api_version pub #id diff --git a/primitives/api/proc-macro/src/impl_runtime_apis.rs b/primitives/api/proc-macro/src/impl_runtime_apis.rs index 0d265293ecf42..c0da8ccf30458 100644 --- a/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -15,15 +15,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::utils::{ - extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait, - extract_parameter_names_types_and_borrows, generate_crate_access, - generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, - versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath, +use crate::{ + common::API_VERSION_ATTRIBUTE, + runtime_metadata::generate_impl_runtime_metadata, + utils::{ + extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait, + extract_parameter_names_types_and_borrows, generate_crate_access, + generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, + versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath, + }, }; -use crate::common::API_VERSION_ATTRIBUTE; - use proc_macro2::{Span, TokenStream}; use quote::quote; @@ -685,6 +687,7 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { let runtime_api_versions = generate_runtime_api_versions(api_impls)?; let wasm_interface = generate_wasm_interface(api_impls)?; let api_impls_for_runtime_api = generate_api_impl_for_runtime_api(api_impls)?; + let runtime_metadata = generate_impl_runtime_metadata(api_impls)?; let impl_ = quote!( #base_runtime_api @@ -695,6 +698,8 @@ fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result { #runtime_api_versions + #runtime_metadata + pub mod api { use super::*; diff --git a/primitives/api/proc-macro/src/lib.rs b/primitives/api/proc-macro/src/lib.rs index cea958426879e..d34f4b7f9cf6a 100644 --- a/primitives/api/proc-macro/src/lib.rs +++ b/primitives/api/proc-macro/src/lib.rs @@ -25,6 +25,7 @@ mod common; mod decl_runtime_apis; mod impl_runtime_apis; mod mock_impl_runtime_apis; +mod runtime_metadata; mod utils; #[proc_macro] diff --git a/primitives/api/proc-macro/src/runtime_metadata.rs b/primitives/api/proc-macro/src/runtime_metadata.rs new file mode 100644 index 0000000000000..02b03baeaeee3 --- /dev/null +++ b/primitives/api/proc-macro/src/runtime_metadata.rs @@ -0,0 +1,271 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{parse_quote, ItemImpl, ItemTrait, Result}; + +use crate::{ + common::CHANGED_IN_ATTRIBUTE, + utils::{ + extract_impl_trait, filter_cfg_attributes, generate_crate_access, + generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath, + }, +}; + +/// Get the type parameter argument without lifetime or mutability +/// of a runtime metadata function. +/// +/// In the following example, both the `AccountId` and `Index` generic +/// type parameters must implement `scale_info::TypeInfo` because they +/// are added into the metadata using `scale_info::meta_type`. +/// +/// ```ignore +/// trait ExampleAccountNonceApi { +/// fn account_nonce<'a>(account: &'a AccountId) -> Index; +/// } +/// ``` +/// +/// Instead of returning `&'a AccountId` for the first parameter, this function +/// returns `AccountId` to place bounds around it. +fn get_type_param(ty: &syn::Type) -> syn::Type { + // Remove the lifetime and mutability of the type T to + // place bounds around it. + let ty_elem = match &ty { + syn::Type::Reference(reference) => &reference.elem, + syn::Type::Ptr(ptr) => &ptr.elem, + syn::Type::Slice(slice) => &slice.elem, + syn::Type::Array(arr) => &arr.elem, + _ => ty, + }; + + ty_elem.clone() +} + +/// Extract the documentation from the provided attributes. +/// +/// It takes into account the `no-metadata-docs` feature. +fn collect_docs(attrs: &[syn::Attribute], crate_: &TokenStream2) -> TokenStream2 { + if cfg!(feature = "no-metadata-docs") { + quote!(#crate_::vec![]) + } else { + let docs = get_doc_literals(&attrs); + quote!(#crate_::vec![ #( #docs, )* ]) + } +} + +/// Generate the runtime metadata of the provided trait. +/// +/// The metadata is exposed as a generic function on the hidden module +/// of the trait generated by the `decl_runtime_apis`. +pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { + let crate_ = generate_crate_access(); + let mut methods = Vec::new(); + + // Ensure that any function parameter that relies on the `BlockT` bounds + // also has `TypeInfo + 'static` bounds (required by `scale_info::meta_type`). + // + // For example, if a runtime API defines a method that has an input: + // `fn func(input: ::Header)` + // then the runtime metadata will imply `::Header: TypeInfo + 'static`. + // + // This restricts the bounds at the metadata level, without needing to modify the `BlockT` + // itself, since the concrete implementations are already satisfying `TypeInfo`. + let mut where_clause = Vec::new(); + for item in &decl.items { + // Collect metadata for methods only. + let syn::TraitItem::Method(method) = item else { + continue + }; + + // Collect metadata only for the latest methods. + let is_changed_in = + method.attrs.iter().any(|attr| attr.path.is_ident(CHANGED_IN_ATTRIBUTE)); + if is_changed_in { + continue + } + + let mut inputs = Vec::new(); + let signature = &method.sig; + for input in &signature.inputs { + // Exclude `self` from metadata collection. + let syn::FnArg::Typed(typed) = input else { + continue + }; + + let pat = &typed.pat; + let name = quote!(#pat).to_string(); + let ty = &typed.ty; + + where_clause.push(get_type_param(ty)); + + inputs.push(quote!( + #crate_::metadata_ir::RuntimeApiMethodParamMetadataIR { + name: #name, + ty: #crate_::scale_info::meta_type::<#ty>(), + } + )); + } + + let output = match &signature.output { + syn::ReturnType::Default => quote!(#crate_::scale_info::meta_type::<()>()), + syn::ReturnType::Type(_, ty) => { + where_clause.push(get_type_param(ty)); + quote!(#crate_::scale_info::meta_type::<#ty>()) + }, + }; + + // String method name including quotes for constructing `v15::RuntimeApiMethodMetadata`. + let method_name = signature.ident.to_string(); + let docs = collect_docs(&method.attrs, &crate_); + + // Include the method metadata only if its `cfg` features are enabled. + let attrs = filter_cfg_attributes(&method.attrs); + methods.push(quote!( + #( #attrs )* + #crate_::metadata_ir::RuntimeApiMethodMetadataIR { + name: #method_name, + inputs: #crate_::vec![ #( #inputs, )* ], + output: #output, + docs: #docs, + } + )); + } + + let trait_name_ident = &decl.ident; + let trait_name = trait_name_ident.to_string(); + let docs = collect_docs(&decl.attrs, &crate_); + let attrs = filter_cfg_attributes(&decl.attrs); + // The trait generics where already extended with `Block: BlockT`. + let mut generics = decl.generics.clone(); + for generic_param in generics.params.iter_mut() { + let syn::GenericParam::Type(ty) = generic_param else { + continue + }; + + // Default type parameters are not allowed in functions. + ty.eq_token = None; + ty.default = None; + } + + let where_clause = where_clause + .iter() + .map(|ty| quote!(#ty: #crate_::scale_info::TypeInfo + 'static)); + + quote!( + #( #attrs )* + #[inline(always)] + pub fn runtime_metadata #generics () -> #crate_::metadata_ir::RuntimeApiMetadataIR + where #( #where_clause, )* + { + #crate_::metadata_ir::RuntimeApiMetadataIR { + name: #trait_name, + methods: #crate_::vec![ #( #methods, )* ], + docs: #docs, + } + } + ) +} + +/// Implement the `runtime_metadata` function on the runtime that +/// generates the metadata for the given traits. +/// +/// The metadata of each trait is extracted from the generic function +/// exposed by `generate_decl_runtime_metadata`. +pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result { + if impls.is_empty() { + return Ok(quote!()) + } + + let crate_ = generate_crate_access(); + + // Get the name of the runtime for which the traits are implemented. + let runtime_name = &impls + .get(0) + .expect("Traits should contain at least one implementation; qed") + .self_ty; + + let mut metadata = Vec::new(); + + for impl_ in impls { + let mut trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone(); + + // Implementation traits are always references with a path `impl client::Core ...` + // The trait name is the last segment of this path. + let trait_name_ident = &trait_ + .segments + .last() + .as_ref() + .expect("Trait path should always contain at least one item; qed") + .ident; + + // Extract the generics from the trait to pass to the `runtime_metadata` + // function on the hidden module. + let generics = trait_ + .segments + .iter() + .find_map(|segment| { + if let syn::PathArguments::AngleBracketed(generics) = &segment.arguments { + Some(generics.clone()) + } else { + None + } + }) + .expect("Trait path should always contain at least one generic parameter; qed"); + + let mod_name = generate_runtime_mod_name_for_trait(&trait_name_ident); + // Get absolute path to the `runtime_decl_for_` module by replacing the last segment. + if let Some(segment) = trait_.segments.last_mut() { + *segment = parse_quote!(#mod_name); + } + + let attrs = filter_cfg_attributes(&impl_.attrs); + metadata.push(quote!( + #( #attrs )* + #trait_::runtime_metadata::#generics() + )); + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro + // construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` + // function. `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime + // references (`& Runtime`), while `InternalImplRuntimeApis` is implemented by the + // `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the + // `construct_runtime!` is called. + + Ok(quote!( + #[doc(hidden)] + trait InternalImplRuntimeApis { + #[inline(always)] + fn runtime_metadata(&self) -> #crate_::vec::Vec<#crate_::metadata_ir::RuntimeApiMetadataIR> { + #crate_::vec![ #( #metadata, )* ] + } + } + #[doc(hidden)] + impl InternalImplRuntimeApis for #runtime_name {} + )) +} diff --git a/primitives/api/proc-macro/src/utils.rs b/primitives/api/proc-macro/src/utils.rs index 4444a2624b669..cffaf317fbb10 100644 --- a/primitives/api/proc-macro/src/utils.rs +++ b/primitives/api/proc-macro/src/utils.rs @@ -253,7 +253,74 @@ pub fn parse_runtime_api_version(version: &Attribute) -> Result { version.base10_parse() } -// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. ParachainHostV2 +/// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. ParachainHostV2 pub fn versioned_trait_name(trait_ident: &Ident, version: u64) -> Ident { format_ident!("{}V{}", trait_ident, version) } + +/// Extract the documentation from the provided attributes. +pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec { + attrs + .iter() + .filter_map(|attr| { + let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() else { + return None + }; + + meta.path.get_ident().filter(|ident| *ident == "doc").map(|_| meta.lit) + }) + .collect() +} + +/// Filters all attributes except the cfg ones. +pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec { + attrs.iter().filter(|a| a.path.is_ident("cfg")).cloned().collect() +} + +#[cfg(test)] +mod tests { + use assert_matches::assert_matches; + + use super::*; + + #[test] + fn check_get_doc_literals() { + const FIRST: &'static str = "hello"; + const SECOND: &'static str = "WORLD"; + + let doc: Attribute = parse_quote!(#[doc = #FIRST]); + let doc_world: Attribute = parse_quote!(#[doc = #SECOND]); + + let attrs = vec![ + doc.clone(), + parse_quote!(#[derive(Debug)]), + parse_quote!(#[test]), + parse_quote!(#[allow(non_camel_case_types)]), + doc_world.clone(), + ]; + + let docs = get_doc_literals(&attrs); + assert_eq!(docs.len(), 2); + assert_matches!(&docs[0], syn::Lit::Str(val) if val.value() == FIRST); + assert_matches!(&docs[1], syn::Lit::Str(val) if val.value() == SECOND); + } + + #[test] + fn check_filter_cfg_attributes() { + let cfg_std: Attribute = parse_quote!(#[cfg(feature = "std")]); + let cfg_benchmarks: Attribute = parse_quote!(#[cfg(feature = "runtime-benchmarks")]); + + let attrs = vec![ + cfg_std.clone(), + parse_quote!(#[derive(Debug)]), + parse_quote!(#[test]), + cfg_benchmarks.clone(), + parse_quote!(#[allow(non_camel_case_types)]), + ]; + + let filtered = filter_cfg_attributes(&attrs); + assert_eq!(filtered.len(), 2); + assert_eq!(cfg_std, filtered[0]); + assert_eq!(cfg_benchmarks, filtered[1]); + } +} diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index ff101c3add947..02770280f7b90 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -76,12 +76,16 @@ pub use codec::{self, Decode, DecodeLimit, Encode}; #[cfg(feature = "std")] pub use hash_db::Hasher; #[doc(hidden)] +pub use scale_info; +#[doc(hidden)] #[cfg(not(feature = "std"))] pub use sp_core::to_substrate_wasm_fn_return_value; use sp_core::OpaqueMetadata; #[doc(hidden)] pub use sp_core::{offchain, ExecutionContext}; #[doc(hidden)] +pub use sp_metadata_ir::{self as metadata_ir, frame_metadata as metadata}; +#[doc(hidden)] #[cfg(feature = "std")] pub use sp_runtime::StateVersion; #[doc(hidden)] @@ -101,7 +105,7 @@ pub use sp_state_machine::{ StorageProof, TrieBackend, TrieBackendBuilder, }; #[doc(hidden)] -pub use sp_std::{mem, slice}; +pub use sp_std::{mem, slice, vec}; #[doc(hidden)] pub use sp_version::{create_apis_vec, ApiId, ApisVec, RuntimeVersion}; #[cfg(feature = "std")] diff --git a/primitives/consensus/beefy/src/lib.rs b/primitives/consensus/beefy/src/lib.rs index cc5d1e8cb9a3b..268e1925b4449 100644 --- a/primitives/consensus/beefy/src/lib.rs +++ b/primitives/consensus/beefy/src/lib.rs @@ -284,7 +284,7 @@ impl OnNewValidatorSet for () { /// the runtime API boundary this type is unknown and as such we keep this /// opaque representation, implementors of the runtime API will have to make /// sure that all usages of `OpaqueKeyOwnershipProof` refer to the same type. -#[derive(Decode, Encode, PartialEq)] +#[derive(Decode, Encode, PartialEq, TypeInfo)] pub struct OpaqueKeyOwnershipProof(Vec); impl OpaqueKeyOwnershipProof { /// Create a new `OpaqueKeyOwnershipProof` using the given encoded diff --git a/primitives/metadata-ir/Cargo.toml b/primitives/metadata-ir/Cargo.toml new file mode 100644 index 0000000000000..27fada9c6f34e --- /dev/null +++ b/primitives/metadata-ir/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "sp-metadata-ir" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Intermediate representation of the runtime metadata." +documentation = "https://docs.rs/sp-metadata-ir" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } +frame-metadata = { version = "15.1.0", default-features = false, features = ["v14", "v15-unstable"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-std = { version = "5.0.0", default-features = false, path = "../std" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-metadata/std", + "scale-info/std", + "sp-std/std", +] diff --git a/frame/support/src/metadata_ir/mod.rs b/primitives/metadata-ir/src/lib.rs similarity index 76% rename from frame/support/src/metadata_ir/mod.rs rename to primitives/metadata-ir/src/lib.rs index bab205d63c118..3ddc2911d4c93 100644 --- a/frame/support/src/metadata_ir/mod.rs +++ b/primitives/metadata-ir/src/lib.rs @@ -17,15 +17,28 @@ //! Intermediate representation of the runtime metadata. +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +// Re-export. +#[doc(hidden)] +pub use frame_metadata; + mod types; use frame_metadata::{RuntimeMetadataPrefixed, RuntimeMetadataV14}; pub use types::*; mod v14; +mod v15; /// Metadata V14. const V14: u32 = 14; +/// Metadata V15. +/// +/// Not yet stable, thus we set it to `u32::MAX`. +const V15: u32 = u32::MAX; + /// Transform the IR to the specified version. /// /// Use [`supported_versions`] to find supported versions. @@ -36,13 +49,18 @@ pub fn into_version(metadata: MetadataIR, version: u32) -> Option { + let v15: frame_metadata::v15::RuntimeMetadataV15 = metadata.into(); + Some(v15.into()) + }, _ => None, } } /// Returns the supported metadata versions. pub fn supported_versions() -> sp_std::vec::Vec { - sp_std::vec![V14,] + sp_std::vec![V14, V15] } /// Transform the IR to the latest stable metadata version. @@ -54,7 +72,6 @@ pub fn into_latest(metadata: MetadataIR) -> RuntimeMetadataPrefixed { #[cfg(test)] mod test { use super::*; - use crate::metadata_ir::ExtrinsicMetadataIR; use frame_metadata::{v14::META_RESERVED, RuntimeMetadata}; use scale_info::meta_type; @@ -67,6 +84,7 @@ mod test { signed_extensions: vec![], }, ty: meta_type::<()>(), + apis: vec![], } } @@ -79,4 +97,14 @@ mod test { assert!(matches!(metadata.1, RuntimeMetadata::V14(_))); } + + #[test] + fn into_version_15() { + let ir = ir_metadata(); + let metadata = into_version(ir, V15).expect("Should return prefixed metadata"); + + assert_eq!(metadata.0, META_RESERVED); + + assert!(matches!(metadata.1, RuntimeMetadata::V15(_))); + } } diff --git a/frame/support/src/metadata_ir/types.rs b/primitives/metadata-ir/src/types.rs similarity index 83% rename from frame/support/src/metadata_ir/types.rs rename to primitives/metadata-ir/src/types.rs index 087fd3dcad249..93ee54891d89f 100644 --- a/frame/support/src/metadata_ir/types.rs +++ b/primitives/metadata-ir/src/types.rs @@ -37,6 +37,77 @@ pub struct MetadataIR { pub extrinsic: ExtrinsicMetadataIR, /// The type of the `Runtime`. pub ty: T::Type, + /// Metadata of the Runtime API. + pub apis: Vec>, +} + +/// Metadata of a runtime trait. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct RuntimeApiMetadataIR { + /// Trait name. + pub name: T::String, + /// Trait methods. + pub methods: Vec>, + /// Trait documentation. + pub docs: Vec, +} + +impl IntoPortable for RuntimeApiMetadataIR { + type Output = RuntimeApiMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMetadataIR { + name: self.name.into_portable(registry), + methods: registry.map_into_portable(self.methods), + docs: registry.map_into_portable(self.docs), + } + } +} + +/// Metadata of a runtime method. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct RuntimeApiMethodMetadataIR { + /// Method name. + pub name: T::String, + /// Method parameters. + pub inputs: Vec>, + /// Method output. + pub output: T::Type, + /// Method documentation. + pub docs: Vec, +} + +impl IntoPortable for RuntimeApiMethodMetadataIR { + type Output = RuntimeApiMethodMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMethodMetadataIR { + name: self.name.into_portable(registry), + inputs: registry.map_into_portable(self.inputs), + output: registry.register_type(&self.output), + docs: registry.map_into_portable(self.docs), + } + } +} + +/// Metadata of a runtime method parameter. +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub struct RuntimeApiMethodParamMetadataIR { + /// Parameter name. + pub name: T::String, + /// Parameter type. + pub ty: T::Type, +} + +impl IntoPortable for RuntimeApiMethodParamMetadataIR { + type Output = RuntimeApiMethodParamMetadataIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RuntimeApiMethodParamMetadataIR { + name: self.name.into_portable(registry), + ty: registry.register_type(&self.ty), + } + } } /// The intermediate representation for a pallet metadata. diff --git a/frame/support/src/metadata_ir/v14.rs b/primitives/metadata-ir/src/v14.rs similarity index 100% rename from frame/support/src/metadata_ir/v14.rs rename to primitives/metadata-ir/src/v14.rs diff --git a/primitives/metadata-ir/src/v15.rs b/primitives/metadata-ir/src/v15.rs new file mode 100644 index 0000000000000..86441228d008e --- /dev/null +++ b/primitives/metadata-ir/src/v15.rs @@ -0,0 +1,188 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Convert the IR to V15 metadata. + +use super::types::{ + ExtrinsicMetadataIR, MetadataIR, PalletCallMetadataIR, PalletConstantMetadataIR, + PalletErrorMetadataIR, PalletEventMetadataIR, PalletMetadataIR, PalletStorageMetadataIR, + RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, + SignedExtensionMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, + StorageHasherIR, +}; + +use frame_metadata::v15::{ + ExtrinsicMetadata, PalletCallMetadata, PalletConstantMetadata, PalletErrorMetadata, + PalletEventMetadata, PalletMetadata, PalletStorageMetadata, RuntimeApiMetadata, + RuntimeApiMethodMetadata, RuntimeApiMethodParamMetadata, RuntimeMetadataV15, + SignedExtensionMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, + StorageHasher, +}; + +impl From for RuntimeMetadataV15 { + fn from(ir: MetadataIR) -> Self { + RuntimeMetadataV15::new( + ir.pallets.into_iter().map(Into::into).collect(), + ir.extrinsic.into(), + ir.ty, + ir.apis.into_iter().map(Into::into).collect(), + ) + } +} + +impl From for RuntimeApiMetadata { + fn from(ir: RuntimeApiMetadataIR) -> Self { + RuntimeApiMetadata { + name: ir.name, + methods: ir.methods.into_iter().map(Into::into).collect(), + docs: ir.docs, + } + } +} + +impl From for RuntimeApiMethodMetadata { + fn from(ir: RuntimeApiMethodMetadataIR) -> Self { + RuntimeApiMethodMetadata { + name: ir.name, + inputs: ir.inputs.into_iter().map(Into::into).collect(), + output: ir.output, + docs: ir.docs, + } + } +} + +impl From for RuntimeApiMethodParamMetadata { + fn from(ir: RuntimeApiMethodParamMetadataIR) -> Self { + RuntimeApiMethodParamMetadata { name: ir.name, ty: ir.ty } + } +} + +impl From for PalletMetadata { + fn from(ir: PalletMetadataIR) -> Self { + PalletMetadata { + name: ir.name, + storage: ir.storage.map(Into::into), + calls: ir.calls.map(Into::into), + event: ir.event.map(Into::into), + constants: ir.constants.into_iter().map(Into::into).collect(), + error: ir.error.map(Into::into), + index: ir.index, + docs: ir.docs, + } + } +} + +impl From for StorageEntryModifier { + fn from(ir: StorageEntryModifierIR) -> Self { + match ir { + StorageEntryModifierIR::Optional => StorageEntryModifier::Optional, + StorageEntryModifierIR::Default => StorageEntryModifier::Default, + } + } +} + +impl From for StorageHasher { + fn from(ir: StorageHasherIR) -> Self { + match ir { + StorageHasherIR::Blake2_128 => StorageHasher::Blake2_128, + StorageHasherIR::Blake2_256 => StorageHasher::Blake2_256, + StorageHasherIR::Blake2_128Concat => StorageHasher::Blake2_128Concat, + StorageHasherIR::Twox128 => StorageHasher::Twox128, + StorageHasherIR::Twox256 => StorageHasher::Twox256, + StorageHasherIR::Twox64Concat => StorageHasher::Twox64Concat, + StorageHasherIR::Identity => StorageHasher::Identity, + } + } +} + +impl From for StorageEntryType { + fn from(ir: StorageEntryTypeIR) -> Self { + match ir { + StorageEntryTypeIR::Plain(ty) => StorageEntryType::Plain(ty), + StorageEntryTypeIR::Map { hashers, key, value } => StorageEntryType::Map { + hashers: hashers.into_iter().map(Into::into).collect(), + key, + value, + }, + } + } +} + +impl From for StorageEntryMetadata { + fn from(ir: StorageEntryMetadataIR) -> Self { + StorageEntryMetadata { + name: ir.name, + modifier: ir.modifier.into(), + ty: ir.ty.into(), + default: ir.default, + docs: ir.docs, + } + } +} + +impl From for PalletStorageMetadata { + fn from(ir: PalletStorageMetadataIR) -> Self { + PalletStorageMetadata { + prefix: ir.prefix, + entries: ir.entries.into_iter().map(Into::into).collect(), + } + } +} + +impl From for PalletCallMetadata { + fn from(ir: PalletCallMetadataIR) -> Self { + PalletCallMetadata { ty: ir.ty } + } +} + +impl From for PalletEventMetadata { + fn from(ir: PalletEventMetadataIR) -> Self { + PalletEventMetadata { ty: ir.ty } + } +} + +impl From for PalletConstantMetadata { + fn from(ir: PalletConstantMetadataIR) -> Self { + PalletConstantMetadata { name: ir.name, ty: ir.ty, value: ir.value, docs: ir.docs } + } +} + +impl From for PalletErrorMetadata { + fn from(ir: PalletErrorMetadataIR) -> Self { + PalletErrorMetadata { ty: ir.ty } + } +} + +impl From for SignedExtensionMetadata { + fn from(ir: SignedExtensionMetadataIR) -> Self { + SignedExtensionMetadata { + identifier: ir.identifier, + ty: ir.ty, + additional_signed: ir.additional_signed, + } + } +} + +impl From for ExtrinsicMetadata { + fn from(ir: ExtrinsicMetadataIR) -> Self { + ExtrinsicMetadata { + ty: ir.ty, + version: ir.version, + signed_extensions: ir.signed_extensions.into_iter().map(Into::into).collect(), + } + } +} diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index deb120104b717..f6d57ce70fc05 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -13,6 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } @@ -28,4 +29,5 @@ std = [ "sp-application-crypto/std", "sp-core/std", "sp-runtime/std", + "scale-info/std", ] diff --git a/primitives/test-primitives/src/lib.rs b/primitives/test-primitives/src/lib.rs index 3a5f3dac40d68..035acc7a35ef6 100644 --- a/primitives/test-primitives/src/lib.rs +++ b/primitives/test-primitives/src/lib.rs @@ -28,7 +28,7 @@ pub use sp_core::{hash::H256, RuntimeDebug}; use sp_runtime::traits::{BlakeTwo256, Extrinsic as ExtrinsicT, Verify}; /// Extrinsic for test-runtime. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] pub enum Extrinsic { IncludeData(Vec), StorageChange(Vec, Option>),