Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metadata difference command #1015

Merged
merged 20 commits into from
Jun 21, 2023
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
464 changes: 464 additions & 0 deletions cli/src/commands/diff.rs

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion cli/src/commands/explore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ pub enum PalletSubcommand {
Storage(StorageSubcommand),
}

/// cargo run -- explore --file=../artifacts/polkadot_metadata.scale
pub async fn run(opts: Opts, output: &mut impl std::io::Write) -> color_eyre::Result<()> {
// get the metadata
let bytes = opts.file_or_url.fetch().await?;
Expand Down
10 changes: 7 additions & 3 deletions cli/src/commands/explore/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub async fn explore_storage(
};

// if specified call storage entry wrong, show user the storage entries to choose from (but this time as an error):
let Some(storage) = storage_metadata.entries().find(|entry| entry.name().to_lowercase() == entry_name.to_lowercase()) else {
let Some(storage) = storage_metadata.entries().iter().find(|entry| entry.name().to_lowercase() == entry_name.to_lowercase()) else {
let storage_entries = print_available_storage_entries(storage_metadata, pallet_name);
let description = format!("Usage:\n subxt explore {pallet_name} storage <STORAGE_ENTRY>\n view details for a specific storage entry\n\n{storage_entries}");
return Err(eyre!("Storage entry \"{entry_name}\" not found in \"{pallet_name}\" pallet!\n\n{description}"));
Expand Down Expand Up @@ -164,14 +164,18 @@ fn print_available_storage_entries(
storage_metadata: &StorageMetadata,
pallet_name: &str,
) -> String {
if storage_metadata.entries().len() == 0 {
if storage_metadata.entries().is_empty() {
format!("No <STORAGE_ENTRY>'s available in the \"{pallet_name}\" pallet.")
} else {
let mut output = format!(
"Available <STORAGE_ENTRY>'s in the \"{}\" pallet:",
pallet_name
);
let mut strings: Vec<_> = storage_metadata.entries().map(|s| s.name()).collect();
let mut strings: Vec<_> = storage_metadata
.entries()
.iter()
.map(|s| s.name())
.collect();
strings.sort();
for entry in strings {
write!(output, "\n {}", entry).unwrap();
Expand Down
1 change: 1 addition & 0 deletions cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

pub mod codegen;
pub mod compatibility;
pub mod diff;
pub mod explore;
pub mod metadata;
pub mod version;
2 changes: 2 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum Command {
Metadata(commands::metadata::Opts),
Codegen(commands::codegen::Opts),
Compatibility(commands::compatibility::Opts),
Diff(commands::diff::Opts),
Version(commands::version::Opts),
Explore(commands::explore::Opts),
}
Expand All @@ -28,6 +29,7 @@ async fn main() -> color_eyre::Result<()> {
Command::Metadata(opts) => commands::metadata::run(opts, &mut output).await,
Command::Codegen(opts) => commands::codegen::run(opts, &mut output).await,
Command::Compatibility(opts) => commands::compatibility::run(opts, &mut output).await,
Command::Diff(opts) => commands::diff::run(opts, &mut output).await,
Command::Version(opts) => commands::version::run(opts, &mut output),
Command::Explore(opts) => commands::explore::run(opts, &mut output).await,
}
Expand Down
27 changes: 26 additions & 1 deletion cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
use clap::Args;
use color_eyre::eyre;

use std::str::FromStr;
use std::{fs, io::Read, path::PathBuf};

use subxt_codegen::utils::{MetadataVersion, Uri};

pub mod type_description;
pub mod type_example;

/// The source of the metadata.
#[derive(Debug, Args)]
#[derive(Debug, Args, Clone)]
pub struct FileOrUrl {
/// The url of the substrate node to query for metadata for codegen.
#[clap(long, value_parser)]
Expand Down Expand Up @@ -94,3 +96,26 @@ pub fn with_indent(s: String, indent: usize) -> String {
.collect::<Vec<_>>()
.join("\n")
}

impl FromStr for FileOrUrl {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let path = std::path::Path::new(s);
if path.exists() {
Ok(FileOrUrl {
url: None,
file: Some(PathBuf::from(s)),
version: None,
})
} else {
Uri::from_str(s)
.map_err(|_| "no path or uri could be crated")
.map(|uri| FileOrUrl {
url: Some(uri),
file: None,
version: None,
})
}
}
}
7 changes: 4 additions & 3 deletions codegen/src/api/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ pub fn generate_storage(
should_gen_docs: bool,
) -> Result<TokenStream2, CodegenError> {
let Some(storage) = pallet.storage() else {
return Ok(quote!())
return Ok(quote!());
};

let storage_fns = storage
.entries()
.iter()
.map(|entry| {
generate_storage_entry_fns(type_gen, pallet, entry, crate_path, should_gen_docs)
})
Expand Down Expand Up @@ -104,7 +105,7 @@ fn generate_storage_entry_fns(
let pallet_name = pallet.name();
let storage_name = storage_entry.name();
let Some(storage_hash) = pallet.storage_hash(storage_name) else {
return Err(CodegenError::MissingStorageMetadata(pallet_name.into(), storage_name.into()))
return Err(CodegenError::MissingStorageMetadata(pallet_name.into(), storage_name.into()));
};

let fn_name = format_ident!("{}", storage_entry.name().to_snake_case());
Expand Down Expand Up @@ -157,7 +158,7 @@ fn generate_storage_entry_fns(
// so expose a function to create this entry, too:
let root_entry_fn = if is_map_type {
let fn_name_root = format_ident!("{}_root", fn_name);
quote! (
quote!(
#docs
pub fn #fn_name_root(
&self,
Expand Down
25 changes: 20 additions & 5 deletions metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod from_into;
mod utils;

use scale_info::{form::PortableForm, PortableRegistry, Variant};
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use utils::ordered_map::OrderedMap;
use utils::variant_index::VariantIndex;
Expand Down Expand Up @@ -138,6 +138,16 @@ impl Metadata {
{
utils::retain::retain_metadata(self, pallet_filter, api_filter);
}

/// Get type hash for a type in the registry
pub fn type_hash(&self, id: u32) -> Option<[u8; 32]> {
self.types.resolve(id)?;
Some(crate::utils::validation::get_type_hash(
&self.types,
id,
&mut HashSet::<u32>::new(),
))
}
}

/// Metadata for a specific pallet.
Expand Down Expand Up @@ -303,8 +313,8 @@ impl StorageMetadata {
}

/// An iterator over the storage entries.
pub fn entries(&self) -> impl ExactSizeIterator<Item = &StorageEntryMetadata> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not really a big deal, but I was wodnering whether there was a reason for this change?

IIRC the reason I tended to iterate over other metadata structs exposed here rather than give back slices was just to give a little more wiggle room in case we wanted to change the internals a bit in the future. There are a couple of other places in this API that return the ExactSizeIterator even though they could return slices too for this reason :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did it because I needed to do an unwrap_or_default() and I could not get a default impl ExactSizeIterator working properly that is the exact same type as the self.entries.values().iter().
Hmm, do you have an idea how I could solve this?

pallet_metadata_1
            .storage()
            .map(|s| s.entries())
            .unwrap_or_default()

self.entries.values().iter()
pub fn entries(&self) -> &[StorageEntryMetadata] {
self.entries.values()
}

/// Return a specific storage entry given its name.
Expand Down Expand Up @@ -387,7 +397,7 @@ pub enum StorageHasher {
}

/// Is the storage entry optional, or does it have a default value.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum StorageEntryModifier {
/// The storage entry returns an `Option<T>`, with `None` if the key is not present.
Optional,
Expand Down Expand Up @@ -490,7 +500,7 @@ pub struct RuntimeApiMetadata<'a> {

impl<'a> RuntimeApiMetadata<'a> {
/// Trait name.
pub fn name(&self) -> &str {
pub fn name(&self) -> &'a str {
&self.inner.name
}
/// Trait documentation.
Expand All @@ -509,6 +519,11 @@ impl<'a> RuntimeApiMetadata<'a> {
pub fn method_hash(&self, method_name: &str) -> Option<[u8; 32]> {
crate::utils::validation::get_runtime_api_hash(self, method_name)
}

/// Return a hash for the runtime API trait.
pub fn hash(&self) -> [u8; 32] {
crate::utils::validation::get_runtime_trait_hash(*self)
}
}

#[derive(Debug, Clone)]
Expand Down
27 changes: 17 additions & 10 deletions metadata/src/utils/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ fn get_type_def_hash(
}

/// Obtain the hash representation of a `scale_info::Type` identified by id.
fn get_type_hash(
pub fn get_type_hash(
registry: &PortableRegistry,
id: u32,
visited_ids: &mut HashSet<u32>,
Expand Down Expand Up @@ -283,7 +283,7 @@ fn get_runtime_method_hash(
}

/// Obtain the hash of all of a runtime API trait, including all of its methods.
fn get_runtime_trait_hash(trait_metadata: RuntimeApiMetadata) -> [u8; HASH_LEN] {
pub fn get_runtime_trait_hash(trait_metadata: RuntimeApiMetadata) -> [u8; HASH_LEN] {
let mut visited_ids = HashSet::new();
let trait_name = &*trait_metadata.inner.name;
let method_bytes = trait_metadata
Expand Down Expand Up @@ -379,14 +379,17 @@ pub fn get_pallet_hash(pallet: PalletMetadata) -> [u8; HASH_LEN] {
let storage_bytes = match pallet.storage() {
Some(storage) => {
let prefix_hash = hash(storage.prefix().as_bytes());
let entries_hash = storage.entries().fold([0u8; HASH_LEN], |bytes, entry| {
// We don't care what order the storage entries occur in, so XOR them together
// to make the order irrelevant.
xor(
bytes,
get_storage_entry_hash(registry, entry, &mut visited_ids),
)
});
let entries_hash = storage
.entries()
.iter()
.fold([0u8; HASH_LEN], |bytes, entry| {
// We don't care what order the storage entries occur in, so XOR them together
// to make the order irrelevant.
xor(
bytes,
get_storage_entry_hash(registry, entry, &mut visited_ids),
)
});
concat_and_hash2(&prefix_hash, &entries_hash)
}
None => [0u8; HASH_LEN],
Expand Down Expand Up @@ -496,6 +499,7 @@ mod tests {
struct A {
pub b: Box<B>,
}

#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
struct B {
Expand All @@ -507,6 +511,7 @@ mod tests {
#[derive(scale_info::TypeInfo)]
// TypeDef::Composite with TypeDef::Array with Typedef::Primitive.
struct AccountId32([u8; HASH_LEN]);

#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
// TypeDef::Variant.
Expand All @@ -525,6 +530,7 @@ mod tests {
// TypeDef::BitSequence.
BitSeq(BitVec<u8, Lsb0>),
}

#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
// Ensure recursive types and TypeDef variants are captured.
Expand All @@ -533,6 +539,7 @@ mod tests {
composite: AccountId32,
type_def: DigestItem,
}

#[allow(dead_code)]
#[derive(scale_info::TypeInfo)]
// Simulate a PalletCallMetadata.
Expand Down