use super::storage_type_info::{StorageHasher, StorageInfo, StorageTypeInfo};
use crate::methods::storage_type_info::StorageInfoError;
use crate::utils::{EncodableValues, IntoEncodableValues};
use alloc::vec::Vec;
use scale_type_resolver::TypeResolver;
#[non_exhaustive]
#[allow(missing_docs)]
#[derive(Debug, thiserror::Error)]
pub enum StorageKeyEncodeError {
#[error("Cannot get storage info: {0}")]
CannotGetInfo(StorageInfoError<'static>),
#[error("Failed to encode storage key: {0}")]
EncodeError(#[from] scale_encode::Error),
#[error("Too many keys provided: expected at most {max_keys_expected}")]
TooManyKeysProvided {
max_keys_expected: usize,
},
}
pub fn encode_storage_key_prefix(pallet_name: &str, storage_entry: &str) -> [u8; 32] {
let mut prefix = [0u8; 32];
let pallet_bytes = sp_crypto_hashing::twox_128(pallet_name.as_bytes());
let entry_bytes = sp_crypto_hashing::twox_128(storage_entry.as_bytes());
prefix[..16].copy_from_slice(&pallet_bytes);
prefix[16..].copy_from_slice(&entry_bytes);
prefix
}
pub fn encode_storage_key<Info, Resolver, Keys>(
pallet_name: &str,
storage_entry: &str,
keys: Keys,
info: &Info,
type_resolver: &Resolver,
) -> Result<Vec<u8>, StorageKeyEncodeError>
where
Keys: IntoEncodableValues,
Info: StorageTypeInfo,
Info::TypeId: Clone + core::fmt::Debug,
Resolver: TypeResolver<TypeId = Info::TypeId>,
{
let mut out = Vec::with_capacity(32);
encode_storage_key_to(
pallet_name,
storage_entry,
keys,
info,
type_resolver,
&mut out,
)?;
Ok(out)
}
pub fn encode_storage_key_to<Info, Resolver, Keys>(
pallet_name: &str,
storage_entry: &str,
keys: Keys,
info: &Info,
type_resolver: &Resolver,
out: &mut Vec<u8>,
) -> Result<(), StorageKeyEncodeError>
where
Keys: IntoEncodableValues,
Info: StorageTypeInfo,
Info::TypeId: Clone + core::fmt::Debug,
Resolver: TypeResolver<TypeId = Info::TypeId>,
{
let storage_info = info
.storage_info(pallet_name, storage_entry)
.map_err(|e| StorageKeyEncodeError::CannotGetInfo(e.into_owned()))?;
encode_storage_key_with_info_to(
pallet_name,
storage_entry,
keys,
&storage_info,
type_resolver,
out,
)
}
pub fn encode_storage_key_with_info<Resolver, Keys>(
pallet_name: &str,
storage_entry: &str,
keys: Keys,
storage_info: &StorageInfo<<Resolver as TypeResolver>::TypeId>,
type_resolver: &Resolver,
) -> Result<Vec<u8>, StorageKeyEncodeError>
where
Keys: IntoEncodableValues,
Resolver: TypeResolver,
<Resolver as TypeResolver>::TypeId: Clone + core::fmt::Debug,
{
let mut out = Vec::with_capacity(32);
encode_storage_key_with_info_to(
pallet_name,
storage_entry,
keys,
storage_info,
type_resolver,
&mut out,
)?;
Ok(out)
}
pub fn encode_storage_key_with_info_to<Resolver, Keys>(
pallet_name: &str,
storage_entry: &str,
keys: Keys,
storage_info: &StorageInfo<<Resolver as TypeResolver>::TypeId>,
type_resolver: &Resolver,
out: &mut Vec<u8>,
) -> Result<(), StorageKeyEncodeError>
where
Keys: IntoEncodableValues,
Resolver: TypeResolver,
<Resolver as TypeResolver>::TypeId: Clone + core::fmt::Debug,
{
let num_encodable_values = keys.num_encodable_values();
if num_encodable_values > storage_info.keys.len() {
return Err(StorageKeyEncodeError::TooManyKeysProvided {
max_keys_expected: storage_info.keys.len(),
});
}
let prefix = encode_storage_key_prefix(pallet_name, storage_entry);
out.extend_from_slice(&prefix);
let mut keys = keys.into_encodable_values();
let mut temp = Vec::with_capacity(32);
let iter = (0..num_encodable_values)
.zip(&*storage_info.keys)
.map(|(_, k)| k);
for key_info in iter {
keys.encode_next_value_to(key_info.key_id.clone(), type_resolver, &mut temp)
.map_err(StorageKeyEncodeError::EncodeError)?;
match key_info.hasher {
StorageHasher::Blake2_128 => {
let hash = sp_crypto_hashing::blake2_128(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Blake2_256 => {
let hash = sp_crypto_hashing::blake2_256(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Blake2_128Concat => {
let hash = sp_crypto_hashing::blake2_128(&temp);
out.extend_from_slice(&hash);
out.extend_from_slice(&temp);
}
StorageHasher::Twox128 => {
let hash = sp_crypto_hashing::twox_128(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Twox256 => {
let hash = sp_crypto_hashing::twox_256(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Twox64Concat => {
let hash = sp_crypto_hashing::twox_64(&temp);
out.extend_from_slice(&hash);
out.extend_from_slice(&temp);
}
StorageHasher::Identity => {
out.extend_from_slice(&temp);
}
}
temp.clear();
}
Ok(())
}
pub fn encode_storage_key_suffix<Info, Resolver, Keys>(
pallet_name: &str,
storage_entry: &str,
keys: Keys,
info: &Info,
type_resolver: &Resolver,
) -> Result<Vec<u8>, StorageKeyEncodeError>
where
Keys: IntoEncodableValues,
Info: StorageTypeInfo,
Info::TypeId: Clone + core::fmt::Debug,
Resolver: TypeResolver<TypeId = Info::TypeId>,
{
let mut out = Vec::with_capacity(32);
encode_storage_key_suffix_to(
pallet_name,
storage_entry,
keys,
info,
type_resolver,
&mut out,
)?;
Ok(out)
}
pub fn encode_storage_key_suffix_to<Info, Resolver, Keys>(
pallet_name: &str,
storage_entry: &str,
keys: Keys,
info: &Info,
type_resolver: &Resolver,
out: &mut Vec<u8>,
) -> Result<(), StorageKeyEncodeError>
where
Keys: IntoEncodableValues,
Info: StorageTypeInfo,
Info::TypeId: Clone + core::fmt::Debug,
Resolver: TypeResolver<TypeId = Info::TypeId>,
{
let storage_info = info
.storage_info(pallet_name, storage_entry)
.map_err(|e| StorageKeyEncodeError::CannotGetInfo(e.into_owned()))?;
encode_storage_key_suffix_with_info_to(keys, &storage_info, type_resolver, out)
}
pub fn encode_storage_key_suffix_with_info_to<Resolver, Keys>(
keys: Keys,
storage_info: &StorageInfo<<Resolver as TypeResolver>::TypeId>,
type_resolver: &Resolver,
out: &mut Vec<u8>,
) -> Result<(), StorageKeyEncodeError>
where
Keys: IntoEncodableValues,
Resolver: TypeResolver,
<Resolver as TypeResolver>::TypeId: Clone + core::fmt::Debug,
{
let num_encodable_values = keys.num_encodable_values();
if num_encodable_values > storage_info.keys.len() {
return Err(StorageKeyEncodeError::TooManyKeysProvided {
max_keys_expected: storage_info.keys.len(),
});
}
let mut keys = keys.into_encodable_values();
let mut temp = Vec::with_capacity(32);
let iter = (0..num_encodable_values)
.zip(&*storage_info.keys)
.map(|(_, k)| k);
for key_info in iter {
keys.encode_next_value_to(key_info.key_id.clone(), type_resolver, &mut temp)
.map_err(StorageKeyEncodeError::EncodeError)?;
match key_info.hasher {
StorageHasher::Blake2_128 => {
let hash = sp_crypto_hashing::blake2_128(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Blake2_256 => {
let hash = sp_crypto_hashing::blake2_256(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Blake2_128Concat => {
let hash = sp_crypto_hashing::blake2_128(&temp);
out.extend_from_slice(&hash);
out.extend_from_slice(&temp);
}
StorageHasher::Twox128 => {
let hash = sp_crypto_hashing::twox_128(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Twox256 => {
let hash = sp_crypto_hashing::twox_256(&temp);
out.extend_from_slice(&hash);
}
StorageHasher::Twox64Concat => {
let hash = sp_crypto_hashing::twox_64(&temp);
out.extend_from_slice(&hash);
out.extend_from_slice(&temp);
}
StorageHasher::Identity => {
out.extend_from_slice(&temp);
}
}
temp.clear();
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_encode_storage_key() {
use crate::storage::encode_storage_key;
use frame_metadata::RuntimeMetadata;
use parity_scale_codec::Decode;
let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
let RuntimeMetadata::V14(metadata) =
RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap()
else {
return;
};
let account_id = [0u8; 32];
encode_storage_key(
"System",
"Account",
&[account_id],
&metadata,
&metadata.types,
)
.expect("Encoding should work");
encode_storage_key(
"System",
"Account",
&(account_id,),
&metadata,
&metadata.types,
)
.expect("Encoding should work");
let out = encode_storage_key("System", "Account", &(), &metadata, &metadata.types)
.expect("Encoding should work");
assert_eq!(&out, &encode_storage_key_prefix("System", "Account"));
let err = encode_storage_key(
"System",
"Account",
&(account_id, 123u16),
&metadata,
&metadata.types,
);
assert!(matches!(
err,
Err(StorageKeyEncodeError::TooManyKeysProvided {
max_keys_expected: 1
})
));
}
}