mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-30 09:37:55 +00:00
12e343449e
* minor api changes * parsing instead of format_ident! * fix self issue * expose types on typegen * web wasm support via feature flag * fmt and clippy * small adjustments * remove exposing of types() in type generator * adjust compile error * remove little any flag * Need access to the type registry from the typegen again * Bump proc-macro2 from 1.0.66 to 1.0.67 (#1164) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.66 to 1.0.67. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.66...1.0.67) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump serde_json from 1.0.106 to 1.0.107 (#1166) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.106 to 1.0.107. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.106...v1.0.107) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump trybuild from 1.0.84 to 1.0.85 (#1167) Bumps [trybuild](https://github.com/dtolnay/trybuild) from 1.0.84 to 1.0.85. - [Release notes](https://github.com/dtolnay/trybuild/releases) - [Commits](https://github.com/dtolnay/trybuild/compare/1.0.84...1.0.85) --- updated-dependencies: - dependency-name: trybuild dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump Swatinem/rust-cache from 2.6.2 to 2.7.0 (#1168) Bumps [Swatinem/rust-cache](https://github.com/swatinem/rust-cache) from 2.6.2 to 2.7.0. - [Release notes](https://github.com/swatinem/rust-cache/releases) - [Changelog](https://github.com/Swatinem/rust-cache/blob/master/CHANGELOG.md) - [Commits](https://github.com/swatinem/rust-cache/compare/e207df5d269b42b69c8bc5101da26f7d31feddb4...a95ba195448af2da9b00fb742d14ffaaf3c21f43) --- updated-dependencies: - dependency-name: Swatinem/rust-cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump jsonrpsee from 0.20.0 to 0.20.1 (#1165) Bumps [jsonrpsee](https://github.com/paritytech/jsonrpsee) from 0.20.0 to 0.20.1. - [Release notes](https://github.com/paritytech/jsonrpsee/releases) - [Changelog](https://github.com/paritytech/jsonrpsee/blob/v0.20.1/CHANGELOG.md) - [Commits](https://github.com/paritytech/jsonrpsee/compare/v0.20.0...v0.20.1) --- updated-dependencies: - dependency-name: jsonrpsee dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump clap from 4.4.2 to 4.4.3 (#1163) Bumps [clap](https://github.com/clap-rs/clap) from 4.4.2 to 4.4.3. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.4.2...v4.4.3) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix double quote import introduced by merge * clippy * put ensure_unique_types on metadata, revert toml changes * fmt * solve frame_metadata import * tidy fetch-metadata and non_exhaustive on errors * remove the getrandom thing * fix toml autoformatting --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: James Wilson <james@jsdw.me>
458 lines
15 KiB
Rust
458 lines
15 KiB
Rust
use clap::Args;
|
|
use codec::Decode;
|
|
|
|
use frame_metadata::RuntimeMetadataPrefixed;
|
|
use std::collections::HashMap;
|
|
use std::hash::Hash;
|
|
|
|
use crate::utils::FileOrUrl;
|
|
use color_eyre::owo_colors::OwoColorize;
|
|
|
|
use scale_info::form::PortableForm;
|
|
use scale_info::Variant;
|
|
|
|
use subxt_metadata::{
|
|
ConstantMetadata, Metadata, PalletMetadata, RuntimeApiMetadata, StorageEntryMetadata,
|
|
};
|
|
|
|
/// Explore the differences between two nodes
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// subxt diff ./artifacts/polkadot_metadata_small.scale ./artifacts/polkadot_metadata_tiny.scale
|
|
/// subxt diff ./artifacts/polkadot_metadata_small.scale wss://rpc.polkadot.io:443
|
|
/// ```
|
|
#[derive(Debug, Args)]
|
|
#[command(author, version, about, long_about = None)]
|
|
pub struct Opts {
|
|
/// metadata file or node URL
|
|
metadata_or_url_1: FileOrUrl,
|
|
/// metadata file or node URL
|
|
metadata_or_url_2: FileOrUrl,
|
|
}
|
|
|
|
pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Result<()> {
|
|
let (entry_1_metadata, entry_2_metadata) = get_metadata(&opts).await?;
|
|
|
|
let metadata_diff = MetadataDiff::construct(&entry_1_metadata, &entry_2_metadata);
|
|
|
|
if metadata_diff.is_empty() {
|
|
writeln!(output, "No difference in metadata found.")?;
|
|
return Ok(());
|
|
}
|
|
if !metadata_diff.pallets.is_empty() {
|
|
writeln!(output, "Pallets:")?;
|
|
for diff in metadata_diff.pallets {
|
|
match diff {
|
|
Diff::Added(new) => {
|
|
writeln!(output, "{}", format!(" + {}", new.name()).green())?
|
|
}
|
|
Diff::Removed(old) => {
|
|
writeln!(output, "{}", format!(" - {}", old.name()).red())?
|
|
}
|
|
Diff::Changed { from, to } => {
|
|
writeln!(output, "{}", format!(" ~ {}", from.name()).yellow())?;
|
|
|
|
let pallet_diff = PalletDiff::construct(&from, &to);
|
|
if !pallet_diff.calls.is_empty() {
|
|
writeln!(output, " Calls:")?;
|
|
for diff in pallet_diff.calls {
|
|
match diff {
|
|
Diff::Added(new) => writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" + {}", &new.name).green()
|
|
)?,
|
|
Diff::Removed(old) => writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" - {}", &old.name).red()
|
|
)?,
|
|
Diff::Changed { from, to: _ } => {
|
|
writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" ~ {}", &from.name).yellow()
|
|
)?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !pallet_diff.constants.is_empty() {
|
|
writeln!(output, " Constants:")?;
|
|
for diff in pallet_diff.constants {
|
|
match diff {
|
|
Diff::Added(new) => writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" + {}", new.name()).green()
|
|
)?,
|
|
Diff::Removed(old) => writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" - {}", old.name()).red()
|
|
)?,
|
|
Diff::Changed { from, to: _ } => writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" ~ {}", from.name()).yellow()
|
|
)?,
|
|
}
|
|
}
|
|
}
|
|
|
|
if !pallet_diff.storage_entries.is_empty() {
|
|
writeln!(output, " Storage Entries:")?;
|
|
for diff in pallet_diff.storage_entries {
|
|
match diff {
|
|
Diff::Added(new) => writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" + {}", new.name()).green()
|
|
)?,
|
|
Diff::Removed(old) => writeln!(
|
|
output,
|
|
"{}",
|
|
format!(" - {}", old.name()).red()
|
|
)?,
|
|
Diff::Changed { from, to } => {
|
|
let storage_diff = StorageEntryDiff::construct(
|
|
from,
|
|
to,
|
|
&entry_1_metadata,
|
|
&entry_2_metadata,
|
|
);
|
|
|
|
writeln!(
|
|
output,
|
|
"{}",
|
|
format!(
|
|
" ~ {} (Changed: {})",
|
|
from.name(),
|
|
storage_diff.to_strings().join(", ")
|
|
)
|
|
.yellow()
|
|
)?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !metadata_diff.runtime_apis.is_empty() {
|
|
writeln!(output, "Runtime APIs:")?;
|
|
for diff in metadata_diff.runtime_apis {
|
|
match diff {
|
|
Diff::Added(new) => {
|
|
writeln!(output, "{}", format!(" + {}", new.name()).green())?
|
|
}
|
|
Diff::Removed(old) => {
|
|
writeln!(output, "{}", format!(" - {}", old.name()).red())?
|
|
}
|
|
Diff::Changed { from, to: _ } => {
|
|
writeln!(output, "{}", format!(" ~ {}", from.name()).yellow())?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
struct MetadataDiff<'a> {
|
|
pallets: Vec<Diff<PalletMetadata<'a>>>,
|
|
runtime_apis: Vec<Diff<RuntimeApiMetadata<'a>>>,
|
|
}
|
|
|
|
impl<'a> MetadataDiff<'a> {
|
|
fn construct(metadata_1: &'a Metadata, metadata_2: &'a Metadata) -> MetadataDiff<'a> {
|
|
let pallets = pallet_differences(metadata_1, metadata_2);
|
|
let runtime_apis = runtime_api_differences(metadata_1, metadata_2);
|
|
MetadataDiff {
|
|
pallets,
|
|
runtime_apis,
|
|
}
|
|
}
|
|
|
|
fn is_empty(&self) -> bool {
|
|
self.pallets.is_empty() && self.runtime_apis.is_empty()
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct PalletDiff<'a> {
|
|
calls: Vec<Diff<&'a Variant<PortableForm>>>,
|
|
constants: Vec<Diff<&'a ConstantMetadata>>,
|
|
storage_entries: Vec<Diff<&'a StorageEntryMetadata>>,
|
|
}
|
|
|
|
impl<'a> PalletDiff<'a> {
|
|
fn construct(
|
|
pallet_metadata_1: &'a PalletMetadata<'a>,
|
|
pallet_metadata_2: &'a PalletMetadata<'a>,
|
|
) -> PalletDiff<'a> {
|
|
let calls = calls_differences(pallet_metadata_1, pallet_metadata_2);
|
|
let constants = constants_differences(pallet_metadata_1, pallet_metadata_2);
|
|
let storage_entries = storage_differences(pallet_metadata_1, pallet_metadata_2);
|
|
PalletDiff {
|
|
calls,
|
|
constants,
|
|
storage_entries,
|
|
}
|
|
}
|
|
}
|
|
|
|
struct StorageEntryDiff {
|
|
key_different: bool,
|
|
value_different: bool,
|
|
default_different: bool,
|
|
modifier_different: bool,
|
|
}
|
|
|
|
impl StorageEntryDiff {
|
|
fn construct(
|
|
storage_entry_1: &StorageEntryMetadata,
|
|
storage_entry_2: &StorageEntryMetadata,
|
|
metadata_1: &Metadata,
|
|
metadata_2: &Metadata,
|
|
) -> Self {
|
|
let value_1_ty_id = storage_entry_1.entry_type().value_ty();
|
|
let value_1_hash = metadata_1
|
|
.type_hash(value_1_ty_id)
|
|
.expect("type should be present");
|
|
let value_2_ty_id = storage_entry_2.entry_type().value_ty();
|
|
let value_2_hash = metadata_1
|
|
.type_hash(value_2_ty_id)
|
|
.expect("type should be present");
|
|
let value_different = value_1_hash != value_2_hash;
|
|
|
|
let key_1_hash = storage_entry_1
|
|
.entry_type()
|
|
.key_ty()
|
|
.map(|key_ty| {
|
|
metadata_1
|
|
.type_hash(key_ty)
|
|
.expect("type should be present")
|
|
})
|
|
.unwrap_or_default();
|
|
let key_2_hash = storage_entry_2
|
|
.entry_type()
|
|
.key_ty()
|
|
.map(|key_ty| {
|
|
metadata_2
|
|
.type_hash(key_ty)
|
|
.expect("type should be present")
|
|
})
|
|
.unwrap_or_default();
|
|
let key_different = key_1_hash != key_2_hash;
|
|
|
|
StorageEntryDiff {
|
|
key_different,
|
|
value_different,
|
|
default_different: storage_entry_1.default_bytes() != storage_entry_2.default_bytes(),
|
|
modifier_different: storage_entry_1.modifier() != storage_entry_2.modifier(),
|
|
}
|
|
}
|
|
|
|
fn to_strings(&self) -> Vec<&str> {
|
|
let mut strings = Vec::<&str>::new();
|
|
if self.key_different {
|
|
strings.push("key type");
|
|
}
|
|
if self.value_different {
|
|
strings.push("value type");
|
|
}
|
|
if self.modifier_different {
|
|
strings.push("modifier");
|
|
}
|
|
if self.default_different {
|
|
strings.push("default value");
|
|
}
|
|
strings
|
|
}
|
|
}
|
|
|
|
async fn get_metadata(opts: &Opts) -> color_eyre::Result<(Metadata, Metadata)> {
|
|
let bytes = opts.metadata_or_url_1.fetch().await?;
|
|
let entry_1_metadata: Metadata =
|
|
RuntimeMetadataPrefixed::decode(&mut &bytes[..])?.try_into()?;
|
|
|
|
let bytes = opts.metadata_or_url_2.fetch().await?;
|
|
let entry_2_metadata: Metadata =
|
|
RuntimeMetadataPrefixed::decode(&mut &bytes[..])?.try_into()?;
|
|
|
|
Ok((entry_1_metadata, entry_2_metadata))
|
|
}
|
|
|
|
fn storage_differences<'a>(
|
|
pallet_metadata_1: &'a PalletMetadata<'a>,
|
|
pallet_metadata_2: &'a PalletMetadata<'a>,
|
|
) -> Vec<Diff<&'a StorageEntryMetadata>> {
|
|
diff(
|
|
pallet_metadata_1
|
|
.storage()
|
|
.map(|s| s.entries())
|
|
.unwrap_or_default(),
|
|
pallet_metadata_2
|
|
.storage()
|
|
.map(|s| s.entries())
|
|
.unwrap_or_default(),
|
|
|e| {
|
|
pallet_metadata_1
|
|
.storage_hash(e.name())
|
|
.expect("storage entry should be present")
|
|
},
|
|
|e| {
|
|
pallet_metadata_2
|
|
.storage_hash(e.name())
|
|
.expect("storage entry should be present")
|
|
},
|
|
|e| e.name(),
|
|
)
|
|
}
|
|
|
|
fn calls_differences<'a>(
|
|
pallet_metadata_1: &'a PalletMetadata<'a>,
|
|
pallet_metadata_2: &'a PalletMetadata<'a>,
|
|
) -> Vec<Diff<&'a Variant<PortableForm>>> {
|
|
return diff(
|
|
pallet_metadata_1.call_variants().unwrap_or_default(),
|
|
pallet_metadata_2.call_variants().unwrap_or_default(),
|
|
|e| {
|
|
pallet_metadata_1
|
|
.call_hash(&e.name)
|
|
.expect("call should be present")
|
|
},
|
|
|e| {
|
|
pallet_metadata_2
|
|
.call_hash(&e.name)
|
|
.expect("call should be present")
|
|
},
|
|
|e| &e.name,
|
|
);
|
|
}
|
|
|
|
fn constants_differences<'a>(
|
|
pallet_metadata_1: &'a PalletMetadata<'a>,
|
|
pallet_metadata_2: &'a PalletMetadata<'a>,
|
|
) -> Vec<Diff<&'a ConstantMetadata>> {
|
|
diff(
|
|
pallet_metadata_1.constants(),
|
|
pallet_metadata_2.constants(),
|
|
|e| {
|
|
pallet_metadata_1
|
|
.constant_hash(e.name())
|
|
.expect("constant should be present")
|
|
},
|
|
|e| {
|
|
pallet_metadata_2
|
|
.constant_hash(e.name())
|
|
.expect("constant should be present")
|
|
},
|
|
|e| e.name(),
|
|
)
|
|
}
|
|
|
|
fn runtime_api_differences<'a>(
|
|
metadata_1: &'a Metadata,
|
|
metadata_2: &'a Metadata,
|
|
) -> Vec<Diff<RuntimeApiMetadata<'a>>> {
|
|
diff(
|
|
metadata_1.runtime_api_traits(),
|
|
metadata_2.runtime_api_traits(),
|
|
RuntimeApiMetadata::hash,
|
|
RuntimeApiMetadata::hash,
|
|
RuntimeApiMetadata::name,
|
|
)
|
|
}
|
|
|
|
fn pallet_differences<'a>(
|
|
metadata_1: &'a Metadata,
|
|
metadata_2: &'a Metadata,
|
|
) -> Vec<Diff<PalletMetadata<'a>>> {
|
|
diff(
|
|
metadata_1.pallets(),
|
|
metadata_2.pallets(),
|
|
PalletMetadata::hash,
|
|
PalletMetadata::hash,
|
|
PalletMetadata::name,
|
|
)
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
enum Diff<T> {
|
|
Added(T),
|
|
Changed { from: T, to: T },
|
|
Removed(T),
|
|
}
|
|
|
|
fn diff<T, C: PartialEq, I: Hash + PartialEq + Eq + Ord>(
|
|
items_a: impl IntoIterator<Item = T>,
|
|
items_b: impl IntoIterator<Item = T>,
|
|
hash_fn_a: impl Fn(&T) -> C,
|
|
hash_fn_b: impl Fn(&T) -> C,
|
|
key_fn: impl Fn(&T) -> I,
|
|
) -> Vec<Diff<T>> {
|
|
let mut entries: HashMap<I, (Option<T>, Option<T>)> = HashMap::new();
|
|
|
|
for t1 in items_a {
|
|
let key = key_fn(&t1);
|
|
let (e1, _) = entries.entry(key).or_default();
|
|
*e1 = Some(t1);
|
|
}
|
|
|
|
for t2 in items_b {
|
|
let key = key_fn(&t2);
|
|
let (e1, e2) = entries.entry(key).or_default();
|
|
// skip all entries with the same hash:
|
|
if let Some(e1_inner) = e1 {
|
|
let e1_hash = hash_fn_a(e1_inner);
|
|
let e2_hash = hash_fn_b(&t2);
|
|
if e1_hash == e2_hash {
|
|
entries.remove(&key_fn(&t2));
|
|
continue;
|
|
}
|
|
}
|
|
*e2 = Some(t2);
|
|
}
|
|
|
|
// sort the values by key before returning
|
|
let mut diff_vec_with_keys: Vec<_> = entries.into_iter().collect();
|
|
diff_vec_with_keys.sort_by(|a, b| a.0.cmp(&b.0));
|
|
diff_vec_with_keys
|
|
.into_iter()
|
|
.map(|(_, tuple)| match tuple {
|
|
(None, None) => panic!("At least one value is inserted when the key exists; qed"),
|
|
(Some(old), None) => Diff::Removed(old),
|
|
(None, Some(new)) => Diff::Added(new),
|
|
(Some(old), Some(new)) => Diff::Changed { from: old, to: new },
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::commands::diff::{diff, Diff};
|
|
|
|
#[test]
|
|
fn test_diff_fn() {
|
|
let old_pallets = [("Babe", 7), ("Claims", 9), ("Balances", 23)];
|
|
let new_pallets = [("Claims", 9), ("Balances", 22), ("System", 3), ("NFTs", 5)];
|
|
let hash_fn = |e: &(&str, i32)| e.0.len() as i32 * e.1;
|
|
let differences = diff(old_pallets, new_pallets, hash_fn, hash_fn, |e| e.0);
|
|
let expected_differences = vec![
|
|
Diff::Removed(("Babe", 7)),
|
|
Diff::Changed {
|
|
from: ("Balances", 23),
|
|
to: ("Balances", 22),
|
|
},
|
|
Diff::Added(("NFTs", 5)),
|
|
Diff::Added(("System", 3)),
|
|
];
|
|
assert_eq!(differences, expected_differences);
|
|
}
|
|
}
|