mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 04:01:10 +00:00
WIP integrating new frame-decode and working out new storage APIS
This commit is contained in:
Generated
-4
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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; );
|
||||
|
||||
+11
-57
@@ -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<MapEntryKey> = 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<MapEntryKey> = 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
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -108,7 +108,7 @@ impl<ArgsData: EncodeAsFields, ReturnTy: DecodeWithMetadata> 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)?;
|
||||
|
||||
+207
-29
@@ -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<u8>) -> Result<(), Error>;
|
||||
/// Encode the suffix of the storage key for this address
|
||||
fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> 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<KeyParts, Value, HasDefaultValue> {
|
||||
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 <KeyParts, Value, HasDefaultValue> StaticFetchableAddress<KeyParts, Value, HasDefaultValue> {
|
||||
/// 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<Cow<'static, str>>,
|
||||
entry_name: impl Into<Cow<'static, str>>,
|
||||
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 <KeyParts, Value, HasDefaultValue> Address for StaticFetchableAddress<KeyParts, Value, HasDefaultValue>
|
||||
where
|
||||
KeyParts: IntoEncodableValues,
|
||||
Value: DecodeAsType
|
||||
{
|
||||
type KeyParts = KeyParts;
|
||||
type Value = Value;
|
||||
|
||||
fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> 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 <KeyParts, Value, HasDefaultValue> FetchableAddress for StaticFetchableAddress<KeyParts, Value, HasDefaultValue>
|
||||
where
|
||||
KeyParts: IntoEncodableValues,
|
||||
Value: DecodeAsType
|
||||
{
|
||||
type HasDefaultValue = HasDefaultValue;
|
||||
}
|
||||
|
||||
/// An address which points to a set of storage values.
|
||||
pub struct StaticIterableAddress<InputKeyParts, OutputKeyParts, Value> {
|
||||
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 <InputKeyParts, OutputKeyParts, Value> StaticIterableAddress<InputKeyParts, OutputKeyParts, Value> {
|
||||
/// 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<Cow<'static, str>>,
|
||||
entry_name: impl Into<Cow<'static, str>>,
|
||||
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 <InputKeyParts, OutputKeyParts, Value> Address for StaticIterableAddress<InputKeyParts, OutputKeyParts, Value>
|
||||
where
|
||||
InputKeyParts: IntoEncodableValues,
|
||||
Value: DecodeAsType
|
||||
{
|
||||
type KeyParts = InputKeyParts;
|
||||
type Value = Value;
|
||||
|
||||
fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> 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 <InputKeyParts, OutputKeyParts, Value> IterableAddress for StaticIterableAddress<InputKeyParts, OutputKeyParts, Value>
|
||||
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)]
|
||||
|
||||
+3
-10
@@ -112,10 +112,7 @@ pub fn decode_value<Addr: Address>(
|
||||
|
||||
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<Addr: Address>(
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ impl<ArgsData: EncodeAsFields, ReturnTy: DecodeWithMetadata> 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)?;
|
||||
|
||||
+13
-12
@@ -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<Item = StorageEntriesItem<'atblock, Client, T>> {
|
||||
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<T>,
|
||||
}
|
||||
@@ -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<Keys: IntoStorageKeys>(
|
||||
pub async fn fetch<Keys: IntoEncodableValues>(
|
||||
&self,
|
||||
keys: Keys,
|
||||
) -> Result<Option<StorageValue<'_, 'atblock>>, 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<Keys: IntoStorageKeys>(
|
||||
pub async fn fetch_or_default<Keys: IntoEncodableValues>(
|
||||
&self,
|
||||
keys: Keys,
|
||||
) -> Result<Option<StorageValue<'_, 'atblock>>, StorageError> {
|
||||
@@ -329,7 +330,7 @@ where
|
||||
}
|
||||
|
||||
/// Iterate over the values underneath the provided keys.
|
||||
pub async fn iter<Keys: IntoStorageKeys>(
|
||||
pub async fn iter<Keys: IntoEncodableValues>(
|
||||
&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<Keys: IntoStorageKeys>(&self, keys: Keys) -> Result<Vec<u8>, StorageError> {
|
||||
fn key<Keys: IntoEncodableValues>(&self, keys: Keys) -> Result<Vec<u8>, StorageError> {
|
||||
with_info!(info = &self.info => {
|
||||
let mut key_bytes = Vec::new();
|
||||
frame_decode::storage::encode_storage_key_with_info_to(
|
||||
|
||||
@@ -44,7 +44,7 @@ impl<'atblock> AnyStorageInfo<'atblock> {
|
||||
Resolver: scale_type_resolver::TypeResolver<TypeId = Info::TypeId>,
|
||||
AnyStorageInfo<'atblock>: From<StorageInfo<'atblock, Info::TypeId, Resolver>>,
|
||||
{
|
||||
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<StorageInfo<'atblock, u32, scale_info::PortableRegistry>>
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -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<frame_metadata::RuntimeMetadataPrefixed> for crate::Metadata {
|
||||
|
||||
+37
-72
@@ -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<v14::RuntimeMetadataV14> 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::<Result<_, TryFromError>>()?,
|
||||
})
|
||||
};
|
||||
|
||||
let constants = p.constants.iter().map(|c| {
|
||||
(name.clone(), from_constant_metadata(c.clone()))
|
||||
});
|
||||
|
||||
let call_variant_index =
|
||||
@@ -58,14 +71,14 @@ impl TryFrom<v14::RuntimeMetadataV14> 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<PortableForm>) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> ConstantMetadata {
|
||||
ConstantMetadata {
|
||||
name,
|
||||
name: s.name,
|
||||
ty: s.ty.id,
|
||||
value: s.value,
|
||||
docs: s.docs,
|
||||
|
||||
+60
-119
@@ -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<v15::RuntimeMetadataV15> for Metadata {
|
||||
fn try_from(m: v15::RuntimeMetadataV15) -> Result<Self, TryFromError> {
|
||||
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::<Result<_, TryFromError>>()?,
|
||||
})
|
||||
};
|
||||
|
||||
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<v15::RuntimeMetadataV15> 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::<Result<_,TryFromError>>()?;
|
||||
|
||||
let runtime_api_metadata = RuntimeApiMetadataInner {
|
||||
name: trait_name.clone(),
|
||||
methods,
|
||||
docs: api.docs.clone()
|
||||
};
|
||||
Ok((trait_name, runtime_api_metadata))
|
||||
}).collect::<Result<_,TryFromError>>()?;
|
||||
|
||||
let dispatch_error_ty = m
|
||||
.types
|
||||
@@ -88,7 +120,7 @@ impl TryFrom<v15::RuntimeMetadataV15> 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<PortableForm>) -> 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<PortableForm>) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> MethodParamMetadata {
|
||||
MethodParamMetadata {
|
||||
name: s.name,
|
||||
ty: s.ty.id,
|
||||
}
|
||||
}
|
||||
|
||||
+77
-142
@@ -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<v16::RuntimeMetadataV16> for Metadata {
|
||||
type Error = TryFromError;
|
||||
fn try_from(m: v16::RuntimeMetadataV16) -> Result<Self, TryFromError> {
|
||||
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::<Result<_, TryFromError>>()?,
|
||||
})
|
||||
};
|
||||
|
||||
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::<Result<_,TryFromError>>()?;
|
||||
|
||||
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<v16::RuntimeMetadataV16> 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<v16::RuntimeMetadataV16> 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::<Result<_,TryFromError>>()?;
|
||||
|
||||
let runtime_api_metadata = RuntimeApiMetadataInner {
|
||||
name: trait_name.clone(),
|
||||
methods,
|
||||
docs: api.docs.clone()
|
||||
};
|
||||
Ok((trait_name, runtime_api_metadata))
|
||||
}).collect::<Result<_,TryFromError>>()?;
|
||||
|
||||
let custom_map = m
|
||||
.custom
|
||||
@@ -103,12 +143,12 @@ impl TryFrom<v16::RuntimeMetadataV16> 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<PortableForm>) -> 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<PortableForm>) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> 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<PortableForm>,
|
||||
) -> 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,
|
||||
}
|
||||
}
|
||||
|
||||
+159
-142
@@ -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<str>;
|
||||
|
||||
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<PortableForm>;
|
||||
|
||||
@@ -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<ArcStr, PalletMetadataInner>,
|
||||
pallets: OrderedMap<String, PalletMetadataInner>,
|
||||
/// Find the location in the pallet Vec by pallet index.
|
||||
pallets_by_index: HashMap<u8, usize>,
|
||||
/// 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<u32>,
|
||||
/// Details about each of the runtime API traits.
|
||||
apis: OrderedMap<ArcStr, RuntimeApiMetadataInner>,
|
||||
apis: OrderedMap<String, RuntimeApiMetadataInner>,
|
||||
/// 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<ExtrinsicSignatureInfo<Self::TypeId>, 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<u8>,
|
||||
) -> Result<ExtrinsicExtensionInfo<'_, Self::TypeId>, 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<StorageInfo<'_, Self::TypeId>, 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<Item = StorageEntry<'_>> {
|
||||
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<RuntimeApiInfo<'_, Self::TypeId>, 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<Item = RuntimeApi<'_>> {
|
||||
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<ViewFunctionInfo<'_, Self::TypeId>, 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<Item = ViewFunction<'_>> {
|
||||
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<ArcStr, ConstantMetadata>,
|
||||
constants: OrderedMap<String, ConstantMetadata>,
|
||||
/// Details about each of the pallet view functions.
|
||||
view_functions: OrderedMap<ArcStr, ViewFunctionMetadataInner>,
|
||||
view_functions: OrderedMap<String, ViewFunctionMetadataInner>,
|
||||
/// Mapping from associated type to type ID describing its shape.
|
||||
associated_types: BTreeMap<String, u32>,
|
||||
/// 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<ArcStr, StorageEntryMetadata>,
|
||||
entries: OrderedMap<String, StorageEntryMetadata>,
|
||||
}
|
||||
|
||||
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<u8>,
|
||||
name: String,
|
||||
/// Information about the storage entry.
|
||||
info: StorageInfo<'static, u32>,
|
||||
/// Storage entry documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
@@ -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<Item = &StorageKeyInfo<u32>> {
|
||||
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<StorageHasher>,
|
||||
/// 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<u32> {
|
||||
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<T>`, 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<ArcStr, RuntimeApiMethodMetadataInner>,
|
||||
methods: OrderedMap<String, RuntimeApiMethodMetadataInner>,
|
||||
/// Trait documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
@@ -841,12 +862,13 @@ impl<'a> RuntimeApiMethodMetadata<'a> {
|
||||
&self.inner.docs
|
||||
}
|
||||
/// Method inputs.
|
||||
pub fn inputs(&self) -> impl ExactSizeIterator<Item = &'a MethodParamMetadata> + use<'a> {
|
||||
self.inner.inputs.iter()
|
||||
pub fn inputs(&self) -> impl ExactSizeIterator<Item = &'a RuntimeApiInput<'static, u32>> + 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<MethodParamMetadata>,
|
||||
/// Method output type.
|
||||
output_ty: u32,
|
||||
name: String,
|
||||
/// Info.
|
||||
info: RuntimeApiInfo<'static, u32>,
|
||||
/// Method documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
@@ -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<Item = &'a MethodParamMetadata> + use<'a> {
|
||||
self.inner.inputs.iter()
|
||||
pub fn inputs(&self) -> impl ExactSizeIterator<Item = &'a ViewFunctionInput<'static, u32>> + 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<MethodParamMetadata>,
|
||||
/// Output type.
|
||||
output_ty: u32,
|
||||
name: String,
|
||||
/// Info.
|
||||
info: ViewFunctionInfo<'static, u32>,
|
||||
/// Documentation.
|
||||
docs: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<dyn std::error::Error>> {
|
||||
// Create a new API client, configured to talk to Polkadot nodes.
|
||||
let api = OnlineClient::<PolkadotConfig>::new().await?;
|
||||
let api = OnlineClient::<PolkadotConfig>::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);
|
||||
|
||||
+1
-1
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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<T, Client> {
|
||||
client: Client,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClient<T, Client> {
|
||||
/// Create a new [`StorageClient`]
|
||||
pub fn new(client: Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClient<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OfflineClientT<T>,
|
||||
{
|
||||
/// 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<Addr: Address>(&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<Addr: Address>(&self, address: &Addr) -> Vec<u8> {
|
||||
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<Addr: Address>(&self, address: &Addr) -> Result<Vec<u8>, Error> {
|
||||
subxt_core::storage::get_address_bytes(address, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Client> StorageClient<T, Client>
|
||||
where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Obtain storage at some block hash.
|
||||
pub fn at(&self, block_ref: impl Into<BlockRef<HashFor<T>>>) -> Storage<T, Client> {
|
||||
Storage::new(self.client.clone(), block_ref.into())
|
||||
}
|
||||
|
||||
/// Obtain storage at the latest finalized block.
|
||||
pub fn at_latest(
|
||||
&self,
|
||||
) -> impl Future<Output = Result<Storage<T, Client>, 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user