Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"subxt",
"scripts/artifacts",
"utils/fetch-metadata",
"utils/strip-metadata",
]

# We exclude any crates that would depend on non mutually
Expand Down Expand Up @@ -79,7 +80,7 @@ derive-where = "1.2.7"
either = { version = "1.13.0", default-features = false }
finito = { version = "0.1.0", default-features = false }
frame-decode = { version = "0.7.0", default-features = false }
frame-metadata = { version = "20.0.0", default-features = false }
frame-metadata = { version = "20.0.0", default-features = false, features = ["unstable"] }
futures = { version = "0.3.31", default-features = false, features = ["std"] }
getrandom = { version = "0.2", default-features = false }
hashbrown = "0.14.5"
Expand Down Expand Up @@ -160,6 +161,7 @@ subxt-signer = { version = "0.41.0", path = "signer", default-features = false }
subxt-rpcs = { version = "0.41.0", path = "rpcs", default-features = false }
subxt-lightclient = { version = "0.41.0", path = "lightclient", default-features = false }
subxt-utils-fetchmetadata = { version = "0.41.0", path = "utils/fetch-metadata", default-features = false }
subxt-utils-stripmetadata = { version = "0.41.0", path = "utils/strip-metadata", default-features = false }
test-runtime = { path = "testing/test-runtime" }
substrate-runner = { path = "testing/substrate-runner" }

Expand Down
1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ chain-spec-pruning = ["smoldot"]
[dependencies]
subxt-codegen = { workspace = true }
subxt-utils-fetchmetadata = { workspace = true, features = ["url"] }
subxt-utils-stripmetadata = { workspace = true }
subxt-metadata = { workspace = true }
subxt = { workspace = true, features = ["default"] }
clap = { workspace = true }
Expand Down
39 changes: 12 additions & 27 deletions cli/src/commands/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use crate::utils::{validate_url_security, FileOrUrl};
use clap::Parser as ClapParser;
use codec::{Decode, Encode};
use color_eyre::eyre::{self, bail};
use frame_metadata::{v15::RuntimeMetadataV15, RuntimeMetadata, RuntimeMetadataPrefixed};
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
use std::{io::Write, path::PathBuf};
use subxt_metadata::Metadata;
use subxt_utils_stripmetadata::StripMetadata;

