mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 14:41:11 +00:00
Utilize Metadata V15 (#1041)
* Update frame-metadata to the latest branch Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Add outer enum types Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Extend the extrinsic with address,call,sign,extra types Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Codegen test Event, Error and Call for outer enums Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Revert "Codegen test Event, Error and Call for outer enums" This reverts commit db542dca0369eedd257a7ec031d5b5549bc46a88. * Update frame-metadata from the latest release Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update scale-info Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen/error: Support v15 message Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Convert v14 to v15 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata/retain: Adjust to extrinsic type for V15 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata/validation: Adjust hashing for extrinsic types V15 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * scripts: Fetch V15 and output codegen for full_client only Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt/blocks: Use extrinsic types directly Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Fetch V15 for build script Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Generate from latest polkadot version Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Fetch legacy with old API for v14 only Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * rpc: Fetch metadata versions Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * client: Fetch latest unstable then V15 then V14 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Adjust testing API to latest interface Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjust clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Generate the `RuntimeError` type for V14 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Remove testing files Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing/staking: Remove controller account from bond Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata/validation: Use specific variants for hashing RuntimeCall Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * XXX: Custom Substrate binary: must revert with next release Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * XXX: To revert: CI use hardcoded substrate Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Use v15 outer enum types Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Retain outer enum types Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Use outer enum types instead of generating them Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update artifacts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Revert "XXX: Custom Substrate binary: must revert with next release" This reverts commit e9705298661919f5769720b35030759fb8a7b01d. Revert "XXX: To revert: CI use hardcoded substrate" This reverts commit b18a5a0985a56ee4ad99bc9a1c0f9cd733cf4271. * testing: Include env for dummy wat contracts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Adjsut clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Use new link for fetching latest substrate binary Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * tests: Include dummy RuntimeEvent into test metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * ci: Bump light-client timeout tests to 25min Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata/validation: Use specific pallets as provided Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Rename metadata constant Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Use call_ty instead of signature_ty Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Rename retaining variant function Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Use Option<&[&str]> Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * online_client: Fetch V15 metadata explicitely Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata/validation: Include the hash of the outer enum types Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Fix sign typo Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Update the artifacts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Remove RootError RootEvent and RootExtrinsic traits Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update polkadot.rs Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata/tests: Ensure outer enum variants are retained Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * scripts: Include multiple pallets for our decoding purposes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Apply clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Update small metadata Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * error: Keep raw bytes for the ModuleError representation Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * error: Modify docs to not include links Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt/tests: Propagate `RuntimeCall` to outer enums Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * subxt: Provide proper byte slice for decoding Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update artifacts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli/tests: Adjust expected pallets message Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Test conversion from v14 to v15 Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata: Fix typo Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update subxt/src/blocks/extrinsic_types.rs Co-authored-by: James Wilson <james@jsdw.me> * metadata: Simplify type path for RuntimeError Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * metadata/validation: Use visited ids per outer enum Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * error: Remove RawModuleError Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Fix new clippy error from updated rust version Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -9,7 +9,7 @@ on:
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# Use latest substrate for nightly runs:
|
||||
SUBSTRATE_URL: https://releases.parity.io/substrate/x86_64-debian:stretch/latest/substrate/substrate
|
||||
SUBSTRATE_URL: https://releases.parity.io/substrate/x86_64-debian:bullseye/latest/substrate/substrate
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
|
||||
@@ -19,7 +19,7 @@ concurrency:
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# TODO: Currently pointing at latest substrate; is there a suitable binary we can pin to here?
|
||||
SUBSTRATE_URL: https://releases.parity.io/substrate/x86_64-debian:stretch/latest/substrate/substrate
|
||||
SUBSTRATE_URL: https://releases.parity.io/substrate/x86_64-debian:bullseye/latest/substrate/substrate
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
Generated
+23
-11
@@ -1373,6 +1373,17 @@ name = "frame-metadata"
|
||||
version = "15.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "frame-metadata"
|
||||
version = "16.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"parity-scale-codec",
|
||||
@@ -1953,7 +1964,7 @@ name = "integration-tests"
|
||||
version = "0.29.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"frame-metadata",
|
||||
"frame-metadata 16.0.0",
|
||||
"futures",
|
||||
"hex",
|
||||
"parity-scale-codec",
|
||||
@@ -3200,9 +3211,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "scale-info"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad560913365790f17cbf12479491169f01b9d46d29cfc7422bf8c64bdc61b731"
|
||||
checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"cfg-if",
|
||||
@@ -3214,9 +3225,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "scale-info-derive"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19df9bd9ace6cc2fe19387c96ce677e823e07d017ceed253e7bb3d1d1bd9c73b"
|
||||
checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -3233,7 +3244,7 @@ dependencies = [
|
||||
"base58",
|
||||
"blake2",
|
||||
"either",
|
||||
"frame-metadata",
|
||||
"frame-metadata 15.1.0",
|
||||
"parity-scale-codec",
|
||||
"scale-bits",
|
||||
"scale-decode",
|
||||
@@ -4157,7 +4168,7 @@ dependencies = [
|
||||
"blake2",
|
||||
"derivative",
|
||||
"either",
|
||||
"frame-metadata",
|
||||
"frame-metadata 16.0.0",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"getrandom 0.2.10",
|
||||
@@ -4195,7 +4206,7 @@ version = "0.29.0"
|
||||
dependencies = [
|
||||
"clap 4.3.11",
|
||||
"color-eyre",
|
||||
"frame-metadata",
|
||||
"frame-metadata 16.0.0",
|
||||
"hex",
|
||||
"jsonrpsee",
|
||||
"parity-scale-codec",
|
||||
@@ -4215,7 +4226,7 @@ name = "subxt-codegen"
|
||||
version = "0.29.0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"frame-metadata",
|
||||
"frame-metadata 16.0.0",
|
||||
"heck",
|
||||
"hex",
|
||||
"jsonrpsee",
|
||||
@@ -4244,9 +4255,10 @@ dependencies = [
|
||||
name = "subxt-metadata"
|
||||
version = "0.29.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"bitvec",
|
||||
"criterion",
|
||||
"frame-metadata",
|
||||
"frame-metadata 16.0.0",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"sp-core-hashing",
|
||||
@@ -4696,7 +4708,7 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
name = "ui-tests"
|
||||
version = "0.29.0"
|
||||
dependencies = [
|
||||
"frame-metadata",
|
||||
"frame-metadata 16.0.0",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"subxt",
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ console_error_panic_hook = "0.1.7"
|
||||
darling = "0.20.0"
|
||||
derivative = "2.2.0"
|
||||
either = "1.8.1"
|
||||
frame-metadata = { version = "15.1.0", features = ["v14", "v15-unstable", "std"] }
|
||||
frame-metadata = { version = "16.0.0", default-features = false, features = ["current", "std"] }
|
||||
futures = { version = "0.3.27", default-features = false, features = ["std"] }
|
||||
getrandom = { version = "0.2", default-features = false }
|
||||
hex = "0.4.3"
|
||||
@@ -58,7 +58,7 @@ proc-macro-error = "1.0.4"
|
||||
proc-macro2 = "1.0.63"
|
||||
quote = "1.0.29"
|
||||
regex = "1.9.1"
|
||||
scale-info = "2.8.0"
|
||||
scale-info = "2.9.0"
|
||||
scale-value = "0.10.0"
|
||||
scale-bits = "0.3"
|
||||
scale-decode = "0.7.0"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -167,7 +167,7 @@ pub mod tests {
|
||||
async fn test_commands() {
|
||||
// show pallets:
|
||||
let output = simulate_run("").await;
|
||||
assert_eq!(output.unwrap(), "Usage:\n subxt explore <PALLET>\n explore a specific pallet\n\nAvailable <PALLET> values are:\n Balances\n Multisig\n Staking\n System\n");
|
||||
assert_eq!(output.unwrap(), "Usage:\n subxt explore <PALLET>\n explore a specific pallet\n\nAvailable <PALLET> values are:\n Balances\n Multisig\n ParaInherent\n Staking\n System\n Timestamp\n");
|
||||
// if incorrect pallet, error:
|
||||
let output = simulate_run("abc123").await;
|
||||
assert!(output.is_err());
|
||||
|
||||
+13
-142
@@ -289,13 +289,12 @@ impl RuntimeGenerator {
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let item_mod_attrs = item_mod.attrs.clone();
|
||||
let item_mod_ir = ir::ItemMod::try_from(item_mod)?;
|
||||
let default_derives = derives.default_derives();
|
||||
|
||||
let type_gen = TypeGenerator::new(
|
||||
self.metadata.types(),
|
||||
"runtime_types",
|
||||
type_substitutes,
|
||||
derives.clone(),
|
||||
derives,
|
||||
crate_path.clone(),
|
||||
should_gen_docs,
|
||||
);
|
||||
@@ -382,118 +381,6 @@ impl RuntimeGenerator {
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
let outer_event_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.event_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Event),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let outer_event = quote! {
|
||||
#default_derives
|
||||
pub enum Event {
|
||||
#( #outer_event_variants )*
|
||||
}
|
||||
};
|
||||
|
||||
let outer_extrinsic_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.call_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Call),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let outer_extrinsic = quote! {
|
||||
#default_derives
|
||||
pub enum Call {
|
||||
#( #outer_extrinsic_variants )*
|
||||
}
|
||||
};
|
||||
|
||||
let root_event_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = &p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
|
||||
p.event_ty_id().map(|_| {
|
||||
// An 'if' arm for the RootEvent impl to match this variant name:
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
return Ok(Event::#variant_name(#mod_name::Event::decode_with_metadata(
|
||||
&mut &*pallet_bytes,
|
||||
pallet_ty,
|
||||
metadata
|
||||
)?));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let root_extrinsic_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
p.call_ty_id().map(|_| {
|
||||
// An 'if' arm for the RootExtrinsic impl to match this variant name:
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
return Ok(Call::#variant_name(#mod_name::Call::decode_with_metadata(
|
||||
&mut &*pallet_bytes,
|
||||
pallet_ty,
|
||||
metadata
|
||||
)?));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let outer_error_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.error_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Error),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let outer_error = quote! {
|
||||
#default_derives
|
||||
pub enum Error {
|
||||
#( #outer_error_variants )*
|
||||
}
|
||||
};
|
||||
|
||||
let root_error_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = &p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
|
||||
p.error_ty_id().map(|type_id| {
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
let variant_error = #mod_name::Error::decode_with_metadata(cursor, #type_id, metadata)?;
|
||||
return Ok(Error::#variant_name(variant_error));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let mod_ident = &item_mod_ir.ident;
|
||||
let pallets_with_constants: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
@@ -526,6 +413,12 @@ impl RuntimeGenerator {
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
// Fetch the paths of the outer enums.
|
||||
// Substrate exposes those under `kitchensink_runtime`, while Polkadot under `polkadot_runtime`.
|
||||
let call_path = type_gen.resolve_type_path(self.metadata.outer_enums().call_enum_ty());
|
||||
let event_path = type_gen.resolve_type_path(self.metadata.outer_enums().event_enum_ty());
|
||||
let error_path = type_gen.resolve_type_path(self.metadata.outer_enums().error_enum_ty());
|
||||
|
||||
Ok(quote! {
|
||||
#( #item_mod_attrs )*
|
||||
#[allow(dead_code, unused_imports, non_camel_case_types)]
|
||||
@@ -551,36 +444,14 @@ impl RuntimeGenerator {
|
||||
/// The error type returned when there is a runtime issue.
|
||||
pub type DispatchError = #types_mod_ident::sp_runtime::DispatchError;
|
||||
|
||||
#outer_event
|
||||
/// The outer event enum.
|
||||
pub type Event = #event_path;
|
||||
|
||||
impl #crate_path::events::RootEvent for Event {
|
||||
fn root_event(pallet_bytes: &[u8], pallet_name: &str, pallet_ty: u32, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
|
||||
use #crate_path::metadata::DecodeWithMetadata;
|
||||
#( #root_event_if_arms )*
|
||||
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Event enum", pallet_name)).into())
|
||||
}
|
||||
}
|
||||
/// The outer extrinsic enum.
|
||||
pub type Call = #call_path;
|
||||
|
||||
#outer_extrinsic
|
||||
|
||||
impl #crate_path::blocks::RootExtrinsic for Call {
|
||||
fn root_extrinsic(pallet_bytes: &[u8], pallet_name: &str, pallet_ty: u32, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
|
||||
use #crate_path::metadata::DecodeWithMetadata;
|
||||
#( #root_extrinsic_if_arms )*
|
||||
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Call enum", pallet_name)).into())
|
||||
}
|
||||
}
|
||||
|
||||
#outer_error
|
||||
|
||||
impl #crate_path::error::RootError for Error {
|
||||
fn root_error(pallet_bytes: &[u8], pallet_name: &str, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
|
||||
use #crate_path::metadata::DecodeWithMetadata;
|
||||
let cursor = &mut &pallet_bytes[..];
|
||||
#( #root_error_if_arms )*
|
||||
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Error enum", pallet_name)).into())
|
||||
}
|
||||
}
|
||||
/// The outer error enum representing the DispatchError's Module variant.
|
||||
pub type Error = #error_path;
|
||||
|
||||
pub fn constants() -> ConstantsApi {
|
||||
ConstantsApi
|
||||
|
||||
@@ -19,7 +19,7 @@ pub enum CodegenError {
|
||||
#[error("Failed IO for {0}, make sure that you are providing the correct file path for metadata: {1}")]
|
||||
Io(String, std::io::Error),
|
||||
/// Cannot decode the metadata bytes.
|
||||
#[error("Could not decode metadata, only V14 metadata is supported: {0}")]
|
||||
#[error("Could not decode metadata, only V14 and V15 metadata are supported: {0}")]
|
||||
Decode(#[from] codec::Error),
|
||||
/// Out of line modules are not supported.
|
||||
#[error("Out-of-line subxt modules are not supported, make sure you are providing a body to your module: pub mod polkadot {{ ... }}")]
|
||||
|
||||
@@ -204,12 +204,9 @@ async fn fetch_metadata(
|
||||
client: &impl ClientT,
|
||||
version: MetadataVersion,
|
||||
) -> Result<Vec<u8>, FetchMetadataError> {
|
||||
if !matches!(
|
||||
version,
|
||||
MetadataVersion::Latest | MetadataVersion::Version(14)
|
||||
) {
|
||||
if !matches!(version, MetadataVersion::Version(14)) {
|
||||
return Err(FetchMetadataError::Other(
|
||||
"The node can only return version 14 metadata but you've asked for something else"
|
||||
"The node can only return version 14 metadata using the legacy API but you've asked for something else"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ thiserror = { workspace = true }
|
||||
bitvec = { workspace = true, features = ["alloc"] }
|
||||
criterion = { workspace = true }
|
||||
scale-info = { workspace = true, features = ["bit-vec"] }
|
||||
assert_matches = { workspace = true }
|
||||
|
||||
[lib]
|
||||
# Without this, libtest cli opts interfere with criteron benches:
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::TryFromError;
|
||||
use crate::Metadata;
|
||||
use frame_metadata::{v14, v15};
|
||||
@@ -21,7 +23,47 @@ impl From<Metadata> for v14::RuntimeMetadataV14 {
|
||||
}
|
||||
}
|
||||
|
||||
fn v15_to_v14(metadata: v15::RuntimeMetadataV15) -> v14::RuntimeMetadataV14 {
|
||||
fn v15_to_v14(mut metadata: v15::RuntimeMetadataV15) -> v14::RuntimeMetadataV14 {
|
||||
let types = &mut metadata.types;
|
||||
|
||||
// In subxt we care about the `Address`, `Call`, `Signature` and `Extra` types.
|
||||
let extrinsic_type = scale_info::Type {
|
||||
path: scale_info::Path {
|
||||
segments: vec![
|
||||
"primitives".to_string(),
|
||||
"runtime".to_string(),
|
||||
"generic".to_string(),
|
||||
"UncheckedExtrinsic".to_string(),
|
||||
],
|
||||
},
|
||||
type_params: vec![
|
||||
scale_info::TypeParameter::<scale_info::form::PortableForm> {
|
||||
name: "Address".to_string(),
|
||||
ty: Some(metadata.extrinsic.address_ty),
|
||||
},
|
||||
scale_info::TypeParameter::<scale_info::form::PortableForm> {
|
||||
name: "Call".to_string(),
|
||||
ty: Some(metadata.extrinsic.call_ty),
|
||||
},
|
||||
scale_info::TypeParameter::<scale_info::form::PortableForm> {
|
||||
name: "Signature".to_string(),
|
||||
ty: Some(metadata.extrinsic.signature_ty),
|
||||
},
|
||||
scale_info::TypeParameter::<scale_info::form::PortableForm> {
|
||||
name: "Extra".to_string(),
|
||||
ty: Some(metadata.extrinsic.extra_ty),
|
||||
},
|
||||
],
|
||||
type_def: scale_info::TypeDef::Composite(scale_info::TypeDefComposite { fields: vec![] }),
|
||||
docs: vec![],
|
||||
};
|
||||
let extrinsic_type_id = types.types.len() as u32;
|
||||
|
||||
types.types.push(scale_info::PortableType {
|
||||
id: extrinsic_type_id,
|
||||
ty: extrinsic_type,
|
||||
});
|
||||
|
||||
v14::RuntimeMetadataV14 {
|
||||
types: metadata.types,
|
||||
pallets: metadata
|
||||
@@ -92,7 +134,7 @@ fn v15_to_v14(metadata: v15::RuntimeMetadataV15) -> v14::RuntimeMetadataV14 {
|
||||
})
|
||||
.collect(),
|
||||
extrinsic: frame_metadata::v14::ExtrinsicMetadata {
|
||||
ty: metadata.extrinsic.ty,
|
||||
ty: extrinsic_type_id.into(),
|
||||
version: metadata.extrinsic.version,
|
||||
signed_extensions: metadata.extrinsic.signed_extensions.into_iter().map(|ext| {
|
||||
frame_metadata::v14::SignedExtensionMetadata {
|
||||
@@ -106,7 +148,13 @@ fn v15_to_v14(metadata: v15::RuntimeMetadataV15) -> v14::RuntimeMetadataV14 {
|
||||
}
|
||||
}
|
||||
|
||||
fn v14_to_v15(metadata: v14::RuntimeMetadataV14) -> v15::RuntimeMetadataV15 {
|
||||
fn v14_to_v15(mut metadata: v14::RuntimeMetadataV14) -> v15::RuntimeMetadataV15 {
|
||||
// Find the extrinsic types.
|
||||
let extrinsic_parts = ExtrinsicPartTypeIds::new(&metadata)
|
||||
.expect("Extrinsic types are always present on V14; qed");
|
||||
|
||||
let outer_enums = generate_outer_enums(&mut metadata);
|
||||
|
||||
v15::RuntimeMetadataV15 {
|
||||
types: metadata.types,
|
||||
pallets: metadata
|
||||
@@ -178,7 +226,6 @@ fn v14_to_v15(metadata: v14::RuntimeMetadataV14) -> v15::RuntimeMetadataV15 {
|
||||
})
|
||||
.collect(),
|
||||
extrinsic: frame_metadata::v15::ExtrinsicMetadata {
|
||||
ty: metadata.extrinsic.ty,
|
||||
version: metadata.extrinsic.version,
|
||||
signed_extensions: metadata.extrinsic.signed_extensions.into_iter().map(|ext| {
|
||||
frame_metadata::v15::SignedExtensionMetadata {
|
||||
@@ -186,9 +233,358 @@ fn v14_to_v15(metadata: v14::RuntimeMetadataV14) -> v15::RuntimeMetadataV15 {
|
||||
ty: ext.ty,
|
||||
additional_signed: ext.additional_signed,
|
||||
}
|
||||
}).collect()
|
||||
}).collect(),
|
||||
address_ty: extrinsic_parts.address.into(),
|
||||
call_ty: extrinsic_parts.call.into(),
|
||||
signature_ty: extrinsic_parts.signature.into(),
|
||||
extra_ty: extrinsic_parts.extra.into(),
|
||||
},
|
||||
ty: metadata.ty,
|
||||
apis: Default::default(),
|
||||
outer_enums,
|
||||
custom: v15::CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The type IDs extracted from the metadata that represent the
|
||||
/// generic type parameters passed to the `UncheckedExtrinsic` from
|
||||
/// the substrate-based chain.
|
||||
struct ExtrinsicPartTypeIds {
|
||||
address: u32,
|
||||
call: u32,
|
||||
signature: u32,
|
||||
extra: u32,
|
||||
}
|
||||
|
||||
impl ExtrinsicPartTypeIds {
|
||||
/// Extract the generic type parameters IDs from the extrinsic type.
|
||||
fn new(metadata: &v14::RuntimeMetadataV14) -> Result<Self, String> {
|
||||
const ADDRESS: &str = "Address";
|
||||
const CALL: &str = "Call";
|
||||
const SIGNATURE: &str = "Signature";
|
||||
const EXTRA: &str = "Extra";
|
||||
|
||||
let extrinsic_id = metadata.extrinsic.ty.id;
|
||||
let Some(extrinsic_ty) = metadata.types.resolve(extrinsic_id) else {
|
||||
return Err("Missing extrinsic type".into())
|
||||
};
|
||||
|
||||
let params: HashMap<_, _> = extrinsic_ty
|
||||
.type_params
|
||||
.iter()
|
||||
.map(|ty_param| {
|
||||
let Some(ty) = ty_param.ty else {
|
||||
return Err("Missing type param type from extrinsic".to_string());
|
||||
};
|
||||
|
||||
Ok((ty_param.name.as_str(), ty.id))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let Some(address) = params.get(ADDRESS) else {
|
||||
return Err("Missing address type from extrinsic".into());
|
||||
};
|
||||
let Some(call) = params.get(CALL) else {
|
||||
return Err("Missing call type from extrinsic".into());
|
||||
};
|
||||
let Some(signature) = params.get(SIGNATURE) else {
|
||||
return Err("Missing signature type from extrinsic".into());
|
||||
};
|
||||
let Some(extra) = params.get(EXTRA) else {
|
||||
return Err("Missing extra type from extrinsic".into());
|
||||
};
|
||||
|
||||
Ok(ExtrinsicPartTypeIds {
|
||||
address: *address,
|
||||
call: *call,
|
||||
signature: *signature,
|
||||
extra: *extra,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_outer_enums(
|
||||
metadata: &mut v14::RuntimeMetadataV14,
|
||||
) -> v15::OuterEnums<scale_info::form::PortableForm> {
|
||||
let call_enum = metadata
|
||||
.types
|
||||
.types
|
||||
.iter()
|
||||
.find(|ty| {
|
||||
let Some(ident) = ty.ty.path.ident() else { return false };
|
||||
ident == "RuntimeCall"
|
||||
})
|
||||
.expect("RuntimeCall exists in V14; qed");
|
||||
|
||||
let event_enum = metadata
|
||||
.types
|
||||
.types
|
||||
.iter()
|
||||
.find(|ty| {
|
||||
let Some(ident) = ty.ty.path.ident() else { return false };
|
||||
ident == "RuntimeEvent"
|
||||
})
|
||||
.expect("RuntimeEvent exists in V14; qed");
|
||||
|
||||
let call_ty = call_enum.id.into();
|
||||
let event_ty = event_enum.id.into();
|
||||
|
||||
let mut path_segments = call_enum.ty.path.segments.clone();
|
||||
let last = path_segments
|
||||
.last_mut()
|
||||
.expect("Should have at least one segment checked above; qed");
|
||||
*last = "RuntimeError".to_string();
|
||||
|
||||
let error_ty_id = generate_runtime_error_type(metadata, path_segments);
|
||||
|
||||
v15::OuterEnums {
|
||||
call_enum_ty: call_ty,
|
||||
event_enum_ty: event_ty,
|
||||
error_enum_ty: error_ty_id.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the `RuntimeError` type and add it to the metadata.
|
||||
///
|
||||
/// Returns the `RuntimeError` Id from the registry.
|
||||
fn generate_runtime_error_type(
|
||||
metadata: &mut v14::RuntimeMetadataV14,
|
||||
path_segments: Vec<String>,
|
||||
) -> u32 {
|
||||
let variants: Vec<_> = metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.filter_map(|pallet| {
|
||||
let Some(pallet_error) = &pallet.error else { return None };
|
||||
let path = format!("{}Error", pallet.name);
|
||||
|
||||
Some(scale_info::Variant {
|
||||
name: pallet.name.clone(),
|
||||
fields: vec![scale_info::Field {
|
||||
name: None,
|
||||
ty: pallet_error.ty.id.into(),
|
||||
type_name: Some(path),
|
||||
docs: vec![],
|
||||
}],
|
||||
index: pallet.index,
|
||||
docs: vec![],
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let error_type = scale_info::Type {
|
||||
path: scale_info::Path {
|
||||
segments: path_segments,
|
||||
},
|
||||
type_params: vec![],
|
||||
type_def: scale_info::TypeDef::Variant(scale_info::TypeDefVariant { variants }),
|
||||
docs: vec![],
|
||||
};
|
||||
|
||||
let error_type_id = metadata.types.types.len() as u32;
|
||||
|
||||
metadata.types.types.push(scale_info::PortableType {
|
||||
id: error_type_id,
|
||||
ty: error_type,
|
||||
});
|
||||
|
||||
error_type_id
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::Decode;
|
||||
use frame_metadata::{v15::RuntimeMetadataV15, RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use scale_info::TypeDef;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
fn load_v15_metadata() -> RuntimeMetadataV15 {
|
||||
let bytes = fs::read(Path::new("../artifacts/polkadot_metadata_full.scale"))
|
||||
.expect("Cannot read metadata blob");
|
||||
let meta: RuntimeMetadataPrefixed =
|
||||
Decode::decode(&mut &*bytes).expect("Cannot decode scale metadata");
|
||||
|
||||
match meta.1 {
|
||||
RuntimeMetadata::V15(v15) => v15,
|
||||
_ => panic!("Unsupported metadata version {:?}", meta.1),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extrinsic_id_generation() {
|
||||
let v15 = load_v15_metadata();
|
||||
let v14 = v15_to_v14(v15.clone());
|
||||
|
||||
let ext_ty = v14.types.resolve(v14.extrinsic.ty.id).unwrap();
|
||||
let addr_id = ext_ty
|
||||
.type_params
|
||||
.iter()
|
||||
.find_map(|ty| {
|
||||
if ty.name == "Address" {
|
||||
Some(ty.ty.unwrap().id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let call_id = ext_ty
|
||||
.type_params
|
||||
.iter()
|
||||
.find_map(|ty| {
|
||||
if ty.name == "Call" {
|
||||
Some(ty.ty.unwrap().id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let extra_id = ext_ty
|
||||
.type_params
|
||||
.iter()
|
||||
.find_map(|ty| {
|
||||
if ty.name == "Extra" {
|
||||
Some(ty.ty.unwrap().id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let signature_id = ext_ty
|
||||
.type_params
|
||||
.iter()
|
||||
.find_map(|ty| {
|
||||
if ty.name == "Signature" {
|
||||
Some(ty.ty.unwrap().id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Position in type registry shouldn't change.
|
||||
assert_eq!(v15.extrinsic.address_ty.id, addr_id);
|
||||
assert_eq!(v15.extrinsic.call_ty.id, call_id);
|
||||
assert_eq!(v15.extrinsic.extra_ty.id, extra_id);
|
||||
assert_eq!(v15.extrinsic.signature_ty.id, signature_id);
|
||||
|
||||
let v15_addr = v15.types.resolve(v15.extrinsic.address_ty.id).unwrap();
|
||||
let v14_addr = v14.types.resolve(addr_id).unwrap();
|
||||
assert_eq!(v15_addr, v14_addr);
|
||||
|
||||
let v15_call = v15.types.resolve(v15.extrinsic.call_ty.id).unwrap();
|
||||
let v14_call = v14.types.resolve(call_id).unwrap();
|
||||
assert_eq!(v15_call, v14_call);
|
||||
|
||||
let v15_extra = v15.types.resolve(v15.extrinsic.extra_ty.id).unwrap();
|
||||
let v14_extra = v14.types.resolve(extra_id).unwrap();
|
||||
assert_eq!(v15_extra, v14_extra);
|
||||
|
||||
let v15_sign = v15.types.resolve(v15.extrinsic.signature_ty.id).unwrap();
|
||||
let v14_sign = v14.types.resolve(signature_id).unwrap();
|
||||
assert_eq!(v15_sign, v14_sign);
|
||||
|
||||
// Ensure we don't lose the information when converting back to v15.
|
||||
let converted_v15 = v14_to_v15(v14);
|
||||
|
||||
let v15_addr = v15.types.resolve(v15.extrinsic.address_ty.id).unwrap();
|
||||
let converted_v15_addr = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15.extrinsic.address_ty.id)
|
||||
.unwrap();
|
||||
assert_eq!(v15_addr, converted_v15_addr);
|
||||
|
||||
let v15_call = v15.types.resolve(v15.extrinsic.call_ty.id).unwrap();
|
||||
let converted_v15_call = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15.extrinsic.call_ty.id)
|
||||
.unwrap();
|
||||
assert_eq!(v15_call, converted_v15_call);
|
||||
|
||||
let v15_extra = v15.types.resolve(v15.extrinsic.extra_ty.id).unwrap();
|
||||
let converted_v15_extra = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15.extrinsic.extra_ty.id)
|
||||
.unwrap();
|
||||
assert_eq!(v15_extra, converted_v15_extra);
|
||||
|
||||
let v15_sign = v15.types.resolve(v15.extrinsic.signature_ty.id).unwrap();
|
||||
let converted_v15_sign = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15.extrinsic.signature_ty.id)
|
||||
.unwrap();
|
||||
assert_eq!(v15_sign, converted_v15_sign);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_outer_enums_generation() {
|
||||
let v15 = load_v15_metadata();
|
||||
let v14 = v15_to_v14(v15.clone());
|
||||
|
||||
// Convert back to v15 and expect to have the enum types properly generated.
|
||||
let converted_v15 = v14_to_v15(v14);
|
||||
|
||||
// RuntimeCall and RuntimeEvent were already present in the metadata v14.
|
||||
let v15_call = v15.types.resolve(v15.outer_enums.call_enum_ty.id).unwrap();
|
||||
let converted_v15_call = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15.outer_enums.call_enum_ty.id)
|
||||
.unwrap();
|
||||
assert_eq!(v15_call, converted_v15_call);
|
||||
|
||||
let v15_event = v15.types.resolve(v15.outer_enums.event_enum_ty.id).unwrap();
|
||||
let converted_v15_event = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15.outer_enums.event_enum_ty.id)
|
||||
.unwrap();
|
||||
assert_eq!(v15_event, converted_v15_event);
|
||||
|
||||
let v15_error = v15.types.resolve(v15.outer_enums.error_enum_ty.id).unwrap();
|
||||
let converted_v15_error = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15.outer_enums.error_enum_ty.id)
|
||||
.unwrap();
|
||||
|
||||
// Ensure they match in terms of variants and fields ids.
|
||||
assert_eq!(v15_error.path, converted_v15_error.path);
|
||||
|
||||
let TypeDef::Variant(v15_variant) = &v15_error.type_def else {
|
||||
panic!("V15 error must be a variant");
|
||||
};
|
||||
|
||||
let TypeDef::Variant(converted_v15_variant) = &converted_v15_error.type_def else {
|
||||
panic!("Converted V15 error must be a variant");
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
v15_variant.variants.len(),
|
||||
converted_v15_variant.variants.len()
|
||||
);
|
||||
|
||||
for (v15_var, converted_v15_var) in v15_variant
|
||||
.variants
|
||||
.iter()
|
||||
.zip(converted_v15_variant.variants.iter())
|
||||
{
|
||||
// Variant name must match.
|
||||
assert_eq!(v15_var.name, converted_v15_var.name);
|
||||
assert_eq!(v15_var.fields.len(), converted_v15_var.fields.len());
|
||||
|
||||
// Fields must have the same type.
|
||||
for (v15_field, converted_v15_field) in
|
||||
v15_var.fields.iter().zip(converted_v15_var.fields.iter())
|
||||
{
|
||||
assert_eq!(v15_field.ty.id, converted_v15_field.ty.id);
|
||||
|
||||
let ty = v15.types.resolve(v15_field.ty.id).unwrap();
|
||||
let converted_ty = converted_v15
|
||||
.types
|
||||
.resolve(converted_v15_field.ty.id)
|
||||
.unwrap();
|
||||
assert_eq!(ty, converted_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use super::TryFromError;
|
||||
use crate::utils::variant_index::VariantIndex;
|
||||
use crate::{
|
||||
utils::ordered_map::OrderedMap, ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata,
|
||||
PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadata,
|
||||
OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadata,
|
||||
RuntimeApiMethodParamMetadata, SignedExtensionMetadata, StorageEntryMetadata,
|
||||
StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata,
|
||||
};
|
||||
@@ -88,6 +88,11 @@ mod from_v15 {
|
||||
runtime_ty: m.ty.id,
|
||||
dispatch_error_ty,
|
||||
apis: apis.collect(),
|
||||
outer_enums: OuterEnumsMetadata {
|
||||
call_enum_ty: m.outer_enums.call_enum_ty.id,
|
||||
event_enum_ty: m.outer_enums.event_enum_ty.id,
|
||||
error_enum_ty: m.outer_enums.error_enum_ty.id,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -104,13 +109,16 @@ mod from_v15 {
|
||||
|
||||
fn from_extrinsic_metadata(value: v15::ExtrinsicMetadata<PortableForm>) -> ExtrinsicMetadata {
|
||||
ExtrinsicMetadata {
|
||||
ty: value.ty.id,
|
||||
version: value.version,
|
||||
signed_extensions: value
|
||||
.signed_extensions
|
||||
.into_iter()
|
||||
.map(from_signed_extension_metadata)
|
||||
.collect(),
|
||||
address_ty: value.address_ty.id,
|
||||
call_ty: value.call_ty.id,
|
||||
signature_ty: value.signature_ty.id,
|
||||
extra_ty: value.extra_ty.id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +276,14 @@ mod into_v15 {
|
||||
.into_iter()
|
||||
.map(from_runtime_api_metadata)
|
||||
.collect(),
|
||||
outer_enums: v15::OuterEnums {
|
||||
call_enum_ty: m.outer_enums.call_enum_ty.into(),
|
||||
event_enum_ty: m.outer_enums.event_enum_ty.into(),
|
||||
error_enum_ty: m.outer_enums.error_enum_ty.into(),
|
||||
},
|
||||
custom: v15::CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,13 +329,16 @@ mod into_v15 {
|
||||
|
||||
fn from_extrinsic_metadata(e: ExtrinsicMetadata) -> v15::ExtrinsicMetadata<PortableForm> {
|
||||
v15::ExtrinsicMetadata {
|
||||
ty: e.ty.into(),
|
||||
version: e.version,
|
||||
signed_extensions: e
|
||||
.signed_extensions
|
||||
.into_iter()
|
||||
.map(from_signed_extension_metadata)
|
||||
.collect(),
|
||||
address_ty: e.address_ty.into(),
|
||||
call_ty: e.call_ty.into(),
|
||||
signature_ty: e.signature_ty.into(),
|
||||
extra_ty: e.extra_ty.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+59
-5
@@ -45,6 +45,8 @@ pub struct Metadata {
|
||||
extrinsic: ExtrinsicMetadata,
|
||||
/// The type ID of the `Runtime` type.
|
||||
runtime_ty: u32,
|
||||
/// The types of the outer enums.
|
||||
outer_enums: OuterEnumsMetadata,
|
||||
/// The type Id of the `DispatchError` type, which Subxt makes use of.
|
||||
dispatch_error_ty: Option<u32>,
|
||||
/// Details about each of the runtime API traits.
|
||||
@@ -77,6 +79,11 @@ impl Metadata {
|
||||
&self.extrinsic
|
||||
}
|
||||
|
||||
/// Return details about the outer enums.
|
||||
pub fn outer_enums(&self) -> OuterEnumsMetadata {
|
||||
self.outer_enums
|
||||
}
|
||||
|
||||
/// An iterator over all of the available pallets.
|
||||
pub fn pallets(&self) -> impl ExactSizeIterator<Item = PalletMetadata<'_>> {
|
||||
self.pallets.values().iter().map(|inner| PalletMetadata {
|
||||
@@ -440,8 +447,14 @@ impl ConstantMetadata {
|
||||
/// Metadata for the extrinsic type.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExtrinsicMetadata {
|
||||
/// The type of the extrinsic.
|
||||
ty: u32,
|
||||
/// The type of the address that signs the extrinsic
|
||||
address_ty: u32,
|
||||
/// The type of the outermost Call enum.
|
||||
call_ty: u32,
|
||||
/// The type of the extrinsic's signature.
|
||||
signature_ty: u32,
|
||||
/// The type of the outermost Extra enum.
|
||||
extra_ty: u32,
|
||||
/// Extrinsic version.
|
||||
version: u8,
|
||||
/// The signed extensions in the order they appear in the extrinsic.
|
||||
@@ -449,9 +462,22 @@ pub struct ExtrinsicMetadata {
|
||||
}
|
||||
|
||||
impl ExtrinsicMetadata {
|
||||
/// Type of the extrinsic.
|
||||
pub fn ty(&self) -> u32 {
|
||||
self.ty
|
||||
/// The type of the address that signs the extrinsic
|
||||
pub fn address_ty(&self) -> u32 {
|
||||
self.address_ty
|
||||
}
|
||||
|
||||
/// The type of the outermost Call enum.
|
||||
pub fn call_ty(&self) -> u32 {
|
||||
self.call_ty
|
||||
}
|
||||
/// The type of the extrinsic's signature.
|
||||
pub fn signature_ty(&self) -> u32 {
|
||||
self.signature_ty
|
||||
}
|
||||
/// The type of the outermost Extra enum.
|
||||
pub fn extra_ty(&self) -> u32 {
|
||||
self.extra_ty
|
||||
}
|
||||
|
||||
/// Extrinsic version.
|
||||
@@ -491,6 +517,34 @@ impl SignedExtensionMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for the outer enums.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct OuterEnumsMetadata {
|
||||
/// The type of the outer call enum.
|
||||
call_enum_ty: u32,
|
||||
/// The type of the outer event enum.
|
||||
event_enum_ty: u32,
|
||||
/// The type of the outer error enum.
|
||||
error_enum_ty: u32,
|
||||
}
|
||||
|
||||
impl OuterEnumsMetadata {
|
||||
/// The type of the outer call enum.
|
||||
pub fn call_enum_ty(&self) -> u32 {
|
||||
self.call_enum_ty
|
||||
}
|
||||
|
||||
/// The type of the outer event enum.
|
||||
pub fn event_enum_ty(&self) -> u32 {
|
||||
self.event_enum_ty
|
||||
}
|
||||
|
||||
/// The type of the outer error enum.
|
||||
pub fn error_enum_ty(&self) -> u32 {
|
||||
self.error_enum_ty
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for the available runtime APIs.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RuntimeApiMetadata<'a> {
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
//! Utility functions to generate a subset of the metadata.
|
||||
|
||||
use crate::{
|
||||
ExtrinsicMetadata, Metadata, PalletMetadataInner, RuntimeApiMetadataInner, StorageEntryType,
|
||||
ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner,
|
||||
StorageEntryType,
|
||||
};
|
||||
use scale_info::TypeDef;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
@@ -82,7 +83,10 @@ fn update_pallet_types(pallet: &mut PalletMetadataInner, map_ids: &BTreeMap<u32,
|
||||
|
||||
/// Collect all type IDs needed to represent the extrinsic metadata.
|
||||
fn collect_extrinsic_types(extrinsic: &ExtrinsicMetadata, type_ids: &mut HashSet<u32>) {
|
||||
type_ids.insert(extrinsic.ty);
|
||||
type_ids.insert(extrinsic.address_ty);
|
||||
type_ids.insert(extrinsic.call_ty);
|
||||
type_ids.insert(extrinsic.signature_ty);
|
||||
type_ids.insert(extrinsic.extra_ty);
|
||||
|
||||
for signed in &extrinsic.signed_extensions {
|
||||
type_ids.insert(signed.extra_ty);
|
||||
@@ -92,7 +96,10 @@ fn collect_extrinsic_types(extrinsic: &ExtrinsicMetadata, type_ids: &mut HashSet
|
||||
|
||||
/// Update all type IDs of the provided extrinsic metadata using the new type IDs from the portable registry.
|
||||
fn update_extrinsic_types(extrinsic: &mut ExtrinsicMetadata, map_ids: &BTreeMap<u32, u32>) {
|
||||
update_type(&mut extrinsic.ty, map_ids);
|
||||
update_type(&mut extrinsic.address_ty, map_ids);
|
||||
update_type(&mut extrinsic.call_ty, map_ids);
|
||||
update_type(&mut extrinsic.signature_ty, map_ids);
|
||||
update_type(&mut extrinsic.extra_ty, map_ids);
|
||||
|
||||
for signed in &mut extrinsic.signed_extensions {
|
||||
update_type(&mut signed.extra_ty, map_ids);
|
||||
@@ -122,6 +129,20 @@ fn update_runtime_api_types(apis: &mut [RuntimeApiMetadataInner], map_ids: &BTre
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect the outer enums type IDs.
|
||||
fn collect_outer_enums(enums: &OuterEnumsMetadata, type_ids: &mut HashSet<u32>) {
|
||||
type_ids.insert(enums.call_enum_ty);
|
||||
type_ids.insert(enums.event_enum_ty);
|
||||
type_ids.insert(enums.error_enum_ty);
|
||||
}
|
||||
|
||||
/// Update all the type IDs for outer enums.
|
||||
fn update_outer_enums(enums: &mut OuterEnumsMetadata, map_ids: &BTreeMap<u32, u32>) {
|
||||
update_type(&mut enums.call_enum_ty, map_ids);
|
||||
update_type(&mut enums.event_enum_ty, map_ids);
|
||||
update_type(&mut enums.error_enum_ty, map_ids);
|
||||
}
|
||||
|
||||
/// Update the given type using the new type ID from the portable registry.
|
||||
///
|
||||
/// # Panics
|
||||
@@ -136,38 +157,36 @@ fn update_type(ty: &mut u32, map_ids: &BTreeMap<u32, u32>) {
|
||||
*ty = new_id;
|
||||
}
|
||||
|
||||
/// Strip any pallets out of the RuntimeCall type that aren't the ones we want to keep.
|
||||
/// The RuntimeCall type is referenced in a bunch of places, so doing this prevents us from
|
||||
/// holding on to stuff in pallets we've asked not to keep.
|
||||
fn retain_pallets_in_runtime_call_type<F>(metadata: &mut Metadata, mut filter: F)
|
||||
/// Retain the enum type identified by ID and keep only the variants that
|
||||
/// match the provided filter.
|
||||
fn retain_variants_in_enum_type<F>(metadata: &mut Metadata, id: u32, mut filter: F)
|
||||
where
|
||||
F: FnMut(&str) -> bool,
|
||||
{
|
||||
let extrinsic_ty = metadata
|
||||
let ty = metadata
|
||||
.types
|
||||
.types
|
||||
.get_mut(metadata.extrinsic.ty as usize)
|
||||
.expect("Metadata should contain extrinsic type in registry");
|
||||
.get_mut(id as usize)
|
||||
.expect("Metadata should contain enum type in registry");
|
||||
|
||||
let Some(call_ty) = extrinsic_ty.ty.type_params
|
||||
.iter_mut()
|
||||
.find(|ty| ty.name == "Call")
|
||||
.and_then(|ty| ty.ty) else { return; };
|
||||
|
||||
let call_ty = metadata
|
||||
.types
|
||||
.types
|
||||
.get_mut(call_ty.id as usize)
|
||||
.expect("Metadata should contain Call type information");
|
||||
|
||||
let TypeDef::Variant(variant) = &mut call_ty.ty.type_def else {
|
||||
panic!("Metadata Call type is expected to be a variant type");
|
||||
let TypeDef::Variant(variant) = &mut ty.ty.type_def else {
|
||||
panic!("Metadata type is expected to be a variant type");
|
||||
};
|
||||
|
||||
// Remove all variants from the call type that aren't the pallet(s) we want to keep.
|
||||
// Remove all variants from the type that aren't the pallet(s) we want to keep.
|
||||
variant.variants.retain(|v| filter(&v.name));
|
||||
}
|
||||
|
||||
/// Strip any pallets out of the outer enum types that aren't the ones we want to keep.
|
||||
fn retain_pallets_in_runtime_outer_types<F>(metadata: &mut Metadata, mut filter: F)
|
||||
where
|
||||
F: FnMut(&str) -> bool,
|
||||
{
|
||||
retain_variants_in_enum_type(metadata, metadata.outer_enums.call_enum_ty, &mut filter);
|
||||
retain_variants_in_enum_type(metadata, metadata.outer_enums.event_enum_ty, &mut filter);
|
||||
retain_variants_in_enum_type(metadata, metadata.outer_enums.error_enum_ty, &mut filter);
|
||||
}
|
||||
|
||||
/// Generate a subset of the metadata that contains only the
|
||||
/// types needed to represent the provided pallets and runtime APIs.
|
||||
///
|
||||
@@ -190,10 +209,13 @@ pub fn retain_metadata<F, G>(
|
||||
{
|
||||
let mut type_ids = HashSet::new();
|
||||
|
||||
// There is a special RuntimeCall type which points to all pallets and call types by default.
|
||||
// There are special outer enum types that point to all pallets types (call, error, event) by default.
|
||||
// This brings in a significant chunk of types. We trim this down to only include variants
|
||||
// for the pallets we're retaining, to avoid this.
|
||||
retain_pallets_in_runtime_call_type(metadata, &mut pallets_filter);
|
||||
retain_pallets_in_runtime_outer_types(metadata, &mut pallets_filter);
|
||||
|
||||
// Collect the stripped outer enums.
|
||||
collect_outer_enums(&metadata.outer_enums, &mut type_ids);
|
||||
|
||||
// Filter our pallet list to only those pallets we want to keep. Keep hold of all
|
||||
// type IDs in the pallets we're keeping. Retain all, if no filter specified.
|
||||
@@ -245,6 +267,7 @@ pub fn retain_metadata<F, G>(
|
||||
let map_ids = metadata.types.retain(|id| type_ids.contains(&id));
|
||||
|
||||
// And finally, we can go and update all of our type IDs in the metadata as a result of this:
|
||||
update_outer_enums(&mut metadata.outer_enums, &map_ids);
|
||||
for pallets in metadata.pallets.values_mut() {
|
||||
update_pallet_types(pallets, &map_ids);
|
||||
}
|
||||
@@ -257,6 +280,7 @@ pub fn retain_metadata<F, G>(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Metadata;
|
||||
use assert_matches::assert_matches;
|
||||
use codec::Decode;
|
||||
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use std::{fs, path::Path};
|
||||
@@ -281,6 +305,7 @@ mod tests {
|
||||
// Retain one pallet at a time ensuring the test does not panic.
|
||||
for pallet in metadata_cache.pallets() {
|
||||
let mut metadata = metadata_cache.clone();
|
||||
|
||||
retain_metadata(
|
||||
&mut metadata,
|
||||
|pallet_name| pallet_name == pallet.name(),
|
||||
@@ -292,6 +317,21 @@ mod tests {
|
||||
&*metadata.pallets.get_by_index(0).unwrap().name,
|
||||
pallet.name()
|
||||
);
|
||||
|
||||
let id = metadata.outer_enums().call_enum_ty;
|
||||
let ty = metadata.types.resolve(id).unwrap();
|
||||
let num_variants = if pallet.call_ty_id().is_some() { 1 } else { 0 };
|
||||
assert_matches!(&ty.type_def, TypeDef::Variant(variant) if variant.variants.len() == num_variants);
|
||||
|
||||
let id = metadata.outer_enums().error_enum_ty;
|
||||
let ty = metadata.types.resolve(id).unwrap();
|
||||
let num_variants = if pallet.error_ty_id().is_some() { 1 } else { 0 };
|
||||
assert_matches!(&ty.type_def, TypeDef::Variant(variant) if variant.variants.len() == num_variants);
|
||||
|
||||
let id = metadata.outer_enums().event_enum_ty;
|
||||
let ty = metadata.types.resolve(id).unwrap();
|
||||
let num_variants = if pallet.event_ty_id().is_some() { 1 } else { 0 };
|
||||
assert_matches!(&ty.type_def, TypeDef::Variant(variant) if variant.variants.len() == num_variants);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
//! Utility functions for metadata validation.
|
||||
|
||||
use crate::{
|
||||
ExtrinsicMetadata, Metadata, PalletMetadata, RuntimeApiMetadata, RuntimeApiMethodMetadata,
|
||||
StorageEntryMetadata, StorageEntryType,
|
||||
ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadata, RuntimeApiMetadata,
|
||||
RuntimeApiMethodMetadata, StorageEntryMetadata, StorageEntryType,
|
||||
};
|
||||
use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, Variant};
|
||||
use scale_info::{form::PortableForm, Field, PortableRegistry, TypeDef, TypeDefVariant, Variant};
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Predefined value to be returned when we already visited a type.
|
||||
@@ -104,6 +104,30 @@ fn get_variant_hash(
|
||||
concat_and_hash2(&variant_name_bytes, &variant_field_bytes)
|
||||
}
|
||||
|
||||
fn get_type_def_variant_hash(
|
||||
registry: &PortableRegistry,
|
||||
variant: &TypeDefVariant<PortableForm>,
|
||||
only_these_variants: Option<&[&str]>,
|
||||
visited_ids: &mut HashSet<u32>,
|
||||
) -> [u8; HASH_LEN] {
|
||||
let variant_id_bytes = [TypeBeingHashed::Variant as u8; HASH_LEN];
|
||||
let variant_field_bytes = variant.variants.iter().fold([0u8; HASH_LEN], |bytes, var| {
|
||||
// With EncodeAsType and DecodeAsType we no longer care which order the variants are in,
|
||||
// as long as all of the names+types are there. XOR to not care about ordering.
|
||||
let should_hash = only_these_variants
|
||||
.as_ref()
|
||||
.map(|only_these_variants| only_these_variants.contains(&var.name.as_str()))
|
||||
.unwrap_or(true);
|
||||
|
||||
if should_hash {
|
||||
xor(bytes, get_variant_hash(registry, var, visited_ids))
|
||||
} else {
|
||||
bytes
|
||||
}
|
||||
});
|
||||
concat_and_hash2(&variant_id_bytes, &variant_field_bytes)
|
||||
}
|
||||
|
||||
/// Obtain the hash representation of a `scale_info::TypeDef`.
|
||||
fn get_type_def_hash(
|
||||
registry: &PortableRegistry,
|
||||
@@ -125,14 +149,7 @@ fn get_type_def_hash(
|
||||
concat_and_hash2(&composite_id_bytes, &composite_field_bytes)
|
||||
}
|
||||
TypeDef::Variant(variant) => {
|
||||
let variant_id_bytes = [TypeBeingHashed::Variant as u8; HASH_LEN];
|
||||
let variant_field_bytes =
|
||||
variant.variants.iter().fold([0u8; HASH_LEN], |bytes, var| {
|
||||
// With EncodeAsType and DecodeAsType we no longer care which order the variants are in,
|
||||
// as long as all of the names+types are there. XOR to not care about ordering.
|
||||
xor(bytes, get_variant_hash(registry, var, visited_ids))
|
||||
});
|
||||
concat_and_hash2(&variant_id_bytes, &variant_field_bytes)
|
||||
get_type_def_variant_hash(registry, variant, None, visited_ids)
|
||||
}
|
||||
TypeDef::Sequence(sequence) => concat_and_hash2(
|
||||
&[TypeBeingHashed::Sequence as u8; HASH_LEN],
|
||||
@@ -198,8 +215,16 @@ fn get_extrinsic_hash(
|
||||
) -> [u8; HASH_LEN] {
|
||||
let mut visited_ids = HashSet::<u32>::new();
|
||||
|
||||
let mut bytes = concat_and_hash2(
|
||||
&get_type_hash(registry, extrinsic.ty, &mut visited_ids),
|
||||
// Get the hashes of the extrinsic type.
|
||||
let address_hash = get_type_hash(registry, extrinsic.address_ty, &mut visited_ids);
|
||||
// The `RuntimeCall` type is intentionally omitted and hashed by the outer enums instead.
|
||||
let signature_hash = get_type_hash(registry, extrinsic.signature_ty, &mut visited_ids);
|
||||
let extra_hash = get_type_hash(registry, extrinsic.extra_ty, &mut visited_ids);
|
||||
|
||||
let mut bytes = concat_and_hash4(
|
||||
&address_hash,
|
||||
&signature_hash,
|
||||
&extra_hash,
|
||||
&[extrinsic.version; 32],
|
||||
);
|
||||
|
||||
@@ -215,6 +240,39 @@ fn get_extrinsic_hash(
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Obtain the hash representation of the `frame_metadata::v15::OuterEnums`.
|
||||
fn get_outer_enums_hash(
|
||||
registry: &PortableRegistry,
|
||||
enums: &OuterEnumsMetadata,
|
||||
only_these_variants: Option<&[&str]>,
|
||||
) -> [u8; HASH_LEN] {
|
||||
/// Hash the provided enum type.
|
||||
fn get_enum_hash(
|
||||
registry: &PortableRegistry,
|
||||
id: u32,
|
||||
only_these_variants: Option<&[&str]>,
|
||||
) -> [u8; HASH_LEN] {
|
||||
let ty = registry
|
||||
.types
|
||||
.get(id as usize)
|
||||
.expect("Metadata should contain enum type in registry");
|
||||
|
||||
if let TypeDef::Variant(variant) = &ty.ty.type_def {
|
||||
get_type_def_variant_hash(registry, variant, only_these_variants, &mut HashSet::new())
|
||||
} else {
|
||||
get_type_hash(registry, id, &mut HashSet::new())
|
||||
}
|
||||
}
|
||||
|
||||
let call_hash = get_enum_hash(registry, enums.call_enum_ty, only_these_variants);
|
||||
|
||||
let event_hash = get_enum_hash(registry, enums.event_enum_ty, only_these_variants);
|
||||
|
||||
let error_hash = get_enum_hash(registry, enums.error_enum_ty, only_these_variants);
|
||||
|
||||
concat_and_hash3(&call_hash, &event_hash, &error_hash)
|
||||
}
|
||||
|
||||
/// Get the hash corresponding to a single storage entry.
|
||||
fn get_storage_entry_hash(
|
||||
registry: &PortableRegistry,
|
||||
@@ -441,8 +499,6 @@ impl<'a> MetadataHasher<'a> {
|
||||
|
||||
/// Hash the given metadata.
|
||||
pub fn hash(&self) -> [u8; HASH_LEN] {
|
||||
let mut visited_ids = HashSet::<u32>::new();
|
||||
|
||||
let metadata = self.metadata;
|
||||
|
||||
let pallet_hash = metadata.pallets().fold([0u8; HASH_LEN], |bytes, pallet| {
|
||||
@@ -480,9 +536,21 @@ impl<'a> MetadataHasher<'a> {
|
||||
});
|
||||
|
||||
let extrinsic_hash = get_extrinsic_hash(&metadata.types, &metadata.extrinsic);
|
||||
let runtime_hash = get_type_hash(&metadata.types, metadata.runtime_ty(), &mut visited_ids);
|
||||
let runtime_hash =
|
||||
get_type_hash(&metadata.types, metadata.runtime_ty(), &mut HashSet::new());
|
||||
let outer_enums_hash = get_outer_enums_hash(
|
||||
&metadata.types,
|
||||
&metadata.outer_enums(),
|
||||
self.specific_pallets.as_deref(),
|
||||
);
|
||||
|
||||
concat_and_hash4(&pallet_hash, &apis_hash, &extrinsic_hash, &runtime_hash)
|
||||
concat_and_hash5(
|
||||
&pallet_hash,
|
||||
&apis_hash,
|
||||
&extrinsic_hash,
|
||||
&runtime_hash,
|
||||
&outer_enums_hash,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,9 +620,12 @@ mod tests {
|
||||
|
||||
fn build_default_extrinsic() -> v15::ExtrinsicMetadata {
|
||||
v15::ExtrinsicMetadata {
|
||||
ty: meta_type::<()>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<()>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,6 +668,14 @@ mod tests {
|
||||
build_default_extrinsic(),
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
v15::OuterEnums {
|
||||
call_enum_ty: meta_type::<()>(),
|
||||
event_enum_ty: meta_type::<()>(),
|
||||
error_enum_ty: meta_type::<()>(),
|
||||
},
|
||||
v15::CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
)
|
||||
.try_into()
|
||||
.expect("can build valid metadata")
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# ```
|
||||
|
||||
# get the full metadata
|
||||
cargo run --bin subxt metadata --version unstable > artifacts/polkadot_metadata_full.scale
|
||||
cargo run --bin subxt metadata --version 15 > artifacts/polkadot_metadata_full.scale
|
||||
# use it to generate polkadot.rs
|
||||
cargo run --bin subxt codegen --file artifacts/polkadot_metadata_full.scale | rustfmt > testing/integration-tests/src/codegen/polkadot.rs
|
||||
# generate a metadata file that only contains the pallets Balances, Staking, System and Multisig
|
||||
cargo run --bin subxt metadata --file artifacts/polkadot_metadata_full.scale --pallets "Balances,Staking,System,Multisig" > artifacts/polkadot_metadata_small.scale
|
||||
cargo run --bin subxt codegen --file artifacts/polkadot_metadata_full.scale | rustfmt > testing/integration-tests/src/full_client/codegen/polkadot.rs
|
||||
# generate a metadata file that only contains a few pallets that we need for our examples.
|
||||
cargo run --bin subxt metadata --file artifacts/polkadot_metadata_full.scale --pallets "Balances,Staking,System,Multisig,Timestamp,ParaInherent" > artifacts/polkadot_metadata_small.scale
|
||||
# generate a metadata file that only contains no pallets
|
||||
cargo run --bin subxt metadata --file artifacts/polkadot_metadata_full.scale --pallets "" > artifacts/polkadot_metadata_tiny.scale
|
||||
|
||||
@@ -16,8 +16,8 @@ use crate::{
|
||||
use crate::utils::strip_compact_prefix;
|
||||
use codec::Decode;
|
||||
use derivative::Derivative;
|
||||
use scale_decode::DecodeAsFields;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use scale_decode::{DecodeAsFields, DecodeAsType};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Trait to uniquely identify the extrinsic's identity from the runtime metadata.
|
||||
///
|
||||
@@ -37,23 +37,6 @@ pub trait StaticExtrinsic: DecodeAsFields {
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is implemented on the statically generated root extrinsic type, so that we're able
|
||||
/// to decode it properly via a pallet that impls `DecodeAsMetadata`. This is necessary
|
||||
/// because the "root extrinsic" type is generated using pallet info but doesn't actually exist in the
|
||||
/// metadata types, so we have no easy way to decode things into it via type information and need a
|
||||
/// little help via codegen.
|
||||
#[doc(hidden)]
|
||||
pub trait RootExtrinsic: Sized {
|
||||
/// Given details of the pallet extrinsic we want to decode, and the name of the pallet, try to hand
|
||||
/// back a "root extrinsic".
|
||||
fn root_extrinsic(
|
||||
pallet_bytes: &[u8],
|
||||
pallet_name: &str,
|
||||
pallet_extrinsic_ty: u32,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
/// The body of a block.
|
||||
pub struct Extrinsics<T: Config, C> {
|
||||
client: C,
|
||||
@@ -420,22 +403,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to decode these [`ExtrinsicDetails`] into a root extrinsic type (which includes
|
||||
/// Attempt to decode these [`ExtrinsicDetails`] into an outer call enum type (which includes
|
||||
/// the pallet and extrinsic enum variants as well as the extrinsic fields). A compatible
|
||||
/// type for this is exposed via static codegen as a root level `Call` type.
|
||||
pub fn as_root_extrinsic<E: RootExtrinsic>(&self) -> Result<E, Error> {
|
||||
let md = self.extrinsic_metadata()?;
|
||||
let pallet_extrinsic_ty = md.pallet.call_ty_id().ok_or_else(|| {
|
||||
Error::Metadata(MetadataError::CallTypeNotFoundInPallet(md.pallet.index()))
|
||||
})?;
|
||||
pub fn as_root_extrinsic<E: DecodeAsType>(&self) -> Result<E, Error> {
|
||||
let decoded = E::decode_as_type(
|
||||
&mut &self.call_bytes()[..],
|
||||
self.metadata.outer_enums().call_enum_ty(),
|
||||
self.metadata.types(),
|
||||
)?;
|
||||
|
||||
// Ignore root enum index.
|
||||
E::root_extrinsic(
|
||||
&self.call_bytes()[1..],
|
||||
md.pallet.name(),
|
||||
pallet_extrinsic_ty,
|
||||
&self.metadata,
|
||||
)
|
||||
Ok(decoded)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,47 +456,11 @@ pub(crate) struct ExtrinsicPartTypeIds {
|
||||
impl ExtrinsicPartTypeIds {
|
||||
/// Extract the generic type parameters IDs from the extrinsic type.
|
||||
pub(crate) fn new(metadata: &Metadata) -> Result<Self, BlockError> {
|
||||
const ADDRESS: &str = "Address";
|
||||
const CALL: &str = "Call";
|
||||
const SIGNATURE: &str = "Signature";
|
||||
const EXTRA: &str = "Extra";
|
||||
|
||||
let id = metadata.extrinsic().ty();
|
||||
|
||||
let Some(ty) = metadata.types().resolve(id) else {
|
||||
return Err(BlockError::MissingType);
|
||||
};
|
||||
|
||||
let params: HashMap<_, _> = ty
|
||||
.type_params
|
||||
.iter()
|
||||
.map(|ty_param| {
|
||||
let Some(ty) = ty_param.ty else {
|
||||
return Err(BlockError::MissingType);
|
||||
};
|
||||
|
||||
Ok((ty_param.name.as_str(), ty.id))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let Some(address) = params.get(ADDRESS) else {
|
||||
return Err(BlockError::MissingType);
|
||||
};
|
||||
let Some(call) = params.get(CALL) else {
|
||||
return Err(BlockError::MissingType);
|
||||
};
|
||||
let Some(signature) = params.get(SIGNATURE) else {
|
||||
return Err(BlockError::MissingType);
|
||||
};
|
||||
let Some(extra) = params.get(EXTRA) else {
|
||||
return Err(BlockError::MissingType);
|
||||
};
|
||||
|
||||
Ok(ExtrinsicPartTypeIds {
|
||||
address: *address,
|
||||
_call: *call,
|
||||
signature: *signature,
|
||||
extra: *extra,
|
||||
address: metadata.extrinsic().address_ty(),
|
||||
_call: metadata.extrinsic().call_ty(),
|
||||
signature: metadata.extrinsic().signature_ty(),
|
||||
extra: metadata.extrinsic().extra_ty(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -620,10 +562,10 @@ impl<T: Config> ExtrinsicEvents<T> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::metadata::DecodeWithMetadata;
|
||||
use crate::{rpc::types::RuntimeVersion, OfflineClient, PolkadotConfig};
|
||||
use assert_matches::assert_matches;
|
||||
use codec::{Decode, Encode};
|
||||
use frame_metadata::v15::{CustomMetadata, OuterEnums};
|
||||
use frame_metadata::{
|
||||
v15::{ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, RuntimeMetadataV15},
|
||||
RuntimeMetadataPrefixed,
|
||||
@@ -660,27 +602,6 @@ mod tests {
|
||||
Test(Pallet),
|
||||
}
|
||||
|
||||
// We need this in order to be able to decode into a root extrinsic type:
|
||||
impl RootExtrinsic for RuntimeCall {
|
||||
fn root_extrinsic(
|
||||
mut pallet_bytes: &[u8],
|
||||
pallet_name: &str,
|
||||
pallet_extrinsic_ty: u32,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Self, Error> {
|
||||
if pallet_name == "Test" {
|
||||
return Ok(RuntimeCall::Test(Pallet::decode_with_metadata(
|
||||
&mut pallet_bytes,
|
||||
pallet_extrinsic_ty,
|
||||
metadata,
|
||||
)?));
|
||||
}
|
||||
panic!(
|
||||
"Asked for pallet name '{pallet_name}', which isn't in our test RuntimeCall type"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// The calls of the pallet.
|
||||
#[allow(unused)]
|
||||
#[derive(
|
||||
@@ -743,12 +664,28 @@ mod tests {
|
||||
}];
|
||||
|
||||
let extrinsic = ExtrinsicMetadata {
|
||||
ty: meta_type::<ExtrinsicType<(), RuntimeCall, (), ()>>(),
|
||||
version: 4,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<RuntimeCall>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
};
|
||||
|
||||
let meta = RuntimeMetadataV15::new(pallets, extrinsic, meta_type::<()>(), vec![]);
|
||||
let meta = RuntimeMetadataV15::new(
|
||||
pallets,
|
||||
extrinsic,
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
OuterEnums {
|
||||
call_enum_ty: meta_type::<RuntimeCall>(),
|
||||
event_enum_ty: meta_type::<()>(),
|
||||
error_enum_ty: meta_type::<()>(),
|
||||
},
|
||||
CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
);
|
||||
let runtime_metadata: RuntimeMetadataPrefixed = meta.into();
|
||||
|
||||
Metadata::new(runtime_metadata.try_into().unwrap())
|
||||
|
||||
@@ -10,6 +10,4 @@ mod extrinsic_types;
|
||||
|
||||
pub use block_types::{Block, BlockBody};
|
||||
pub use blocks_client::{subscribe_to_block_headers_filling_in_gaps, BlocksClient};
|
||||
pub use extrinsic_types::{
|
||||
ExtrinsicDetails, ExtrinsicEvents, Extrinsics, RootExtrinsic, StaticExtrinsic,
|
||||
};
|
||||
pub use extrinsic_types::{ExtrinsicDetails, ExtrinsicEvents, Extrinsics, StaticExtrinsic};
|
||||
|
||||
@@ -129,16 +129,32 @@ impl<T: Config> OnlineClient<T> {
|
||||
async fn fetch_metadata(rpc: &Rpc<T>) -> Result<Metadata, Error> {
|
||||
#[cfg(feature = "unstable-metadata")]
|
||||
{
|
||||
/// The unstable metadata version number.
|
||||
const UNSTABLE_METADATA_VERSION: u32 = u32::MAX;
|
||||
|
||||
// Try to fetch the latest unstable metadata, if that fails fall back to
|
||||
// fetching the latest stable metadata.
|
||||
const V15_METADATA_VERSION: u32 = u32::MAX;
|
||||
match rpc.metadata_at_version(V15_METADATA_VERSION).await {
|
||||
match rpc.metadata_at_version(UNSTABLE_METADATA_VERSION).await {
|
||||
Ok(bytes) => Ok(bytes),
|
||||
Err(_) => rpc.metadata().await,
|
||||
Err(_) => OnlineClient::fetch_latest_stable_metadata(rpc).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unstable-metadata"))]
|
||||
OnlineClient::fetch_latest_stable_metadata(rpc).await
|
||||
}
|
||||
|
||||
/// Fetch the latest stable metadata from the node.
|
||||
async fn fetch_latest_stable_metadata(rpc: &Rpc<T>) -> Result<Metadata, Error> {
|
||||
// This is the latest stable metadata that subxt can utilize.
|
||||
const V15_METADATA_VERSION: u32 = 15;
|
||||
|
||||
// Try to fetch the metadata version.
|
||||
if let Ok(bytes) = rpc.metadata_at_version(V15_METADATA_VERSION).await {
|
||||
return Ok(bytes);
|
||||
}
|
||||
|
||||
// If that fails, fetch the metadata V14 using the old API.
|
||||
rpc.metadata().await
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
|
||||
use crate::metadata::{DecodeWithMetadata, Metadata};
|
||||
use core::fmt::Debug;
|
||||
use scale_decode::visitor::DecodeAsTypeResult;
|
||||
use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::{Error, MetadataError};
|
||||
use crate::error::RootError;
|
||||
|
||||
/// An error dispatching a transaction.
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
@@ -127,13 +126,17 @@ pub enum TransactionalError {
|
||||
#[non_exhaustive]
|
||||
pub struct ModuleError {
|
||||
metadata: Metadata,
|
||||
raw: RawModuleError,
|
||||
/// Bytes representation:
|
||||
/// - `bytes[0]`: pallet index
|
||||
/// - `bytes[1]`: error index
|
||||
/// - `bytes[2..]`: 3 bytes specific for the module error
|
||||
bytes: [u8; 5],
|
||||
}
|
||||
|
||||
impl PartialEq for ModuleError {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// A module error is the same if the raw underlying details are the same.
|
||||
self.raw == other.raw
|
||||
self.bytes == other.bytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,27 +157,38 @@ impl std::fmt::Display for ModuleError {
|
||||
impl ModuleError {
|
||||
/// Return more details about this error.
|
||||
pub fn details(&self) -> Result<ModuleErrorDetails, MetadataError> {
|
||||
let pallet = self.metadata.pallet_by_index_err(self.raw.pallet_index)?;
|
||||
let pallet = self.metadata.pallet_by_index_err(self.pallet_index())?;
|
||||
let variant = pallet
|
||||
.error_variant_by_index(self.raw.error[0])
|
||||
.ok_or_else(|| MetadataError::VariantIndexNotFound(self.raw.error[0]))?;
|
||||
.error_variant_by_index(self.error_index())
|
||||
.ok_or_else(|| MetadataError::VariantIndexNotFound(self.error_index()))?;
|
||||
|
||||
Ok(ModuleErrorDetails { pallet, variant })
|
||||
}
|
||||
|
||||
/// Return the underlying module error data that was decoded.
|
||||
pub fn raw(&self) -> RawModuleError {
|
||||
self.raw
|
||||
pub fn bytes(&self) -> [u8; 5] {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
/// Attempts to decode the ModuleError into a value implementing the trait `RootError`
|
||||
/// where the actual type of value is the generated top level enum `Error`.
|
||||
pub fn as_root_error<E: RootError>(&self) -> Result<E, Error> {
|
||||
E::root_error(
|
||||
&self.raw.error,
|
||||
self.details()?.pallet.name(),
|
||||
&self.metadata,
|
||||
)
|
||||
/// Obtain the pallet index from the underlying byte data.
|
||||
pub fn pallet_index(&self) -> u8 {
|
||||
self.bytes[0]
|
||||
}
|
||||
|
||||
/// Obtain the error index from the underlying byte data.
|
||||
pub fn error_index(&self) -> u8 {
|
||||
self.bytes[1]
|
||||
}
|
||||
|
||||
/// Attempts to decode the ModuleError into the top outer Error enum.
|
||||
pub fn as_root_error<E: DecodeAsType>(&self) -> Result<E, Error> {
|
||||
let decoded = E::decode_as_type(
|
||||
&mut &self.bytes[..],
|
||||
self.metadata.outer_enums().error_enum_ty(),
|
||||
self.metadata.types(),
|
||||
)?;
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,25 +200,6 @@ pub struct ModuleErrorDetails<'a> {
|
||||
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
|
||||
}
|
||||
|
||||
/// The error details about a module error that has occurred.
|
||||
///
|
||||
/// **Note**: Structure used to obtain the underlying bytes of a ModuleError.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct RawModuleError {
|
||||
/// Index of the pallet that the error came from.
|
||||
pub pallet_index: u8,
|
||||
/// Raw error bytes.
|
||||
pub error: [u8; 4],
|
||||
}
|
||||
|
||||
impl RawModuleError {
|
||||
/// Obtain the error index from the underlying byte data.
|
||||
pub fn error_index(&self) -> u8 {
|
||||
// Error index is utilized as the first byte from the error array.
|
||||
self.error[0]
|
||||
}
|
||||
}
|
||||
|
||||
impl DispatchError {
|
||||
/// Attempt to decode a runtime [`DispatchError`].
|
||||
#[doc(hidden)]
|
||||
@@ -290,21 +285,16 @@ impl DispatchError {
|
||||
|
||||
// The old version is 2 bytes; a pallet and error index.
|
||||
// The new version is 5 bytes; a pallet and error index and then 3 extra bytes.
|
||||
let raw = if module_bytes.len() == 2 {
|
||||
RawModuleError {
|
||||
pallet_index: module_bytes[0],
|
||||
error: [module_bytes[1], 0, 0, 0],
|
||||
}
|
||||
let bytes = if module_bytes.len() == 2 {
|
||||
[module_bytes[0], module_bytes[1], 0, 0, 0]
|
||||
} else if module_bytes.len() == 5 {
|
||||
RawModuleError {
|
||||
pallet_index: module_bytes[0],
|
||||
error: [
|
||||
module_bytes[1],
|
||||
module_bytes[2],
|
||||
module_bytes[3],
|
||||
module_bytes[4],
|
||||
],
|
||||
}
|
||||
[
|
||||
module_bytes[0],
|
||||
module_bytes[1],
|
||||
module_bytes[2],
|
||||
module_bytes[3],
|
||||
module_bytes[4],
|
||||
]
|
||||
} else {
|
||||
tracing::warn!("Can't decode error sp_runtime::DispatchError: bytes do not match known shapes");
|
||||
// Return _all_ of the bytes; every "unknown" return should be consistent.
|
||||
@@ -312,7 +302,7 @@ impl DispatchError {
|
||||
};
|
||||
|
||||
// And return our outward-facing version:
|
||||
DispatchError::Module(ModuleError { metadata, raw })
|
||||
DispatchError::Module(ModuleError { metadata, bytes })
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
-13
@@ -13,7 +13,7 @@ pub use crate::client::LightClientError;
|
||||
|
||||
// Re-export dispatch error types:
|
||||
pub use dispatch_error::{
|
||||
ArithmeticError, DispatchError, ModuleError, RawModuleError, TokenError, TransactionalError,
|
||||
ArithmeticError, DispatchError, ModuleError, TokenError, TransactionalError,
|
||||
};
|
||||
|
||||
// Re-expose the errors we use from other crates here:
|
||||
@@ -225,15 +225,3 @@ pub enum MetadataError {
|
||||
#[error("The generated code is not compatible with the node")]
|
||||
IncompatibleCodegen,
|
||||
}
|
||||
|
||||
/// This trait is implemented on the statically generated root ModuleError type
|
||||
#[doc(hidden)]
|
||||
pub trait RootError: Sized {
|
||||
/// Given details of the pallet error we want to decode
|
||||
fn root_error(
|
||||
// typically a [u8; 4] encodes the error of a pallet
|
||||
pallet_bytes: &[u8],
|
||||
pallet_name: &str,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::{
|
||||
};
|
||||
use codec::{Compact, Decode};
|
||||
use derivative::Derivative;
|
||||
use scale_decode::DecodeAsType;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A collection of events obtained from a block, bundled with the necessary
|
||||
@@ -386,20 +387,16 @@ impl<T: Config> EventDetails<T> {
|
||||
/// Attempt to decode these [`EventDetails`] into a root event type (which includes
|
||||
/// the pallet and event enum variants as well as the event fields). A compatible
|
||||
/// type for this is exposed via static codegen as a root level `Event` type.
|
||||
pub fn as_root_event<E: RootEvent>(&self) -> Result<E, Error> {
|
||||
let ev_metadata = self.event_metadata();
|
||||
let pallet_bytes = &self.all_bytes[self.event_start_idx + 1..self.event_fields_end_idx];
|
||||
let pallet_event_ty = ev_metadata
|
||||
.pallet
|
||||
.event_ty_id()
|
||||
.ok_or_else(|| MetadataError::EventTypeNotFoundInPallet(ev_metadata.pallet.index()))?;
|
||||
pub fn as_root_event<E: DecodeAsType>(&self) -> Result<E, Error> {
|
||||
let bytes = &self.all_bytes[self.event_start_idx..self.event_fields_end_idx];
|
||||
|
||||
E::root_event(
|
||||
pallet_bytes,
|
||||
self.pallet_name(),
|
||||
pallet_event_ty,
|
||||
&self.metadata,
|
||||
)
|
||||
let decoded = E::decode_as_type(
|
||||
&mut &bytes[..],
|
||||
self.metadata.outer_enums().event_enum_ty(),
|
||||
self.metadata.types(),
|
||||
)?;
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
/// Return the topics associated with this event.
|
||||
@@ -414,32 +411,17 @@ pub struct EventMetadataDetails<'a> {
|
||||
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
|
||||
}
|
||||
|
||||
/// This trait is implemented on the statically generated root event type, so that we're able
|
||||
/// to decode it properly via a pallet event that impls `DecodeAsMetadata`. This is necessary
|
||||
/// becasue the "root event" type is generated using pallet info but doesn't actually exist in the
|
||||
/// metadata types, so we have no easy way to decode things into it via type information and need a
|
||||
/// little help via codegen.
|
||||
#[doc(hidden)]
|
||||
pub trait RootEvent: Sized {
|
||||
/// Given details of the pallet event we want to decode, and the name of the pallet, try to hand
|
||||
/// back a "root event".
|
||||
fn root_event(
|
||||
pallet_bytes: &[u8],
|
||||
pallet_name: &str,
|
||||
pallet_event_ty: u32,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
/// Event related test utilities used outside this module.
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test_utils {
|
||||
use super::*;
|
||||
use crate::metadata::DecodeWithMetadata;
|
||||
use crate::{Config, SubstrateConfig};
|
||||
use codec::Encode;
|
||||
use frame_metadata::{
|
||||
v15::{ExtrinsicMetadata, PalletEventMetadata, PalletMetadata, RuntimeMetadataV15},
|
||||
v15::{
|
||||
CustomMetadata, ExtrinsicMetadata, OuterEnums, PalletEventMetadata, PalletMetadata,
|
||||
RuntimeMetadataV15,
|
||||
},
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use scale_info::{meta_type, TypeInfo};
|
||||
@@ -460,25 +442,6 @@ pub(crate) mod test_utils {
|
||||
Test(Ev),
|
||||
}
|
||||
|
||||
// We need this in order to be able to decode into a root event type:
|
||||
impl<Ev: DecodeWithMetadata> RootEvent for AllEvents<Ev> {
|
||||
fn root_event(
|
||||
mut bytes: &[u8],
|
||||
pallet_name: &str,
|
||||
pallet_event_ty: u32,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Self, Error> {
|
||||
if pallet_name == "Test" {
|
||||
return Ok(AllEvents::Test(Ev::decode_with_metadata(
|
||||
&mut bytes,
|
||||
pallet_event_ty,
|
||||
metadata,
|
||||
)?));
|
||||
}
|
||||
panic!("Asked for pallet name '{pallet_name}', which isn't in our test AllEvents type")
|
||||
}
|
||||
}
|
||||
|
||||
/// This encodes to the same format an event is expected to encode to
|
||||
/// in node System.Events storage.
|
||||
#[derive(Encode)]
|
||||
@@ -546,12 +509,28 @@ pub(crate) mod test_utils {
|
||||
}];
|
||||
|
||||
let extrinsic = ExtrinsicMetadata {
|
||||
ty: meta_type::<ExtrinsicType<RuntimeCall>>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<RuntimeCall>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
};
|
||||
|
||||
let meta = RuntimeMetadataV15::new(pallets, extrinsic, meta_type::<()>(), vec![]);
|
||||
let meta = RuntimeMetadataV15::new(
|
||||
pallets,
|
||||
extrinsic,
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
OuterEnums {
|
||||
call_enum_ty: meta_type::<()>(),
|
||||
event_enum_ty: meta_type::<AllEvents<E>>(),
|
||||
error_enum_ty: meta_type::<()>(),
|
||||
},
|
||||
CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
);
|
||||
let runtime_metadata: RuntimeMetadataPrefixed = meta.into();
|
||||
|
||||
Metadata::new(runtime_metadata.try_into().unwrap())
|
||||
|
||||
@@ -11,12 +11,7 @@ mod events_type;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
pub use events_client::EventsClient;
|
||||
pub use events_type::{
|
||||
EventDetails,
|
||||
Events,
|
||||
// Used in codegen but hidden from docs:
|
||||
RootEvent,
|
||||
};
|
||||
pub use events_type::{EventDetails, Events};
|
||||
use scale_decode::DecodeAsFields;
|
||||
|
||||
/// Trait to uniquely identify the events's identity from the runtime metadata.
|
||||
|
||||
@@ -365,6 +365,15 @@ impl<T: Config> Rpc<T> {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Provide a list of the supported metadata versions of the node.
|
||||
pub async fn metadata_versions(&self) -> Result<Vec<u32>, Error> {
|
||||
let versions = self
|
||||
.state_call("Metadata_metadata_versions", None, None)
|
||||
.await?;
|
||||
|
||||
Ok(versions)
|
||||
}
|
||||
|
||||
/// Execute runtime API call and return the specified runtime metadata version.
|
||||
pub async fn metadata_at_version(&self, version: u32) -> Result<Metadata, Error> {
|
||||
let param = version.encode();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -269,12 +269,10 @@ async fn storage_total_issuance() {
|
||||
async fn storage_balance_lock() -> Result<(), subxt::Error> {
|
||||
let bob_signer = dev::bob();
|
||||
let bob: AccountId32 = dev::bob().public_key().into();
|
||||
let charlie: AccountId32 = dev::charlie().public_key().into();
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let tx = node_runtime::tx().staking().bond(
|
||||
charlie.into(),
|
||||
100_000_000_000_000,
|
||||
runtime_types::pallet_staking::RewardDestination::Stash,
|
||||
);
|
||||
|
||||
@@ -22,10 +22,12 @@ struct ContractsTestContext {
|
||||
type Hash = <SubstrateConfig as Config>::Hash;
|
||||
type AccountId = <SubstrateConfig as Config>::AccountId;
|
||||
|
||||
/// A dummy contract which does nothing at all.
|
||||
const CONTRACT: &str = r#"
|
||||
(module
|
||||
(func (export "call"))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "deploy"))
|
||||
(func (export "call"))
|
||||
)
|
||||
"#;
|
||||
|
||||
|
||||
@@ -35,39 +35,39 @@ fn default_validator_prefs() -> ValidatorPrefs {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_with_controller_account() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = dev::alice();
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.validate(default_validator_prefs());
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await
|
||||
.expect("should be successful");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
async fn validate_with_stash_account() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice_stash = get_from_seed("Alice//stash");
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.validate(default_validator_prefs());
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice_stash)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await
|
||||
.expect("should be successful");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn validate_not_possible_for_controller_account() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = dev::alice();
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.validate(default_validator_prefs());
|
||||
|
||||
let announce_validator = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice_stash)
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
@@ -80,41 +80,41 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn nominate_with_controller_account() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = dev::alice();
|
||||
let bob = dev::bob();
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.nominate(vec![bob.public_key().to_address()]);
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await
|
||||
.expect("should be successful");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
async fn nominate_with_stash_account() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice_stash = get_from_seed("Alice//stash");
|
||||
let bob = dev::bob();
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.nominate(vec![bob.public_key().to_address()]);
|
||||
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice_stash)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await
|
||||
.expect("should be successful");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn nominate_not_possible_for_controller_account() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = dev::alice();
|
||||
let bob = dev::bob();
|
||||
|
||||
let tx = node_runtime::tx()
|
||||
.staking()
|
||||
.nominate(vec![bob.public_key().to_address()]);
|
||||
|
||||
let nomination = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&tx, &alice_stash)
|
||||
.sign_and_submit_then_watch_default(&tx, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
@@ -129,7 +129,7 @@ async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn chill_works_for_controller_only() -> Result<(), Error> {
|
||||
async fn chill_works_for_stash_only() -> Result<(), Error> {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
@@ -142,14 +142,14 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
|
||||
.staking()
|
||||
.nominate(vec![bob_stash.public_key().to_address()]);
|
||||
api.tx()
|
||||
.sign_and_submit_then_watch_default(&nominate_tx, &alice)
|
||||
.sign_and_submit_then_watch_default(&nominate_tx, &alice_stash)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
|
||||
let ledger_addr = node_runtime::storage()
|
||||
.staking()
|
||||
.ledger(alice.public_key().to_account_id());
|
||||
.ledger(alice_stash.public_key().to_account_id());
|
||||
let ledger = api
|
||||
.storage()
|
||||
.at_latest()
|
||||
@@ -163,7 +163,7 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
|
||||
|
||||
let chill = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&chill_tx, &alice_stash)
|
||||
.sign_and_submit_then_watch_default(&chill_tx, &alice)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await;
|
||||
@@ -176,7 +176,7 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
|
||||
|
||||
let is_chilled = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&chill_tx, &alice)
|
||||
.sign_and_submit_then_watch_default(&chill_tx, &alice_stash)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?
|
||||
@@ -193,11 +193,9 @@ async fn tx_bond() -> Result<(), Error> {
|
||||
|
||||
let alice = dev::alice();
|
||||
|
||||
let bond_tx = node_runtime::tx().staking().bond(
|
||||
dev::bob().public_key().into(),
|
||||
100_000_000_000_000,
|
||||
RewardDestination::Stash,
|
||||
);
|
||||
let bond_tx = node_runtime::tx()
|
||||
.staking()
|
||||
.bond(100_000_000_000_000, RewardDestination::Stash);
|
||||
|
||||
let bond = api
|
||||
.tx()
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
use crate::{node_runtime, test_context, TestContext};
|
||||
use frame_metadata::v15::{
|
||||
ExtrinsicMetadata, PalletCallMetadata, PalletMetadata, PalletStorageMetadata,
|
||||
RuntimeMetadataV15, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
|
||||
CustomMetadata, ExtrinsicMetadata, OuterEnums, PalletCallMetadata, PalletMetadata,
|
||||
PalletStorageMetadata, RuntimeMetadataV15, StorageEntryMetadata, StorageEntryModifier,
|
||||
StorageEntryType,
|
||||
};
|
||||
use scale_info::{
|
||||
build::{Fields, Variants},
|
||||
@@ -76,12 +77,23 @@ fn pallets_to_metadata(pallets: Vec<PalletMetadata>) -> Metadata {
|
||||
v15_to_metadata(RuntimeMetadataV15::new(
|
||||
pallets,
|
||||
ExtrinsicMetadata {
|
||||
ty: meta_type::<ExtrinsicType<RuntimeCall>>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<RuntimeCall>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
},
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
OuterEnums {
|
||||
call_enum_ty: meta_type::<()>(),
|
||||
event_enum_ty: meta_type::<()>(),
|
||||
error_enum_ty: meta_type::<()>(),
|
||||
},
|
||||
CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
@@ -114,7 +126,7 @@ async fn constant_values_are_not_validated() {
|
||||
|
||||
// Modify the metadata.
|
||||
let metadata = modified_metadata(api.metadata(), |md| {
|
||||
let mut existential = md
|
||||
let existential = md
|
||||
.pallets
|
||||
.iter_mut()
|
||||
.find(|pallet| pallet.name == "Balances")
|
||||
|
||||
@@ -37,7 +37,7 @@ async fn run() {
|
||||
|
||||
// Download metadata from binary. Avoid Subxt dep on `subxt::rpc::types::Bytes`and just impl here.
|
||||
// This may at least prevent this script from running so often (ie whenever we change Subxt).
|
||||
const V15_METADATA_VERSION: u32 = u32::MAX;
|
||||
const V15_METADATA_VERSION: u32 = 15;
|
||||
let bytes = V15_METADATA_VERSION.encode();
|
||||
let version: String = format!("0x{}", hex::encode(&bytes));
|
||||
let raw: String = {
|
||||
|
||||
@@ -7,8 +7,8 @@ mod metadata_test_runner;
|
||||
|
||||
use frame_metadata::{
|
||||
v15::{
|
||||
ExtrinsicMetadata, PalletMetadata, PalletStorageMetadata, RuntimeMetadataV15,
|
||||
StorageEntryMetadata,
|
||||
CustomMetadata, ExtrinsicMetadata, OuterEnums, PalletMetadata, PalletStorageMetadata,
|
||||
RuntimeMetadataV15, StorageEntryMetadata,
|
||||
},
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
@@ -24,9 +24,12 @@ pub fn generate_metadata_from_pallets_custom_dispatch_error<DispatchError: TypeI
|
||||
) -> RuntimeMetadataPrefixed {
|
||||
// We don't care about the extrinsic type.
|
||||
let extrinsic = ExtrinsicMetadata {
|
||||
ty: meta_type::<()>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<()>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
};
|
||||
|
||||
// Construct metadata manually from our types (See `RuntimeMetadataV15::new()`).
|
||||
@@ -41,10 +44,13 @@ pub fn generate_metadata_from_pallets_custom_dispatch_error<DispatchError: TypeI
|
||||
enum RuntimeCall {}
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeEvent {}
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeError {}
|
||||
|
||||
let ty = registry.register_type(&meta_type::<Runtime>());
|
||||
registry.register_type(&meta_type::<RuntimeCall>());
|
||||
registry.register_type(&meta_type::<RuntimeEvent>());
|
||||
let runtime_call = registry.register_type(&meta_type::<RuntimeCall>());
|
||||
let runtime_event = registry.register_type(&meta_type::<RuntimeEvent>());
|
||||
let runtime_error = registry.register_type(&meta_type::<RuntimeError>());
|
||||
|
||||
// Metadata needs to contain this DispatchError, since codegen looks for it.
|
||||
registry.register_type(&meta_type::<DispatchError>());
|
||||
@@ -55,6 +61,14 @@ pub fn generate_metadata_from_pallets_custom_dispatch_error<DispatchError: TypeI
|
||||
extrinsic,
|
||||
ty,
|
||||
apis: vec![],
|
||||
outer_enums: OuterEnums {
|
||||
call_enum_ty: runtime_call,
|
||||
event_enum_ty: runtime_event,
|
||||
error_enum_ty: runtime_error,
|
||||
},
|
||||
custom: CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
};
|
||||
|
||||
RuntimeMetadataPrefixed::from(metadata)
|
||||
|
||||
Reference in New Issue
Block a user