Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
18 changes: 10 additions & 8 deletions Cargo.lock

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

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ darling = "0.20.10"
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.14.0", default-features = false }
frame-decode = { version = "0.15.0", default-features = false }
frame-metadata = { version = "23.0.0", default-features = false }
futures = { version = "0.3.31", default-features = false, features = ["std"] }
getrandom = { version = "0.2", default-features = false }
Expand All @@ -100,10 +100,10 @@ regex = { version = "1.11.0", default-features = false }
scale-info = { version = "2.11.4", default-features = false }
scale-value = { version = "0.18.1", default-features = false }
scale-bits = { version = "0.7.0", default-features = false }
scale-decode = { version = "0.16.0", default-features = false }
scale-decode = { version = "0.16.1", default-features = false }
scale-encode = { version = "0.10.0", default-features = false }
scale-type-resolver = { version = "0.2.0" }
scale-info-legacy = { version = "0.3.2", default-features = false }
scale-info-legacy = { version = "0.4.0", default-features = false }
scale-typegen = "0.12.0"
scale-typegen-description = "0.11.0"
serde = { version = "1.0.210", default-features = false, features = ["derive"] }
Expand Down
4 changes: 3 additions & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,18 @@ subxt-codegen = { workspace = true }
scale-typegen = { workspace = true }
subxt-utils-fetchmetadata = { workspace = true, features = ["url"] }
subxt-utils-stripmetadata = { workspace = true }
subxt-metadata = { workspace = true }
subxt-metadata = { workspace = true, features = ["legacy"] }
subxt = { workspace = true, features = ["default"] }
clap = { workspace = true }
serde = { workspace = true, features = ["derive"] }
color-eyre = { workspace = true }
serde_json = { workspace = true }
hex = { workspace = true }
frame-decode = { workspace = true, features = ["legacy-types"] }
frame-metadata = { workspace = true }
codec = { package = "parity-scale-codec", workspace = true }
scale-info = { workspace = true }
scale-info-legacy = { workspace = true }
scale-value = { workspace = true }
syn = { workspace = true }
quote = { workspace = true }
Expand Down
96 changes: 93 additions & 3 deletions cli/src/commands/codegen.rs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I updated the codegen to work with historic metadatas so that it's possible to see what sorts of types we get back for things in actual generated code.

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

use crate::utils::{FileOrUrl, validate_url_security};
use clap::Parser as ClapParser;
use codec::Decode;
use color_eyre::eyre::eyre;
use scale_typegen_description::scale_typegen::typegen::{
settings::substitutes::path_segments,
validation::{registry_contains_type_path, similar_type_paths_in_registry},
};
use std::path::PathBuf;
use subxt_codegen::CodegenBuilder;
use subxt_metadata::Metadata;

