#![doc = include_str!("../README.md")]
#![allow(clippy::expect_used)]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
rustdoc::broken_intra_doc_links,
rustdoc::missing_crate_level_docs,
rustdoc::invalid_codeblock_attributes,
missing_docs
)]
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(any(feature = "runtime-benchmarks", test))]
mod test_common;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
use common_primitives::benchmarks::{MsaBenchmarkHelper, SchemaBenchmarkHelper};
use sp_std::prelude::*;
mod stateful_child_tree;
pub mod types;
pub mod weights;
use crate::{stateful_child_tree::StatefulChildTree, types::*};
use common_primitives::{
msa::{
DelegatorId, MessageSourceId, MsaLookup, MsaValidator, ProviderId, SchemaGrantValidator,
},
schema::{PayloadLocation, SchemaId, SchemaInfoResponse, SchemaProvider, SchemaSetting},
stateful_storage::{
ItemizedStoragePageResponse, ItemizedStorageResponse, PageHash, PageId,
PaginatedStorageResponse,
},
};
use frame_support::{dispatch::DispatchResult, ensure, pallet_prelude::*, traits::Get};
use frame_system::pallet_prelude::*;
pub use pallet::*;
use sp_core::{bounded::BoundedVec, crypto::AccountId32};
use sp_runtime::{traits::Convert, DispatchError, MultiSignature};
pub use weights::*;
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type WeightInfo: WeightInfo;
type MsaInfoProvider: MsaLookup + MsaValidator<AccountId = Self::AccountId>;
type SchemaGrantValidator: SchemaGrantValidator<BlockNumberFor<Self>>;
type SchemaProvider: SchemaProvider<SchemaId>;
#[pallet::constant]
type MaxItemizedPageSizeBytes: Get<u32> + Default;
#[pallet::constant]
type MaxPaginatedPageSizeBytes: Get<u32> + Default;
#[pallet::constant]
type MaxItemizedBlobSizeBytes: Get<u32> + Clone + sp_std::fmt::Debug + PartialEq;
#[pallet::constant]
type MaxPaginatedPageId: Get<u16>;
#[pallet::constant]
type MaxItemizedActionsCount: Get<u32>;
#[cfg(feature = "runtime-benchmarks")]
type MsaBenchmarkHelper: MsaBenchmarkHelper<Self::AccountId>;
#[cfg(feature = "runtime-benchmarks")]
type SchemaBenchmarkHelper: SchemaBenchmarkHelper;
type KeyHasher: stateful_child_tree::MultipartKeyStorageHasher;
type ConvertIntoAccountId32: Convert<Self::AccountId, AccountId32>;
#[pallet::constant]
type MortalityWindowSize: Get<u32>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::error]
pub enum Error<T> {
PageIdExceedsMaxAllowed,
PageExceedsMaxPageSizeBytes,
InvalidSchemaId,
UnsupportedOperationForSchema,
SchemaPayloadLocationMismatch,
InvalidMessageSourceAccount,
UnauthorizedDelegate,
CorruptedState,
InvalidItemAction,
StalePageState,
InvalidSignature,
ProofHasExpired,
ProofNotYetValid,
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
ItemizedPageUpdated {
msa_id: MessageSourceId,
schema_id: SchemaId,
prev_content_hash: PageHash,
curr_content_hash: PageHash,
},
ItemizedPageDeleted {
msa_id: MessageSourceId,
schema_id: SchemaId,
prev_content_hash: PageHash,
},
PaginatedPageUpdated {
msa_id: MessageSourceId,
schema_id: SchemaId,
page_id: PageId,
prev_content_hash: PageHash,
curr_content_hash: PageHash,
},
PaginatedPageDeleted {
msa_id: MessageSourceId,
schema_id: SchemaId,
page_id: PageId,
prev_content_hash: PageHash,
},
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(
T::WeightInfo::apply_item_actions_delete(actions.len() as u32)
.max(T::WeightInfo::apply_item_actions_add(Pallet::<T>::sum_add_actions_bytes(actions)))
)]
pub fn apply_item_actions(
origin: OriginFor<T>,
#[pallet::compact] state_owner_msa_id: MessageSourceId,
#[pallet::compact] schema_id: SchemaId,
#[pallet::compact] target_hash: PageHash,
actions: BoundedVec<
ItemAction<T::MaxItemizedBlobSizeBytes>,
T::MaxItemizedActionsCount,
>,
) -> DispatchResult {
let key = ensure_signed(origin)?;
let is_pruning = actions.iter().any(|a| matches!(a, ItemAction::Delete { .. }));
let caller_msa_id = Self::check_msa_and_grants(key, state_owner_msa_id, schema_id)?;
let caller_is_state_owner = caller_msa_id == state_owner_msa_id;
Self::check_schema_for_write(
schema_id,
PayloadLocation::Itemized,
caller_is_state_owner,
is_pruning,
)?;
Self::update_itemized(state_owner_msa_id, schema_id, target_hash, actions)?;
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::upsert_page(payload.len() as u32))]
pub fn upsert_page(
origin: OriginFor<T>,
#[pallet::compact] state_owner_msa_id: MessageSourceId,
#[pallet::compact] schema_id: SchemaId,
#[pallet::compact] page_id: PageId,
#[pallet::compact] target_hash: PageHash,
payload: BoundedVec<u8, <T>::MaxPaginatedPageSizeBytes>,
) -> DispatchResult {
let provider_key = ensure_signed(origin)?;
ensure!(page_id <= T::MaxPaginatedPageId::get(), Error::<T>::PageIdExceedsMaxAllowed);
let caller_msa_id =
Self::check_msa_and_grants(provider_key, state_owner_msa_id, schema_id)?;
let caller_is_state_owner = caller_msa_id == state_owner_msa_id;
Self::check_schema_for_write(
schema_id,
PayloadLocation::Paginated,
caller_is_state_owner,
false,
)?;
Self::update_paginated(
state_owner_msa_id,
schema_id,
page_id,
target_hash,
PaginatedPage::<T>::from(payload),
)?;
Ok(())
}
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::delete_page())]
pub fn delete_page(
origin: OriginFor<T>,
#[pallet::compact] state_owner_msa_id: MessageSourceId,
#[pallet::compact] schema_id: SchemaId,
#[pallet::compact] page_id: PageId,
#[pallet::compact] target_hash: PageHash,
) -> DispatchResult {
let provider_key = ensure_signed(origin)?;
ensure!(page_id <= T::MaxPaginatedPageId::get(), Error::<T>::PageIdExceedsMaxAllowed);
let caller_msa_id =
Self::check_msa_and_grants(provider_key, state_owner_msa_id, schema_id)?;
let caller_is_state_owner = caller_msa_id == state_owner_msa_id;
Self::check_schema_for_write(
schema_id,
PayloadLocation::Paginated,
caller_is_state_owner,
true,
)?;
Self::delete_paginated(state_owner_msa_id, schema_id, page_id, target_hash)?;
Ok(())
}
#[pallet::call_index(3)]
#[pallet::weight(
T::WeightInfo::apply_item_actions_with_signature_v2_delete(payload.actions.len() as u32)
.max(T::WeightInfo::apply_item_actions_with_signature_v2_add(Pallet::<T>::sum_add_actions_bytes(&payload.actions)))
)]
#[allow(deprecated)]
#[deprecated(note = "please use `apply_item_actions_with_signature_v2` instead")]
pub fn apply_item_actions_with_signature(
origin: OriginFor<T>,
delegator_key: T::AccountId,
proof: MultiSignature,
payload: ItemizedSignaturePayload<T>,
) -> DispatchResult {
ensure_signed(origin)?;
let is_pruning = payload.actions.iter().any(|a| matches!(a, ItemAction::Delete { .. }));
Self::check_payload_expiration(
frame_system::Pallet::<T>::block_number(),
payload.expiration,
)?;
Self::check_signature(&proof, &delegator_key.clone(), payload.encode())?;
Self::check_msa(delegator_key, payload.msa_id)?;
Self::check_schema_for_write(
payload.schema_id,
PayloadLocation::Itemized,
true,
is_pruning,
)?;
Self::update_itemized(
payload.msa_id,
payload.schema_id,
payload.target_hash,
payload.actions,
)?;
Ok(())
}
#[pallet::call_index(4)]
#[pallet::weight(T::WeightInfo::upsert_page_with_signature_v2(payload.payload.len() as u32))]
#[allow(deprecated)]
#[deprecated(note = "please use `upsert_page_with_signature_v2` instead")]
pub fn upsert_page_with_signature(
origin: OriginFor<T>,
delegator_key: T::AccountId,
proof: MultiSignature,
payload: PaginatedUpsertSignaturePayload<T>,
) -> DispatchResult {
ensure_signed(origin)?;
ensure!(
payload.page_id <= T::MaxPaginatedPageId::get(),
Error::<T>::PageIdExceedsMaxAllowed
);
Self::check_payload_expiration(
frame_system::Pallet::<T>::block_number(),
payload.expiration,
)?;
Self::check_signature(&proof, &delegator_key.clone(), payload.encode())?;
Self::check_msa(delegator_key, payload.msa_id)?;
Self::check_schema_for_write(
payload.schema_id,
PayloadLocation::Paginated,
true,
false,
)?;
Self::update_paginated(
payload.msa_id,
payload.schema_id,
payload.page_id,
payload.target_hash,
PaginatedPage::<T>::from(payload.payload),
)?;
Ok(())
}
#[pallet::call_index(5)]
#[pallet::weight(T::WeightInfo::delete_page_with_signature_v2())]
#[allow(deprecated)]
#[deprecated(note = "please use `delete_page_with_signature_v2` instead")]
pub fn delete_page_with_signature(
origin: OriginFor<T>,
delegator_key: T::AccountId,
proof: MultiSignature,
payload: PaginatedDeleteSignaturePayload<T>,
) -> DispatchResult {
ensure_signed(origin)?;
ensure!(
payload.page_id <= T::MaxPaginatedPageId::get(),
Error::<T>::PageIdExceedsMaxAllowed
);
Self::check_payload_expiration(
frame_system::Pallet::<T>::block_number(),
payload.expiration,
)?;
Self::check_signature(&proof, &delegator_key.clone(), payload.encode())?;
Self::check_msa(delegator_key, payload.msa_id)?;
Self::check_schema_for_write(
payload.schema_id,
PayloadLocation::Paginated,
true,
true,
)?;
Self::delete_paginated(
payload.msa_id,
payload.schema_id,
payload.page_id,
payload.target_hash,
)?;
Ok(())
}
#[pallet::call_index(6)]
#[pallet::weight(
T::WeightInfo::apply_item_actions_with_signature_v2_delete(payload.actions.len() as u32)
.max(T::WeightInfo::apply_item_actions_with_signature_v2_add(Pallet::<T>::sum_add_actions_bytes(&payload.actions)))
)]
pub fn apply_item_actions_with_signature_v2(
origin: OriginFor<T>,
delegator_key: T::AccountId,
proof: MultiSignature,
payload: ItemizedSignaturePayloadV2<T>,
) -> DispatchResult {
ensure_signed(origin)?;
let is_pruning = payload.actions.iter().any(|a| matches!(a, ItemAction::Delete { .. }));
Self::check_payload_expiration(
frame_system::Pallet::<T>::block_number(),
payload.expiration,
)?;
Self::check_signature(&proof, &delegator_key.clone(), payload.encode())?;
let state_owner_msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&delegator_key)
.map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
Self::check_schema_for_write(
payload.schema_id,
PayloadLocation::Itemized,
true,
is_pruning,
)?;
Self::update_itemized(
state_owner_msa_id,
payload.schema_id,
payload.target_hash,
payload.actions,
)?;
Ok(())
}
#[pallet::call_index(7)]
#[pallet::weight(T::WeightInfo::upsert_page_with_signature_v2(payload.payload.len() as u32))]
pub fn upsert_page_with_signature_v2(
origin: OriginFor<T>,
delegator_key: T::AccountId,
proof: MultiSignature,
payload: PaginatedUpsertSignaturePayloadV2<T>,
) -> DispatchResult {
ensure_signed(origin)?;
ensure!(
payload.page_id <= T::MaxPaginatedPageId::get(),
Error::<T>::PageIdExceedsMaxAllowed
);
Self::check_payload_expiration(
frame_system::Pallet::<T>::block_number(),
payload.expiration,
)?;
Self::check_signature(&proof, &delegator_key.clone(), payload.encode())?;
let state_owner_msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&delegator_key)
.map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
Self::check_schema_for_write(
payload.schema_id,
PayloadLocation::Paginated,
true,
false,
)?;
Self::update_paginated(
state_owner_msa_id,
payload.schema_id,
payload.page_id,
payload.target_hash,
PaginatedPage::<T>::from(payload.payload),
)?;
Ok(())
}
#[pallet::call_index(8)]
#[pallet::weight(T::WeightInfo::delete_page_with_signature_v2())]
pub fn delete_page_with_signature_v2(
origin: OriginFor<T>,
delegator_key: T::AccountId,
proof: MultiSignature,
payload: PaginatedDeleteSignaturePayloadV2<T>,
) -> DispatchResult {
ensure_signed(origin)?;
ensure!(
payload.page_id <= T::MaxPaginatedPageId::get(),
Error::<T>::PageIdExceedsMaxAllowed
);
Self::check_payload_expiration(
frame_system::Pallet::<T>::block_number(),
payload.expiration,
)?;
Self::check_signature(&proof, &delegator_key.clone(), payload.encode())?;
let state_owner_msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&delegator_key)
.map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
Self::check_schema_for_write(
payload.schema_id,
PayloadLocation::Paginated,
true,
true,
)?;
Self::delete_paginated(
state_owner_msa_id,
payload.schema_id,
payload.page_id,
payload.target_hash,
)?;
Ok(())
}
}
}
impl<T: Config> Pallet<T> {
pub fn sum_add_actions_bytes(
actions: &BoundedVec<
ItemAction<<T as Config>::MaxItemizedBlobSizeBytes>,
<T as Config>::MaxItemizedActionsCount,
>,
) -> u32 {
actions.iter().fold(0, |acc, a| {
acc.saturating_add(match a {
ItemAction::Add { data } => data.len() as u32,
_ => 0,
})
})
}
pub fn get_paginated_storage(
msa_id: MessageSourceId,
schema_id: SchemaId,
) -> Result<Vec<PaginatedStorageResponse>, DispatchError> {
Self::check_schema_for_read(schema_id, PayloadLocation::Paginated)?;
let prefix: PaginatedPrefixKey = (schema_id,);
Ok(StatefulChildTree::<T::KeyHasher>::prefix_iterator::<
PaginatedPage<T>,
PaginatedKey,
PaginatedPrefixKey,
>(&msa_id, PALLET_STORAGE_PREFIX, PAGINATED_STORAGE_PREFIX, &prefix)
.map(|(k, v)| {
let content_hash = v.get_hash();
let nonce = v.nonce;
PaginatedStorageResponse::new(
k.1,
msa_id,
schema_id,
content_hash,
nonce,
v.data.into_inner(),
)
})
.collect())
}
pub fn get_itemized_storage(
msa_id: MessageSourceId,
schema_id: SchemaId,
) -> Result<ItemizedStoragePageResponse, DispatchError> {
Self::check_schema_for_read(schema_id, PayloadLocation::Itemized)?;
let page = Self::get_itemized_page_for(msa_id, schema_id)?.unwrap_or_default();
let items: Vec<ItemizedStorageResponse> = ItemizedOperations::<T>::try_parse(&page, false)
.map_err(|_| Error::<T>::CorruptedState)?
.items
.iter()
.map(|(key, v)| ItemizedStorageResponse::new(*key, v.to_vec()))
.collect();
Ok(ItemizedStoragePageResponse::new(msa_id, schema_id, page.get_hash(), page.nonce, items))
}
pub fn check_payload_expiration(
current_block: BlockNumberFor<T>,
payload_expire_block: BlockNumberFor<T>,
) -> Result<(), DispatchError> {
ensure!(payload_expire_block > current_block, Error::<T>::ProofHasExpired);
let max_supported_signature_block = Self::mortality_block_limit(current_block);
ensure!(payload_expire_block < max_supported_signature_block, Error::<T>::ProofNotYetValid);
Ok(())
}
pub fn check_signature(
signature: &MultiSignature,
signer: &T::AccountId,
payload: Vec<u8>,
) -> DispatchResult {
let key = T::ConvertIntoAccountId32::convert(signer.clone());
ensure!(
common_runtime::signature::check_signature(signature, key, payload),
Error::<T>::InvalidSignature
);
Ok(())
}
fn mortality_block_limit(current_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
current_block + BlockNumberFor::<T>::from(T::MortalityWindowSize::get())
}
fn check_schema_for_read(
schema_id: SchemaId,
expected_payload_location: PayloadLocation,
) -> Result<SchemaInfoResponse, DispatchError> {
let schema = T::SchemaProvider::get_schema_info_by_id(schema_id)
.ok_or(Error::<T>::InvalidSchemaId)?;
ensure!(
schema.payload_location == expected_payload_location,
Error::<T>::SchemaPayloadLocationMismatch
);
Ok(schema)
}
fn check_schema_for_write(
schema_id: SchemaId,
expected_payload_location: PayloadLocation,
is_payload_signed: bool,
is_deleting: bool,
) -> DispatchResult {
let schema = Self::check_schema_for_read(schema_id, expected_payload_location)?;
if schema.settings.contains(&SchemaSetting::SignatureRequired) {
ensure!(is_payload_signed, Error::<T>::UnsupportedOperationForSchema);
}
if schema.settings.contains(&SchemaSetting::AppendOnly) {
ensure!(!is_deleting, Error::<T>::UnsupportedOperationForSchema);
}
Ok(())
}
fn check_msa_and_grants(
key: T::AccountId,
state_owner_msa_id: MessageSourceId,
schema_id: SchemaId,
) -> Result<MessageSourceId, DispatchError> {
let caller_msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&key)
.map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
if caller_msa_id != state_owner_msa_id {
let current_block = frame_system::Pallet::<T>::block_number();
T::SchemaGrantValidator::ensure_valid_schema_grant(
ProviderId(caller_msa_id),
DelegatorId(state_owner_msa_id),
schema_id,
current_block,
)
.map_err(|_| Error::<T>::UnauthorizedDelegate)?;
}
Ok(caller_msa_id)
}
fn check_msa(key: T::AccountId, expected_msa_id: MessageSourceId) -> DispatchResult {
let state_owner_msa_id = T::MsaInfoProvider::ensure_valid_msa_key(&key)
.map_err(|_| Error::<T>::InvalidMessageSourceAccount)?;
ensure!(state_owner_msa_id == expected_msa_id, Error::<T>::InvalidMessageSourceAccount);
Ok(())
}
fn update_itemized(
state_owner_msa_id: MessageSourceId,
schema_id: SchemaId,
target_hash: PageHash,
actions: BoundedVec<ItemAction<T::MaxItemizedBlobSizeBytes>, T::MaxItemizedActionsCount>,
) -> DispatchResult {
let key: ItemizedKey = (schema_id,);
let existing_page =
Self::get_itemized_page_for(state_owner_msa_id, schema_id)?.unwrap_or_default();
let prev_content_hash = existing_page.get_hash();
ensure!(target_hash == prev_content_hash, Error::<T>::StalePageState);
let mut updated_page =
ItemizedOperations::<T>::apply_item_actions(&existing_page, &actions[..]).map_err(
|e| match e {
PageError::ErrorParsing(err) => {
log::warn!(
"failed parsing Itemized msa={:?} schema_id={:?} {:?}",
state_owner_msa_id,
schema_id,
err
);
Error::<T>::CorruptedState
},
_ => Error::<T>::InvalidItemAction,
},
)?;
updated_page.nonce = existing_page.nonce.wrapping_add(1);
match updated_page.is_empty() {
true => {
StatefulChildTree::<T::KeyHasher>::kill(
&state_owner_msa_id,
PALLET_STORAGE_PREFIX,
ITEMIZED_STORAGE_PREFIX,
&key,
);
Self::deposit_event(Event::ItemizedPageDeleted {
msa_id: state_owner_msa_id,
schema_id,
prev_content_hash,
});
},
false => {
StatefulChildTree::<T::KeyHasher>::write(
&state_owner_msa_id,
PALLET_STORAGE_PREFIX,
ITEMIZED_STORAGE_PREFIX,
&key,
&updated_page,
);
Self::deposit_event(Event::ItemizedPageUpdated {
msa_id: state_owner_msa_id,
schema_id,
curr_content_hash: updated_page.get_hash(),
prev_content_hash,
});
},
};
Ok(())
}
fn update_paginated(
state_owner_msa_id: MessageSourceId,
schema_id: SchemaId,
page_id: PageId,
target_hash: PageHash,
mut new_page: PaginatedPage<T>,
) -> DispatchResult {
let keys: PaginatedKey = (schema_id, page_id);
let existing_page: PaginatedPage<T> =
Self::get_paginated_page_for(state_owner_msa_id, schema_id, page_id)?
.unwrap_or_default();
let prev_content_hash: PageHash = existing_page.get_hash();
ensure!(target_hash == prev_content_hash, Error::<T>::StalePageState);
new_page.nonce = existing_page.nonce.wrapping_add(1);
StatefulChildTree::<T::KeyHasher>::write(
&state_owner_msa_id,
PALLET_STORAGE_PREFIX,
PAGINATED_STORAGE_PREFIX,
&keys,
&new_page,
);
Self::deposit_event(Event::PaginatedPageUpdated {
msa_id: state_owner_msa_id,
schema_id,
page_id,
curr_content_hash: new_page.get_hash(),
prev_content_hash,
});
Ok(())
}
fn delete_paginated(
state_owner_msa_id: MessageSourceId,
schema_id: SchemaId,
page_id: PageId,
target_hash: PageHash,
) -> DispatchResult {
let keys: PaginatedKey = (schema_id, page_id);
if let Some(existing_page) =
Self::get_paginated_page_for(state_owner_msa_id, schema_id, page_id)?
{
let prev_content_hash: PageHash = existing_page.get_hash();
ensure!(target_hash == prev_content_hash, Error::<T>::StalePageState);
StatefulChildTree::<T::KeyHasher>::kill(
&state_owner_msa_id,
PALLET_STORAGE_PREFIX,
PAGINATED_STORAGE_PREFIX,
&keys,
);
Self::deposit_event(Event::PaginatedPageDeleted {
msa_id: state_owner_msa_id,
schema_id,
page_id,
prev_content_hash,
});
}
Ok(())
}
pub fn get_paginated_page_for(
msa_id: MessageSourceId,
schema_id: SchemaId,
page_id: PageId,
) -> Result<Option<PaginatedPage<T>>, DispatchError> {
let keys: PaginatedKey = (schema_id, page_id);
Ok(StatefulChildTree::<T::KeyHasher>::try_read::<_, PaginatedPage<T>>(
&msa_id,
PALLET_STORAGE_PREFIX,
PAGINATED_STORAGE_PREFIX,
&keys,
)
.map_err(|_| Error::<T>::CorruptedState)?)
}
pub fn get_itemized_page_for(
msa_id: MessageSourceId,
schema_id: SchemaId,
) -> Result<Option<ItemizedPage<T>>, DispatchError> {
let keys: ItemizedKey = (schema_id,);
Ok(StatefulChildTree::<T::KeyHasher>::try_read::<_, ItemizedPage<T>>(
&msa_id,
PALLET_STORAGE_PREFIX,
ITEMIZED_STORAGE_PREFIX,
&keys,
)
.map_err(|_| Error::<T>::CorruptedState)?)
}
}