diff --git a/Cargo.lock b/Cargo.lock index 326f6b761e..33ffbe833b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1932,8 +1932,6 @@ dependencies = [ [[package]] name = "frame-decode" version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641e3739fa708a278d35b008a05244008c221240abc3e1c27138466c13e999ed" dependencies = [ "frame-metadata 23.0.0", "parity-scale-codec", @@ -4432,8 +4430,6 @@ dependencies = [ [[package]] name = "scale-info-legacy" version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5da3f59983b08a37d8d979d2326bdc00e8cca57b3d28fb05bdc0f6d7c28600c" dependencies = [ "hashbrown 0.15.3", "scale-type-resolver", diff --git a/Cargo.toml b/Cargo.toml index cc855dc993..1c387e072a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -192,3 +192,7 @@ opt-level = 2 opt-level = 2 [profile.test.package.smoldot] opt-level = 2 + +[patch.crates-io] +frame-decode = { path = "../frame-decode" } +scale-info-legacy = { path = "../scale-info-legacy" } \ No newline at end of file diff --git a/codegen/src/api/pallet_view_functions.rs b/codegen/src/api/pallet_view_functions.rs index 2a1b9932c9..8b341af792 100644 --- a/codegen/src/api/pallet_view_functions.rs +++ b/codegen/src/api/pallet_view_functions.rs @@ -68,7 +68,7 @@ fn generate_pallet_view_function( // Path to the actual type we'll have generated for this input. let type_path = type_gen - .resolve_type_path(input.ty) + .resolve_type_path(input.id) .expect("view function input type is in metadata; qed") .to_token_stream(type_gen.settings()); diff --git a/codegen/src/api/runtime_apis.rs b/codegen/src/api/runtime_apis.rs index d669313165..54307bbc34 100644 --- a/codegen/src/api/runtime_apis.rs +++ b/codegen/src/api/runtime_apis.rs @@ -77,7 +77,7 @@ fn generate_runtime_api( // Generate alias for runtime type. let ty = type_gen - .resolve_type_path(input.ty) + .resolve_type_path(input.id) .expect("runtime api input type is in metadata; qed") .to_token_stream(type_gen.settings()); let aliased_param = quote!( pub type #alias_name = #ty; ); diff --git a/codegen/src/api/storage.rs b/codegen/src/api/storage.rs index 922aca7d7a..1426c6c999 100644 --- a/codegen/src/api/storage.rs +++ b/codegen/src/api/storage.rs @@ -5,10 +5,9 @@ use heck::{ToSnakeCase as _, ToUpperCamelCase}; use proc_macro2::{Ident, TokenStream as TokenStream2, TokenStream}; use quote::{format_ident, quote}; -use scale_info::TypeDef; use scale_typegen::TypeGenerator; use subxt_metadata::{ - PalletMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, + PalletMetadata, StorageEntryMetadata, StorageHasher, }; use super::CodegenError; @@ -68,7 +67,7 @@ fn generate_storage_entry_fns( crate_path: &syn::Path, ) -> Result<(TokenStream2, TokenStream2), CodegenError> { let snake_case_name = storage_entry.name().to_snake_case(); - let storage_entry_ty = storage_entry.entry_type().value_ty(); + let storage_entry_ty = storage_entry.value_ty(); let storage_entry_value_ty = type_gen .resolve_type_path(storage_entry_ty) .expect("storage type is in metadata; qed") @@ -105,57 +104,11 @@ fn generate_storage_entry_fns( } }; - let keys: Vec = match storage_entry.entry_type() { - StorageEntryType::Plain(_) => vec![], - StorageEntryType::Map { - key_ty, hashers, .. - } => { - if hashers.len() == 1 { - // If there's exactly 1 hasher, then we have a plain StorageMap. We can't - // break the key down (even if it's a tuple) because the hasher applies to - // the whole key. - vec![map_entry_key(0, *key_ty, hashers[0])] - } else { - // If there are multiple hashers, then we have a StorageDoubleMap or StorageNMap. - // We expect the key type to be tuple, and we will return a MapEntryKey for each - // key in the tuple. - let hasher_count = hashers.len(); - let tuple = match &type_gen - .resolve_type(*key_ty) - .expect("key type should be present") - .type_def - { - TypeDef::Tuple(tuple) => tuple, - _ => { - return Err(CodegenError::InvalidStorageHasherCount { - storage_entry_name: storage_entry.name().to_owned(), - key_count: 1, - hasher_count, - }); - } - }; - - // We should have the same number of hashers and keys. - let key_count = tuple.fields.len(); - if hasher_count != key_count { - return Err(CodegenError::InvalidStorageHasherCount { - storage_entry_name: storage_entry.name().to_owned(), - key_count, - hasher_count, - }); - } - - // Collect them together. - tuple - .fields - .iter() - .zip(hashers) - .enumerate() - .map(|(idx, (field, hasher))| map_entry_key(idx, field.id, *hasher)) - .collect() - } - } - }; + let keys: Vec = storage_entry + .keys() + .enumerate() + .map(|(idx, key)| map_entry_key(idx, key.key_id, key.hasher)) + .collect(); let pallet_name = pallet.name(); let storage_name = storage_entry.name(); @@ -173,9 +126,10 @@ fn generate_storage_entry_fns( .then_some(quote! { #( #[doc = #docs ] )* }) .unwrap_or_default(); - let is_defaultable_type = match storage_entry.modifier() { - StorageEntryModifier::Default => quote!(#crate_path::utils::Yes), - StorageEntryModifier::Optional => quote!(()), + let is_defaultable_type = if storage_entry.default_value().is_some() { + quote!(#crate_path::utils::Yes) + } else { + quote!(()) }; // Note: putting `#crate_path::storage::address::StaticStorageKey` into this variable is necessary diff --git a/core/src/dynamic.rs b/core/src/dynamic.rs index 8f02b2d04c..a314950110 100644 --- a/core/src/dynamic.rs +++ b/core/src/dynamic.rs @@ -23,7 +23,7 @@ pub use crate::tx::payload::dynamic as tx; pub use crate::constants::address::dynamic as constant; // Lookup storage values dynamically. -pub use crate::storage::address::dynamic as storage; +// pub use crate::storage::address::dynamic as storage; // TODO re-add. // Execute runtime API function call dynamically. pub use crate::runtime_api::payload::dynamic as runtime_api_call; diff --git a/core/src/runtime_api/payload.rs b/core/src/runtime_api/payload.rs index ee3e32e655..8074da4320 100644 --- a/core/src/runtime_api/payload.rs +++ b/core/src/runtime_api/payload.rs @@ -108,7 +108,7 @@ impl Payload .ok_or_else(|| MetadataError::RuntimeMethodNotFound((*self.method_name).to_owned()))?; let mut fields = api_method .inputs() - .map(|input| scale_encode::Field::named(input.ty, &input.name)); + .map(|input| scale_encode::Field::named(input.id, &input.name)); self.args_data .encode_as_fields_to(&mut fields, metadata.types(), out)?; diff --git a/core/src/storage/address.rs b/core/src/storage/address.rs index d17157a370..b680d7f1e2 100644 --- a/core/src/storage/address.rs +++ b/core/src/storage/address.rs @@ -10,50 +10,228 @@ use crate::{ metadata::{DecodeWithMetadata, Metadata}, utils::Yes, }; +use scale_decode::DecodeAsType; use derive_where::derive_where; - +use frame_decode::storage::{IntoEncodableValues, IntoDecodableValues}; use alloc::borrow::{Cow, ToOwned}; use alloc::string::String; use alloc::vec::Vec; -// Re-export types used here: -pub use super::storage_key::{StaticStorageKey, StorageHashers, StorageHashersIter, StorageKey}; - -/// This represents a storage address. Anything implementing this trait -/// can be used to fetch and iterate over storage entries. +/// A storage address. Concrete addresses are expected to implement either [`FetchableAddress`] +/// or [`IterableAddress`], which extends this to define fetchable and iterable storage keys. pub trait Address { - /// The target type of the value that lives at this address. - type Target: DecodeWithMetadata; - /// The keys type used to construct this address. - type Keys: StorageKey; - /// Can an entry be fetched from this address? - /// Set this type to [`Yes`] to enable the corresponding calls to be made. - type IsFetchable; - /// Can a default entry be obtained from this address? - /// Set this type to [`Yes`] to enable the corresponding calls to be made. - type IsDefaultable; - /// Can this address be iterated over? - /// Set this type to [`Yes`] to enable the corresponding calls to be made. - type IsIterable; + /// A set of types we'll hash and append to the prefix to build the storage key. + type KeyParts: IntoEncodableValues; + /// Type of the storage value at this location. + type Value: DecodeAsType; - /// The name of the pallet that the entry lives under. + /// The pallet containing this storage entry. fn pallet_name(&self) -> &str; - /// The name of the entry in a given pallet that the item is at. + /// The name of the storage entry. fn entry_name(&self) -> &str; - /// Output the non-prefix bytes; that is, any additional bytes that need - /// to be appended to the key to dig into maps. - fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error>; + /// Encode the suffix of the storage key for this address + fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error>; - /// An optional hash which, if present, will be checked against - /// the node metadata to confirm that the return type matches what - /// we are expecting. - fn validation_hash(&self) -> Option<[u8; 32]> { - None + /// Return a unique hash for this address which can be used to validate it against metadata. + fn validation_hash(&self) -> Option<[u8; 32]>; +} + +pub trait FetchableAddress: Address { + /// Does the address have a default value defined for it. + /// Set to [`Yes`] to enable APIs which require one. + type HasDefaultValue; +} + +pub trait IterableAddress: Address { + /// The storage key values that we'll decode for each value + type OutputKeys: IntoDecodableValues; +} + +/// An address which points to an individual storage value. +pub struct StaticFetchableAddress { + pallet_name: Cow<'static, str>, + entry_name: Cow<'static, str>, + key_parts: KeyParts, + validation_hash: Option<[u8; 32]>, + marker: core::marker::PhantomData<(Value, HasDefaultValue)> +} + +impl StaticFetchableAddress { + /// Create a new [`StaticFetchableAddress`] using static strings for the pallet and call name. + /// This is only expected to be used from codegen. + #[doc(hidden)] + pub fn new_static( + pallet_name: &'static str, + entry_name: &'static str, + key_parts: KeyParts, + hash: [u8; 32], + ) -> Self { + Self { + pallet_name: Cow::Borrowed(pallet_name), + entry_name: Cow::Borrowed(entry_name), + key_parts, + validation_hash: Some(hash), + marker: core::marker::PhantomData, + } + } + + /// Create a new [`StaticFetchableAddress`]. + pub fn new( + pallet_name: impl Into>, + entry_name: impl Into>, + key_parts: KeyParts, + ) -> Self { + Self { + pallet_name: pallet_name.into(), + entry_name: entry_name.into(), + key_parts, + validation_hash: None, + marker: core::marker::PhantomData, + } + } + + /// Do not validate this storage entry prior to accessing it. + pub fn unvalidated(mut self) -> Self { + self.validation_hash = None; + self } } +impl Address for StaticFetchableAddress +where + KeyParts: IntoEncodableValues, + Value: DecodeAsType +{ + type KeyParts = KeyParts; + type Value = Value; + + fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error> { + frame_decode::storage::encode_storage_key_suffix( + &self.pallet_name, + &self.entry_name, + &self.key_parts, + metadata.types(), + metadata + ).map_err(Into::into) + } + + fn pallet_name(&self) -> &str { + &self.pallet_name + } + + fn entry_name(&self) -> &str { + &self.entry_name + } + + fn validation_hash(&self) -> Option<[u8; 32]> { + self.validation_hash + } +} + +impl FetchableAddress for StaticFetchableAddress +where + KeyParts: IntoEncodableValues, + Value: DecodeAsType +{ + type HasDefaultValue = HasDefaultValue; +} + +/// An address which points to a set of storage values. +pub struct StaticIterableAddress { + pallet_name: Cow<'static, str>, + entry_name: Cow<'static, str>, + input_key_parts: InputKeyParts, + validation_hash: Option<[u8; 32]>, + marker: core::marker::PhantomData<(OutputKeyParts, Value)> +} + +impl StaticIterableAddress { + /// Create a new [`StaticIterableAddress`] using static strings for the pallet and call name. + /// This is only expected to be used from codegen. + #[doc(hidden)] + pub fn new_static( + pallet_name: &'static str, + entry_name: &'static str, + input_key_parts: InputKeyParts, + hash: [u8; 32], + ) -> Self { + Self { + pallet_name: Cow::Borrowed(pallet_name), + entry_name: Cow::Borrowed(entry_name), + input_key_parts, + validation_hash: Some(hash), + marker: core::marker::PhantomData, + } + } + + /// Create a new [`StaticIterableAddress`]. + pub fn new( + pallet_name: impl Into>, + entry_name: impl Into>, + input_key_parts: InputKeyParts, + ) -> Self { + Self { + pallet_name: pallet_name.into(), + entry_name: entry_name.into(), + input_key_parts, + validation_hash: None, + marker: core::marker::PhantomData, + } + } + + /// Do not validate this storage entry prior to accessing it. + pub fn unvalidated(mut self) -> Self { + self.validation_hash = None; + self + } +} + +impl Address for StaticIterableAddress +where + InputKeyParts: IntoEncodableValues, + Value: DecodeAsType +{ + type KeyParts = InputKeyParts; + type Value = Value; + + fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error> { + frame_decode::storage::encode_storage_key_suffix( + &self.pallet_name, + &self.entry_name, + &self.input_key_parts, + metadata.types(), + metadata + ).map_err(Into::into) + } + + fn pallet_name(&self) -> &str { + &self.pallet_name + } + + fn entry_name(&self) -> &str { + &self.entry_name + } + + fn validation_hash(&self) -> Option<[u8; 32]> { + self.validation_hash + } +} + +impl IterableAddress for StaticIterableAddress +where + InputKeyParts: IntoEncodableValues, + OutputKeyParts: IntoDecodableValues, + Value: DecodeAsType +{ + type OutputKeys = OutputKeyParts; +} + + + + + /// A concrete storage address. This can be created from static values (ie those generated /// via the `subxt` macro) or dynamic values via [`dynamic`]. #[derive_where(Clone, Debug, Eq, Ord, PartialEq, PartialOrd; Keys)] diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index e276bbb470..81e026a330 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -112,10 +112,7 @@ pub fn decode_value( let (_, entry_metadata) = utils::lookup_storage_entry_details(pallet_name, entry_name, metadata)?; - let value_ty_id = match entry_metadata.entry_type() { - subxt_metadata::StorageEntryType::Plain(ty) => *ty, - subxt_metadata::StorageEntryType::Map { value_ty, .. } => *value_ty, - }; + let value_ty_id = entry_metadata.value_ty(); let val = Addr::Target::decode_with_metadata(bytes, value_ty_id, metadata)?; Ok(val) @@ -131,12 +128,8 @@ pub fn default_value( let (_, entry_metadata) = utils::lookup_storage_entry_details(pallet_name, entry_name, metadata)?; - let value_ty_id = match entry_metadata.entry_type() { - subxt_metadata::StorageEntryType::Plain(ty) => *ty, - subxt_metadata::StorageEntryType::Map { value_ty, .. } => *value_ty, - }; - - let default_bytes = entry_metadata.default_bytes(); + let value_ty_id = entry_metadata.value_ty(); + let default_bytes = entry_metadata.default_value(); let val = Addr::Target::decode_with_metadata(&mut &*default_bytes, value_ty_id, metadata)?; Ok(val) } diff --git a/core/src/view_functions/payload.rs b/core/src/view_functions/payload.rs index a231d519c0..85f39fa232 100644 --- a/core/src/view_functions/payload.rs +++ b/core/src/view_functions/payload.rs @@ -91,7 +91,7 @@ impl Payload .ok_or(MetadataError::ViewFunctionNotFound(self.query_id))?; let mut fields = view_function .inputs() - .map(|input| scale_encode::Field::named(input.ty, &input.name)); + .map(|input| scale_encode::Field::named(input.id, &input.name)); self.args_data .encode_as_fields_to(&mut fields, metadata.types(), out)?; diff --git a/historic/src/storage.rs b/historic/src/storage.rs index 14496445fa..cf77c102e3 100644 --- a/historic/src/storage.rs +++ b/historic/src/storage.rs @@ -14,7 +14,7 @@ pub use storage_entry::StorageEntry; pub use storage_key::{StorageHasher, StorageKey, StorageKeyPart}; pub use storage_value::StorageValue; // We take how storage keys can be passed in from `frame-decode`, so re-export here. -pub use frame_decode::storage::{IntoStorageKeys, StorageKeys}; +pub use frame_decode::storage::{IntoEncodableValues, EncodableValues}; /// Work with storage. pub struct StorageClient<'atblock, Client, T> { @@ -78,6 +78,7 @@ where pub fn entries(&self) -> impl Iterator> { let client = self.client; let metadata = client.metadata(); + frame_decode::helpers::list_storage_entries_any(metadata).map(|entry| StorageEntriesItem { entry, client: self.client, @@ -88,7 +89,7 @@ where /// Working with a specific storage entry. pub struct StorageEntriesItem<'atblock, Client, T> { - entry: frame_decode::helpers::StorageEntry<'atblock>, + entry: frame_decode::storage::StorageEntry<'atblock>, client: &'atblock Client, marker: std::marker::PhantomData, } @@ -100,12 +101,12 @@ where { /// The pallet name. pub fn pallet_name(&self) -> &str { - self.entry.pallet() + &self.entry.pallet_name } /// The storage entry name. pub fn storage_name(&self) -> &str { - self.entry.entry() + &self.entry.storage_entry } /// Extract the relevant storage information so that we can work with this entry. @@ -115,8 +116,8 @@ where marker: std::marker::PhantomData, } .entry( - self.entry.pallet().to_owned(), - self.entry.entry().to_owned(), + self.entry.pallet_name.to_owned(), + self.entry.storage_entry.to_owned(), ) } } @@ -296,7 +297,7 @@ where /// Fetch a specific key in this map. If the number of keys provided is not equal /// to the number of keys required to fetch a single value from the map, then an error /// will be emitted. - pub async fn fetch( + pub async fn fetch( &self, keys: Keys, ) -> Result>, StorageError> { @@ -304,9 +305,9 @@ where info.info.keys.len() }); - if expected_num_keys != keys.num_keys() { + if expected_num_keys != keys.num_encodable_values() { return Err(StorageError::WrongNumberOfKeysProvided { - num_keys_provided: keys.num_keys(), + num_keys_provided: keys.num_encodable_values(), num_keys_expected: expected_num_keys, }); } @@ -319,7 +320,7 @@ where /// Fetch a specific key in this map as per [`StorageEntryMapClient::fetch`], but return the default /// value for the storage entry if one exists and the entry was not found. - pub async fn fetch_or_default( + pub async fn fetch_or_default( &self, keys: Keys, ) -> Result>, StorageError> { @@ -329,7 +330,7 @@ where } /// Iterate over the values underneath the provided keys. - pub async fn iter( + pub async fn iter( &self, keys: Keys, ) -> Result< @@ -384,7 +385,7 @@ where // Dev note: We don't have any functions that can take an already-encoded key and fetch an entry from // it yet, so we don't expose this. If we did expose it, we might want to return some struct that wraps // the key bytes and some metadata about them. Or maybe just fetch_raw and iter_raw. - fn key(&self, keys: Keys) -> Result, StorageError> { + fn key(&self, keys: Keys) -> Result, StorageError> { with_info!(info = &self.info => { let mut key_bytes = Vec::new(); frame_decode::storage::encode_storage_key_with_info_to( diff --git a/historic/src/storage/storage_info.rs b/historic/src/storage/storage_info.rs index 4c660890b3..76909f6bdd 100644 --- a/historic/src/storage/storage_info.rs +++ b/historic/src/storage/storage_info.rs @@ -44,7 +44,7 @@ impl<'atblock> AnyStorageInfo<'atblock> { Resolver: scale_type_resolver::TypeResolver, AnyStorageInfo<'atblock>: From>, { - m.get_storage_info(pallet_name, entry_name) + m.storage_info(pallet_name, entry_name) .map(|frame_storage_info| { let info = StorageInfo { info: frame_storage_info, @@ -84,7 +84,7 @@ impl<'atblock> From> } } -pub struct StorageInfo<'atblock, TypeId, Resolver> { +pub struct StorageInfo<'atblock, TypeId: Clone, Resolver> { pub info: frame_decode::storage::StorageInfo<'atblock, TypeId>, pub resolver: &'atblock Resolver, } diff --git a/metadata/src/from/mod.rs b/metadata/src/from/mod.rs index 777b00451f..16e0f816d1 100644 --- a/metadata/src/from/mod.rs +++ b/metadata/src/from/mod.rs @@ -33,6 +33,15 @@ pub enum TryFromError { /// Invalid type path. #[error("Type has an invalid path {0}")] InvalidTypePath(String), + /// Cannot decode storage entry information. + #[error("Error decoding storage entry information: {0}")] + StorageInfoError(#[from] frame_decode::storage::StorageInfoError<'static>), + /// Cannot decode Runtime API information. + #[error("Error decoding Runtime API information: {0}")] + RuntimeInfoError(#[from] frame_decode::runtime_apis::RuntimeApiInfoError<'static>), + /// Cannot decode View Function information. + #[error("Error decoding View Function information: {0}")] + ViewFunctionInfoError(#[from] frame_decode::view_functions::ViewFunctionInfoError<'static>), } impl TryFrom for crate::Metadata { diff --git a/metadata/src/from/v14.rs b/metadata/src/from/v14.rs index 5326f158bc..6a2a6a5c0a 100644 --- a/metadata/src/from/v14.rs +++ b/metadata/src/from/v14.rs @@ -6,9 +6,9 @@ use super::TryFromError; use crate::utils::variant_index::VariantIndex; use crate::{ - ArcStr, ConstantMetadata, CustomMetadataInner, ExtrinsicMetadata, Metadata, OuterEnumsMetadata, - PalletMetadataInner, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, - StorageHasher, StorageMetadata, TransactionExtensionMetadataInner, + ConstantMetadata, CustomMetadataInner, ExtrinsicMetadata, Metadata, OuterEnumsMetadata, + PalletMetadataInner, StorageEntryMetadata, + StorageMetadata, TransactionExtensionMetadataInner, utils::ordered_map::OrderedMap, }; use alloc::borrow::ToOwned; @@ -17,6 +17,7 @@ use alloc::string::String; use alloc::vec::Vec; use alloc::{format, vec}; use frame_metadata::v14; +use frame_decode::storage::StorageTypeInfo; use hashbrown::HashMap; use scale_info::form::PortableForm; @@ -28,23 +29,35 @@ impl TryFrom for Metadata { let mut pallets = OrderedMap::new(); let mut pallets_by_index = HashMap::new(); - for (pos, p) in m.pallets.into_iter().enumerate() { - let name: ArcStr = p.name.into(); + for (pos, p) in m.pallets.iter().enumerate() { + let name: String = p.name.clone(); - let storage = p.storage.map(|s| StorageMetadata { - prefix: s.prefix, - entries: s - .entries - .into_iter() - .map(|s| { - let name: ArcStr = s.name.clone().into(); - (name.clone(), from_storage_entry_metadata(name, s)) - }) - .collect(), - }); - let constants = p.constants.into_iter().map(|c| { - let name: ArcStr = c.name.clone().into(); - (name.clone(), from_constant_metadata(name, c)) + let storage = match &p.storage { + None => None, + Some(s) => Some(StorageMetadata { + prefix: s.prefix.clone(), + entries: s + .entries + .iter() + .map(|s| { + let entry_name: String = s.name.clone().into(); + let storage_info = m.storage_info(&name, &entry_name) + .map_err(|e| e.into_owned())? + .into_owned(); + let storage_entry = StorageEntryMetadata { + name: entry_name, + info: storage_info, + docs: s.docs.clone().into(), + }; + + Ok::<_, TryFromError>((name.clone(), storage_entry)) + }) + .collect::>()?, + }) + }; + + let constants = p.constants.iter().map(|c| { + (name.clone(), from_constant_metadata(c.clone())) }); let call_variant_index = @@ -58,14 +71,14 @@ impl TryFrom for Metadata { pallets.push_insert( name.clone(), PalletMetadataInner { - name, + name: name.clone(), index: p.index, storage, - call_ty: p.calls.map(|c| c.ty.id), + call_ty: p.calls.as_ref().map(|c| c.ty.id), call_variant_index, - event_ty: p.event.map(|e| e.ty.id), + event_ty: p.event.as_ref().map(|e| e.ty.id), event_variant_index, - error_ty: p.error.map(|e| e.ty.id), + error_ty: p.error.as_ref().map(|e| e.ty.id), error_variant_index, constants: constants.collect(), view_functions: Default::default(), @@ -135,59 +148,11 @@ fn from_extrinsic_metadata( } } -fn from_storage_hasher(value: v14::StorageHasher) -> StorageHasher { - match value { - v14::StorageHasher::Blake2_128 => StorageHasher::Blake2_128, - v14::StorageHasher::Blake2_256 => StorageHasher::Blake2_256, - v14::StorageHasher::Blake2_128Concat => StorageHasher::Blake2_128Concat, - v14::StorageHasher::Twox128 => StorageHasher::Twox128, - v14::StorageHasher::Twox256 => StorageHasher::Twox256, - v14::StorageHasher::Twox64Concat => StorageHasher::Twox64Concat, - v14::StorageHasher::Identity => StorageHasher::Identity, - } -} - -fn from_storage_entry_type(value: v14::StorageEntryType) -> StorageEntryType { - match value { - v14::StorageEntryType::Plain(ty) => StorageEntryType::Plain(ty.id), - v14::StorageEntryType::Map { - hashers, - key, - value, - } => StorageEntryType::Map { - hashers: hashers.into_iter().map(from_storage_hasher).collect(), - key_ty: key.id, - value_ty: value.id, - }, - } -} - -fn from_storage_entry_modifier(value: v14::StorageEntryModifier) -> StorageEntryModifier { - match value { - v14::StorageEntryModifier::Optional => StorageEntryModifier::Optional, - v14::StorageEntryModifier::Default => StorageEntryModifier::Default, - } -} - -fn from_storage_entry_metadata( - name: ArcStr, - s: v14::StorageEntryMetadata, -) -> StorageEntryMetadata { - StorageEntryMetadata { - name, - modifier: from_storage_entry_modifier(s.modifier), - entry_type: from_storage_entry_type(s.ty), - default: s.default, - docs: s.docs, - } -} - fn from_constant_metadata( - name: ArcStr, s: v14::PalletConstantMetadata, ) -> ConstantMetadata { ConstantMetadata { - name, + name: s.name, ty: s.ty.id, value: s.value, docs: s.docs, diff --git a/metadata/src/from/v15.rs b/metadata/src/from/v15.rs index 3fc0f07db9..80641e34b5 100644 --- a/metadata/src/from/v15.rs +++ b/metadata/src/from/v15.rs @@ -6,15 +6,17 @@ use super::TryFromError; use crate::utils::variant_index::VariantIndex; use crate::{ - ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata, MethodParamMetadata, OuterEnumsMetadata, + ConstantMetadata, ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadataInner, - StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata, + StorageEntryMetadata, StorageMetadata, TransactionExtensionMetadataInner, utils::ordered_map::OrderedMap, }; use alloc::collections::BTreeMap; use alloc::vec; use alloc::vec::Vec; use frame_metadata::v15; +use frame_decode::storage::StorageTypeInfo; +use frame_decode::runtime_apis::RuntimeApiTypeInfo; use hashbrown::HashMap; use scale_info::form::PortableForm; @@ -23,23 +25,36 @@ impl TryFrom for Metadata { fn try_from(m: v15::RuntimeMetadataV15) -> Result { let mut pallets = OrderedMap::new(); let mut pallets_by_index = HashMap::new(); - for (pos, p) in m.pallets.into_iter().enumerate() { - let name: ArcStr = p.name.into(); + for (pos, p) in m.pallets.iter().enumerate() { + let name = p.name.clone(); - let storage = p.storage.map(|s| StorageMetadata { - prefix: s.prefix, - entries: s - .entries - .into_iter() - .map(|s| { - let name: ArcStr = s.name.clone().into(); - (name.clone(), from_storage_entry_metadata(name, s)) - }) - .collect(), - }); - let constants = p.constants.into_iter().map(|c| { - let name: ArcStr = c.name.clone().into(); - (name.clone(), from_constant_metadata(name, c)) + let storage = match &p.storage { + None => None, + Some(s) => Some(StorageMetadata { + prefix: s.prefix.clone(), + entries: s + .entries + .iter() + .map(|s| { + let entry_name = s.name.clone(); + let storage_info = m.storage_info(&name, &entry_name) + .map_err(|e| e.into_owned())? + .into_owned(); + let storage_entry = StorageEntryMetadata { + name: entry_name.clone(), + info: storage_info, + docs: s.docs.clone().into(), + }; + + Ok::<_, TryFromError>((entry_name, storage_entry)) + }) + .collect::>()?, + }) + }; + + let constants = p.constants.iter().map(|c| { + let name = c.name.clone(); + (name, from_constant_metadata(c.clone())) }); let call_variant_index = @@ -56,24 +71,41 @@ impl TryFrom for Metadata { name, index: p.index, storage, - call_ty: p.calls.map(|c| c.ty.id), + call_ty: p.calls.as_ref().map(|c| c.ty.id), call_variant_index, - event_ty: p.event.map(|e| e.ty.id), + event_ty: p.event.as_ref().map(|e| e.ty.id), event_variant_index, - error_ty: p.error.map(|e| e.ty.id), + error_ty: p.error.as_ref().map(|e| e.ty.id), error_variant_index, constants: constants.collect(), view_functions: Default::default(), associated_types: Default::default(), - docs: p.docs, + docs: p.docs.clone(), }, ); } - let apis = m.apis.into_iter().map(|api| { - let name: ArcStr = api.name.clone().into(); - (name.clone(), from_runtime_api_metadata(name, api)) - }); + let apis = m.apis.iter().map(|api| { + let trait_name = api.name.clone(); + let methods = api.methods.iter().map(|method| { + let method_name = method.name.clone(); + let method_info = RuntimeApiMethodMetadataInner { + info: m.runtime_api_info(&trait_name, &method.name) + .map_err(|e| e.into_owned())? + .into_owned(), + name: method.name.clone(), + docs: method.docs.clone() + }; + Ok((method_name, method_info)) + }).collect::>()?; + + let runtime_api_metadata = RuntimeApiMetadataInner { + name: trait_name.clone(), + methods, + docs: api.docs.clone() + }; + Ok((trait_name, runtime_api_metadata)) + }).collect::>()?; let dispatch_error_ty = m .types @@ -88,7 +120,7 @@ impl TryFrom for Metadata { pallets_by_index, extrinsic: from_extrinsic_metadata(m.extrinsic), dispatch_error_ty, - apis: apis.collect(), + apis: apis, outer_enums: OuterEnumsMetadata { call_enum_ty: m.outer_enums.call_enum_ty.id, event_enum_ty: m.outer_enums.event_enum_ty.id, @@ -130,104 +162,13 @@ fn from_extrinsic_metadata(value: v15::ExtrinsicMetadata) -> Extri } } -fn from_storage_hasher(value: v15::StorageHasher) -> StorageHasher { - match value { - v15::StorageHasher::Blake2_128 => StorageHasher::Blake2_128, - v15::StorageHasher::Blake2_256 => StorageHasher::Blake2_256, - v15::StorageHasher::Blake2_128Concat => StorageHasher::Blake2_128Concat, - v15::StorageHasher::Twox128 => StorageHasher::Twox128, - v15::StorageHasher::Twox256 => StorageHasher::Twox256, - v15::StorageHasher::Twox64Concat => StorageHasher::Twox64Concat, - v15::StorageHasher::Identity => StorageHasher::Identity, - } -} - -fn from_storage_entry_type(value: v15::StorageEntryType) -> StorageEntryType { - match value { - v15::StorageEntryType::Plain(ty) => StorageEntryType::Plain(ty.id), - v15::StorageEntryType::Map { - hashers, - key, - value, - } => StorageEntryType::Map { - hashers: hashers.into_iter().map(from_storage_hasher).collect(), - key_ty: key.id, - value_ty: value.id, - }, - } -} - -fn from_storage_entry_modifier(value: v15::StorageEntryModifier) -> StorageEntryModifier { - match value { - v15::StorageEntryModifier::Optional => StorageEntryModifier::Optional, - v15::StorageEntryModifier::Default => StorageEntryModifier::Default, - } -} - -fn from_storage_entry_metadata( - name: ArcStr, - s: v15::StorageEntryMetadata, -) -> StorageEntryMetadata { - StorageEntryMetadata { - name, - modifier: from_storage_entry_modifier(s.modifier), - entry_type: from_storage_entry_type(s.ty), - default: s.default, - docs: s.docs, - } -} - fn from_constant_metadata( - name: ArcStr, s: v15::PalletConstantMetadata, ) -> ConstantMetadata { ConstantMetadata { - name, + name: s.name, ty: s.ty.id, value: s.value, docs: s.docs, } } - -fn from_runtime_api_metadata( - name: ArcStr, - s: v15::RuntimeApiMetadata, -) -> RuntimeApiMetadataInner { - RuntimeApiMetadataInner { - name, - docs: s.docs, - methods: s - .methods - .into_iter() - .map(|m| { - let name: ArcStr = m.name.clone().into(); - (name.clone(), from_runtime_api_method_metadata(name, m)) - }) - .collect(), - } -} - -fn from_runtime_api_method_metadata( - name: ArcStr, - s: v15::RuntimeApiMethodMetadata, -) -> RuntimeApiMethodMetadataInner { - RuntimeApiMethodMetadataInner { - name, - inputs: s - .inputs - .into_iter() - .map(from_runtime_api_method_param_metadata) - .collect(), - output_ty: s.output.id, - docs: s.docs, - } -} - -fn from_runtime_api_method_param_metadata( - s: v15::RuntimeApiMethodParamMetadata, -) -> MethodParamMetadata { - MethodParamMetadata { - name: s.name, - ty: s.ty.id, - } -} diff --git a/metadata/src/from/v16.rs b/metadata/src/from/v16.rs index 53a0d26a1f..8bab6ead9f 100644 --- a/metadata/src/from/v16.rs +++ b/metadata/src/from/v16.rs @@ -6,43 +6,66 @@ use super::TryFromError; use crate::utils::variant_index::VariantIndex; use crate::{ - ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata, MethodParamMetadata, OuterEnumsMetadata, + ConstantMetadata, ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadataInner, - StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata, + StorageEntryMetadata, StorageMetadata, TransactionExtensionMetadataInner, ViewFunctionMetadataInner, utils::ordered_map::OrderedMap, }; +use frame_decode::view_functions::ViewFunctionTypeInfo; use frame_metadata::{v15, v16}; use hashbrown::HashMap; +use frame_decode::storage::StorageTypeInfo; +use frame_decode::runtime_apis::RuntimeApiTypeInfo; use scale_info::form::PortableForm; impl TryFrom for Metadata { type Error = TryFromError; fn try_from(m: v16::RuntimeMetadataV16) -> Result { - let types = m.types; + let types = &m.types; let mut pallets = OrderedMap::new(); let mut pallets_by_index = HashMap::new(); - for (pos, p) in m.pallets.into_iter().enumerate() { - let name: ArcStr = p.name.into(); + for (pos, p) in m.pallets.iter().enumerate() { + let name = p.name.clone(); - let storage = p.storage.map(|s| StorageMetadata { - prefix: s.prefix, - entries: s - .entries - .into_iter() - .map(|s| { - let name: ArcStr = s.name.clone().into(); - (name.clone(), from_storage_entry_metadata(name, s)) - }) - .collect(), - }); - let constants = p.constants.into_iter().map(|c| { - let name: ArcStr = c.name.clone().into(); - (name.clone(), from_constant_metadata(name, c)) - }); - let view_functions = p.view_functions.into_iter().map(|v| { - let name: ArcStr = v.name.clone().into(); - (name.clone(), from_view_function_metadata(name, v)) + let storage = match &p.storage { + None => None, + Some(s) => Some(StorageMetadata { + prefix: s.prefix.clone(), + entries: s + .entries + .iter() + .map(|s| { + let entry_name = s.name.clone(); + let storage_info = m.storage_info(&name, &entry_name) + .map_err(|e| e.into_owned())? + .into_owned(); + let storage_entry = StorageEntryMetadata { + name: entry_name.clone(), + info: storage_info, + docs: s.docs.clone().into(), + }; + + Ok::<_, TryFromError>((entry_name, storage_entry)) + }) + .collect::>()?, + }) + }; + + let view_functions = p.view_functions.iter().map(|vf| { + let view_function_metadata = ViewFunctionMetadataInner { + name: vf.name.clone(), + info: m.view_function_info(&name, &vf.name) + .map_err(|e| e.into_owned())? + .into_owned(), + docs: vf.docs.clone() + }; + Ok((vf.name.clone(), view_function_metadata)) + }).collect::>()?; + + let constants = p.constants.iter().map(|c| { + let name = c.name.clone(); + (name, from_constant_metadata(c.clone())) }); let call_variant_index = VariantIndex::build(p.calls.as_ref().map(|c| c.ty.id), &types); @@ -53,8 +76,8 @@ impl TryFrom for Metadata { let associated_types = p .associated_types - .into_iter() - .map(|t| (t.name, t.ty.id)) + .iter() + .map(|t| (t.name.clone(), t.ty.id)) .collect(); pallets_by_index.insert(p.index, pos); @@ -64,24 +87,41 @@ impl TryFrom for Metadata { name, index: p.index, storage, - call_ty: p.calls.map(|c| c.ty.id), + call_ty: p.calls.as_ref().map(|c| c.ty.id), call_variant_index, - event_ty: p.event.map(|e| e.ty.id), + event_ty: p.event.as_ref().map(|e| e.ty.id), event_variant_index, - error_ty: p.error.map(|e| e.ty.id), + error_ty: p.error.as_ref().map(|e| e.ty.id), error_variant_index, constants: constants.collect(), - view_functions: view_functions.collect(), + view_functions, associated_types, - docs: p.docs, + docs: p.docs.clone(), }, ); } - let apis = m.apis.into_iter().map(|api| { - let name: ArcStr = api.name.clone().into(); - (name.clone(), from_runtime_api_metadata(name, api)) - }); + let apis = m.apis.iter().map(|api| { + let trait_name = api.name.clone(); + let methods = api.methods.iter().map(|method| { + let method_name = method.name.clone(); + let method_info = RuntimeApiMethodMetadataInner { + info: m.runtime_api_info(&trait_name, &method.name) + .map_err(|e| e.into_owned())? + .into_owned(), + name: method.name.clone(), + docs: method.docs.clone() + }; + Ok((method_name, method_info)) + }).collect::>()?; + + let runtime_api_metadata = RuntimeApiMetadataInner { + name: trait_name.clone(), + methods, + docs: api.docs.clone() + }; + Ok((trait_name, runtime_api_metadata)) + }).collect::>()?; let custom_map = m .custom @@ -103,12 +143,12 @@ impl TryFrom for Metadata { .map(|ty| ty.id); Ok(Metadata { - types, + types: m.types, pallets, pallets_by_index, extrinsic: from_extrinsic_metadata(m.extrinsic), dispatch_error_ty, - apis: apis.collect(), + apis, outer_enums: OuterEnumsMetadata { call_enum_ty: m.outer_enums.call_enum_ty.id, event_enum_ty: m.outer_enums.event_enum_ty.id, @@ -147,118 +187,13 @@ fn from_extrinsic_metadata(value: v16::ExtrinsicMetadata) -> Extri } } -fn from_storage_hasher(value: v16::StorageHasher) -> StorageHasher { - match value { - v16::StorageHasher::Blake2_128 => StorageHasher::Blake2_128, - v16::StorageHasher::Blake2_256 => StorageHasher::Blake2_256, - v16::StorageHasher::Blake2_128Concat => StorageHasher::Blake2_128Concat, - v16::StorageHasher::Twox128 => StorageHasher::Twox128, - v16::StorageHasher::Twox256 => StorageHasher::Twox256, - v16::StorageHasher::Twox64Concat => StorageHasher::Twox64Concat, - v16::StorageHasher::Identity => StorageHasher::Identity, - } -} - -fn from_storage_entry_type(value: v16::StorageEntryType) -> StorageEntryType { - match value { - v16::StorageEntryType::Plain(ty) => StorageEntryType::Plain(ty.id), - v16::StorageEntryType::Map { - hashers, - key, - value, - } => StorageEntryType::Map { - hashers: hashers.into_iter().map(from_storage_hasher).collect(), - key_ty: key.id, - value_ty: value.id, - }, - } -} - -fn from_storage_entry_modifier(value: v16::StorageEntryModifier) -> StorageEntryModifier { - match value { - v16::StorageEntryModifier::Optional => StorageEntryModifier::Optional, - v16::StorageEntryModifier::Default => StorageEntryModifier::Default, - } -} - -fn from_storage_entry_metadata( - name: ArcStr, - s: v16::StorageEntryMetadata, -) -> StorageEntryMetadata { - StorageEntryMetadata { - name, - modifier: from_storage_entry_modifier(s.modifier), - entry_type: from_storage_entry_type(s.ty), - default: s.default, - docs: s.docs, - } -} - fn from_constant_metadata( - name: ArcStr, s: v16::PalletConstantMetadata, ) -> ConstantMetadata { ConstantMetadata { - name, + name: s.name, ty: s.ty.id, value: s.value, docs: s.docs, } } - -fn from_runtime_api_metadata( - name: ArcStr, - s: v16::RuntimeApiMetadata, -) -> RuntimeApiMetadataInner { - RuntimeApiMetadataInner { - name, - docs: s.docs, - methods: s - .methods - .into_iter() - .map(|m| { - let name: ArcStr = m.name.clone().into(); - (name.clone(), from_runtime_api_method_metadata(name, m)) - }) - .collect(), - } -} - -fn from_runtime_api_method_metadata( - name: ArcStr, - s: v16::RuntimeApiMethodMetadata, -) -> RuntimeApiMethodMetadataInner { - RuntimeApiMethodMetadataInner { - name, - inputs: s - .inputs - .into_iter() - .map(|param| MethodParamMetadata { - name: param.name, - ty: param.ty.id, - }) - .collect(), - output_ty: s.output.id, - docs: s.docs, - } -} - -fn from_view_function_metadata( - name: ArcStr, - s: v16::PalletViewFunctionMetadata, -) -> ViewFunctionMetadataInner { - ViewFunctionMetadataInner { - name, - query_id: s.id, - inputs: s - .inputs - .into_iter() - .map(|param| MethodParamMetadata { - name: param.name, - ty: param.ty.id, - }) - .collect(), - output_ty: s.output.id, - docs: s.docs, - } -} diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index f680b36883..74739d6a29 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -24,13 +24,22 @@ mod utils; use alloc::borrow::Cow; use alloc::collections::BTreeMap; -use alloc::string::String; -use alloc::sync::Arc; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use frame_decode::extrinsics::{ ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicInfoArg, ExtrinsicInfoError, ExtrinsicSignatureInfo, }; +use frame_decode::storage::{ + StorageEntry, StorageInfo, StorageInfoError, StorageKeyInfo +}; +use frame_decode::runtime_apis::{ + RuntimeApi, RuntimeApiInfo, RuntimeApiInfoError, RuntimeApiInput +}; +use frame_decode::view_functions::{ + ViewFunction, ViewFunctionInfo, ViewFunctionInfoError, ViewFunctionInput +}; + use hashbrown::HashMap; use scale_info::{PortableRegistry, Variant, form::PortableForm}; use utils::{ @@ -39,11 +48,10 @@ use utils::{ variant_index::VariantIndex, }; -type ArcStr = Arc; - pub use from::SUPPORTED_METADATA_VERSIONS; pub use from::TryFromError; pub use utils::validation::MetadataHasher; +pub use frame_decode::storage::StorageHasher; type CustomMetadataInner = frame_metadata::v15::CustomMetadata; @@ -55,7 +63,7 @@ pub struct Metadata { /// Type registry containing all types used in the metadata. types: PortableRegistry, /// Metadata of all the pallets. - pallets: OrderedMap, + pallets: OrderedMap, /// Find the location in the pallet Vec by pallet index. pallets_by_index: HashMap, /// Metadata of the extrinsic. @@ -65,7 +73,7 @@ pub struct Metadata { /// The type Id of the `DispatchError` type, which Subxt makes use of. dispatch_error_ty: Option, /// Details about each of the runtime API traits. - apis: OrderedMap, + apis: OrderedMap, /// Allows users to add custom types to the metadata. A map that associates a string key to a `CustomValueMetadata`. custom: CustomMetadataInner, } @@ -75,7 +83,7 @@ pub struct Metadata { impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { type TypeId = u32; - fn get_call_info( + fn extrinsic_call_info( &self, pallet_index: u8, call_index: u8, @@ -108,7 +116,7 @@ impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { }) } - fn get_signature_info( + fn extrinsic_signature_info( &self, ) -> Result, ExtrinsicInfoError<'_>> { Ok(ExtrinsicSignatureInfo { @@ -117,7 +125,7 @@ impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { }) } - fn get_extension_info( + fn extrinsic_extension_info( &self, extension_version: Option, ) -> Result, ExtrinsicInfoError<'_>> { @@ -142,6 +150,112 @@ impl frame_decode::extrinsics::ExtrinsicTypeInfo for Metadata { Ok(ExtrinsicExtensionInfo { extension_ids }) } } +impl frame_decode::storage::StorageTypeInfo for Metadata { + type TypeId = u32; + + fn storage_info( + &self, + pallet_name: &str, + storage_entry: &str, + ) -> Result, StorageInfoError<'_>> { + let pallet = self.pallet_by_name(pallet_name) + .ok_or_else(|| StorageInfoError::PalletNotFound { pallet_name: pallet_name.to_string() })?; + let entry = pallet + .storage() + .map(|storage| storage.entry_by_name(storage_entry)) + .flatten() + .ok_or_else(|| StorageInfoError::StorageNotFound { name: storage_entry.to_string(), pallet_name: Cow::Borrowed(pallet.name()) })?; + + let info = StorageInfo { + keys: Cow::Borrowed(&*entry.info.keys), + value_id: entry.info.value_id, + default_value: entry.info.default_value.as_ref().map(|def| Cow::Borrowed(&**def)) + }; + + Ok(info) + } + + fn storage_entries(&self) -> impl Iterator> { + self.pallets().flat_map(|pallet| { + let pallet_name = pallet.name(); + pallet.storage().into_iter().flat_map(|storage| { + storage.entries().iter().map(|entry| { + StorageEntry { + pallet_name: Cow::Borrowed(pallet_name), + storage_entry: Cow::Borrowed(entry.name()) + } + }) + }) + }) + } +} +impl frame_decode::runtime_apis::RuntimeApiTypeInfo for Metadata { + type TypeId = u32; + + fn runtime_api_info( + &self, + trait_name: &str, + method_name: &str, + ) -> Result, RuntimeApiInfoError<'_>> { + let api_trait = self.apis.get_by_key(trait_name) + .ok_or_else(|| RuntimeApiInfoError::TraitNotFound { trait_name: trait_name.to_string() })?; + let api_method = api_trait.methods.get_by_key(method_name) + .ok_or_else(|| RuntimeApiInfoError::MethodNotFound { trait_name: Cow::Borrowed(&api_trait.name), method_name: method_name.to_string() })?; + + let info = RuntimeApiInfo { + inputs: Cow::Borrowed(&api_method.info.inputs), + output_id: api_method.info.output_id + }; + + Ok(info) + } + + fn runtime_apis(&self) -> impl Iterator> { + self.runtime_api_traits().flat_map(|api_trait| { + let trait_name = api_trait.name(); + api_trait.methods().map(|method| { + RuntimeApi { + trait_name: Cow::Borrowed(trait_name), + method_name: Cow::Borrowed(method.name()) + } + }) + }) + } +} +impl frame_decode::view_functions::ViewFunctionTypeInfo for Metadata { + type TypeId = u32; + + fn view_function_info( + &self, + pallet_name: &str, + function_name: &str, + ) -> Result, ViewFunctionInfoError<'_>> { + let pallet = self.pallet_by_name(pallet_name) + .ok_or_else(|| ViewFunctionInfoError::PalletNotFound { pallet_name: pallet_name.to_string() })?; + let function = pallet.view_function_by_name(function_name) + .ok_or_else(|| ViewFunctionInfoError::FunctionNotFound { pallet_name: Cow::Borrowed(pallet.name()), function_name: function_name.to_string() })?; + + let info = ViewFunctionInfo { + inputs: Cow::Borrowed(&function.inner.info.inputs), + output_id: function.inner.info.output_id, + query_id: *function.query_id() + }; + + Ok(info) + } + + fn view_functions(&self) -> impl Iterator> { + self.pallets().flat_map(|pallet| { + let pallet_name = pallet.name(); + pallet.view_functions().map(|function| { + ViewFunction { + pallet_name: Cow::Borrowed(pallet_name), + function_name: Cow::Borrowed(function.name()) + } + }) + }) + } +} impl Metadata { /// Access the underlying type registry. @@ -418,7 +532,7 @@ impl<'a> PalletMetadata<'a> { #[derive(Debug, Clone)] struct PalletMetadataInner { /// Pallet name. - name: ArcStr, + name: String, /// Pallet index. index: u8, /// Pallet storage metadata. @@ -436,9 +550,9 @@ struct PalletMetadataInner { /// Error variants by name/u8. error_variant_index: VariantIndex, /// Map from constant name to constant details. - constants: OrderedMap, + constants: OrderedMap, /// Details about each of the pallet view functions. - view_functions: OrderedMap, + view_functions: OrderedMap, /// Mapping from associated type to type ID describing its shape. associated_types: BTreeMap, /// Pallet documentation. @@ -451,7 +565,7 @@ pub struct StorageMetadata { /// The common prefix used by all storage entries. prefix: String, /// Map from storage entry name to details. - entries: OrderedMap, + entries: OrderedMap, } impl StorageMetadata { @@ -475,13 +589,9 @@ impl StorageMetadata { #[derive(Debug, Clone)] pub struct StorageEntryMetadata { /// Variable name of the storage entry. - name: ArcStr, - /// An `Option` modifier of that storage entry. - modifier: StorageEntryModifier, - /// Type of the value stored in the entry. - entry_type: StorageEntryType, - /// Default value (SCALE encoded). - default: Vec, + name: String, + /// Information about the storage entry. + info: StorageInfo<'static, u32>, /// Storage entry documentation. docs: Vec, } @@ -491,17 +601,18 @@ impl StorageEntryMetadata { pub fn name(&self) -> &str { &self.name } - /// Is the entry value optional or does it have a default value. - pub fn modifier(&self) -> StorageEntryModifier { - self.modifier + /// Keys in this storage entry. + pub fn keys(&self) -> impl ExactSizeIterator> { + let keys = &*self.info.keys; + keys.iter() + } + /// Value type for this storage entry. + pub fn value_ty(&self) -> u32 { + self.info.value_id } - /// Type of the storage entry. - pub fn entry_type(&self) -> &StorageEntryType { - &self.entry_type - } - /// The SCALE encoded default value for this entry. - pub fn default_bytes(&self) -> &[u8] { - &self.default + /// The default value, if one exists, for this entry. + pub fn default_value(&self) -> Option<&[u8]> { + self.info.default_value.as_deref() } /// Storage entry documentation. pub fn docs(&self) -> &[String] { @@ -509,101 +620,11 @@ impl StorageEntryMetadata { } } -/// The type of a storage entry. -#[derive(Debug, Clone)] -pub enum StorageEntryType { - /// Plain storage entry (just the value). - Plain(u32), - /// A storage map. - Map { - /// One or more hashers, should be one hasher per key element. - hashers: Vec, - /// The type of the key, can be a tuple with elements for each of the hashers. - key_ty: u32, - /// The type of the value. - value_ty: u32, - }, -} - -impl StorageEntryType { - /// The type of the value. - pub fn value_ty(&self) -> u32 { - match self { - StorageEntryType::Map { value_ty, .. } | StorageEntryType::Plain(value_ty) => *value_ty, - } - } - - /// The type of the key, can be a tuple with elements for each of the hashers. None for a Plain storage entry. - pub fn key_ty(&self) -> Option { - match self { - StorageEntryType::Map { key_ty, .. } => Some(*key_ty), - StorageEntryType::Plain(_) => None, - } - } -} - -/// Hasher used by storage maps. -#[derive(Debug, Clone, Copy)] -pub enum StorageHasher { - /// 128-bit Blake2 hash. - Blake2_128, - /// 256-bit Blake2 hash. - Blake2_256, - /// Multiple 128-bit Blake2 hashes concatenated. - Blake2_128Concat, - /// 128-bit XX hash. - Twox128, - /// 256-bit XX hash. - Twox256, - /// Multiple 64-bit XX hashes concatenated. - Twox64Concat, - /// Identity hashing (no hashing). - Identity, -} - -impl StorageHasher { - /// The hash produced by a [`StorageHasher`] can have these two components, in order: - /// - /// 1. A fixed size hash. (not present for [`StorageHasher::Identity`]). - /// 2. The SCALE encoded key that was used as an input to the hasher (only present for - /// [`StorageHasher::Twox64Concat`], [`StorageHasher::Blake2_128Concat`] or [`StorageHasher::Identity`]). - /// - /// This function returns the number of bytes used to represent the first of these. - pub fn len_excluding_key(&self) -> usize { - match self { - StorageHasher::Blake2_128Concat => 16, - StorageHasher::Twox64Concat => 8, - StorageHasher::Blake2_128 => 16, - StorageHasher::Blake2_256 => 32, - StorageHasher::Twox128 => 16, - StorageHasher::Twox256 => 32, - StorageHasher::Identity => 0, - } - } - - /// Returns true if the key used to produce the hash is appended to the hash itself. - pub fn ends_with_key(&self) -> bool { - matches!( - self, - StorageHasher::Blake2_128Concat | StorageHasher::Twox64Concat | StorageHasher::Identity - ) - } -} - -/// Is the storage entry optional, or does it have a default value. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum StorageEntryModifier { - /// The storage entry returns an `Option`, with `None` if the key is not present. - Optional, - /// The storage entry returns `T::Default` if the key is not present. - Default, -} - /// Metadata for a single constant. #[derive(Debug, Clone)] pub struct ConstantMetadata { /// Name of the pallet constant. - name: ArcStr, + name: String, /// Type of the pallet constant. ty: u32, /// Value stored in the constant (SCALE encoded). @@ -816,9 +837,9 @@ impl<'a> RuntimeApiMetadata<'a> { #[derive(Debug, Clone)] struct RuntimeApiMetadataInner { /// Trait name. - name: ArcStr, + name: String, /// Trait methods. - methods: OrderedMap, + methods: OrderedMap, /// Trait documentation. docs: Vec, } @@ -841,12 +862,13 @@ impl<'a> RuntimeApiMethodMetadata<'a> { &self.inner.docs } /// Method inputs. - pub fn inputs(&self) -> impl ExactSizeIterator + use<'a> { - self.inner.inputs.iter() + pub fn inputs(&self) -> impl ExactSizeIterator> + use<'a> { + let inputs = &*self.inner.info.inputs; + inputs.iter() } /// Method return type. pub fn output_ty(&self) -> u32 { - self.inner.output_ty + self.inner.info.output_id } /// Return a hash for the method. pub fn hash(&self) -> [u8; HASH_LEN] { @@ -857,11 +879,9 @@ impl<'a> RuntimeApiMethodMetadata<'a> { #[derive(Debug, Clone)] struct RuntimeApiMethodMetadataInner { /// Method name. - name: ArcStr, - /// Method parameters. - inputs: Vec, - /// Method output type. - output_ty: u32, + name: String, + /// Info. + info: RuntimeApiInfo<'static, u32>, /// Method documentation. docs: Vec, } @@ -882,19 +902,20 @@ impl<'a> ViewFunctionMetadata<'a> { /// Query ID. This is used to query the function. Roughly, it is constructed by doing /// `twox_128(pallet_name) ++ twox_128("fn_name(fnarg_types) -> return_ty")` . pub fn query_id(&self) -> &'a [u8; 32] { - &self.inner.query_id + &self.inner.info.query_id } /// Method documentation. pub fn docs(&self) -> &'a [String] { &self.inner.docs } /// Method inputs. - pub fn inputs(&self) -> impl ExactSizeIterator + use<'a> { - self.inner.inputs.iter() + pub fn inputs(&self) -> impl ExactSizeIterator> + use<'a> { + let inputs = &*self.inner.info.inputs; + inputs.iter() } /// Method return type. pub fn output_ty(&self) -> u32 { - self.inner.output_ty + self.inner.info.output_id } /// Return a hash for the method. The query ID of a view function validates it to some /// degree, but only takes type _names_ into account. This hash takes into account the @@ -907,13 +928,9 @@ impl<'a> ViewFunctionMetadata<'a> { #[derive(Debug, Clone)] struct ViewFunctionMetadataInner { /// View function name. - name: ArcStr, - /// View function query ID. - query_id: [u8; 32], - /// Input types. - inputs: Vec, - /// Output type. - output_ty: u32, + name: String, + /// Info. + info: ViewFunctionInfo<'static, u32>, /// Documentation. docs: Vec, } diff --git a/metadata/src/utils/validation.rs b/metadata/src/utils/validation.rs index 7ad7245205..1cb0021eb6 100644 --- a/metadata/src/utils/validation.rs +++ b/metadata/src/utils/validation.rs @@ -6,7 +6,7 @@ use crate::{ CustomMetadata, CustomValueMetadata, ExtrinsicMetadata, Metadata, PalletMetadata, - RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, StorageEntryType, + RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, ViewFunctionMetadata, }; use alloc::vec::Vec; @@ -290,29 +290,19 @@ fn get_extrinsic_hash(registry: &PortableRegistry, extrinsic: &ExtrinsicMetadata fn get_storage_entry_hash(registry: &PortableRegistry, entry: &StorageEntryMetadata) -> Hash { let mut bytes = concat_and_hash3( &hash(entry.name.as_bytes()), - // Cloning 'entry.modifier' should essentially be a copy. - &[entry.modifier as u8; HASH_LEN], - &hash(&entry.default), + &get_type_hash(registry, entry.info.value_id), + &hash(entry.info.default_value.as_ref().map(|b| &**b).unwrap_or_default()) ); - match &entry.entry_type { - StorageEntryType::Plain(ty) => concat_and_hash2(&bytes, &get_type_hash(registry, *ty)), - StorageEntryType::Map { - hashers, - key_ty, - value_ty, - } => { - for hasher in hashers { - // Cloning the hasher should essentially be a copy. - bytes = concat_and_hash2(&bytes, &[*hasher as u8; HASH_LEN]); - } - concat_and_hash3( - &bytes, - &get_type_hash(registry, *key_ty), - &get_type_hash(registry, *value_ty), - ) - } + for key in &*entry.info.keys { + bytes = concat_and_hash3( + &bytes, + &[key.hasher as u8; HASH_LEN], + &get_type_hash(registry, key.key_id), + ) } + + bytes } fn get_custom_metadata_hash(custom_metadata: &CustomMetadata) -> Hash { @@ -382,7 +372,7 @@ pub fn get_runtime_api_hash(runtime_api: &RuntimeApiMethodMetadata) -> Hash { bytes = concat_and_hash3( &bytes, &hash(input.name.as_bytes()), - &get_type_hash(registry, input.ty), + &get_type_hash(registry, input.id), ); } @@ -419,7 +409,7 @@ pub fn get_view_function_hash(view_function: &ViewFunctionMetadata) -> Hash { bytes = concat_and_hash3( &bytes, &hash(input.name.as_bytes()), - &get_type_hash(registry, input.ty), + &get_type_hash(registry, input.id), ); } diff --git a/subxt/examples/storage_iterating.rs b/subxt/examples/storage_iterating.rs index f64fad6c8d..996a202eab 100644 --- a/subxt/examples/storage_iterating.rs +++ b/subxt/examples/storage_iterating.rs @@ -1,20 +1,16 @@ #![allow(missing_docs)] use subxt::{OnlineClient, PolkadotConfig}; -#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_full.scale")] -pub mod polkadot {} +#[subxt::subxt(runtime_metadata_path = "../westend_ah.scale")] +pub mod runtime {} #[tokio::main] async fn main() -> Result<(), Box> { // Create a new API client, configured to talk to Polkadot nodes. - let api = OnlineClient::::new().await?; + let api = OnlineClient::::from_url("wss://westend-asset-hub-rpc.polkadot.io").await?; - // Build a storage query to iterate over account information. - let storage_query = polkadot::storage().system().account_iter(); - - // Get back an iterator of results (here, we are fetching 10 items at - // a time from the node, but we always iterate over one at a time). - let mut results = api.storage().at_latest().await?.iter(storage_query).await?; + let query = runtime::storage().staking().era_pruning_state_iter(); + let mut results = api.storage().at_latest().await?.iter(query).await?; while let Some(Ok(kv)) = results.next().await { println!("Keys decoded: {:?}", kv.keys); diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index b6b872952b..589f0ca6af 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -76,7 +76,7 @@ pub mod metadata { /// Submit dynamic transactions. pub mod dynamic { pub use subxt_core::dynamic::{ - At, DecodedValue, DecodedValueThunk, Value, constant, runtime_api_call, storage, tx, + At, DecodedValue, DecodedValueThunk, Value, constant, runtime_api_call, tx, // storage // TODO re-add view_function_call, }; } diff --git a/subxt/src/storage/mod.rs b/subxt/src/storage/mod.rs index e7867c8b1c..6a1b18bf0b 100644 --- a/subxt/src/storage/mod.rs +++ b/subxt/src/storage/mod.rs @@ -4,11 +4,94 @@ //! Types associated with accessing and working with storage items. -mod storage_client; -mod storage_type; +// mod storage_client; +// mod storage_type; -pub use storage_client::StorageClient; -pub use storage_type::{Storage, StorageKeyValuePair}; -pub use subxt_core::storage::address::{ - Address, DefaultAddress, DynamicAddress, StaticAddress, StaticStorageKey, StorageKey, dynamic, +// pub use storage_client::StorageClient; +// pub use storage_type::{Storage, StorageKeyValuePair}; +// pub use subxt_core::storage::address::{ +// Address, DefaultAddress, DynamicAddress, StaticAddress, StaticStorageKey, StorageKey, dynamic, +// }; // TODO re-add + +use crate::{ + backend::BlockRef, + client::{OfflineClientT, OnlineClientT}, + config::{Config, HashFor}, + error::Error, }; +use derive_where::derive_where; +use std::{future::Future, marker::PhantomData}; + + + +/// Query the runtime storage. +#[derive_where(Clone; Client)] +pub struct StorageClient { + client: Client, + _marker: PhantomData, +} + +impl StorageClient { + /// Create a new [`StorageClient`] + pub fn new(client: Client) -> Self { + Self { + client, + _marker: PhantomData, + } + } +} + +impl StorageClient +where + T: Config, + Client: OfflineClientT, +{ + /// Run the validation logic against some storage address you'd like to access. Returns `Ok(())` + /// if the address is valid (or if it's not possible to check since the address has no validation hash). + /// Return an error if the address was not valid or something went wrong trying to validate it (ie + /// the pallet or storage entry in question do not exist at all). + pub fn validate(&self, address: &Addr) -> Result<(), Error> { + subxt_core::storage::validate(address, &self.client.metadata()).map_err(Into::into) + } + + /// Convert some storage address into the raw bytes that would be submitted to the node in order + /// to retrieve the entries at the root of the associated address. + pub fn address_root_bytes(&self, address: &Addr) -> Vec { + subxt_core::storage::get_address_root_bytes(address) + } + + /// Convert some storage address into the raw bytes that would be submitted to the node in order + /// to retrieve an entry. This fails if [`Address::append_entry_bytes`] does; in the built-in + /// implementation this would be if the pallet and storage entry being asked for is not available on the + /// node you're communicating with, or if the metadata is missing some type information (which should not + /// happen). + pub fn address_bytes(&self, address: &Addr) -> Result, Error> { + subxt_core::storage::get_address_bytes(address, &self.client.metadata()).map_err(Into::into) + } +} + +impl StorageClient +where + T: Config, + Client: OnlineClientT, +{ + /// Obtain storage at some block hash. + pub fn at(&self, block_ref: impl Into>>) -> Storage { + Storage::new(self.client.clone(), block_ref.into()) + } + + /// Obtain storage at the latest finalized block. + pub fn at_latest( + &self, + ) -> impl Future, Error>> + Send + 'static { + // Clone and pass the client in like this so that we can explicitly + // return a Future that's Send + 'static, rather than tied to &self. + let client = self.client.clone(); + async move { + // get the ref for the latest finalized block and use that. + let block_ref = client.backend().latest_finalized_block_ref().await?; + + Ok(Storage::new(client, block_ref)) + } + } +} \ No newline at end of file