v0.50.0: Integrate frame-decode, redo storage APIs and break up Error. (#2100)

* WIP integrating new frame-decode and working out new storage APIS

* WIP: first pass adding new storage things to subxt-core

* Second pass over Address type and start impl in Subxt

* WIP new storage APIs

* WIP New storage APIs roughly completed, lots of errors still

* Remove PlainorMap enum; plain and map values now use same struct to simplify usage

* Begin 'fixing' errors

* WIP splitting errors and tidying payload/address traits

* Get subxt-core compiling

* Small fixes in subxt-core and remove metadata mod

* subxt-core: cargo check --all-targets passes

* Fix test

* WIP starting to update subxt from subxt-core changes

* WIP splitting up subxt errors into smaller variants

* WIP errors: add DispatchError errors

* Port new Storage APIs to subxt-core

* cargo check -p subxt passes

* Quick-fix errors in subxt-cli (explore subcommand)

* fmt

* Finish fixing codegen up and start fixing examples

* get Subxt examples compiling and bytes_at for constants

* Add some arcs to limit lifetimes in subxt/subxt-core storage APIs

* A little Arcing to allow more method chaining in Storage APIs, aligning with Subxt

* Update codegen test

* cargo check --all-targets passing

* cargo check --features 'unstable-light-client' passing

* clippy

* Remove unused dep in subxt

* use published frame-decode

* fix wasm-example

* Add new tx extension to fix daily tests

* Remove unused subxt_core::dynamic::DecodedValue type

* Update book to match changes

* Update docs to fix more broken bits

* Add missing docs

* fmt

* allow larger result errs for now

* Add missing alloc imports in subxt-core

* Fix doc tests and fix bug getting constant info

* Fix V14 -> Metadata transform for storage & constants

* Fix parachain example

* Fix FFI example

* BlockLength decodes t ostruct, not u128

* use fetch/iter shorthands rather than entry in most storage tests

* Fix some integration tests

* Fix Runtime codegen tests

* Expose the dynamic custom_value selecter and use in a UI test

* Update codegen metadata

* Tidy CLI storage query and support (str,str) as a storage address

* Add (str,str) as valid constant address too

* Show string tuple in constants example

* Via the magic of traits, avoid needing any clones of queries/addresses and accept references to them

* clippy
This commit is contained in:
James Wilson
2025-11-10 11:38:07 +00:00
committed by GitHub
parent 7b4b23981c
commit 8329990a33
138 changed files with 11154 additions and 16363 deletions
+3 -5
View File
@@ -74,11 +74,9 @@ fn bench_get_storage_hash(c: &mut Criterion) {
};
for storage in storage_entries.entries() {
let storage_name = storage.name();
let bench_name = format!("{pallet_name}/{storage_name}");
group.bench_function(&bench_name, |b| {
b.iter(|| pallet.storage_hash(storage_name))
});
let entry_name = storage.name();
let bench_name = format!("{pallet_name}/{entry_name}");
group.bench_function(&bench_name, |b| b.iter(|| pallet.storage_hash(entry_name)));
}
}
}
+9
View File
@@ -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 {
+39 -75
View File
@@ -6,9 +6,8 @@ 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;
@@ -16,6 +15,7 @@ use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use alloc::{format, vec};
use frame_decode::storage::StorageTypeInfo;
use frame_metadata::v14;
use hashbrown::HashMap;
use scale_info::form::PortableForm;
@@ -28,23 +28,37 @@ 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();
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(),
};
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 =
@@ -58,14 +72,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 +149,9 @@ 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 {
fn from_constant_metadata(s: v14::PalletConstantMetadata<PortableForm>) -> ConstantMetadata {
ConstantMetadata {
name,
name: s.name,
ty: s.ty.id,
value: s.value,
docs: s.docs,
+71 -123
View File
@@ -6,14 +6,15 @@ use super::TryFromError;
use crate::utils::variant_index::VariantIndex;
use crate::{
ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata, MethodParamMetadata, OuterEnumsMetadata,
PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadataInner,
StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata,
ConstantMetadata, ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner,
RuntimeApiMetadataInner, RuntimeApiMethodMetadataInner, StorageEntryMetadata, StorageMetadata,
TransactionExtensionMetadataInner, utils::ordered_map::OrderedMap,
};
use alloc::collections::BTreeMap;
use alloc::vec;
use alloc::vec::Vec;
use frame_decode::runtime_apis::RuntimeApiTypeInfo;
use frame_decode::storage::StorageTypeInfo;
use frame_metadata::v15;
use hashbrown::HashMap;
use scale_info::form::PortableForm;
@@ -23,23 +24,37 @@ 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(),
};
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,50 @@ 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 +129,7 @@ impl TryFrom<v15::RuntimeMetadataV15> for Metadata {
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,
@@ -130,104 +171,11 @@ 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 {
fn from_constant_metadata(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,
}
}
+96 -151
View File
@@ -6,11 +6,13 @@ use super::TryFromError;
use crate::utils::variant_index::VariantIndex;
use crate::{
ArcStr, ConstantMetadata, ExtrinsicMetadata, Metadata, MethodParamMetadata, OuterEnumsMetadata,
PalletMetadataInner, RuntimeApiMetadataInner, RuntimeApiMethodMetadataInner,
StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher, StorageMetadata,
ConstantMetadata, ExtrinsicMetadata, Metadata, OuterEnumsMetadata, PalletMetadataInner,
RuntimeApiMetadataInner, RuntimeApiMethodMetadataInner, StorageEntryMetadata, StorageMetadata,
TransactionExtensionMetadataInner, ViewFunctionMetadataInner, utils::ordered_map::OrderedMap,
};
use frame_decode::runtime_apis::RuntimeApiTypeInfo;
use frame_decode::storage::StorageTypeInfo;
use frame_decode::view_functions::ViewFunctionTypeInfo;
use frame_metadata::{v15, v16};
use hashbrown::HashMap;
use scale_info::form::PortableForm;
@@ -18,43 +20,67 @@ 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(),
};
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);
let error_variant_index =
VariantIndex::build(p.error.as_ref().map(|e| e.ty.id), &types);
let event_variant_index =
VariantIndex::build(p.event.as_ref().map(|e| e.ty.id), &types);
let call_variant_index = VariantIndex::build(p.calls.as_ref().map(|c| c.ty.id), types);
let error_variant_index = VariantIndex::build(p.error.as_ref().map(|e| e.ty.id), types);
let event_variant_index = VariantIndex::build(p.event.as_ref().map(|e| e.ty.id), types);
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 +90,50 @@ 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 +155,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 +199,11 @@ 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 {
fn from_constant_metadata(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,
}
}
+251 -155
View File
@@ -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::constants::{Constant, ConstantInfo, ConstantInfoError};
use frame_decode::custom_values::{CustomValue, CustomValueInfo, CustomValueInfoError};
use frame_decode::extrinsics::{
ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicInfoArg, ExtrinsicInfoError,
ExtrinsicSignatureInfo,
};
use frame_decode::runtime_apis::{
RuntimeApi, RuntimeApiInfo, RuntimeApiInfoError, RuntimeApiInput,
};
use frame_decode::storage::{StorageEntry, StorageInfo, StorageInfoError, StorageKeyInfo};
use frame_decode::view_functions::{
ViewFunction, ViewFunctionInfo, ViewFunctionInfoError, ViewFunctionInput,
};
use hashbrown::HashMap;
use scale_info::{PortableRegistry, Variant, form::PortableForm};
use utils::{
@@ -39,8 +48,7 @@ use utils::{
variant_index::VariantIndex,
};
type ArcStr = Arc<str>;
pub use frame_decode::storage::StorageHasher;
pub use from::SUPPORTED_METADATA_VERSIONS;
pub use from::TryFromError;
pub use utils::validation::MetadataHasher;
@@ -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,8 +150,203 @@ 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()
.and_then(|storage| storage.entry_by_name(storage_entry))
.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 frame_decode::constants::ConstantTypeInfo for Metadata {
type TypeId = u32;
fn constant_info(
&self,
pallet_name: &str,
constant_name: &str,
) -> Result<ConstantInfo<'_, Self::TypeId>, ConstantInfoError<'_>> {
let pallet =
self.pallet_by_name(pallet_name)
.ok_or_else(|| ConstantInfoError::PalletNotFound {
pallet_name: pallet_name.to_string(),
})?;
let constant = pallet.constant_by_name(constant_name).ok_or_else(|| {
ConstantInfoError::ConstantNotFound {
pallet_name: Cow::Borrowed(pallet.name()),
constant_name: constant_name.to_string(),
}
})?;
let info = ConstantInfo {
bytes: &constant.value,
type_id: constant.ty,
};
Ok(info)
}
fn constants(&self) -> impl Iterator<Item = Constant<'_>> {
self.pallets().flat_map(|pallet| {
let pallet_name = pallet.name();
pallet.constants().map(|constant| Constant {
pallet_name: Cow::Borrowed(pallet_name),
constant_name: Cow::Borrowed(constant.name()),
})
})
}
}
impl frame_decode::custom_values::CustomValueTypeInfo for Metadata {
type TypeId = u32;
fn custom_value_info(
&self,
name: &str,
) -> Result<CustomValueInfo<'_, Self::TypeId>, CustomValueInfoError> {
let custom_value = self
.custom()
.get(name)
.ok_or_else(|| CustomValueInfoError {
not_found: name.to_string(),
})?;
let info = CustomValueInfo {
bytes: custom_value.data,
type_id: custom_value.type_id,
};
Ok(info)
}
fn custom_values(&self) -> impl Iterator<Item = CustomValue<'_>> {
self.custom.map.keys().map(|name| CustomValue {
name: Cow::Borrowed(name),
})
}
}
impl Metadata {
/// This is essentiall an alias for `<Metadata as codec::Decode>::decode(&mut bytes)`
pub fn decode_from(mut bytes: &[u8]) -> Result<Self, codec::Error> {
<Self as codec::Decode>::decode(&mut bytes)
}
/// Access the underlying type registry.
pub fn types(&self) -> &PortableRegistry {
&self.types
@@ -217,20 +420,6 @@ impl Metadata {
})
}
/// Access a view function given its query ID, if any.
pub fn view_function_by_query_id(
&'_ self,
query_id: &[u8; 32],
) -> Option<ViewFunctionMetadata<'_>> {
// Dev note: currently, we only have pallet view functions, and here
// we just do a naive thing of iterating over the pallets to find the one
// we're looking for. Eventually we should construct a separate map of view
// functions for easy querying here.
self.pallets()
.flat_map(|p| p.view_functions())
.find(|vf| vf.query_id() == query_id)
}
/// Returns custom user defined types
pub fn custom(&self) -> CustomMetadata<'_> {
CustomMetadata {
@@ -418,7 +607,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 +625,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 +640,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 +664,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 +676,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()
}
/// Type of the storage entry.
pub fn entry_type(&self) -> &StorageEntryType {
&self.entry_type
/// Value type for this storage entry.
pub fn value_ty(&self) -> u32 {
self.info.value_id
}
/// 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 +695,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 +912,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 +937,15 @@ 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 +956,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 +979,22 @@ 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 +1007,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>,
}
+13 -24
View File
@@ -6,8 +6,7 @@
use crate::{
CustomMetadata, CustomValueMetadata, ExtrinsicMetadata, Metadata, PalletMetadata,
RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, StorageEntryType,
ViewFunctionMetadata,
RuntimeApiMetadata, RuntimeApiMethodMetadata, StorageEntryMetadata, ViewFunctionMetadata,
};
use alloc::vec::Vec;
use hashbrown::HashMap;
@@ -290,29 +289,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_deref().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 +371,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 +408,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),
);
}