/// Download metadata from a substrate node, for use with `subxt` codegen.
#[derive(Debug, ClapParser)]
Expand Down Expand Up @@ -43,35 +43,26 @@ pub struct Opts {
pub async fn run(opts: Opts, output: &mut impl Write) -> color_eyre::Result<()> {
validate_url_security(opts.file_or_url.url.as_ref(), opts.allow_insecure)?;
let bytes = opts.file_or_url.fetch().await?;
let mut metadata = RuntimeMetadataPrefixed::decode(&mut &bytes[..])?;

let version = match &metadata.1 {
RuntimeMetadata::V14(_) => Version::V14,
RuntimeMetadata::V15(_) => Version::V15,
_ => Version::Unknown,
};
let mut metadata = RuntimeMetadataPrefixed::decode(&mut &bytes[..])?;

// Strip pallets or runtime APIs if names are provided:
if opts.pallets.is_some() || opts.runtime_apis.is_some() {
// convert to internal type:
let mut md = Metadata::try_from(metadata)?;

// retain pallets and/or runtime APIs given:
let retain_pallets_fn: Box<dyn Fn(&str) -> bool> = match opts.pallets.as_ref() {
let keep_pallets_fn: Box<dyn Fn(&str) -> bool> = match opts.pallets.as_ref() {
Some(pallets) => Box::new(|name| pallets.iter().any(|p| &**p == name)),
None => Box::new(|_| true),
};
let retain_runtime_apis_fn: Box<dyn Fn(&str) -> bool> = match opts.runtime_apis.as_ref() {
let keep_runtime_apis_fn: Box<dyn Fn(&str) -> bool> = match opts.runtime_apis.as_ref() {
Some(apis) => Box::new(|name| apis.iter().any(|p| &**p == name)),
None => Box::new(|_| true),
};
md.retain(retain_pallets_fn, retain_runtime_apis_fn);

// Convert back to wire format, preserving version:
metadata = match version {
Version::V14 => RuntimeMetadataV15::from(md).into(),
Version::V15 => RuntimeMetadataV15::from(md).into(),
Version::Unknown => {
bail!("Unsupported metadata version; V14 or V15 metadata is expected.")
match &mut metadata.1 {
RuntimeMetadata::V14(md) => md.strip_metadata(keep_pallets_fn, keep_runtime_apis_fn),
RuntimeMetadata::V15(md) => md.strip_metadata(keep_pallets_fn, keep_runtime_apis_fn),
RuntimeMetadata::V16(md) => md.strip_metadata(keep_pallets_fn, keep_runtime_apis_fn),
_ => {
bail!("Unsupported metadata version for stripping pallets/runtime APIs: V14, V15 or V16 metadata is expected.")
}
}
}
Expand Down Expand Up @@ -103,9 +94,3 @@ pub async fn run(opts: Opts, output: &mut impl Write) -> color_eyre::Result<()>
)),
}
}

enum Version {
V14,
V15,
Unknown,
}
5 changes: 2 additions & 3 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,6 @@ impl RuntimeGenerator {
.collect();
let runtime_api_names_len = runtime_api_names.len();

let metadata_hash = self.metadata.hasher().hash();

let modules = pallets_with_mod_names
.iter()
.map(|(pallet, mod_name)| {
Expand Down Expand Up @@ -219,7 +217,6 @@ impl RuntimeGenerator {

// 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())?
.to_token_stream(type_gen.settings());
Expand All @@ -230,6 +227,8 @@ impl RuntimeGenerator {
.resolve_type_path(self.metadata.outer_enums().error_enum_ty())?
.to_token_stream(type_gen.settings());

let metadata_hash = self.metadata.hasher().hash();

let custom_values = generate_custom_values(&self.metadata, &type_gen, &crate_path);

Ok(quote! {
Expand Down
12 changes: 3 additions & 9 deletions codegen/src/api/runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn generate_runtime_api(
.then_some(quote! { #( #[doc = #docs ] )* })
.unwrap_or_default();

let structs_and_methods: Vec<_> = api
let structs_and_methods = api
.methods()
.map(|method| {
let method_name = format_ident!("{}", method.name());
Expand Down Expand Up @@ -126,13 +126,7 @@ fn generate_runtime_api(
}
);

let Some(call_hash) = api.method_hash(method.name()) else {
return Err(CodegenError::MissingRuntimeApiMetadata(
trait_name_str.to_owned(),
method_name_str.to_owned(),
))
};

let call_hash = method.hash();
let method = quote!(
#docs
pub fn #method_name(&self, #( #fn_params, )* ) -> #crate_path::runtime_api::payload::StaticPayload<types::#struct_name, types::#method_name::output::Output> {
Expand All @@ -147,7 +141,7 @@ fn generate_runtime_api(

Ok((struct_input, method))
})
.collect::<Result<_, _>>()?;
.collect::<Result<Vec<_>, CodegenError>>()?;

let trait_name = format_ident!("{}", trait_name_str);

Expand Down
4 changes: 2 additions & 2 deletions core/src/config/transaction_extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ macro_rules! impl_tuples {
// there is one, and add it to a map with that index as the key.
let mut exts_by_index = HashMap::new();
$({
for (idx, e) in metadata.extrinsic().transaction_extensions().iter().enumerate() {
for (idx, e) in metadata.extrinsic().transaction_extensions_to_use_for_encoding().enumerate() {
// Skip over any exts that have a match already:
if exts_by_index.contains_key(&idx) {
continue
Expand All @@ -604,7 +604,7 @@ macro_rules! impl_tuples {

// Next, turn these into an ordered vec, erroring if we haven't matched on any exts yet.
let mut params = Vec::new();
for (idx, e) in metadata.extrinsic().transaction_extensions().iter().enumerate() {
for (idx, e) in metadata.extrinsic().transaction_extensions_to_use_for_encoding().enumerate() {
let Some(ext) = exts_by_index.remove(&idx) else {
if is_type_empty(e.extra_ty(), types) {
continue
Expand Down
5 changes: 3 additions & 2 deletions core/src/runtime_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ pub fn validate<P: Payload>(payload: &P, metadata: &Metadata) -> Result<(), Erro
};

let api_trait = metadata.runtime_api_trait_by_name_err(payload.trait_name())?;

let Some(runtime_hash) = api_trait.method_hash(payload.method_name()) else {
let Some(api_method) = api_trait.method_by_name(payload.method_name()) else {
return Err(MetadataError::IncompatibleCodegen.into());
};

let runtime_hash = api_method.hash();
if static_hash != runtime_hash {
return Err(MetadataError::IncompatibleCodegen.into());
}
Expand Down
5 changes: 2 additions & 3 deletions core/src/tx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,11 @@ pub fn create_v5_general<T: Config, Call: Payload>(
// with a hash allowing us to do so.
validate(call, &client_state.metadata)?;

// 2. Work out which TX extension version to target based on metadata (unless we
// explicitly ask for a specific transaction version at a later step).
// 2. Work out which TX extension version to target based on metadata.
let tx_extensions_version = client_state
.metadata
.extrinsic()
.transaction_extensions_version();
.transaction_extension_version_to_use_for_encoding();

// 3. SCALE encode call data to bytes (pallet u8, call u8, call params).
let call_data = call_data(call, &client_state.metadata)?;
Expand Down
28 changes: 14 additions & 14 deletions examples/wasm-example/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions examples/wasm-example/src/services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ pub async fn extension_signature_for_extrinsic(
let signed_extensions: Vec<String> = api
.metadata()
.extrinsic()
.transaction_extensions()
.iter()
.transaction_extensions_by_version(0)
.unwrap()
.map(|e| e.identifier().to_string())
.collect();
let tip = encode_then_hex(&Compact(0u128));
Expand Down
1 change: 1 addition & 0 deletions metadata/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ thiserror = { workspace = true, default-features = false }
bitvec = { workspace = true, features = ["alloc"] }
criterion = { workspace = true }
scale-info = { workspace = true, features = ["bit-vec"] }
subxt-utils-stripmetadata = { workspace = true }

[lib]
# Without this, libtest cli opts interfere with criterion benches:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use alloc::string::String;
use thiserror::Error as DeriveError;
mod v14;
mod v15;
mod v16;

/// An error emitted if something goes wrong converting [`frame_metadata`]
/// types into [`crate::Metadata`].
Expand All @@ -29,13 +30,6 @@ pub enum TryFromError {
InvalidTypePath(String),
}

impl From<crate::Metadata> for frame_metadata::RuntimeMetadataPrefixed {
fn from(value: crate::Metadata) -> Self {
let m: frame_metadata::v15::RuntimeMetadataV15 = value.into();
m.into()
}
}

impl TryFrom<frame_metadata::RuntimeMetadataPrefixed> for crate::Metadata {
type Error = TryFromError;

Expand Down
Loading
Loading