mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 17:41:01 +00:00
custom values and runtime apis
This commit is contained in:
@@ -37,7 +37,7 @@ pub fn validate_constant<Address: ConstantAddress>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn access_constant<Address: ConstantAddress>(
|
pub fn get_constant<Address: ConstantAddress>(
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
address: &Address,
|
address: &Address,
|
||||||
) -> Result<Address::Target, Error> {
|
) -> Result<Address::Target, Error> {
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
use derivative::Derivative;
|
||||||
|
|
||||||
|
use crate::dynamic::DecodedValueThunk;
|
||||||
|
use crate::metadata::DecodeWithMetadata;
|
||||||
|
|
||||||
|
/// This represents the address of a custom value in in the metadata.
|
||||||
|
/// Anything, that implements the [CustomValueAddress] trait can be used, to fetch
|
||||||
|
/// custom values from the metadata.
|
||||||
|
/// The trait is implemented by [str] for dynamic loopup and [StaticAddress] for static queries.
|
||||||
|
pub trait CustomValueAddress {
|
||||||
|
/// The type of the custom value.
|
||||||
|
type Target: DecodeWithMetadata;
|
||||||
|
/// Should be set to `Yes` for Dynamic values and static values that have a valid type.
|
||||||
|
/// Should be `()` for custom values, that have an invalid type id.
|
||||||
|
type IsDecodable;
|
||||||
|
|
||||||
|
/// the name (key) by which the custom value can be accessed in the metadata.
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
|
||||||
|
/// An optional hash which, if present, can be checked against node metadata.
|
||||||
|
fn validation_hash(&self) -> Option<[u8; 32]> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomValueAddress for str {
|
||||||
|
type Target = DecodedValueThunk;
|
||||||
|
type IsDecodable = Yes;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to signal whether a [`CustomValueAddress`] can be decoded.
|
||||||
|
pub struct Yes;
|
||||||
|
|
||||||
|
/// A static address to a custom value.
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(Clone(bound = ""), Debug(bound = ""))]
|
||||||
|
pub struct StaticAddress<ReturnTy, IsDecodable> {
|
||||||
|
name: &'static str,
|
||||||
|
hash: Option<[u8; 32]>,
|
||||||
|
phantom: core::marker::PhantomData<(ReturnTy, IsDecodable)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ReturnTy, IsDecodable> StaticAddress<ReturnTy, IsDecodable> {
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Creates a new StaticAddress.
|
||||||
|
pub fn new_static(name: &'static str, hash: [u8; 32]) -> StaticAddress<ReturnTy, IsDecodable> {
|
||||||
|
StaticAddress::<ReturnTy, IsDecodable> {
|
||||||
|
name,
|
||||||
|
hash: Some(hash),
|
||||||
|
phantom: core::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do not validate this custom value prior to accessing it.
|
||||||
|
pub fn unvalidated(self) -> Self {
|
||||||
|
Self {
|
||||||
|
name: self.name,
|
||||||
|
hash: None,
|
||||||
|
phantom: self.phantom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: DecodeWithMetadata, Y> CustomValueAddress for StaticAddress<R, Y> {
|
||||||
|
type Target = R;
|
||||||
|
type IsDecodable = Y;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validation_hash(&self) -> Option<[u8; 32]> {
|
||||||
|
self.hash
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
|
// see LICENSE for license details.
|
||||||
|
|
||||||
|
//! Types associated with accessing custom types
|
||||||
|
|
||||||
|
mod custom_value_address;
|
||||||
|
|
||||||
|
pub use custom_value_address::{CustomValueAddress, StaticAddress, Yes};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
metadata::{DecodeWithMetadata, MetadatExt},
|
||||||
|
Error, Metadata, MetadataError,
|
||||||
|
};
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
/// Run the validation logic against some custom value 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).
|
||||||
|
/// Returns an error if the address was not valid (wrong name, type or raw bytes)
|
||||||
|
pub fn validate_custom_value<Address: CustomValueAddress + ?Sized>(
|
||||||
|
metadata: &Metadata,
|
||||||
|
address: &Address,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if let Some(actual_hash) = address.validation_hash() {
|
||||||
|
let custom = metadata.custom();
|
||||||
|
let custom_value = custom
|
||||||
|
.get(address.name())
|
||||||
|
.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().into()))?;
|
||||||
|
let expected_hash = custom_value.hash();
|
||||||
|
if actual_hash != expected_hash {
|
||||||
|
return Err(MetadataError::IncompatibleCodegen.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if metadata.custom().get(address.name()).is_none() {
|
||||||
|
return Err(MetadataError::IncompatibleCodegen.into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access a custom value by the address it is registered under. This can be just a [str] to get back a dynamic value,
|
||||||
|
/// or a static address from the generated static interface to get a value of a static type returned.
|
||||||
|
pub fn get_custom_value<Address: CustomValueAddress<IsDecodable = Yes> + ?Sized>(
|
||||||
|
metadata: &Metadata,
|
||||||
|
address: &Address,
|
||||||
|
) -> Result<Address::Target, Error> {
|
||||||
|
// 1. Validate custom value shape if hash given:
|
||||||
|
validate_custom_value(metadata, address)?;
|
||||||
|
|
||||||
|
// 2. Attempt to decode custom value:
|
||||||
|
let custom_value = metadata.custom_value_by_name_err(address.name())?;
|
||||||
|
let value = <Address::Target as DecodeWithMetadata>::decode_with_metadata(
|
||||||
|
&mut custom_value.bytes(),
|
||||||
|
custom_value.type_id(),
|
||||||
|
&metadata,
|
||||||
|
)?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the bytes of a custom value by the address it is registered under.
|
||||||
|
pub fn get_custom_value_bytes<Address: CustomValueAddress + ?Sized>(
|
||||||
|
metadata: &Metadata,
|
||||||
|
address: &Address,
|
||||||
|
) -> Result<Vec<u8>, Error> {
|
||||||
|
// 1. Validate custom value shape if hash given:
|
||||||
|
validate_custom_value(metadata, address)?;
|
||||||
|
|
||||||
|
// 2. Return the underlying bytes:
|
||||||
|
let custom_value = metadata.custom_value_by_name_err(address.name())?;
|
||||||
|
Ok(custom_value.bytes().to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use alloc::collections::BTreeMap;
|
||||||
|
use codec::Encode;
|
||||||
|
use scale_decode::DecodeAsType;
|
||||||
|
use scale_info::form::PortableForm;
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
|
||||||
|
use crate::custom_values::get_custom_value;
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Encode, TypeInfo, DecodeAsType)]
|
||||||
|
pub struct Person {
|
||||||
|
age: u16,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mock_metadata() -> Metadata {
|
||||||
|
let person_ty = scale_info::MetaType::new::<Person>();
|
||||||
|
let unit = scale_info::MetaType::new::<()>();
|
||||||
|
let mut types = scale_info::Registry::new();
|
||||||
|
let person_ty_id = types.register_type(&person_ty);
|
||||||
|
let unit_id = types.register_type(&unit);
|
||||||
|
let types: scale_info::PortableRegistry = types.into();
|
||||||
|
|
||||||
|
let person = Person {
|
||||||
|
age: 42,
|
||||||
|
name: "Neo".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let person_value_metadata: frame_metadata::v15::CustomValueMetadata<PortableForm> =
|
||||||
|
frame_metadata::v15::CustomValueMetadata {
|
||||||
|
ty: person_ty_id,
|
||||||
|
value: person.encode(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let frame_metadata = frame_metadata::v15::RuntimeMetadataV15 {
|
||||||
|
types,
|
||||||
|
pallets: vec![],
|
||||||
|
extrinsic: frame_metadata::v15::ExtrinsicMetadata {
|
||||||
|
version: 0,
|
||||||
|
address_ty: unit_id,
|
||||||
|
call_ty: unit_id,
|
||||||
|
signature_ty: unit_id,
|
||||||
|
extra_ty: unit_id,
|
||||||
|
signed_extensions: vec![],
|
||||||
|
},
|
||||||
|
ty: unit_id,
|
||||||
|
apis: vec![],
|
||||||
|
outer_enums: frame_metadata::v15::OuterEnums {
|
||||||
|
call_enum_ty: unit_id,
|
||||||
|
event_enum_ty: unit_id,
|
||||||
|
error_enum_ty: unit_id,
|
||||||
|
},
|
||||||
|
custom: frame_metadata::v15::CustomMetadata {
|
||||||
|
map: BTreeMap::from_iter([("Mr. Robot".to_string(), person_value_metadata)]),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let metadata: subxt_metadata::Metadata = frame_metadata.try_into().unwrap();
|
||||||
|
Metadata::new(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decoding() {
|
||||||
|
let metadata = mock_metadata();
|
||||||
|
|
||||||
|
assert!(get_custom_value(&metadata, "Invalid Address").is_err());
|
||||||
|
let person_decoded_value_thunk = get_custom_value(&metadata, "Mr. Robot").unwrap();
|
||||||
|
let person: Person = person_decoded_value_thunk.as_type().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
person,
|
||||||
|
Person {
|
||||||
|
age: 42,
|
||||||
|
name: "Neo".into()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -16,17 +16,17 @@ pub use scale_value::{At, Value};
|
|||||||
/// for dynamic requests.
|
/// for dynamic requests.
|
||||||
pub type DecodedValue = scale_value::Value<scale_value::scale::TypeId>;
|
pub type DecodedValue = scale_value::Value<scale_value::scale::TypeId>;
|
||||||
|
|
||||||
// // Submit dynamic transactions.
|
/// Submit dynamic transactions.
|
||||||
pub use crate::tx::dynamic as tx;
|
pub use crate::tx::dynamic as tx;
|
||||||
|
|
||||||
// // Lookup constants dynamically.
|
/// Lookup constants dynamically.
|
||||||
pub use crate::constants::dynamic as constant;
|
pub use crate::constants::dynamic as constant;
|
||||||
|
|
||||||
// // Lookup storage values dynamically.
|
/// Lookup storage values dynamically.
|
||||||
pub use crate::storage::dynamic as storage;
|
pub use crate::storage::dynamic as storage;
|
||||||
|
|
||||||
// // Execute runtime API function call dynamically.
|
/// Execute runtime API function call dynamically.
|
||||||
// pub use crate::runtime_api::dynamic as runtime_api_call;
|
pub use crate::runtime_api::dynamic as runtime_api_call;
|
||||||
|
|
||||||
/// This is the result of making a dynamic request to a node. From this,
|
/// This is the result of making a dynamic request to a node. From this,
|
||||||
/// we can return the raw SCALE bytes that we were handed back, or we can
|
/// we can return the raw SCALE bytes that we were handed back, or we can
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ extern crate alloc;
|
|||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod custom_values;
|
||||||
pub mod dynamic;
|
pub mod dynamic;
|
||||||
mod error;
|
mod error;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
|
pub mod runtime_api;
|
||||||
pub mod signer;
|
pub mod signer;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub mod tx;
|
pub mod tx;
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ pub trait MetadatExt {
|
|||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError>;
|
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError>;
|
||||||
|
|
||||||
|
fn custom_value_by_name_err(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<subxt_metadata::CustomValueMetadata, MetadataError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetadatExt for subxt_metadata::Metadata {
|
impl MetadatExt for subxt_metadata::Metadata {
|
||||||
@@ -71,6 +76,16 @@ impl MetadatExt for subxt_metadata::Metadata {
|
|||||||
self.runtime_api_trait_by_name(name)
|
self.runtime_api_trait_by_name(name)
|
||||||
.ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned()))
|
.ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Identical to `metadata.runtime_api_trait_by_name()`, but returns an error if the trait is not found.
|
||||||
|
fn custom_value_by_name_err(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<subxt_metadata::CustomValueMetadata, MetadataError> {
|
||||||
|
self.custom()
|
||||||
|
.get(name)
|
||||||
|
.ok_or_else(|| MetadataError::CustomValueNameNotFound(name.to_owned()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<subxt_metadata::Metadata> for Metadata {
|
impl From<subxt_metadata::Metadata> for Metadata {
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||||
|
// see LICENSE for license details.
|
||||||
|
|
||||||
|
use alloc::borrow::Cow;
|
||||||
|
use alloc::borrow::ToOwned;
|
||||||
|
use alloc::string::String;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use derivative::Derivative;
|
||||||
|
use scale_encode::EncodeAsFields;
|
||||||
|
use scale_value::Composite;
|
||||||
|
|
||||||
|
use crate::dynamic::DecodedValueThunk;
|
||||||
|
use crate::error::MetadataError;
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
use crate::metadata::{DecodeWithMetadata, MetadatExt, Metadata};
|
||||||
|
|
||||||
|
/// This represents a runtime API payload that can call into the runtime of node.
|
||||||
|
///
|
||||||
|
/// # Components
|
||||||
|
///
|
||||||
|
/// - associated return type
|
||||||
|
///
|
||||||
|
/// Resulting bytes of the call are interpreted into this type.
|
||||||
|
///
|
||||||
|
/// - runtime function name
|
||||||
|
///
|
||||||
|
/// The function name of the runtime API call. This is obtained by concatenating
|
||||||
|
/// the runtime trait name with the trait's method.
|
||||||
|
///
|
||||||
|
/// For example, the substrate runtime trait [Metadata](https://github.com/paritytech/substrate/blob/cb954820a8d8d765ce75021e244223a3b4d5722d/primitives/api/src/lib.rs#L745)
|
||||||
|
/// contains the `metadata_at_version` function. The corresponding runtime function
|
||||||
|
/// is `Metadata_metadata_at_version`.
|
||||||
|
///
|
||||||
|
/// - encoded arguments
|
||||||
|
///
|
||||||
|
/// Each argument of the runtime function must be scale-encoded.
|
||||||
|
pub trait RuntimeApiPayload {
|
||||||
|
/// The return type of the function call.
|
||||||
|
// Note: `DecodeWithMetadata` is needed to decode the function call result
|
||||||
|
// with the `subxt::Metadata.
|
||||||
|
type ReturnType: DecodeWithMetadata;
|
||||||
|
|
||||||
|
/// The runtime API trait name.
|
||||||
|
fn trait_name(&self) -> &str;
|
||||||
|
|
||||||
|
/// The runtime API method name.
|
||||||
|
fn method_name(&self) -> &str;
|
||||||
|
|
||||||
|
/// Scale encode the arguments data.
|
||||||
|
fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Encode arguments data and return the output. This is a convenience
|
||||||
|
/// wrapper around [`RuntimeApiPayload::encode_args_to`].
|
||||||
|
fn encode_args(&self, metadata: &Metadata) -> Result<Vec<u8>, Error> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
self.encode_args_to(metadata, &mut v)?;
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the statically generated validation hash.
|
||||||
|
fn validation_hash(&self) -> Option<[u8; 32]> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A runtime API payload containing the generic argument data
|
||||||
|
/// and interpreting the result of the call as `ReturnTy`.
|
||||||
|
///
|
||||||
|
/// This can be created from static values (ie those generated
|
||||||
|
/// via the `subxt` macro) or dynamic values via [`dynamic`].
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(
|
||||||
|
Clone(bound = "ArgsData: Clone"),
|
||||||
|
Debug(bound = "ArgsData: core::fmt::Debug")
|
||||||
|
)]
|
||||||
|
pub struct Payload<ArgsData, ReturnTy> {
|
||||||
|
trait_name: Cow<'static, str>,
|
||||||
|
method_name: Cow<'static, str>,
|
||||||
|
args_data: ArgsData,
|
||||||
|
validation_hash: Option<[u8; 32]>,
|
||||||
|
_marker: PhantomData<ReturnTy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ArgsData: EncodeAsFields, ReturnTy: DecodeWithMetadata> RuntimeApiPayload
|
||||||
|
for Payload<ArgsData, ReturnTy>
|
||||||
|
{
|
||||||
|
type ReturnType = ReturnTy;
|
||||||
|
|
||||||
|
fn trait_name(&self) -> &str {
|
||||||
|
&self.trait_name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_name(&self) -> &str {
|
||||||
|
&self.method_name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
|
||||||
|
let api_method = metadata
|
||||||
|
.runtime_api_trait_by_name_err(&self.trait_name)?
|
||||||
|
.method_by_name(&self.method_name)
|
||||||
|
.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));
|
||||||
|
|
||||||
|
self.args_data
|
||||||
|
.encode_as_fields_to(&mut fields, metadata.types(), out)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validation_hash(&self) -> Option<[u8; 32]> {
|
||||||
|
self.validation_hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A dynamic runtime API payload.
|
||||||
|
pub type DynamicRuntimeApiPayload = Payload<Composite<()>, DecodedValueThunk>;
|
||||||
|
|
||||||
|
impl<ReturnTy, ArgsData> Payload<ArgsData, ReturnTy> {
|
||||||
|
/// Create a new [`Payload`].
|
||||||
|
pub fn new(
|
||||||
|
trait_name: impl Into<String>,
|
||||||
|
method_name: impl Into<String>,
|
||||||
|
args_data: ArgsData,
|
||||||
|
) -> Self {
|
||||||
|
Payload {
|
||||||
|
trait_name: Cow::Owned(trait_name.into()),
|
||||||
|
method_name: Cow::Owned(method_name.into()),
|
||||||
|
args_data,
|
||||||
|
validation_hash: None,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new static [`Payload`] using static function name
|
||||||
|
/// and scale-encoded argument data.
|
||||||
|
///
|
||||||
|
/// This is only expected to be used from codegen.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn new_static(
|
||||||
|
trait_name: &'static str,
|
||||||
|
method_name: &'static str,
|
||||||
|
args_data: ArgsData,
|
||||||
|
hash: [u8; 32],
|
||||||
|
) -> Payload<ArgsData, ReturnTy> {
|
||||||
|
Payload {
|
||||||
|
trait_name: Cow::Borrowed(trait_name),
|
||||||
|
method_name: Cow::Borrowed(method_name),
|
||||||
|
args_data,
|
||||||
|
validation_hash: Some(hash),
|
||||||
|
_marker: core::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do not validate this call prior to submitting it.
|
||||||
|
pub fn unvalidated(self) -> Self {
|
||||||
|
Self {
|
||||||
|
validation_hash: None,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the trait name.
|
||||||
|
pub fn trait_name(&self) -> &str {
|
||||||
|
&self.trait_name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the method name.
|
||||||
|
pub fn method_name(&self) -> &str {
|
||||||
|
&self.method_name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the arguments data.
|
||||||
|
pub fn args_data(&self) -> &ArgsData {
|
||||||
|
&self.args_data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new [`DynamicRuntimeApiPayload`].
|
||||||
|
pub fn dynamic(
|
||||||
|
trait_name: impl Into<String>,
|
||||||
|
method_name: impl Into<String>,
|
||||||
|
args_data: impl Into<Composite<()>>,
|
||||||
|
) -> DynamicRuntimeApiPayload {
|
||||||
|
Payload::new(trait_name, method_name, args_data.into())
|
||||||
|
}
|
||||||
+1
-93
@@ -7,100 +7,8 @@
|
|||||||
/// Types representing an address which describes where a storage
|
/// Types representing an address which describes where a storage
|
||||||
/// entry lives and how to properly decode it.
|
/// entry lives and how to properly decode it.
|
||||||
pub mod storage_address;
|
pub mod storage_address;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
// For consistency with other modules, also expose
|
// For consistency with other modules, also expose
|
||||||
// the basic address stuff at the root of the module.
|
// the basic address stuff at the root of the module.
|
||||||
pub use storage_address::{dynamic, Address, DynamicAddress, StorageAddress};
|
pub use storage_address::{dynamic, Address, DynamicAddress, StorageAddress};
|
||||||
|
|
||||||
pub mod utils {
|
|
||||||
use crate::{
|
|
||||||
metadata::{DecodeWithMetadata, MetadatExt},
|
|
||||||
Error, Metadata, MetadataError,
|
|
||||||
};
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use subxt_metadata::{PalletMetadata, StorageEntryMetadata};
|
|
||||||
|
|
||||||
use super::StorageAddress;
|
|
||||||
/// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name
|
|
||||||
/// and append those bytes to the output.
|
|
||||||
pub fn write_storage_address_root_bytes<Address: StorageAddress>(
|
|
||||||
addr: &Address,
|
|
||||||
out: &mut Vec<u8>,
|
|
||||||
) {
|
|
||||||
out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes()));
|
|
||||||
out.extend(sp_core_hashing::twox_128(addr.entry_name().as_bytes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outputs the [`storage_address_root_bytes`] as well as any additional bytes that represent
|
|
||||||
/// a lookup in a storage map at that location.
|
|
||||||
pub fn storage_address_bytes<Address: StorageAddress>(
|
|
||||||
addr: &Address,
|
|
||||||
metadata: &Metadata,
|
|
||||||
) -> Result<Vec<u8>, Error> {
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
write_storage_address_root_bytes(addr, &mut bytes);
|
|
||||||
addr.append_entry_bytes(metadata, &mut bytes)?;
|
|
||||||
Ok(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outputs a vector containing the bytes written by [`write_storage_address_root_bytes`].
|
|
||||||
pub fn storage_address_root_bytes<Address: StorageAddress>(addr: &Address) -> Vec<u8> {
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
write_storage_address_root_bytes(addr, &mut bytes);
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return details about the given storage entry.
|
|
||||||
pub fn lookup_entry_details<'a>(
|
|
||||||
pallet_name: &str,
|
|
||||||
entry_name: &str,
|
|
||||||
metadata: &'a subxt_metadata::Metadata,
|
|
||||||
) -> Result<(PalletMetadata<'a>, &'a StorageEntryMetadata), Error> {
|
|
||||||
let pallet_metadata = metadata.pallet_by_name_err(pallet_name)?;
|
|
||||||
let storage_metadata = pallet_metadata
|
|
||||||
.storage()
|
|
||||||
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(pallet_name.to_owned()))?;
|
|
||||||
let storage_entry = storage_metadata
|
|
||||||
.entry_by_name(entry_name)
|
|
||||||
.ok_or_else(|| MetadataError::StorageEntryNotFound(entry_name.to_owned()))?;
|
|
||||||
Ok((pallet_metadata, storage_entry))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate a storage address against the metadata.
|
|
||||||
pub fn validate_storage_address<Address: StorageAddress>(
|
|
||||||
address: &Address,
|
|
||||||
pallet: PalletMetadata<'_>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
if let Some(hash) = address.validation_hash() {
|
|
||||||
validate_storage(pallet, address.entry_name(), hash)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate a storage entry against the metadata.
|
|
||||||
pub fn validate_storage(
|
|
||||||
pallet: PalletMetadata<'_>,
|
|
||||||
storage_name: &str,
|
|
||||||
hash: [u8; 32],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let Some(expected_hash) = pallet.storage_hash(storage_name) else {
|
|
||||||
return Err(MetadataError::IncompatibleCodegen.into());
|
|
||||||
};
|
|
||||||
if expected_hash != hash {
|
|
||||||
return Err(MetadataError::IncompatibleCodegen.into());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given some bytes, a pallet and storage name, decode the response.
|
|
||||||
pub fn decode_storage_with_metadata<T: DecodeWithMetadata>(
|
|
||||||
bytes: &mut &[u8],
|
|
||||||
metadata: &Metadata,
|
|
||||||
storage_metadata: &StorageEntryMetadata,
|
|
||||||
) -> Result<T, Error> {
|
|
||||||
let return_ty = storage_metadata.entry_type().value_ty();
|
|
||||||
let val = T::decode_with_metadata(bytes, return_ty, metadata)?;
|
|
||||||
Ok(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
use crate::{
|
||||||
|
metadata::{DecodeWithMetadata, MetadatExt},
|
||||||
|
Error, Metadata, MetadataError,
|
||||||
|
};
|
||||||
|
use alloc::borrow::ToOwned;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use subxt_metadata::{PalletMetadata, StorageEntryMetadata};
|
||||||
|
|
||||||
|
use super::StorageAddress;
|
||||||
|
/// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name
|
||||||
|
/// and append those bytes to the output.
|
||||||
|
pub fn write_storage_address_root_bytes<Address: StorageAddress>(
|
||||||
|
addr: &Address,
|
||||||
|
out: &mut Vec<u8>,
|
||||||
|
) {
|
||||||
|
out.extend(sp_core_hashing::twox_128(addr.pallet_name().as_bytes()));
|
||||||
|
out.extend(sp_core_hashing::twox_128(addr.entry_name().as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Outputs the [`storage_address_root_bytes`] as well as any additional bytes that represent
|
||||||
|
/// a lookup in a storage map at that location.
|
||||||
|
pub fn storage_address_bytes<Address: StorageAddress>(
|
||||||
|
addr: &Address,
|
||||||
|
metadata: &Metadata,
|
||||||
|
) -> Result<Vec<u8>, Error> {
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
write_storage_address_root_bytes(addr, &mut bytes);
|
||||||
|
addr.append_entry_bytes(metadata, &mut bytes)?;
|
||||||
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Outputs a vector containing the bytes written by [`write_storage_address_root_bytes`].
|
||||||
|
pub fn storage_address_root_bytes<Address: StorageAddress>(addr: &Address) -> Vec<u8> {
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
write_storage_address_root_bytes(addr, &mut bytes);
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return details about the given storage entry.
|
||||||
|
pub fn lookup_entry_details<'a>(
|
||||||
|
pallet_name: &str,
|
||||||
|
entry_name: &str,
|
||||||
|
metadata: &'a subxt_metadata::Metadata,
|
||||||
|
) -> Result<(PalletMetadata<'a>, &'a StorageEntryMetadata), Error> {
|
||||||
|
let pallet_metadata = metadata.pallet_by_name_err(pallet_name)?;
|
||||||
|
let storage_metadata = pallet_metadata
|
||||||
|
.storage()
|
||||||
|
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(pallet_name.to_owned()))?;
|
||||||
|
let storage_entry = storage_metadata
|
||||||
|
.entry_by_name(entry_name)
|
||||||
|
.ok_or_else(|| MetadataError::StorageEntryNotFound(entry_name.to_owned()))?;
|
||||||
|
Ok((pallet_metadata, storage_entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate a storage address against the metadata.
|
||||||
|
pub fn validate_storage_address<Address: StorageAddress>(
|
||||||
|
address: &Address,
|
||||||
|
pallet: PalletMetadata<'_>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if let Some(hash) = address.validation_hash() {
|
||||||
|
validate_storage(pallet, address.entry_name(), hash)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate a storage entry against the metadata.
|
||||||
|
pub fn validate_storage(
|
||||||
|
pallet: PalletMetadata<'_>,
|
||||||
|
storage_name: &str,
|
||||||
|
hash: [u8; 32],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let Some(expected_hash) = pallet.storage_hash(storage_name) else {
|
||||||
|
return Err(MetadataError::IncompatibleCodegen.into());
|
||||||
|
};
|
||||||
|
if expected_hash != hash {
|
||||||
|
return Err(MetadataError::IncompatibleCodegen.into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given some bytes, a pallet and storage name, decode the response.
|
||||||
|
pub fn decode_storage_with_metadata<T: DecodeWithMetadata>(
|
||||||
|
bytes: &mut &[u8],
|
||||||
|
metadata: &Metadata,
|
||||||
|
storage_metadata: &StorageEntryMetadata,
|
||||||
|
) -> Result<T, Error> {
|
||||||
|
let return_ty = storage_metadata.entry_type().value_ty();
|
||||||
|
let val = T::decode_with_metadata(bytes, return_ty, metadata)?;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user