diff --git a/core/src/error.rs b/core/src/error.rs index 9517b30856..c4eaf51ca6 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -131,7 +131,7 @@ pub enum MetadataError { } /// Something went wrong trying to encode or decode a storage address. -#[derive(Clone, Debug, DeriveError)] +#[derive(Debug, DeriveError)] #[non_exhaustive] pub enum StorageAddressError { /// Storage lookup does not have the expected number of keys. @@ -169,6 +169,15 @@ pub enum StorageAddressError { /// The invalid hasher that caused this error. hasher: StorageHasher, }, + /// Cannot obtain storage information from metadata + #[error("Cannot obtain storage information from metadata: {0}")] + StorageInfoError(frame_decode::storage::StorageInfoError<'static>), + /// Cannot decode storage value + #[error("Cannot decode storage value: {0}")] + StorageValueDecodeError(frame_decode::storage::StorageValueDecodeError), + /// Cannot encode storage key + #[error("Cannot encode storage key: {0}")] + StorageKeyEncodeError(frame_decode::storage::StorageKeyEncodeError), } /// An error that can be encountered when constructing a transaction. diff --git a/core/src/storage/address.rs b/core/src/storage/address.rs index b680d7f1e2..433caf8704 100644 --- a/core/src/storage/address.rs +++ b/core/src/storage/address.rs @@ -4,18 +4,10 @@ //! Construct addresses to access storage entries with. -use crate::{ - dynamic::DecodedValueThunk, - error::{Error, MetadataError}, - 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::borrow::Cow; use alloc::vec::Vec; +use frame_decode::storage::{IntoDecodableValues, IntoEncodableValues}; +use scale_decode::DecodeAsType; /// A storage address. Concrete addresses are expected to implement either [`FetchableAddress`] /// or [`IterableAddress`], which extends this to define fetchable and iterable storage keys. @@ -31,19 +23,21 @@ pub trait Address { /// The name of the storage entry. fn entry_name(&self) -> &str; - /// Encode the suffix of the storage key for this address - fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error>; + /// Return the input key parts needed to point to this storage entry / entries. + fn key_parts(&self) -> impl IntoEncodableValues; /// Return a unique hash for this address which can be used to validate it against metadata. fn validation_hash(&self) -> Option<[u8; 32]>; } +/// This trait represents any storage address which points to a single value we can fetch. pub trait FetchableAddress: Address { - /// Does the address have a default value defined for it. + /// Does the address have a default value defined for it. /// Set to [`Yes`] to enable APIs which require one. type HasDefaultValue; } +/// This trait represents any storage address which points to multiple 0 or more values to iterate over. pub trait IterableAddress: Address { /// The storage key values that we'll decode for each value type OutputKeys: IntoDecodableValues; @@ -55,10 +49,10 @@ pub struct StaticFetchableAddress { entry_name: Cow<'static, str>, key_parts: KeyParts, validation_hash: Option<[u8; 32]>, - marker: core::marker::PhantomData<(Value, HasDefaultValue)> + marker: core::marker::PhantomData<(Value, HasDefaultValue)>, } -impl StaticFetchableAddress { +impl StaticFetchableAddress { /// Create a new [`StaticFetchableAddress`] using static strings for the pallet and call name. /// This is only expected to be used from codegen. #[doc(hidden)] @@ -99,22 +93,17 @@ impl StaticFetchableAddress Address for StaticFetchableAddress +impl Address + for StaticFetchableAddress where KeyParts: IntoEncodableValues, - Value: DecodeAsType + Value: DecodeAsType, { type KeyParts = KeyParts; type Value = Value; - - fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error> { - frame_decode::storage::encode_storage_key_suffix( - &self.pallet_name, - &self.entry_name, - &self.key_parts, - metadata.types(), - metadata - ).map_err(Into::into) + + fn key_parts(&self) -> impl IntoEncodableValues { + &self.key_parts } fn pallet_name(&self) -> &str { @@ -130,10 +119,11 @@ where } } -impl FetchableAddress for StaticFetchableAddress +impl FetchableAddress + for StaticFetchableAddress where KeyParts: IntoEncodableValues, - Value: DecodeAsType + Value: DecodeAsType, { type HasDefaultValue = HasDefaultValue; } @@ -144,10 +134,12 @@ pub struct StaticIterableAddress { entry_name: Cow<'static, str>, input_key_parts: InputKeyParts, validation_hash: Option<[u8; 32]>, - marker: core::marker::PhantomData<(OutputKeyParts, Value)> + marker: core::marker::PhantomData<(OutputKeyParts, Value)>, } -impl StaticIterableAddress { +impl + StaticIterableAddress +{ /// Create a new [`StaticIterableAddress`] using static strings for the pallet and call name. /// This is only expected to be used from codegen. #[doc(hidden)] @@ -188,22 +180,17 @@ impl StaticIterableAddress Address for StaticIterableAddress +impl Address + for StaticIterableAddress where InputKeyParts: IntoEncodableValues, - Value: DecodeAsType + Value: DecodeAsType, { type KeyParts = InputKeyParts; type Value = Value; - - fn encode_key_suffix(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error> { - frame_decode::storage::encode_storage_key_suffix( - &self.pallet_name, - &self.entry_name, - &self.input_key_parts, - metadata.types(), - metadata - ).map_err(Into::into) + + fn key_parts(&self) -> impl IntoEncodableValues { + &self.input_key_parts } fn pallet_name(&self) -> &str { @@ -219,140 +206,38 @@ where } } -impl IterableAddress for StaticIterableAddress +impl IterableAddress + for StaticIterableAddress where InputKeyParts: IntoEncodableValues, OutputKeyParts: IntoDecodableValues, - Value: DecodeAsType + 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)] -pub struct DefaultAddress { - pallet_name: Cow<'static, str>, - entry_name: Cow<'static, str>, - keys: Keys, - validation_hash: Option<[u8; 32]>, - _marker: core::marker::PhantomData<(ReturnTy, Fetchable, Defaultable, Iterable)>, -} - -/// A storage address constructed by the static codegen. -pub type StaticAddress = - DefaultAddress; -/// A typical storage address constructed at runtime rather than via the `subxt` macro; this -/// has no restriction on what it can be used for (since we don't statically know). -pub type DynamicAddress = DefaultAddress; - -impl DynamicAddress { - /// Creates a new dynamic address. As `Keys` you can use a `Vec` - pub fn new(pallet_name: impl Into, entry_name: impl Into, keys: Keys) -> Self { - Self { - pallet_name: Cow::Owned(pallet_name.into()), - entry_name: Cow::Owned(entry_name.into()), - keys, - validation_hash: None, - _marker: core::marker::PhantomData, - } - } -} - -impl - DefaultAddress -where - Keys: StorageKey, - ReturnTy: DecodeWithMetadata, -{ - /// Create a new [`Address`] 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, - keys: Keys, - hash: [u8; 32], - ) -> Self { - Self { - pallet_name: Cow::Borrowed(pallet_name), - entry_name: Cow::Borrowed(entry_name), - keys, - validation_hash: Some(hash), - _marker: core::marker::PhantomData, - } - } -} - -impl - DefaultAddress -where - Keys: StorageKey, - ReturnTy: DecodeWithMetadata, -{ - /// Do not validate this storage entry prior to accessing it. - pub fn unvalidated(self) -> Self { - Self { - validation_hash: None, - ..self - } - } - - /// Return bytes representing the root of this storage entry (a hash of the pallet and entry name). - pub fn to_root_bytes(&self) -> Vec { - super::get_address_root_bytes(self) - } -} - -impl Address - for DefaultAddress -where - Keys: StorageKey, - ReturnTy: DecodeWithMetadata, -{ - type Target = ReturnTy; - type Keys = Keys; - type IsFetchable = Fetchable; - type IsDefaultable = Defaultable; - type IsIterable = Iterable; - - fn pallet_name(&self) -> &str { - &self.pallet_name - } - - fn entry_name(&self) -> &str { - &self.entry_name - } - - fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec) -> Result<(), Error> { - let pallet = metadata.pallet_by_name_err(self.pallet_name())?; - let storage = pallet - .storage() - .ok_or_else(|| MetadataError::StorageNotFoundInPallet(self.pallet_name().to_owned()))?; - let entry = storage - .entry_by_name(self.entry_name()) - .ok_or_else(|| MetadataError::StorageEntryNotFound(self.entry_name().to_owned()))?; - - let hashers = StorageHashers::new(entry.entry_type(), metadata.types())?; - self.keys - .encode_storage_key(bytes, &mut hashers.iter(), metadata.types())?; - Ok(()) - } - - fn validation_hash(&self) -> Option<[u8; 32]> { - self.validation_hash - } -} - -/// Construct a new dynamic storage lookup. -pub fn dynamic( - pallet_name: impl Into, - entry_name: impl Into, +/// Construct a new dynamic storage fetch address. +pub fn dynamic_fetch( + pallet_name: impl Into>, + entry_name: impl Into>, storage_entry_keys: Keys, -) -> DynamicAddress { - DynamicAddress::new(pallet_name, entry_name, storage_entry_keys) +) -> impl FetchableAddress { + StaticFetchableAddress::::new( + pallet_name, + entry_name, + storage_entry_keys, + ) +} + +/// Construct a new dynamic storage iter address. +pub fn dynamic_iter( + pallet_name: impl Into>, + entry_name: impl Into>, + storage_entry_keys: Keys, +) -> impl IterableAddress { + StaticIterableAddress::, scale_value::Value>::new( + pallet_name, + entry_name, + storage_entry_keys, + ) } diff --git a/core/src/storage/mod.rs b/core/src/storage/mod.rs index 81e026a330..3c4ae9fc6c 100644 --- a/core/src/storage/mod.rs +++ b/core/src/storage/mod.rs @@ -41,18 +41,16 @@ //! println!("Alice's account info: {value:?}"); //! ``` -mod storage_key; -mod utils; - pub mod address; -use crate::{Error, Metadata, error::MetadataError, metadata::DecodeWithMetadata}; +use crate::{ + Error, Metadata, + error::{MetadataError, StorageAddressError}, +}; use address::Address; use alloc::vec::Vec; - -// This isn't a part of the public API, but expose here because it's useful in Subxt. -#[doc(hidden)] -pub use utils::lookup_storage_entry_details; +use frame_decode::storage::StorageTypeInfo; +use scale_decode::IntoVisitor; /// When the provided `address` is statically generated via the `#[subxt]` macro, this validates /// that the shape of the storage value is the same as the shape expected by the static address. @@ -84,19 +82,21 @@ pub fn get_address_bytes( address: &Addr, metadata: &Metadata, ) -> Result, Error> { - let mut bytes = Vec::new(); - utils::write_storage_address_root_bytes(address, &mut bytes); - address.append_entry_bytes(metadata, &mut bytes)?; - Ok(bytes) + frame_decode::storage::encode_storage_key( + address.pallet_name(), + address.entry_name(), + &address.key_parts(), + &**metadata, + metadata.types(), + ) + .map_err(|e| StorageAddressError::StorageKeyEncodeError(e).into()) } /// Given a storage address and some metadata, this encodes the root of the address (ie the pallet /// and storage entry part) into bytes. If the entry being addressed is inside a map, this returns /// the bytes needed to iterate over all of the entries within it. -pub fn get_address_root_bytes(address: &Addr) -> Vec { - let mut bytes = Vec::new(); - utils::write_storage_address_root_bytes(address, &mut bytes); - bytes +pub fn get_address_root_bytes(address: &Addr) -> [u8; 32] { + frame_decode::storage::encode_storage_key_prefix(address.pallet_name(), address.entry_name()) } /// Given some storage value that we've retrieved from a node, the address used to retrieve it, and @@ -106,30 +106,32 @@ pub fn decode_value( bytes: &mut &[u8], address: &Addr, metadata: &Metadata, -) -> Result { - let pallet_name = address.pallet_name(); - let entry_name = address.entry_name(); - - let (_, entry_metadata) = - utils::lookup_storage_entry_details(pallet_name, entry_name, metadata)?; - let value_ty_id = entry_metadata.value_ty(); - - let val = Addr::Target::decode_with_metadata(bytes, value_ty_id, metadata)?; - Ok(val) +) -> Result { + frame_decode::storage::decode_storage_value( + address.pallet_name(), + address.entry_name(), + bytes, + &**metadata, + metadata.types(), + Addr::Value::into_visitor(), + ) + .map_err(|e| StorageAddressError::StorageValueDecodeError(e).into()) } -/// Return the default value at a given storage address if one is available, or an error otherwise. +/// Return the default value at a given storage address if one is available, or None otherwise. pub fn default_value( address: &Addr, metadata: &Metadata, -) -> Result { - let pallet_name = address.pallet_name(); - let entry_name = address.entry_name(); +) -> Result, Error> { + let storage_info = metadata + .storage_info(address.pallet_name(), address.entry_name()) + .map_err(|e| StorageAddressError::StorageInfoError(e.into_owned()))?; + let value = frame_decode::storage::decode_default_storage_value_with_info( + &storage_info, + metadata.types(), + Addr::Value::into_visitor(), + ) + .map_err(|e| StorageAddressError::StorageValueDecodeError(e))?; - let (_, entry_metadata) = - utils::lookup_storage_entry_details(pallet_name, entry_name, metadata)?; - 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) + Ok(value) } diff --git a/core/src/storage/storage_key.rs b/core/src/storage/storage_key.rs deleted file mode 100644 index bac737c66f..0000000000 --- a/core/src/storage/storage_key.rs +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2019-2024 Parity Technologies (UK) Ltd. -// This file is dual-licensed as Apache-2.0 or GPL-3.0. -// see LICENSE for license details. - -use super::utils::hash_bytes; -use crate::error::{Error, MetadataError, StorageAddressError}; -use alloc::vec; -use alloc::vec::Vec; -use scale_decode::{DecodeAsType, visitor::IgnoreVisitor}; -use scale_encode::EncodeAsType; -use scale_info::{PortableRegistry, TypeDef}; -use scale_value::Value; -use subxt_metadata::{StorageEntryType, StorageHasher}; - -/// A collection of storage hashers paired with the type ids of the types they should hash. -/// Can be created for each storage entry in the metadata via [`StorageHashers::new()`]. -#[derive(Debug)] -pub struct StorageHashers { - hashers_and_ty_ids: Vec<(StorageHasher, u32)>, -} - -impl StorageHashers { - /// Creates new [`StorageHashers`] from a storage entry. Looks at the [`StorageEntryType`] and - /// assigns a hasher to each type id that makes up the key. - pub fn new(storage_entry: &StorageEntryType, types: &PortableRegistry) -> Result { - let mut hashers_and_ty_ids = vec![]; - if let StorageEntryType::Map { - hashers, key_ty, .. - } = storage_entry - { - let ty = types - .resolve(*key_ty) - .ok_or(MetadataError::TypeNotFound(*key_ty))?; - - 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. - hashers_and_ty_ids = vec![(hashers[0], *key_ty)]; - } 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 &ty.type_def { - TypeDef::Tuple(tuple) => tuple, - _ => { - return Err(StorageAddressError::WrongNumberOfHashers { - hashers: hasher_count, - fields: 1, - } - .into()); - } - }; - - // We should have the same number of hashers and keys. - let key_count = tuple.fields.len(); - if hasher_count != key_count { - return Err(StorageAddressError::WrongNumberOfHashers { - hashers: hasher_count, - fields: key_count, - } - .into()); - } - - // Collect them together. - hashers_and_ty_ids = tuple - .fields - .iter() - .zip(hashers) - .map(|(field, hasher)| (*hasher, field.id)) - .collect(); - } - } - - Ok(Self { hashers_and_ty_ids }) - } - - /// Creates an iterator over the storage hashers and type ids. - pub fn iter(&self) -> StorageHashersIter<'_> { - StorageHashersIter { - hashers: self, - idx: 0, - } - } -} - -/// An iterator over all type ids of the key and the respective hashers. -/// See [`StorageHashers::iter()`]. -#[derive(Debug)] -pub struct StorageHashersIter<'a> { - hashers: &'a StorageHashers, - idx: usize, -} - -impl StorageHashersIter<'_> { - fn next_or_err(&mut self) -> Result<(StorageHasher, u32), Error> { - self.next().ok_or_else(|| { - StorageAddressError::TooManyKeys { - expected: self.hashers.hashers_and_ty_ids.len(), - } - .into() - }) - } -} - -impl Iterator for StorageHashersIter<'_> { - type Item = (StorageHasher, u32); - - fn next(&mut self) -> Option { - let item = self.hashers.hashers_and_ty_ids.get(self.idx).copied()?; - self.idx += 1; - Some(item) - } -} - -impl ExactSizeIterator for StorageHashersIter<'_> { - fn len(&self) -> usize { - self.hashers.hashers_and_ty_ids.len() - self.idx - } -} - -/// This trait should be implemented by anything that can be used as one or multiple storage keys. -pub trait StorageKey { - /// Encodes the storage key into some bytes - fn encode_storage_key( - &self, - bytes: &mut Vec, - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result<(), Error>; - - /// Attempts to decode the StorageKey given some bytes and a set of hashers and type IDs that they are meant to represent. - /// The bytes passed to `decode` should start with: - /// - 1. some fixed size hash (for all hashers except `Identity`) - /// - 2. the plain key value itself (for `Identity`, `Blake2_128Concat` and `Twox64Concat` hashers) - fn decode_storage_key( - bytes: &mut &[u8], - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result - where - Self: Sized + 'static; -} - -/// Implement `StorageKey` for `()` which can be used for keyless storage entries, -/// or to otherwise just ignore some entry. -impl StorageKey for () { - fn encode_storage_key( - &self, - _bytes: &mut Vec, - hashers: &mut StorageHashersIter, - _types: &PortableRegistry, - ) -> Result<(), Error> { - _ = hashers.next_or_err(); - Ok(()) - } - - fn decode_storage_key( - bytes: &mut &[u8], - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result { - let (hasher, ty_id) = match hashers.next_or_err() { - Ok((hasher, ty_id)) => (hasher, ty_id), - Err(_) if bytes.is_empty() => return Ok(()), - Err(err) => return Err(err), - }; - consume_hash_returning_key_bytes(bytes, hasher, ty_id, types)?; - Ok(()) - } -} - -/// A storage key used as part of the static codegen. -#[derive(Clone, Debug, PartialOrd, PartialEq, Eq)] -pub struct StaticStorageKey { - key: K, -} - -impl StaticStorageKey { - /// Creates a new static storage key. - pub fn new(key: K) -> Self { - StaticStorageKey { key } - } -} - -impl StaticStorageKey { - /// Returns the decoded storage key. - pub fn into_key(self) -> K { - self.key - } -} - -impl StorageKey for StaticStorageKey { - fn encode_storage_key( - &self, - bytes: &mut Vec, - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result<(), Error> { - let (hasher, ty_id) = hashers.next_or_err()?; - let encoded_value = self.key.encode_as_type(ty_id, types)?; - hash_bytes(&encoded_value, hasher, bytes); - Ok(()) - } - - fn decode_storage_key( - bytes: &mut &[u8], - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result - where - Self: Sized + 'static, - { - let (hasher, ty_id) = hashers.next_or_err()?; - let key_bytes = consume_hash_returning_key_bytes(bytes, hasher, ty_id, types)?; - - // if the hasher had no key appended, we can't decode it into a `StaticStorageKey`. - let Some(key_bytes) = key_bytes else { - return Err(StorageAddressError::HasherCannotReconstructKey { ty_id, hasher }.into()); - }; - - // Decode and return the key. - let key = K::decode_as_type(&mut &*key_bytes, ty_id, types)?; - let key = StaticStorageKey { key }; - Ok(key) - } -} - -impl StorageKey for Vec { - fn encode_storage_key( - &self, - bytes: &mut Vec, - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result<(), Error> { - for value in self.iter() { - let (hasher, ty_id) = hashers.next_or_err()?; - let encoded_value = value.encode_as_type(ty_id, types)?; - hash_bytes(&encoded_value, hasher, bytes); - } - Ok(()) - } - - fn decode_storage_key( - bytes: &mut &[u8], - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result - where - Self: Sized + 'static, - { - let mut result: Vec = vec![]; - for (hasher, ty_id) in hashers.by_ref() { - match consume_hash_returning_key_bytes(bytes, hasher, ty_id, types)? { - Some(value_bytes) => { - let value = - scale_value::scale::decode_as_type(&mut &*value_bytes, ty_id, types)?; - - result.push(value.remove_context()); - } - None => { - result.push(Value::unnamed_composite([])); - } - } - } - - // We've consumed all of the hashers, so we expect to also consume all of the bytes: - if !bytes.is_empty() { - return Err(StorageAddressError::TooManyBytes.into()); - } - - Ok(result) - } -} - -// Skip over the hash bytes (including any key at the end), returning bytes -// representing the key if one exists, or None if the hasher has no key appended. -fn consume_hash_returning_key_bytes<'a>( - bytes: &mut &'a [u8], - hasher: StorageHasher, - ty_id: u32, - types: &PortableRegistry, -) -> Result, Error> { - // Strip the bytes off for the actual hash, consuming them. - let bytes_to_strip = hasher.len_excluding_key(); - if bytes.len() < bytes_to_strip { - return Err(StorageAddressError::NotEnoughBytes.into()); - } - *bytes = &bytes[bytes_to_strip..]; - - // Now, find the bytes representing the key, consuming them. - let before_key = *bytes; - if hasher.ends_with_key() { - scale_decode::visitor::decode_with_visitor( - bytes, - ty_id, - types, - IgnoreVisitor::::new(), - ) - .map_err(|err| Error::Decode(err.into()))?; - // Return the key bytes, having advanced the input cursor past them. - let key_bytes = &before_key[..before_key.len() - bytes.len()]; - - Ok(Some(key_bytes)) - } else { - // There are no key bytes, so return None. - Ok(None) - } -} - -/// Generates StorageKey implementations for tuples -macro_rules! impl_tuples { - ($($ty:ident $n:tt),+) => {{ - impl<$($ty: StorageKey),+> StorageKey for ($( $ty ),+) { - fn encode_storage_key( - &self, - bytes: &mut Vec, - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result<(), Error> { - $( self.$n.encode_storage_key(bytes, hashers, types)?; )+ - Ok(()) - } - - fn decode_storage_key( - bytes: &mut &[u8], - hashers: &mut StorageHashersIter, - types: &PortableRegistry, - ) -> Result - where - Self: Sized + 'static, - { - Ok( ( $( $ty::decode_storage_key(bytes, hashers, types)?, )+ ) ) - } - } - }}; -} - -#[rustfmt::skip] -const _: () = { - impl_tuples!(A 0, B 1); - impl_tuples!(A 0, B 1, C 2); - impl_tuples!(A 0, B 1, C 2, D 3); - impl_tuples!(A 0, B 1, C 2, D 3, E 4); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6); - impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7); -}; - -#[cfg(test)] -mod tests { - - use codec::Encode; - use scale_info::{PortableRegistry, Registry, TypeInfo, meta_type}; - use subxt_metadata::StorageHasher; - - use crate::utils::Era; - - use alloc::string::String; - use alloc::vec; - use alloc::vec::Vec; - - use super::{StaticStorageKey, StorageKey}; - - struct KeyBuilder { - registry: Registry, - bytes: Vec, - hashers_and_ty_ids: Vec<(StorageHasher, u32)>, - } - - impl KeyBuilder { - fn new() -> KeyBuilder { - KeyBuilder { - registry: Registry::new(), - bytes: vec![], - hashers_and_ty_ids: vec![], - } - } - - fn add(mut self, value: T, hasher: StorageHasher) -> Self { - let id = self.registry.register_type(&meta_type::()).id; - - self.hashers_and_ty_ids.push((hasher, id)); - for _i in 0..hasher.len_excluding_key() { - self.bytes.push(0); - } - value.encode_to(&mut self.bytes); - self - } - - fn build(self) -> (PortableRegistry, Vec, Vec<(StorageHasher, u32)>) { - (self.registry.into(), self.bytes, self.hashers_and_ty_ids) - } - } - - #[test] - fn storage_key_decoding_fuzz() { - let hashers = [ - StorageHasher::Blake2_128, - StorageHasher::Blake2_128Concat, - StorageHasher::Blake2_256, - StorageHasher::Identity, - StorageHasher::Twox128, - StorageHasher::Twox256, - StorageHasher::Twox64Concat, - ]; - - let key_preserving_hashers = [ - StorageHasher::Blake2_128Concat, - StorageHasher::Identity, - StorageHasher::Twox64Concat, - ]; - - type T4A = ( - (), - StaticStorageKey, - StaticStorageKey, - StaticStorageKey, - ); - type T4B = ( - (), - (StaticStorageKey, StaticStorageKey), - StaticStorageKey, - ); - type T4C = ( - ((), StaticStorageKey), - (StaticStorageKey, StaticStorageKey), - ); - - let era = Era::Immortal; - for h0 in hashers { - for h1 in key_preserving_hashers { - for h2 in key_preserving_hashers { - for h3 in key_preserving_hashers { - let (types, bytes, hashers_and_ty_ids) = KeyBuilder::new() - .add((), h0) - .add(13u32, h1) - .add("Hello", h2) - .add(era, h3) - .build(); - - let hashers = super::StorageHashers { hashers_and_ty_ids }; - let keys_a = - T4A::decode_storage_key(&mut &bytes[..], &mut hashers.iter(), &types) - .unwrap(); - - let keys_b = - T4B::decode_storage_key(&mut &bytes[..], &mut hashers.iter(), &types) - .unwrap(); - - let keys_c = - T4C::decode_storage_key(&mut &bytes[..], &mut hashers.iter(), &types) - .unwrap(); - - assert_eq!(keys_a.1.into_key(), 13); - assert_eq!(keys_b.1.0.into_key(), 13); - assert_eq!(keys_c.0.1.into_key(), 13); - - assert_eq!(keys_a.2.into_key(), "Hello"); - assert_eq!(keys_b.1.1.into_key(), "Hello"); - assert_eq!(keys_c.1.0.into_key(), "Hello"); - assert_eq!(keys_a.3.into_key(), era); - assert_eq!(keys_b.2.into_key(), era); - assert_eq!(keys_c.1.1.into_key(), era); - } - } - } - } - } -} diff --git a/core/src/storage/utils.rs b/core/src/storage/utils.rs deleted file mode 100644 index dc5d10ace4..0000000000 --- a/core/src/storage/utils.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019-2024 Parity Technologies (UK) Ltd. -// This file is dual-licensed as Apache-2.0 or GPL-3.0. -// see LICENSE for license details. - -//! these utility methods complement the [`Address`] trait, but -//! aren't things that should ever be overridden, and so don't exist on -//! the trait itself. - -use super::address::Address; -use crate::error::{Error, MetadataError}; -use crate::metadata::Metadata; -use alloc::borrow::ToOwned; -use alloc::vec::Vec; -use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageHasher}; - -/// Return the root of a given [`Address`]: hash the pallet name and entry name -/// and append those bytes to the output. -pub fn write_storage_address_root_bytes(addr: &Addr, out: &mut Vec) { - out.extend(sp_crypto_hashing::twox_128(addr.pallet_name().as_bytes())); - out.extend(sp_crypto_hashing::twox_128(addr.entry_name().as_bytes())); -} - -/// Take some SCALE encoded bytes and a [`StorageHasher`] and hash the bytes accordingly. -pub fn hash_bytes(input: &[u8], hasher: StorageHasher, bytes: &mut Vec) { - match hasher { - StorageHasher::Identity => bytes.extend(input), - StorageHasher::Blake2_128 => bytes.extend(sp_crypto_hashing::blake2_128(input)), - StorageHasher::Blake2_128Concat => { - bytes.extend(sp_crypto_hashing::blake2_128(input)); - bytes.extend(input); - } - StorageHasher::Blake2_256 => bytes.extend(sp_crypto_hashing::blake2_256(input)), - StorageHasher::Twox128 => bytes.extend(sp_crypto_hashing::twox_128(input)), - StorageHasher::Twox256 => bytes.extend(sp_crypto_hashing::twox_256(input)), - StorageHasher::Twox64Concat => { - bytes.extend(sp_crypto_hashing::twox_64(input)); - bytes.extend(input); - } - } -} - -/// Return details about the given storage entry. -pub fn lookup_storage_entry_details<'a>( - pallet_name: &str, - entry_name: &str, - metadata: &'a 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)) -} diff --git a/historic/src/storage.rs b/historic/src/storage.rs index cf77c102e3..144a6f38e7 100644 --- a/historic/src/storage.rs +++ b/historic/src/storage.rs @@ -250,7 +250,7 @@ where let pallet_name = &*self.pallet_name; let storage_name = &*self.storage_name; - frame_decode::storage::encode_prefix(pallet_name, storage_name) + frame_decode::storage::encode_storage_key_prefix(pallet_name, storage_name) } }