use super::Value;
use crate::metadata::{Metadata, StorageLocation};
use crate::{ScaleInfoTypeId, TypeId};
use frame_metadata::v14::StorageEntryType as FrameStorageEntryType;
use serde::Serialize;
use sp_core::twox_128;
use std::borrow::Cow;
use std::collections::HashMap;
pub struct StorageDecoder {
entries_by_hashed_prefix: HashMap<[u8; 16], StorageEntries>,
}
struct StorageEntries {
index: usize,
entry_by_hashed_name: HashMap<[u8; 16], usize>,
}
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
pub enum StorageDecodeError {
#[error("Not enough bytes in the input data to decode the storage prefix and name; got {0} bytes but expected 32")]
NotEnoughBytesForPrefixAndName(usize),
#[error("Couldn't decode the value associated with the hasher for key {key} ({hasher:?}): {decode_error}")]
CouldNotDecodeHasherValue {
key: usize,
hasher: frame_metadata::v14::StorageHasher,
decode_error: super::DecodeValueError,
},
#[error("Couldn't find a storage entry corresponding to the prefix hash provided in the data")]
PrefixNotFound,
#[error("Couldn't find a storage entry corresponding to the name hash provided in the data")]
NameNotFound,
}
impl StorageDecoder {
pub(super) fn generate_from_metadata(metadata: &Metadata) -> StorageDecoder {
let entries_by_hashed_prefix = metadata
.storage_entries()
.enumerate()
.map(|(index, entries)| {
let prefix_hash = twox_128(entries.prefix().as_bytes());
let entry_by_hashed_name = entries
.entries()
.enumerate()
.map(|(entry_index, entry)| {
let name_hash = twox_128(entry.name.as_bytes());
(name_hash, entry_index)
})
.collect();
(prefix_hash, StorageEntries { index, entry_by_hashed_name })
})
.collect();
StorageDecoder { entries_by_hashed_prefix }
}
pub fn decode_key<'m, 'b>(
&self,
metadata: &'m Metadata,
bytes: &mut &'b [u8],
) -> Result<StorageEntry<'m, 'b>, StorageDecodeError> {
let location = self.decode_prefix_and_name_to_location(bytes)?;
let storage_entry = metadata.storage_entry(location);
let prefix_str = storage_entry.prefix;
let name_str = &*storage_entry.metadata.name;
match &storage_entry.metadata.ty {
FrameStorageEntryType::Plain(ty) => {
Ok(StorageEntry {
prefix: prefix_str.into(),
name: name_str.into(),
ty: ty.into(),
details: StorageEntryType::Plain,
})
}
FrameStorageEntryType::Map { hashers, key, value } => {
let keys = storage_map_key_to_type_id_vec(metadata, key);
if keys.len() != hashers.len() {
panic!(
"Metadata inconsistency: keys and hashers for storage lookup {}.{} don't line up",
prefix_str, name_str
);
}
let mut storage_keys = vec![];
for (idx, (hasher, ty)) in hashers.iter().zip(keys).enumerate() {
pub use frame_metadata::v14::StorageHasher as FrameStorageHasher;
let initial_hash_bytes = match hasher {
FrameStorageHasher::Blake2_128
| FrameStorageHasher::Twox128
| FrameStorageHasher::Blake2_128Concat => 16,
FrameStorageHasher::Blake2_256 | FrameStorageHasher::Twox256 => 32,
FrameStorageHasher::Twox64Concat => 8,
FrameStorageHasher::Identity => 0,
};
let is_value_next = match hasher {
FrameStorageHasher::Blake2_128Concat
| FrameStorageHasher::Twox64Concat
| FrameStorageHasher::Identity => true,
_other => false,
};
let (hasher, bytes_consumed) = if is_value_next {
let value_bytes = &mut &bytes[initial_hash_bytes..];
let start_len = value_bytes.len();
let value = super::decode_value_by_id(metadata, ty, value_bytes).map_err(|e| {
StorageDecodeError::CouldNotDecodeHasherValue {
key: idx,
hasher: hasher.clone(),
decode_error: e,
}
})?;
let value_len = start_len - value_bytes.len();
(StorageHasher::expect_from_with_value(hasher, value), initial_hash_bytes + value_len)
} else {
(StorageHasher::expect_from(hasher), initial_hash_bytes)
};
let hash_bytes = &bytes[..bytes_consumed];
*bytes = &bytes[bytes_consumed..];
storage_keys.push(StorageMapKey { bytes: Cow::Borrowed(hash_bytes), hasher, ty });
}
Ok(StorageEntry {
prefix: prefix_str.into(),
name: name_str.into(),
ty: value.into(),
details: StorageEntryType::Map(storage_keys),
})
}
}
}
fn decode_prefix_and_name_to_location(&self, data: &mut &[u8]) -> Result<StorageLocation, StorageDecodeError> {
if data.len() < 32 {
return Err(StorageDecodeError::NotEnoughBytesForPrefixAndName(data.len()));
}
let prefix_hash = &data[..16];
let name_hash = &data[16..32];
let entries = self.entries_by_hashed_prefix.get(prefix_hash).ok_or(StorageDecodeError::PrefixNotFound)?;
let entry_index = entries.entry_by_hashed_name.get(name_hash).ok_or(StorageDecodeError::NameNotFound)?;
*data = &data[32..];
Ok(StorageLocation { prefix_index: entries.index, entry_index: *entry_index })
}
}
fn storage_map_key_to_type_id_vec(metadata: &Metadata, key: &ScaleInfoTypeId) -> Vec<TypeId> {
let ty = match metadata.resolve(key) {
Some(ty) => ty,
None => panic!("Metadata inconsistency: type #{} not found", key.id()),
};
match ty.type_def() {
scale_info::TypeDef::Tuple(vals) => vals.fields().iter().map(|f| TypeId::from_u32(f.id())).collect(),
_ => vec![key.into()],
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct StorageEntry<'m, 'b> {
pub prefix: Cow<'m, str>,
pub name: Cow<'m, str>,
pub ty: TypeId,
pub details: StorageEntryType<'b>,
}
impl<'m, 'b> StorageEntry<'m, 'b> {
pub fn into_owned(self) -> StorageEntry<'static, 'static> {
StorageEntry {
prefix: Cow::Owned(self.prefix.into_owned()),
name: Cow::Owned(self.name.into_owned()),
ty: self.ty,
details: self.details.into_owned(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum StorageEntryType<'b> {
Plain,
Map(Vec<StorageMapKey<'b>>),
}
impl<'b> StorageEntryType<'b> {
pub fn into_owned(self) -> StorageEntryType<'static> {
match self {
Self::Plain => StorageEntryType::Plain,
Self::Map(keys) => StorageEntryType::Map(keys.into_iter().map(|k| k.into_owned()).collect()),
}
}
pub fn map_keys(&self) -> &[StorageMapKey<'b>] {
match self {
Self::Plain => &[],
Self::Map(keys) => keys,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct StorageMapKey<'b> {
pub bytes: Cow<'b, [u8]>,
pub ty: TypeId,
pub hasher: StorageHasher,
}
impl<'m, 'b> StorageMapKey<'b> {
pub fn into_owned(self) -> StorageMapKey<'static> {
StorageMapKey { bytes: Cow::Owned(self.bytes.into_owned()), ty: self.ty, hasher: self.hasher }
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum StorageHasher {
Blake2_128,
Blake2_256,
Blake2_128Concat(Value<TypeId>),
Twox128,
Twox256,
Twox64Concat(Value<TypeId>),
Identity(Value<TypeId>),
}
impl StorageHasher {
fn expect_from(hasher: &frame_metadata::v14::StorageHasher) -> Self {
match hasher {
frame_metadata::v14::StorageHasher::Blake2_128 => StorageHasher::Blake2_128,
frame_metadata::v14::StorageHasher::Blake2_256 => StorageHasher::Blake2_256,
frame_metadata::v14::StorageHasher::Twox128 => StorageHasher::Twox128,
frame_metadata::v14::StorageHasher::Twox256 => StorageHasher::Twox256,
frame_metadata::v14::StorageHasher::Identity
| frame_metadata::v14::StorageHasher::Blake2_128Concat
| frame_metadata::v14::StorageHasher::Twox64Concat => {
panic!("Cannot convert {:?} into a StorageHasher; needs Value", hasher)
}
}
}
fn expect_from_with_value(hasher: &frame_metadata::v14::StorageHasher, value: Value<TypeId>) -> Self {
match hasher {
frame_metadata::v14::StorageHasher::Blake2_128
| frame_metadata::v14::StorageHasher::Blake2_256
| frame_metadata::v14::StorageHasher::Twox128
| frame_metadata::v14::StorageHasher::Twox256 => {
panic!("Cannot convert {:?} into a StorageHasher; no Value expected", hasher)
}
frame_metadata::v14::StorageHasher::Identity => StorageHasher::Identity(value),
frame_metadata::v14::StorageHasher::Blake2_128Concat => StorageHasher::Blake2_128Concat(value),
frame_metadata::v14::StorageHasher::Twox64Concat => StorageHasher::Twox64Concat(value),
}
}
}