Expand All @@ -28,6 +28,12 @@ pub struct Opts {
/// Additional attributes
#[clap(long = "attribute")]
attributes: Vec<String>,
/// Path to legacy type definitions (required for metadatas pre-V14)
#[clap(long)]
legacy_types: Option<PathBuf>,
/// The spec version of the legacy metadata (required for metadatas pre-V14)
#[clap(long)]
legacy_spec_version: Option<u64>,
/// Additional derives for a given type.
///
/// Example 1: `--derive-for-type my_module::my_type=serde::Serialize`.
Expand Down Expand Up @@ -145,9 +151,20 @@ pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Re
validate_url_security(opts.file_or_url.url.as_ref(), opts.allow_insecure)?;

let bytes = opts.file_or_url.fetch().await?;
let legacy_types = opts
.legacy_types
.map(|path| {
let bytes = std::fs::read(path).map_err(|e| eyre!("Cannot read legacy_types: {e}"))?;
let types = frame_decode::legacy_types::from_bytes(&bytes)
.map_err(|e| eyre!("Cannot deserialize legacy_types: {e}"))?;
Ok::<_, color_eyre::eyre::Error>(types)
})
.transpose()?;

codegen(
&bytes,
legacy_types,
opts.legacy_spec_version,
opts.derives,
opts.attributes,
opts.derives_for_type,
Expand Down Expand Up @@ -175,6 +192,8 @@ impl syn::parse::Parse for OuterAttribute {
#[allow(clippy::too_many_arguments)]
fn codegen(
metadata_bytes: &[u8],
legacy_types: Option<scale_info_legacy::ChainTypeRegistry>,
legacy_spec_version: Option<u64>,
raw_derives: Vec<String>,
raw_attributes: Vec<String>,
derives_for_type: Vec<DeriveForType>,
Expand Down Expand Up @@ -211,8 +230,79 @@ fn codegen(
}

let metadata = {
let mut metadata = subxt_metadata::Metadata::decode(&mut &*metadata_bytes)
.map_err(|e| eyre!("Cannot decode the provided metadata: {e}"))?;
let runtime_metadata = subxt_metadata::decode_runtime_metadata(metadata_bytes)?;
let mut metadata = match runtime_metadata {
// Too old to work with:
frame_metadata::RuntimeMetadata::V0(_)
| frame_metadata::RuntimeMetadata::V1(_)
| frame_metadata::RuntimeMetadata::V2(_)
| frame_metadata::RuntimeMetadata::V3(_)
| frame_metadata::RuntimeMetadata::V4(_)
| frame_metadata::RuntimeMetadata::V5(_)
| frame_metadata::RuntimeMetadata::V6(_)
| frame_metadata::RuntimeMetadata::V7(_) => {
Err(eyre!("Metadata V1-V7 cannot be decoded from"))
}
// Converting legacy metadatas:
frame_metadata::RuntimeMetadata::V8(md) => {
let legacy_types = legacy_types
.ok_or_else(|| eyre!("--legacy-types needed to load V8 metadata"))?;
let legacy_spec = legacy_spec_version
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V8 metadata"))?;
Metadata::from_v8(&md, &legacy_types.for_spec_version(legacy_spec))
.map_err(|e| eyre!("Cannot load V8 metadata: {e}"))
}
frame_metadata::RuntimeMetadata::V9(md) => {
let legacy_types = legacy_types
.ok_or_else(|| eyre!("--legacy-types needed to load V9 metadata"))?;
let legacy_spec = legacy_spec_version
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V9 metadata"))?;
Metadata::from_v9(&md, &legacy_types.for_spec_version(legacy_spec))
.map_err(|e| eyre!("Cannot load V9 metadata: {e}"))
}
frame_metadata::RuntimeMetadata::V10(md) => {
let legacy_types = legacy_types
.ok_or_else(|| eyre!("--legacy-types needed to load V10 metadata"))?;
let legacy_spec = legacy_spec_version
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V10 metadata"))?;
Metadata::from_v10(&md, &legacy_types.for_spec_version(legacy_spec))
.map_err(|e| eyre!("Cannot load V10 metadata: {e}"))
}
frame_metadata::RuntimeMetadata::V11(md) => {
let legacy_types = legacy_types
.ok_or_else(|| eyre!("--legacy-types needed to load V11 metadata"))?;
let legacy_spec = legacy_spec_version
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V11 metadata"))?;
Metadata::from_v11(&md, &legacy_types.for_spec_version(legacy_spec))
.map_err(|e| eyre!("Cannot load V11 metadata: {e}"))
}
frame_metadata::RuntimeMetadata::V12(md) => {
let legacy_types = legacy_types
.ok_or_else(|| eyre!("--legacy-types needed to load V12 metadata"))?;
let legacy_spec = legacy_spec_version
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V12 metadata"))?;
Metadata::from_v12(&md, &legacy_types.for_spec_version(legacy_spec))
.map_err(|e| eyre!("Cannot load V12 metadata: {e}"))
}
frame_metadata::RuntimeMetadata::V13(md) => {
let legacy_types = legacy_types
.ok_or_else(|| eyre!("--legacy-types needed to load V13 metadata"))?;
let legacy_spec = legacy_spec_version
.ok_or_else(|| eyre!("--legacy-spec-version needed to load V13 metadata"))?;
Metadata::from_v13(&md, &legacy_types.for_spec_version(legacy_spec))
.map_err(|e| eyre!("Cannot load V13 metadata: {e}"))
}
// Converting modern metadatas:
frame_metadata::RuntimeMetadata::V14(md) => {
Metadata::from_v14(md).map_err(|e| eyre!("Cannot load V14 metadata: {e}"))
}
frame_metadata::RuntimeMetadata::V15(md) => {
Metadata::from_v15(md).map_err(|e| eyre!("Cannot load V15 metadata: {e}"))
}
frame_metadata::RuntimeMetadata::V16(md) => {
Metadata::from_v16(md).map_err(|e| eyre!("Cannot load V16 metadata: {e}"))
}
}?;

// Run this first to ensure type paths are unique (which may result in 1,2,3 suffixes being added
// to type paths), so that when we validate derives/substitutions below, they are allowed for such
Expand Down
4 changes: 2 additions & 2 deletions historic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jsonrpsee = [
subxt-rpcs = { workspace = true }
frame-decode = { workspace = true, features = ["legacy", "legacy-types"] }
frame-metadata = { workspace = true, features = ["std", "legacy"] }
scale-type-resolver = { workspace = true }
scale-type-resolver = { workspace = true, features = ["scale-info"] }
codec = { workspace = true }
primitive-types = { workspace = true }
scale-info = { workspace = true }
Expand All @@ -60,4 +60,4 @@ futures = { workspace = true }
tokio = { workspace = true, features = ["full"] }
scale-value = { workspace = true }
scale-decode = { workspace = true, features = ["derive"] }
hex = { workspace = true }
hex = { workspace = true }
90 changes: 85 additions & 5 deletions historic/examples/extrinsics.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
#![allow(missing_docs)]
use subxt_historic::{Error, OnlineClient, PolkadotConfig};
use subxt_historic::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Error> {
async fn main() -> Result<(), Box<dyn core::error::Error + Send + Sync + 'static>> {
// Configuration for the Polkadot relay chain.
let config = PolkadotConfig::new();

// Create an online client for the Polkadot relay chain, pointed at a Polkadot archive node.
let client = OnlineClient::from_url(config, "wss://rpc.polkadot.io").await?;

// Iterate through some randomly selected old blocks to show how to fetch and decode extrinsics.
for block_number in 123456.. {
for block_number in 1234567.. {
println!("=== Block {block_number} ===");

// Point the client at a specific block number. By default this will download and cache
Expand Down Expand Up @@ -40,10 +40,23 @@ async fn main() -> Result<(), Error> {
// scale_value::Value type, which can represent any SCALE encoded data, but if you
// have an idea of the type then you can try to decode into that type instead):
for field in extrinsic.call().fields().iter() {
// We can visit fields, which gives us the ability to inspect and decode information
// from them selectively, returning whatever we like from it. Here we demo our
// type name visitor which is defined below:
let tn = field
.visit(type_name::GetTypeName::new())?
.unwrap_or_default();

// We can also obtain and decode things without the complexity of the above:
println!(
" {}: {}",
" {}: {} {}",
field.name(),
field.decode_as::<scale_value::Value>().unwrap()
field.decode_as::<scale_value::Value>().unwrap(),
if tn.is_empty() {
String::new()
} else {
format!("(type name: {tn})")
},
);
}

Expand Down Expand Up @@ -81,3 +94,70 @@ async fn main() -> Result<(), Error> {

Ok(())
}

/// This module defines an example visitor which retrieves the name of a type.
/// This is a more advanced use case and can typically be avoided.
mod type_name {
use scale_decode::{
Visitor,
visitor::types::{Composite, Sequence, Variant},
visitor::{TypeIdFor, Unexpected},
};
use scale_info_legacy::LookupName;
use scale_type_resolver::TypeResolver;

/// This is a visitor which obtains type names.
pub struct GetTypeName<R> {
marker: core::marker::PhantomData<R>,
}

impl<R> GetTypeName<R> {
/// Construct our TypeName visitor.
pub fn new() -> Self {
GetTypeName {
marker: core::marker::PhantomData,
}
}
}

impl<R> Visitor for GetTypeName<R>
where
R: TypeResolver,
R::TypeId: TryInto<LookupName>,
{
type Value<'scale, 'resolver> = Option<&'resolver str>;
type Error = scale_decode::Error;
type TypeResolver = R;

// Look at the path of types that have paths and return the ident from that.
fn visit_composite<'scale, 'resolver>(
self,
value: &mut Composite<'scale, 'resolver, Self::TypeResolver>,
_type_id: TypeIdFor<Self>,
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
Ok(value.path().last())
}
fn visit_variant<'scale, 'resolver>(
self,
value: &mut Variant<'scale, 'resolver, Self::TypeResolver>,
_type_id: TypeIdFor<Self>,
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
Ok(value.path().last())
}
fn visit_sequence<'scale, 'resolver>(
self,
value: &mut Sequence<'scale, 'resolver, Self::TypeResolver>,
_type_id: TypeIdFor<Self>,
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
Ok(value.path().last())
}

// Else, we return nothing as we can't find a name for the type.
fn visit_unexpected<'scale, 'resolver>(
self,
_unexpected: Unexpected,
) -> Result<Self::Value<'scale, 'resolver>, Self::Error> {
Ok(None)
}
}
}
Loading
Loading