use external_memory_tools::{AddressableBuffer, ExternalMemory};
use frame_metadata::v14::{StorageEntryMetadata, StorageEntryType, StorageHasher};
use scale_info::{form::PortableForm, interner::UntrackedSymbol, TypeDef};
use sp_crypto_hashing::{blake2_128, twox_64};
use crate::std::{string::String, vec::Vec};
#[cfg(feature = "std")]
use std::any::TypeId;
#[cfg(not(feature = "std"))]
use core::any::TypeId;
use crate::cards::{Documented, ExtendedData, Info};
use crate::decode_all_as_type;
use crate::decoding_sci::{decode_with_type, Ty};
use crate::error::{ParserError, StorageError};
use crate::propagated::Propagated;
use crate::traits::{AsMetadata, ResolveType};
#[derive(Debug, Eq, PartialEq)]
pub struct Storage {
pub key: KeyData,
pub value: ExtendedData,
pub docs: String,
}
#[derive(Debug, Eq, PartialEq)]
pub enum KeyData {
Plain,
SingleHash {
content: KeyPart,
},
TupleHash {
content: Vec<KeyPart>,
info: Info,
},
}
#[derive(Debug, Eq, PartialEq)]
pub enum KeyPart {
Hash(HashData),
Parsed(ExtendedData),
}
#[derive(Debug, Eq, PartialEq)]
pub struct HashData {
pub hash: Hash,
pub type_id: u32,
}
#[derive(Debug, Eq, PartialEq)]
pub enum Hash {
Blake2_128([u8; BLAKE2_128_LEN]),
Blake2_256([u8; BLAKE2_256_LEN]),
Twox128([u8; TWOX128_LEN]),
Twox256([u8; TWOX256_LEN]),
}
pub const BLAKE2_128_LEN: usize = 16;
pub const BLAKE2_256_LEN: usize = 32;
pub const TWOX128_LEN: usize = 16;
pub const TWOX256_LEN: usize = 32;
pub const TWOX64_LEN: usize = 8;
pub fn decode_as_storage_entry<B, E, M>(
key_input: &B,
value_input: &B,
ext_memory: &mut E,
entry_metadata: &StorageEntryMetadata<PortableForm>,
registry: &M::TypeRegistry,
) -> Result<Storage, StorageError<E>>
where
B: AddressableBuffer<E>,
E: ExternalMemory,
M: AsMetadata<E>,
{
let docs = entry_metadata.collect_docs();
let position_key_after_prefix = 2 * TWOX128_LEN;
if position_key_after_prefix > key_input.total_len() {
return Err(StorageError::KeyShorterThanPrefix);
}
let (key, value) = match &entry_metadata.ty {
StorageEntryType::Plain(value_ty) => {
if position_key_after_prefix != key_input.total_len() {
return Err(StorageError::PlainKeyExceedsPrefix);
}
let key = KeyData::Plain;
let value = decode_all_as_type::<B, E, M>(value_ty, value_input, ext_memory, registry)
.map_err(StorageError::ParsingValue)?;
(key, value)
}
StorageEntryType::Map {
hashers,
key: key_ty,
value: value_ty,
} => {
let key = process_key_mapped::<B, E, M>(
hashers,
key_ty,
key_input,
ext_memory,
position_key_after_prefix,
registry,
)?;
let value = decode_all_as_type::<B, E, M>(value_ty, value_input, ext_memory, registry)
.map_err(StorageError::ParsingValue)?;
(key, value)
}
};
Ok(Storage { key, value, docs })
}
macro_rules! cut_hash {
($func:ident, $hash_len:ident, $enum_variant:ident) => {
fn $func<B, E>(
key_ty: &UntrackedSymbol<TypeId>,
key_input: &B,
ext_memory: &mut E,
position: &mut usize,
) -> Result<KeyPart, StorageError<E>>
where
B: AddressableBuffer<E>,
E: ExternalMemory,
{
let slice = key_input
.read_slice(ext_memory, *position, $hash_len)
.map_err(|e| StorageError::ParsingKey(ParserError::Buffer(e)))?;
let hash_part: [u8; $hash_len] = slice
.as_ref()
.try_into()
.expect("constant length, always fits");
*position += $hash_len;
Ok(KeyPart::Hash(HashData {
hash: Hash::$enum_variant(hash_part),
type_id: key_ty.id,
}))
}
};
}
cut_hash!(cut_blake2_128, BLAKE2_128_LEN, Blake2_128);
cut_hash!(cut_blake2_256, BLAKE2_256_LEN, Blake2_256);
cut_hash!(cut_twox_128, TWOX128_LEN, Twox128);
cut_hash!(cut_twox_256, TWOX256_LEN, Twox256);
macro_rules! check_hash {
($func:ident, $hash_len:ident, $fn_into:ident) => {
fn $func<B, E, M>(
ty: &UntrackedSymbol<TypeId>,
key_input: &B,
ext_memory: &mut E,
position: &mut usize,
registry: &M::TypeRegistry,
) -> Result<KeyPart, StorageError<E>>
where
B: AddressableBuffer<E>,
E: ExternalMemory,
M: AsMetadata<E>,
{
let slice = key_input
.read_slice(ext_memory, *position, $hash_len)
.map_err(|e| StorageError::ParsingKey(ParserError::Buffer(e)))?;
let hash_part: [u8; $hash_len] = slice
.as_ref()
.try_into()
.expect("constant length, always fits");
*position += $hash_len;
let position_decoder_starts = *position;
let parsed_key = decode_with_type::<B, E, M>(
&Ty::Symbol(&ty),
key_input,
ext_memory,
position,
registry,
Propagated::new(),
)
.map_err(StorageError::ParsingKey)?;
if hash_part
!= $fn_into(
&key_input
.read_slice(
ext_memory,
position_decoder_starts,
*position - position_decoder_starts,
)
.expect("positions checked, valid slice")
.as_ref(),
)
{
Err(StorageError::KeyPartHashMismatch)
} else {
Ok(KeyPart::Parsed(parsed_key))
}
}
};
}
check_hash!(check_blake2_128, BLAKE2_128_LEN, blake2_128);
check_hash!(check_twox_64, TWOX64_LEN, twox_64);
pub fn process_key_mapped<B, E, M>(
hashers: &[StorageHasher],
key_ty: &UntrackedSymbol<TypeId>,
key_input: &B,
ext_memory: &mut E,
mut position: usize,
registry: &M::TypeRegistry,
) -> Result<KeyData, StorageError<E>>
where
B: AddressableBuffer<E>,
E: ExternalMemory,
M: AsMetadata<E>,
{
let key_data = {
if hashers.len() == 1 {
match hashers[0] {
StorageHasher::Blake2_128 => KeyData::SingleHash {
content: cut_blake2_128::<B, E>(key_ty, key_input, ext_memory, &mut position)?,
},
StorageHasher::Blake2_256 => KeyData::SingleHash {
content: cut_blake2_256::<B, E>(key_ty, key_input, ext_memory, &mut position)?,
},
StorageHasher::Blake2_128Concat => KeyData::SingleHash {
content: check_blake2_128::<B, E, M>(
key_ty,
key_input,
ext_memory,
&mut position,
registry,
)?,
},
StorageHasher::Twox128 => KeyData::SingleHash {
content: cut_twox_128::<B, E>(key_ty, key_input, ext_memory, &mut position)?,
},
StorageHasher::Twox256 => KeyData::SingleHash {
content: cut_twox_256::<B, E>(key_ty, key_input, ext_memory, &mut position)?,
},
StorageHasher::Twox64Concat => KeyData::SingleHash {
content: check_twox_64::<B, E, M>(
key_ty,
key_input,
ext_memory,
&mut position,
registry,
)?,
},
StorageHasher::Identity => {
let parsed_key = decode_with_type::<B, E, M>(
&Ty::Symbol(key_ty),
key_input,
ext_memory,
&mut position,
registry,
Propagated::new(),
)
.map_err(StorageError::ParsingKey)?;
KeyData::SingleHash {
content: KeyPart::Parsed(parsed_key),
}
}
}
} else {
let key_ty_resolved = registry
.resolve_ty(key_ty.id, ext_memory)
.map_err(|e| StorageError::ParsingKey(ParserError::Registry(e)))?;
let info = Info::from_ty(&key_ty_resolved);
match key_ty_resolved.type_def {
TypeDef::Tuple(t) => {
let tuple_elements = &t.fields;
if tuple_elements.len() != hashers.len() {
return Err(StorageError::MultipleHashesNumberMismatch);
}
let mut content: Vec<KeyPart> = Vec::new();
for index in 0..tuple_elements.len() {
match hashers[index] {
StorageHasher::Blake2_128 => content.push(cut_blake2_128::<B, E>(
&tuple_elements[index],
key_input,
ext_memory,
&mut position,
)?),
StorageHasher::Blake2_256 => content.push(cut_blake2_256::<B, E>(
&tuple_elements[index],
key_input,
ext_memory,
&mut position,
)?),
StorageHasher::Blake2_128Concat => {
content.push(check_blake2_128::<B, E, M>(
&tuple_elements[index],
key_input,
ext_memory,
&mut position,
registry,
)?)
}
StorageHasher::Twox128 => content.push(cut_twox_128::<B, E>(
&tuple_elements[index],
key_input,
ext_memory,
&mut position,
)?),
StorageHasher::Twox256 => content.push(cut_twox_256::<B, E>(
&tuple_elements[index],
key_input,
ext_memory,
&mut position,
)?),
StorageHasher::Twox64Concat => content.push(check_twox_64::<B, E, M>(
&tuple_elements[index],
key_input,
ext_memory,
&mut position,
registry,
)?),
StorageHasher::Identity => {
let parsed_key = decode_with_type::<B, E, M>(
&Ty::Symbol(&tuple_elements[index]),
key_input,
ext_memory,
&mut position,
registry,
Propagated::new(),
)
.map_err(StorageError::ParsingKey)?;
content.push(KeyPart::Parsed(parsed_key))
}
}
}
KeyData::TupleHash { content, info }
}
_ => return Err(StorageError::MultipleHashesNotATuple),
}
}
};
if position == key_input.total_len() {
Ok(key_data)
} else {
Err(StorageError::KeyPartsUnused)
}
}