feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,94 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Traits, types and structs to support a bounded BTreeMap.
use crate::storage::StorageDecodeLength;
pub use pezsp_runtime::BoundedBTreeMap;
impl<K, V, S> StorageDecodeLength for BoundedBTreeMap<K, V, S> {}
#[cfg(test)]
pub mod test {
use super::*;
use crate::Twox128;
use alloc::collections::btree_map::BTreeMap;
use pezframe_support::traits::{ConstU32, Get};
use pezsp_io::TestExternalities;
#[crate::storage_alias]
type Foo = StorageValue<Prefix, BoundedBTreeMap<u32, (), ConstU32<7>>>;
#[crate::storage_alias]
type FooMap = StorageMap<Prefix, Twox128, u32, BoundedBTreeMap<u32, (), ConstU32<7>>>;
#[crate::storage_alias]
type FooDoubleMap =
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, BoundedBTreeMap<u32, (), ConstU32<7>>>;
fn map_from_keys<K>(keys: &[K]) -> BTreeMap<K, ()>
where
K: Ord + Copy,
{
keys.iter().copied().zip(std::iter::repeat(())).collect()
}
fn boundedmap_from_keys<K, S>(keys: &[K]) -> BoundedBTreeMap<K, (), S>
where
K: Ord + Copy,
S: Get<u32>,
{
map_from_keys(keys).try_into().unwrap()
}
#[test]
fn decode_len_works() {
TestExternalities::default().execute_with(|| {
let bounded = boundedmap_from_keys::<u32, ConstU32<7>>(&[1, 2, 3]);
Foo::put(bounded);
assert_eq!(Foo::decode_len().unwrap(), 3);
});
TestExternalities::default().execute_with(|| {
let bounded = boundedmap_from_keys::<u32, ConstU32<7>>(&[1, 2, 3]);
FooMap::insert(1, bounded);
assert_eq!(FooMap::decode_len(1).unwrap(), 3);
assert!(FooMap::decode_len(0).is_none());
assert!(FooMap::decode_len(2).is_none());
});
TestExternalities::default().execute_with(|| {
let bounded = boundedmap_from_keys::<u32, ConstU32<7>>(&[1, 2, 3]);
FooDoubleMap::insert(1, 1, bounded);
assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3);
assert!(FooDoubleMap::decode_len(2, 1).is_none());
assert!(FooDoubleMap::decode_len(1, 2).is_none());
assert!(FooDoubleMap::decode_len(2, 2).is_none());
});
TestExternalities::default().execute_with(|| {
let bounded = boundedmap_from_keys::<u32, ConstU32<7>>(&[1, 2, 3]);
FooDoubleMap::insert(1, 1, bounded.clone());
FooDoubleMap::insert(2, 2, bounded); // duplicate value
assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3);
assert_eq!(FooDoubleMap::decode_len(2, 2).unwrap(), 3);
assert!(FooDoubleMap::decode_len(2, 1).is_none());
assert!(FooDoubleMap::decode_len(1, 2).is_none());
});
}
}
@@ -0,0 +1,83 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Traits, types and structs to support a bounded `BTreeSet`.
use pezframe_support::storage::StorageDecodeNonDedupLength;
pub use pezsp_runtime::BoundedBTreeSet;
impl<T, S> StorageDecodeNonDedupLength for BoundedBTreeSet<T, S> {}
#[cfg(test)]
pub mod test {
use super::*;
use crate::Twox128;
use alloc::collections::btree_set::BTreeSet;
use pezframe_support::traits::{ConstU32, Get};
use pezsp_io::TestExternalities;
#[crate::storage_alias]
type Foo = StorageValue<Prefix, BoundedBTreeSet<u32, ConstU32<7>>>;
#[crate::storage_alias]
type FooMap = StorageMap<Prefix, Twox128, u32, BoundedBTreeSet<u32, ConstU32<7>>>;
#[crate::storage_alias]
type FooDoubleMap =
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, BoundedBTreeSet<u32, ConstU32<7>>>;
fn set_from_keys<T>(keys: &[T]) -> BTreeSet<T>
where
T: Ord + Copy,
{
keys.iter().copied().collect()
}
fn boundedset_from_keys<T, S>(keys: &[T]) -> BoundedBTreeSet<T, S>
where
T: Ord + Copy,
S: Get<u32>,
{
set_from_keys(keys).try_into().unwrap()
}
#[test]
fn decode_non_dedup_len_works() {
TestExternalities::default().execute_with(|| {
let bounded = boundedset_from_keys::<u32, ConstU32<7>>(&[1, 2, 3]);
Foo::put(bounded);
assert_eq!(Foo::decode_non_dedup_len().unwrap(), 3);
});
TestExternalities::default().execute_with(|| {
let bounded = boundedset_from_keys::<u32, ConstU32<7>>(&[1, 2, 3]);
FooMap::insert(1, bounded);
assert_eq!(FooMap::decode_non_dedup_len(1).unwrap(), 3);
assert!(FooMap::decode_non_dedup_len(0).is_none());
assert!(FooMap::decode_non_dedup_len(2).is_none());
});
TestExternalities::default().execute_with(|| {
let bounded = boundedset_from_keys::<u32, ConstU32<7>>(&[1, 2, 3]);
FooDoubleMap::insert(1, 1, bounded);
assert_eq!(FooDoubleMap::decode_non_dedup_len(1, 1).unwrap(), 3);
assert!(FooDoubleMap::decode_non_dedup_len(2, 1).is_none());
assert!(FooDoubleMap::decode_non_dedup_len(1, 2).is_none());
assert!(FooDoubleMap::decode_non_dedup_len(2, 2).is_none());
});
}
}
@@ -0,0 +1,77 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Traits, types and structs to support putting a bounded vector into storage, as a raw value, map
//! or a double map.
use crate::{
storage::{StorageDecodeLength, StorageTryAppend},
traits::Get,
};
pub use pezsp_runtime::{BoundedSlice, BoundedVec};
impl<T, S> StorageDecodeLength for BoundedVec<T, S> {}
impl<T, S: Get<u32>> StorageTryAppend<T> for BoundedVec<T, S> {
fn bound() -> usize {
S::get() as usize
}
}
#[cfg(test)]
pub mod test {
use super::*;
use crate::{traits::ConstU32, Twox128};
use pezsp_io::TestExternalities;
use pezsp_runtime::bounded_vec;
#[crate::storage_alias]
type Foo = StorageValue<Prefix, BoundedVec<u32, ConstU32<7>>>;
#[crate::storage_alias]
type FooMap = StorageMap<Prefix, Twox128, u32, BoundedVec<u32, ConstU32<7>>>;
#[crate::storage_alias]
type FooDoubleMap =
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, BoundedVec<u32, ConstU32<7>>>;
#[test]
fn decode_len_works() {
TestExternalities::default().execute_with(|| {
let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
Foo::put(bounded);
assert_eq!(Foo::decode_len().unwrap(), 3);
});
TestExternalities::default().execute_with(|| {
let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
FooMap::insert(1, bounded);
assert_eq!(FooMap::decode_len(1).unwrap(), 3);
assert!(FooMap::decode_len(0).is_none());
assert!(FooMap::decode_len(2).is_none());
});
TestExternalities::default().execute_with(|| {
let bounded: BoundedVec<u32, ConstU32<7>> = bounded_vec![1, 2, 3];
FooDoubleMap::insert(1, 1, bounded);
assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3);
assert!(FooDoubleMap::decode_len(2, 1).is_none());
assert!(FooDoubleMap::decode_len(1, 2).is_none());
assert!(FooDoubleMap::decode_len(2, 2).is_none());
});
}
}
@@ -0,0 +1,239 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Operation on runtime child storages.
//!
//! This module is a currently only a variant of unhashed with additional `child_info`.
// NOTE: could replace unhashed by having only one kind of storage (top trie being the child info
// of null length parent storage key).
use alloc::vec::Vec;
use codec::{Codec, Decode, Encode};
pub use pezsp_core::storage::{ChildInfo, ChildType, StateVersion};
pub use pezsp_io::{KillStorageResult, MultiRemovalResults};
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T: Decode + Sized>(child_info: &ChildInfo, key: &[u8]) -> Option<T> {
match child_info.child_type() {
ChildType::ParentKeyId => {
let storage_key = child_info.storage_key();
pezsp_io::default_child_storage::get(storage_key, key).and_then(|v| {
Decode::decode(&mut &v[..]).map(Some).unwrap_or_else(|_| {
// TODO #3700: error should be handleable.
log::error!(
target: "runtime::storage",
"Corrupted state in child trie at {:?}/{:?}",
storage_key,
key,
);
None
})
})
},
}
}
/// Return the value of the item in storage under `key`, or the type's default if there is no
/// explicit entry.
pub fn get_or_default<T: Decode + Sized + Default>(child_info: &ChildInfo, key: &[u8]) -> T {
get(child_info, key).unwrap_or_default()
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry.
pub fn get_or<T: Decode + Sized>(child_info: &ChildInfo, key: &[u8], default_value: T) -> T {
get(child_info, key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry.
pub fn get_or_else<T: Decode + Sized, F: FnOnce() -> T>(
child_info: &ChildInfo,
key: &[u8],
default_value: F,
) -> T {
get(child_info, key).unwrap_or_else(default_value)
}
/// Put `value` in storage under `key`.
pub fn put<T: Encode>(child_info: &ChildInfo, key: &[u8], value: &T) {
match child_info.child_type() {
ChildType::ParentKeyId => value.using_encoded(|slice| {
pezsp_io::default_child_storage::set(child_info.storage_key(), key, slice)
}),
}
}
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
pub fn take<T: Decode + Sized>(child_info: &ChildInfo, key: &[u8]) -> Option<T> {
let r = get(child_info, key);
if r.is_some() {
kill(child_info, key);
}
r
}
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
/// the default for its type.
pub fn take_or_default<T: Codec + Sized + Default>(child_info: &ChildInfo, key: &[u8]) -> T {
take(child_info, key).unwrap_or_default()
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or<T: Codec + Sized>(child_info: &ChildInfo, key: &[u8], default_value: T) -> T {
take(child_info, key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or_else<T: Codec + Sized, F: FnOnce() -> T>(
child_info: &ChildInfo,
key: &[u8],
default_value: F,
) -> T {
take(child_info, key).unwrap_or_else(default_value)
}
/// Check to see if `key` has an explicit entry in storage.
pub fn exists(child_info: &ChildInfo, key: &[u8]) -> bool {
match child_info.child_type() {
ChildType::ParentKeyId =>
pezsp_io::default_child_storage::exists(child_info.storage_key(), key),
}
}
/// Remove all `storage_key` key/values
///
/// Deletes all keys from the overlay and up to `limit` keys from the backend if
/// it is set to `Some`. No limit is applied when `limit` is set to `None`.
///
/// The limit can be used to partially delete a child trie in case it is too large
/// to delete in one go (block).
///
/// # Note
///
/// Please note that keys that are residing in the overlay for that child trie when
/// issuing this call are all deleted without counting towards the `limit`. Only keys
/// written during the current block are part of the overlay. Deleting with a `limit`
/// mostly makes sense with an empty overlay for that child trie.
///
/// Calling this function multiple times per block for the same `storage_key` does
/// not make much sense because it is not cumulative when called inside the same block.
/// Use this function to distribute the deletion of a single child trie across multiple
/// blocks.
#[deprecated = "Use `clear_storage` instead"]
pub fn kill_storage(child_info: &ChildInfo, limit: Option<u32>) -> KillStorageResult {
match child_info.child_type() {
ChildType::ParentKeyId =>
pezsp_io::default_child_storage::storage_kill(child_info.storage_key(), limit),
}
}
/// Partially clear the child storage of each key-value pair.
///
/// # Limit
///
/// A *limit* should always be provided through `maybe_limit`. This is one fewer than the
/// maximum number of backend iterations which may be done by this operation and as such
/// represents the maximum number of backend deletions which may happen. A *limit* of zero
/// implies that no keys will be deleted, though there may be a single iteration done.
///
/// The limit can be used to partially delete storage items in case it is too large or costly
/// to delete all in a single operation.
///
/// # Cursor
///
/// A *cursor* may be passed in to this operation with `maybe_cursor`. `None` should only be
/// passed once (in the initial call) for any attempt to clear storage. In general, subsequent calls
/// operating on the same prefix should pass `Some` and this value should be equal to the
/// previous call result's `maybe_cursor` field. The only exception to this is when you can
/// guarantee that the subsequent call is in a new block; in this case the previous call's result
/// cursor need not be passed in and a `None` may be passed instead. This exception may be useful
/// then making this call solely from a block-hook such as `on_initialize`.
/// Returns [`MultiRemovalResults`] to inform about the result. Once the resultant `maybe_cursor`
/// field is `None`, then no further items remain to be deleted.
///
/// NOTE: After the initial call for any given child storage, it is important that no keys further
/// keys are inserted. If so, then they may or may not be deleted by subsequent calls.
///
/// # Note
///
/// Please note that keys which are residing in the overlay for the child are deleted without
/// counting towards the `limit`.
pub fn clear_storage(
child_info: &ChildInfo,
maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> MultiRemovalResults {
// TODO: Once the network has upgraded to include the new host functions, this code can be
// enabled.
// pezsp_io::default_child_storage::storage_kill(prefix, maybe_limit, maybe_cursor)
let r = match child_info.child_type() {
ChildType::ParentKeyId =>
pezsp_io::default_child_storage::storage_kill(child_info.storage_key(), maybe_limit),
};
use pezsp_io::KillStorageResult::*;
let (maybe_cursor, backend) = match r {
AllRemoved(db) => (None, db),
SomeRemaining(db) => (Some(child_info.storage_key().to_vec()), db),
};
MultiRemovalResults { maybe_cursor, backend, unique: backend, loops: backend }
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill(child_info: &ChildInfo, key: &[u8]) {
match child_info.child_type() {
ChildType::ParentKeyId => {
pezsp_io::default_child_storage::clear(child_info.storage_key(), key);
},
}
}
/// Get a Vec of bytes from storage.
pub fn get_raw(child_info: &ChildInfo, key: &[u8]) -> Option<Vec<u8>> {
match child_info.child_type() {
ChildType::ParentKeyId => pezsp_io::default_child_storage::get(child_info.storage_key(), key),
}
}
/// Put a raw byte slice into storage.
pub fn put_raw(child_info: &ChildInfo, key: &[u8], value: &[u8]) {
match child_info.child_type() {
ChildType::ParentKeyId =>
pezsp_io::default_child_storage::set(child_info.storage_key(), key, value),
}
}
/// Calculate current child root value.
pub fn root(child_info: &ChildInfo, version: StateVersion) -> Vec<u8> {
match child_info.child_type() {
ChildType::ParentKeyId =>
pezsp_io::default_child_storage::root(child_info.storage_key(), version),
}
}
/// Return the length in bytes of the value without reading it. `None` if it does not exist.
pub fn len(child_info: &ChildInfo, key: &[u8]) -> Option<u32> {
match child_info.child_type() {
ChildType::ParentKeyId => {
let mut buffer = [0; 0];
pezsp_io::default_child_storage::read(child_info.storage_key(), key, &mut buffer, 0)
},
}
}
@@ -0,0 +1,656 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
hash::{ReversibleStorageHasher, StorageHasher},
storage::{self, storage_prefix, unhashed, KeyPrefixIterator, PrefixIterator, StorageAppend},
Never,
};
use alloc::vec::Vec;
use codec::{Decode, Encode, EncodeLike, FullCodec, FullEncode};
/// Generator for `StorageDoubleMap` used by `decl_storage`.
///
/// # Mapping of keys to a storage path
///
/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts.
/// The first part is a hash of a concatenation of the `key1_prefix` and `Key1`. And the second part
/// is a hash of a `Key2`.
///
/// Thus value for (key1, key2) is stored at:
/// ```nocompile
/// Twox128(pezpallet_prefix) ++ Twox128(storage_prefix) ++ Hasher1(encode(key1)) ++ Hasher2(encode(key2))
/// ```
///
/// # Warning
///
/// If the key1s are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
/// `blake2_256` must be used for Hasher1. Otherwise, other values in storage can be compromised.
/// If the key2s are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
/// `blake2_256` must be used for Hasher2. Otherwise, other items in storage with the same first
/// key can be compromised.
pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
/// The type that get/take returns.
type Query;
/// Hasher for the first key.
type Hasher1: StorageHasher;
/// Hasher for the second key.
type Hasher2: StorageHasher;
/// Pallet prefix. Used for generating final key.
fn pezpallet_prefix() -> &'static [u8];
/// Storage prefix. Used for generating final key.
fn storage_prefix() -> &'static [u8];
/// The full prefix; just the hash of `pezpallet_prefix` concatenated to the hash of
/// `storage_prefix`.
fn prefix_hash() -> [u8; 32];
/// Convert an optional value retrieved from storage to the type queried.
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
/// Convert a query to an optional value into storage.
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
/// Generate the first part of the key used in top storage.
fn storage_double_map_final_key1<KArg1>(k1: KArg1) -> Vec<u8>
where
KArg1: EncodeLike<K1>,
{
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key_hashed = k1.using_encoded(Self::Hasher1::hash);
let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.as_ref().len());
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key_hashed.as_ref());
final_key
}
/// Generate the full key used in top storage.
fn storage_double_map_final_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
{
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key1_hashed = k1.using_encoded(Self::Hasher1::hash);
let key2_hashed = k2.using_encoded(Self::Hasher2::hash);
let mut final_key = Vec::with_capacity(
storage_prefix.len() + key1_hashed.as_ref().len() + key2_hashed.as_ref().len(),
);
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key1_hashed.as_ref());
final_key.extend_from_slice(key2_hashed.as_ref());
final_key
}
}
impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G
where
K1: FullEncode,
K2: FullEncode,
V: FullCodec,
G: StorageDoubleMap<K1, K2, V>,
{
type Query = G::Query;
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
{
Self::storage_double_map_final_key(k1, k2)
}
fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
{
unhashed::exists(&Self::storage_double_map_final_key(k1, k2))
}
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
{
G::from_optional_value_to_query(unhashed::get(&Self::storage_double_map_final_key(k1, k2)))
}
fn try_get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Result<V, ()>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
{
unhashed::get(&Self::storage_double_map_final_key(k1, k2)).ok_or(())
}
fn set<KArg1: EncodeLike<K1>, KArg2: EncodeLike<K2>>(k1: KArg1, k2: KArg2, q: Self::Query) {
match G::from_query_to_optional_value(q) {
Some(v) => Self::insert(k1, k2, v),
None => Self::remove(k1, k2),
}
}
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
{
let final_key = Self::storage_double_map_final_key(k1, k2);
let value = unhashed::take(&final_key);
G::from_optional_value_to_query(value)
}
fn swap<XKArg1, XKArg2, YKArg1, YKArg2>(x_k1: XKArg1, x_k2: XKArg2, y_k1: YKArg1, y_k2: YKArg2)
where
XKArg1: EncodeLike<K1>,
XKArg2: EncodeLike<K2>,
YKArg1: EncodeLike<K1>,
YKArg2: EncodeLike<K2>,
{
let final_x_key = Self::storage_double_map_final_key(x_k1, x_k2);
let final_y_key = Self::storage_double_map_final_key(y_k1, y_k2);
let v1 = unhashed::get_raw(&final_x_key);
if let Some(val) = unhashed::get_raw(&final_y_key) {
unhashed::put_raw(&final_x_key, &val);
} else {
unhashed::kill(&final_x_key)
}
if let Some(val) = v1 {
unhashed::put_raw(&final_y_key, &val);
} else {
unhashed::kill(&final_y_key)
}
}
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
VArg: EncodeLike<V>,
{
unhashed::put(&Self::storage_double_map_final_key(k1, k2), &val)
}
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
{
unhashed::kill(&Self::storage_double_map_final_key(k1, k2))
}
fn remove_prefix<KArg1>(k1: KArg1, maybe_limit: Option<u32>) -> pezsp_io::KillStorageResult
where
KArg1: EncodeLike<K1>,
{
unhashed::clear_prefix(Self::storage_double_map_final_key1(k1).as_ref(), maybe_limit, None)
.into()
}
fn clear_prefix<KArg1>(
k1: KArg1,
limit: u32,
maybe_cursor: Option<&[u8]>,
) -> pezsp_io::MultiRemovalResults
where
KArg1: EncodeLike<K1>,
{
unhashed::clear_prefix(
Self::storage_double_map_final_key1(k1).as_ref(),
Some(limit),
maybe_cursor,
)
.into()
}
fn contains_prefix<KArg1>(k1: KArg1) -> bool
where
KArg1: EncodeLike<K1>,
{
unhashed::contains_prefixed_key(Self::storage_double_map_final_key1(k1).as_ref())
}
fn iter_prefix_values<KArg1>(k1: KArg1) -> storage::PrefixIterator<V>
where
KArg1: ?Sized + EncodeLike<K1>,
{
let prefix = Self::storage_double_map_final_key1(k1);
storage::PrefixIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |_raw_key, mut raw_value| V::decode(&mut raw_value),
phantom: Default::default(),
}
}
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
F: FnOnce(&mut Self::Query) -> R,
{
Self::try_mutate(k1, k2, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
fn mutate_exists<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
F: FnOnce(&mut Option<V>) -> R,
{
Self::try_mutate_exists(k1, k2, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
fn try_mutate<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
F: FnOnce(&mut Self::Query) -> Result<R, E>,
{
let final_key = Self::storage_double_map_final_key(k1, k2);
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
let ret = f(&mut val);
if ret.is_ok() {
match G::from_query_to_optional_value(val) {
Some(ref val) => unhashed::put(final_key.as_ref(), val),
None => unhashed::kill(final_key.as_ref()),
}
}
ret
}
fn try_mutate_exists<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
F: FnOnce(&mut Option<V>) -> Result<R, E>,
{
let final_key = Self::storage_double_map_final_key(k1, k2);
let mut val = unhashed::get(final_key.as_ref());
let ret = f(&mut val);
if ret.is_ok() {
match val {
Some(ref val) => unhashed::put(final_key.as_ref(), val),
None => unhashed::kill(final_key.as_ref()),
}
}
ret
}
fn append<Item, EncodeLikeItem, KArg1, KArg2>(k1: KArg1, k2: KArg2, item: EncodeLikeItem)
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
V: StorageAppend<Item>,
{
let final_key = Self::storage_double_map_final_key(k1, k2);
pezsp_io::storage::append(&final_key, item.encode());
}
fn migrate_keys<
OldHasher1: StorageHasher,
OldHasher2: StorageHasher,
KeyArg1: EncodeLike<K1>,
KeyArg2: EncodeLike<K2>,
>(
key1: KeyArg1,
key2: KeyArg2,
) -> Option<V> {
let old_key = {
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key1_hashed = key1.using_encoded(OldHasher1::hash);
let key2_hashed = key2.using_encoded(OldHasher2::hash);
let mut final_key = Vec::with_capacity(
storage_prefix.len() + key1_hashed.as_ref().len() + key2_hashed.as_ref().len(),
);
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key1_hashed.as_ref());
final_key.extend_from_slice(key2_hashed.as_ref());
final_key
};
unhashed::take(old_key.as_ref()).inspect(|value| {
unhashed::put(Self::storage_double_map_final_key(key1, key2).as_ref(), &value);
})
}
}
impl<K1: FullCodec, K2: FullCodec, V: FullCodec, G: StorageDoubleMap<K1, K2, V>>
storage::IterableStorageDoubleMap<K1, K2, V> for G
where
G::Hasher1: ReversibleStorageHasher,
G::Hasher2: ReversibleStorageHasher,
{
type PartialKeyIterator = KeyPrefixIterator<K2>;
type PrefixIterator = PrefixIterator<(K2, V)>;
type FullKeyIterator = KeyPrefixIterator<(K1, K2)>;
type Iterator = PrefixIterator<(K1, K2, V)>;
fn iter_prefix(k1: impl EncodeLike<K1>) -> Self::PrefixIterator {
let prefix = G::storage_double_map_final_key1(k1);
Self::PrefixIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |raw_key_without_prefix, mut raw_value| {
let mut key_material = G::Hasher2::reverse(raw_key_without_prefix);
Ok((K2::decode(&mut key_material)?, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
fn iter_prefix_from(
k1: impl EncodeLike<K1>,
starting_raw_key: Vec<u8>,
) -> Self::PrefixIterator {
let mut iter = Self::iter_prefix(k1);
iter.set_last_raw_key(starting_raw_key);
iter
}
fn iter_key_prefix(k1: impl EncodeLike<K1>) -> Self::PartialKeyIterator {
let prefix = G::storage_double_map_final_key1(k1);
Self::PartialKeyIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |raw_key_without_prefix| {
let mut key_material = G::Hasher2::reverse(raw_key_without_prefix);
K2::decode(&mut key_material)
},
}
}
fn iter_key_prefix_from(
k1: impl EncodeLike<K1>,
starting_raw_key: Vec<u8>,
) -> Self::PartialKeyIterator {
let mut iter = Self::iter_key_prefix(k1);
iter.set_last_raw_key(starting_raw_key);
iter
}
fn drain_prefix(k1: impl EncodeLike<K1>) -> Self::PrefixIterator {
let mut iterator = Self::iter_prefix(k1);
iterator.drain = true;
iterator
}
fn iter() -> Self::Iterator {
let prefix = G::prefix_hash().to_vec();
Self::Iterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |raw_key_without_prefix, mut raw_value| {
let mut k1_k2_material = G::Hasher1::reverse(raw_key_without_prefix);
let k1 = K1::decode(&mut k1_k2_material)?;
let mut k2_material = G::Hasher2::reverse(k1_k2_material);
let k2 = K2::decode(&mut k2_material)?;
Ok((k1, k2, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
fn iter_from(starting_raw_key: Vec<u8>) -> Self::Iterator {
let mut iter = Self::iter();
iter.set_last_raw_key(starting_raw_key);
iter
}
fn iter_keys() -> Self::FullKeyIterator {
let prefix = G::prefix_hash().to_vec();
Self::FullKeyIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |raw_key_without_prefix| {
let mut k1_k2_material = G::Hasher1::reverse(raw_key_without_prefix);
let k1 = K1::decode(&mut k1_k2_material)?;
let mut k2_material = G::Hasher2::reverse(k1_k2_material);
let k2 = K2::decode(&mut k2_material)?;
Ok((k1, k2))
},
}
}
fn iter_keys_from(starting_raw_key: Vec<u8>) -> Self::FullKeyIterator {
let mut iter = Self::iter_keys();
iter.set_last_raw_key(starting_raw_key);
iter
}
fn drain() -> Self::Iterator {
let mut iterator = Self::iter();
iterator.drain = true;
iterator
}
fn translate<O: Decode, F: FnMut(K1, K2, O) -> Option<V>>(mut f: F) {
let prefix = G::prefix_hash().to_vec();
let mut previous_key = prefix.clone();
while let Some(next) =
pezsp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))
{
previous_key = next;
let value = match unhashed::get::<O>(&previous_key) {
Some(value) => value,
None => {
log::error!("Invalid translate: fail to decode old value");
continue;
},
};
let mut key_material = G::Hasher1::reverse(&previous_key[prefix.len()..]);
let key1 = match K1::decode(&mut key_material) {
Ok(key1) => key1,
Err(_) => {
log::error!("Invalid translate: fail to decode key1");
continue;
},
};
let mut key2_material = G::Hasher2::reverse(key_material);
let key2 = match K2::decode(&mut key2_material) {
Ok(key2) => key2,
Err(_) => {
log::error!("Invalid translate: fail to decode key2");
continue;
},
};
match f(key1, key2, value) {
Some(new) => unhashed::put::<V>(&previous_key, &new),
None => unhashed::kill(&previous_key),
}
}
}
}
/// Test iterators for StorageDoubleMap
#[cfg(test)]
mod test_iterators {
use crate::{
hash::StorageHasher,
storage::{
generator::{tests::*, StorageDoubleMap},
unhashed,
},
};
use alloc::vec;
use codec::Encode;
#[test]
fn double_map_iter_from() {
pezsp_io::TestExternalities::default().execute_with(|| {
use crate::hash::Identity;
#[crate::storage_alias]
type MyDoubleMap = StorageDoubleMap<MyModule, Identity, u64, Identity, u64, u64>;
MyDoubleMap::insert(1, 10, 100);
MyDoubleMap::insert(1, 21, 201);
MyDoubleMap::insert(1, 31, 301);
MyDoubleMap::insert(1, 41, 401);
MyDoubleMap::insert(2, 20, 200);
MyDoubleMap::insert(3, 30, 300);
MyDoubleMap::insert(4, 40, 400);
MyDoubleMap::insert(5, 50, 500);
let starting_raw_key = MyDoubleMap::storage_double_map_final_key(1, 21);
let iter = MyDoubleMap::iter_key_prefix_from(1, starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![31, 41]);
let starting_raw_key = MyDoubleMap::storage_double_map_final_key(1, 31);
let iter = MyDoubleMap::iter_prefix_from(1, starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![(41, 401)]);
let starting_raw_key = MyDoubleMap::storage_double_map_final_key(2, 20);
let iter = MyDoubleMap::iter_keys_from(starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![(3, 30), (4, 40), (5, 50)]);
let starting_raw_key = MyDoubleMap::storage_double_map_final_key(3, 30);
let iter = MyDoubleMap::iter_from(starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![(4, 40, 400), (5, 50, 500)]);
});
}
#[test]
fn double_map_reversible_reversible_iteration() {
pezsp_io::TestExternalities::default().execute_with(|| {
type DoubleMap = self::pezframe_system::DoubleMap<Runtime>;
// All map iterator
let prefix = DoubleMap::prefix_hash().to_vec();
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
DoubleMap::insert(i as u16, i as u32, i as u64);
}
assert_eq!(
DoubleMap::iter().collect::<Vec<_>>(),
vec![(3, 3, 3), (0, 0, 0), (2, 2, 2), (1, 1, 1)],
);
assert_eq!(
DoubleMap::iter_keys().collect::<Vec<_>>(),
vec![(3, 3), (0, 0), (2, 2), (1, 1)],
);
assert_eq!(DoubleMap::iter_values().collect::<Vec<_>>(), vec![3, 0, 2, 1]);
assert_eq!(
DoubleMap::drain().collect::<Vec<_>>(),
vec![(3, 3, 3), (0, 0, 0), (2, 2, 2), (1, 1, 1)],
);
assert_eq!(DoubleMap::iter().collect::<Vec<_>>(), vec![]);
assert_eq!(unhashed::get(&key_before_prefix(prefix.clone())), Some(1u64));
assert_eq!(unhashed::get(&key_after_prefix(prefix.clone())), Some(1u64));
// Prefix iterator
let k1 = 3 << 8;
let prefix = DoubleMap::storage_double_map_final_key1(k1);
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
DoubleMap::insert(k1, i as u32, i as u64);
}
assert_eq!(
DoubleMap::iter_prefix(k1).collect::<Vec<_>>(),
vec![(1, 1), (2, 2), (0, 0), (3, 3)],
);
assert_eq!(DoubleMap::iter_key_prefix(k1).collect::<Vec<_>>(), vec![1, 2, 0, 3]);
assert_eq!(DoubleMap::iter_prefix_values(k1).collect::<Vec<_>>(), vec![1, 2, 0, 3]);
assert_eq!(
DoubleMap::drain_prefix(k1).collect::<Vec<_>>(),
vec![(1, 1), (2, 2), (0, 0), (3, 3)],
);
assert_eq!(DoubleMap::iter_prefix(k1).collect::<Vec<_>>(), vec![]);
assert_eq!(unhashed::get(&key_before_prefix(prefix.clone())), Some(1u64));
assert_eq!(unhashed::get(&key_after_prefix(prefix.clone())), Some(1u64));
// Translate
let prefix = DoubleMap::prefix_hash().to_vec();
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
DoubleMap::insert(i as u16, i as u32, i as u64);
}
// Wrong key1
unhashed::put(&[prefix.clone(), vec![1, 2, 3]].concat(), &3u64.encode());
// Wrong key2
unhashed::put(
&[prefix.clone(), crate::Blake2_128Concat::hash(&1u16.encode())].concat(),
&3u64.encode(),
);
// Wrong value
unhashed::put(
&[
prefix.clone(),
crate::Blake2_128Concat::hash(&1u16.encode()),
crate::Twox64Concat::hash(&2u32.encode()),
]
.concat(),
&vec![1],
);
DoubleMap::translate(|_k1, _k2, v: u64| Some(v * 2));
assert_eq!(
DoubleMap::iter().collect::<Vec<_>>(),
vec![(3, 3, 6), (0, 0, 0), (2, 2, 4), (1, 1, 2)],
);
})
}
}
@@ -0,0 +1,465 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
hash::{ReversibleStorageHasher, StorageHasher},
storage::{self, storage_prefix, unhashed, KeyPrefixIterator, PrefixIterator, StorageAppend},
Never,
};
use alloc::vec::Vec;
use codec::{Decode, Encode, EncodeLike, FullCodec, FullEncode};
/// Generator for `StorageMap` used by `decl_storage`.
///
/// By default each key value is stored at:
/// ```nocompile
/// Twox128(pezpallet_prefix) ++ Twox128(storage_prefix) ++ Hasher(encode(key))
/// ```
///
/// # Warning
///
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
/// `blake2_256` must be used. Otherwise, other values in storage can be compromised.
pub trait StorageMap<K: FullEncode, V: FullCodec> {
/// The type that get/take returns.
type Query;
/// Hasher. Used for generating final key.
type Hasher: StorageHasher;
/// Pallet prefix. Used for generating final key.
fn pezpallet_prefix() -> &'static [u8];
/// Storage prefix. Used for generating final key.
fn storage_prefix() -> &'static [u8];
/// The full prefix; just the hash of `pezpallet_prefix` concatenated to the hash of
/// `storage_prefix`.
fn prefix_hash() -> [u8; 32];
/// Convert an optional value retrieved from storage to the type queried.
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
/// Convert a query to an optional value into storage.
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
/// Generate the full key used in top storage.
fn storage_map_final_key<KeyArg>(key: KeyArg) -> Vec<u8>
where
KeyArg: EncodeLike<K>,
{
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key_hashed = key.using_encoded(Self::Hasher::hash);
let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.as_ref().len());
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key_hashed.as_ref());
final_key
}
}
impl<K: FullCodec, V: FullCodec, G: StorageMap<K, V>> storage::IterableStorageMap<K, V> for G
where
G::Hasher: ReversibleStorageHasher,
{
type Iterator = PrefixIterator<(K, V)>;
type KeyIterator = KeyPrefixIterator<K>;
/// Enumerate all elements in the map.
fn iter() -> Self::Iterator {
let prefix = G::prefix_hash().to_vec();
PrefixIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |raw_key_without_prefix, mut raw_value| {
let mut key_material = G::Hasher::reverse(raw_key_without_prefix);
Ok((K::decode(&mut key_material)?, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
/// Enumerate all elements in the map after a given key.
fn iter_from(starting_raw_key: Vec<u8>) -> Self::Iterator {
let mut iter = Self::iter();
iter.set_last_raw_key(starting_raw_key);
iter
}
/// Enumerate all keys in the map.
fn iter_keys() -> Self::KeyIterator {
let prefix = G::prefix_hash().to_vec();
KeyPrefixIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |raw_key_without_prefix| {
let mut key_material = G::Hasher::reverse(raw_key_without_prefix);
K::decode(&mut key_material)
},
}
}
/// Enumerate all keys in the map after a given key.
fn iter_keys_from(starting_raw_key: Vec<u8>) -> Self::KeyIterator {
let mut iter = Self::iter_keys();
iter.set_last_raw_key(starting_raw_key);
iter
}
/// Enumerate all elements in the map.
fn drain() -> Self::Iterator {
let mut iterator = Self::iter();
iterator.drain = true;
iterator
}
fn translate<O: Decode, F: FnMut(K, O) -> Option<V>>(mut f: F) {
let mut previous_key = None;
loop {
previous_key = Self::translate_next(previous_key, &mut f);
if previous_key.is_none() {
break;
}
}
}
fn translate_next<O: Decode, F: FnMut(K, O) -> Option<V>>(
previous_key: Option<Vec<u8>>,
mut f: F,
) -> Option<Vec<u8>> {
let prefix = G::prefix_hash().to_vec();
let previous_key = previous_key.unwrap_or_else(|| prefix.clone());
let current_key =
pezsp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))?;
let value = match unhashed::get::<O>(&current_key) {
Some(value) => value,
None => {
crate::defensive!(
"Invalid translation: failed to decode old value for key",
array_bytes::bytes2hex("0x", &current_key)
);
return Some(current_key);
},
};
let mut key_material = G::Hasher::reverse(&current_key[prefix.len()..]);
let key = match K::decode(&mut key_material) {
Ok(key) => key,
Err(_) => {
crate::defensive!(
"Invalid translation: failed to decode key",
array_bytes::bytes2hex("0x", &current_key)
);
return Some(current_key);
},
};
match f(key, value) {
Some(new) => unhashed::put::<V>(&current_key, &new),
None => unhashed::kill(&current_key),
}
Some(current_key)
}
}
impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V> for G {
type Query = G::Query;
fn hashed_key_for<KeyArg: EncodeLike<K>>(key: KeyArg) -> Vec<u8> {
Self::storage_map_final_key(key)
}
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
let k1 = Self::storage_map_final_key(key1);
let k2 = Self::storage_map_final_key(key2);
let v1 = unhashed::get_raw(k1.as_ref());
if let Some(val) = unhashed::get_raw(k2.as_ref()) {
unhashed::put_raw(k1.as_ref(), &val);
} else {
unhashed::kill(k1.as_ref())
}
if let Some(val) = v1 {
unhashed::put_raw(k2.as_ref(), &val);
} else {
unhashed::kill(k2.as_ref())
}
}
fn contains_key<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
unhashed::exists(Self::storage_map_final_key(key).as_ref())
}
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
G::from_optional_value_to_query(unhashed::get(Self::storage_map_final_key(key).as_ref()))
}
fn try_get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<V, ()> {
unhashed::get(Self::storage_map_final_key(key).as_ref()).ok_or(())
}
fn set<KeyArg: EncodeLike<K>>(key: KeyArg, q: Self::Query) {
match G::from_query_to_optional_value(q) {
Some(v) => Self::insert(key, v),
None => Self::remove(key),
}
}
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
unhashed::put(Self::storage_map_final_key(key).as_ref(), &val)
}
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
unhashed::kill(Self::storage_map_final_key(key).as_ref())
}
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
Self::try_mutate(key, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
fn mutate_exists<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Option<V>) -> R>(
key: KeyArg,
f: F,
) -> R {
Self::try_mutate_exists(key, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
fn try_mutate<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>(
key: KeyArg,
f: F,
) -> Result<R, E> {
let final_key = Self::storage_map_final_key(key);
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
let ret = f(&mut val);
if ret.is_ok() {
match G::from_query_to_optional_value(val) {
Some(ref val) => unhashed::put(final_key.as_ref(), &val),
None => unhashed::kill(final_key.as_ref()),
}
}
ret
}
fn try_mutate_exists<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Option<V>) -> Result<R, E>>(
key: KeyArg,
f: F,
) -> Result<R, E> {
let final_key = Self::storage_map_final_key(key);
let mut val = unhashed::get(final_key.as_ref());
let ret = f(&mut val);
if ret.is_ok() {
match val {
Some(ref val) => unhashed::put(final_key.as_ref(), &val),
None => unhashed::kill(final_key.as_ref()),
}
}
ret
}
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
let key = Self::storage_map_final_key(key);
let value = unhashed::take(key.as_ref());
G::from_optional_value_to_query(value)
}
fn append<Item, EncodeLikeItem, EncodeLikeKey>(key: EncodeLikeKey, item: EncodeLikeItem)
where
EncodeLikeKey: EncodeLike<K>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
V: StorageAppend<Item>,
{
let key = Self::storage_map_final_key(key);
pezsp_io::storage::append(&key, item.encode());
}
fn migrate_key<OldHasher: StorageHasher, KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V> {
let old_key = {
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key_hashed = key.using_encoded(OldHasher::hash);
let mut final_key =
Vec::with_capacity(storage_prefix.len() + key_hashed.as_ref().len());
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key_hashed.as_ref());
final_key
};
unhashed::take(old_key.as_ref()).inspect(|value| {
unhashed::put(Self::storage_map_final_key(key).as_ref(), &value);
})
}
}
/// Test iterators for StorageMap
#[cfg(test)]
mod test_iterators {
use crate::{
hash::StorageHasher,
storage::{
generator::{tests::*, StorageMap},
unhashed,
},
};
use alloc::vec;
use codec::Encode;
#[test]
fn map_iter_from() {
pezsp_io::TestExternalities::default().execute_with(|| {
use crate::hash::Identity;
#[crate::storage_alias]
type MyMap = StorageMap<MyModule, Identity, u64, u64>;
MyMap::insert(1, 10);
MyMap::insert(2, 20);
MyMap::insert(3, 30);
MyMap::insert(4, 40);
MyMap::insert(5, 50);
let starting_raw_key = MyMap::storage_map_final_key(3);
let iter = MyMap::iter_from(starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![(4, 40), (5, 50)]);
let starting_raw_key = MyMap::storage_map_final_key(2);
let iter = MyMap::iter_keys_from(starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![3, 4, 5]);
});
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn map_translate_with_bad_key_in_debug_mode() {
pezsp_io::TestExternalities::default().execute_with(|| {
type Map = self::pezframe_system::Map<Runtime>;
let prefix = Map::prefix_hash().to_vec();
// Wrong key
unhashed::put(&[prefix.clone(), vec![1, 2, 3]].concat(), &3u64.encode());
// debug_assert should cause a
Map::translate(|_k1, v: u64| Some(v * 2));
assert_eq!(Map::iter().collect::<Vec<_>>(), vec![(3, 6), (0, 0), (2, 4), (1, 2)]);
})
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn map_translate_with_bad_value_in_debug_mode() {
pezsp_io::TestExternalities::default().execute_with(|| {
type Map = self::pezframe_system::Map<Runtime>;
let prefix = Map::prefix_hash().to_vec();
// Wrong value
unhashed::put(
&[prefix.clone(), crate::Blake2_128Concat::hash(&6u16.encode())].concat(),
&vec![1],
);
Map::translate(|_k1, v: u64| Some(v * 2));
assert_eq!(Map::iter().collect::<Vec<_>>(), vec![(3, 6), (0, 0), (2, 4), (1, 2)]);
})
}
#[cfg(not(debug_assertions))]
#[test]
fn map_translate_with_bad_key_in_production_mode() {
pezsp_io::TestExternalities::default().execute_with(|| {
type Map = self::pezframe_system::Map<Runtime>;
let prefix = Map::prefix_hash().to_vec();
// Wrong key
unhashed::put(&[prefix.clone(), vec![1, 2, 3]].concat(), &3u64.encode());
Map::translate(|_k1, v: u64| Some(v * 2));
assert_eq!(Map::iter().collect::<Vec<_>>(), vec![]);
})
}
#[cfg(not(debug_assertions))]
#[test]
fn map_translate_with_bad_value_in_production_mode() {
pezsp_io::TestExternalities::default().execute_with(|| {
type Map = self::pezframe_system::Map<Runtime>;
let prefix = Map::prefix_hash().to_vec();
// Wrong value
unhashed::put(
&[prefix.clone(), crate::Blake2_128Concat::hash(&6u16.encode())].concat(),
&vec![1],
);
Map::translate(|_k1, v: u64| Some(v * 2));
assert_eq!(Map::iter().collect::<Vec<_>>(), vec![]);
})
}
#[test]
fn map_reversible_reversible_iteration() {
pezsp_io::TestExternalities::default().execute_with(|| {
type Map = self::pezframe_system::Map<Runtime>;
// All map iterator
let prefix = Map::prefix_hash().to_vec();
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
Map::insert(i as u16, i as u64);
}
assert_eq!(Map::iter().collect::<Vec<_>>(), vec![(3, 3), (0, 0), (2, 2), (1, 1)]);
assert_eq!(Map::iter_keys().collect::<Vec<_>>(), vec![3, 0, 2, 1]);
assert_eq!(Map::iter_values().collect::<Vec<_>>(), vec![3, 0, 2, 1]);
assert_eq!(Map::drain().collect::<Vec<_>>(), vec![(3, 3), (0, 0), (2, 2), (1, 1)]);
assert_eq!(Map::iter().collect::<Vec<_>>(), vec![]);
assert_eq!(unhashed::get(&key_before_prefix(prefix.clone())), Some(1u64));
assert_eq!(unhashed::get(&key_after_prefix(prefix.clone())), Some(1u64));
// Translate
let prefix = Map::prefix_hash().to_vec();
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
Map::insert(i as u16, i as u64);
}
Map::translate(|_k1, v: u64| Some(v * 2));
assert_eq!(Map::iter().collect::<Vec<_>>(), vec![(3, 6), (0, 0), (2, 4), (1, 2)]);
})
}
}
@@ -0,0 +1,268 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Generators are a set of trait on which storage traits are implemented.
//!
//! (i.e. implementing the generator for StorageValue on a type will automatically derive the
//! implementation of StorageValue for this type).
//!
//! They are used by `decl_storage`.
//!
//! This is internal api and is subject to change.
pub(crate) mod double_map;
pub(crate) mod map;
pub(crate) mod nmap;
pub(crate) mod value;
pub use double_map::StorageDoubleMap;
pub use map::StorageMap;
pub use nmap::StorageNMap;
pub use value::StorageValue;
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use codec::Encode;
use pezsp_io::TestExternalities;
use pezsp_runtime::{generic, traits::BlakeTwo256, BuildStorage};
use crate::{
assert_noop, assert_ok,
storage::{generator::StorageValue, unhashed},
};
#[crate::pallet]
pub mod pezframe_system {
#[allow(unused)]
use super::{pezframe_system, pezframe_system::pezpallet_prelude::*};
pub use crate::dispatch::RawOrigin;
use crate::pezpallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
#[pallet::disable_pezframe_system_supertrait_check]
pub trait Config: 'static {
type Block: pezsp_runtime::traits::Block;
type AccountId;
type BaseCallFilter: crate::traits::Contains<Self::RuntimeCall>;
type RuntimeOrigin;
type RuntimeCall;
type RuntimeTask;
type PalletInfo: crate::traits::PalletInfo;
type DbWeight: Get<crate::weights::RuntimeDbWeight>;
}
#[pallet::origin]
pub type Origin<T> = RawOrigin<<T as Config>::AccountId>;
#[pallet::error]
pub enum Error<T> {
/// Required by construct_runtime
CallFiltered,
}
#[pallet::call]
impl<T: Config> Pallet<T> {}
#[pallet::storage]
pub type Value<T> = StorageValue<_, (u64, u64), ValueQuery>;
#[pallet::storage]
pub type Map<T> = StorageMap<_, Blake2_128Concat, u16, u64, ValueQuery>;
#[pallet::storage]
pub type NumberMap<T> = StorageMap<_, Identity, u32, u64, ValueQuery>;
#[pallet::storage]
pub type DoubleMap<T> =
StorageDoubleMap<_, Blake2_128Concat, u16, Twox64Concat, u32, u64, ValueQuery>;
#[pallet::storage]
pub type NMap<T> = StorageNMap<
_,
(storage::Key<Blake2_128Concat, u16>, storage::Key<Twox64Concat, u32>),
u64,
ValueQuery,
>;
pub mod pezpallet_prelude {
pub type OriginFor<T> = <T as super::Config>::RuntimeOrigin;
pub type HeaderFor<T> =
<<T as super::Config>::Block as pezsp_runtime::traits::HeaderProvider>::HeaderT;
pub type BlockNumberFor<T> = <HeaderFor<T> as pezsp_runtime::traits::Header>::Number;
}
}
type BlockNumber = u32;
type AccountId = u32;
type Header = generic::Header<BlockNumber, BlakeTwo256>;
type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, RuntimeCall, (), ()>;
type Block = generic::Block<Header, UncheckedExtrinsic>;
crate::construct_runtime!(
pub enum Runtime
{
System: self::pezframe_system,
}
);
impl self::pezframe_system::Config for Runtime {
type AccountId = AccountId;
type Block = Block;
type BaseCallFilter = crate::traits::Everything;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type RuntimeTask = RuntimeTask;
type PalletInfo = PalletInfo;
type DbWeight = ();
}
pub fn key_before_prefix(mut prefix: Vec<u8>) -> Vec<u8> {
let last = prefix.iter_mut().last().unwrap();
assert_ne!(*last, 0, "mock function not implemented for this prefix");
*last -= 1;
prefix
}
pub fn key_after_prefix(mut prefix: Vec<u8>) -> Vec<u8> {
let last = prefix.iter_mut().last().unwrap();
assert_ne!(*last, 255, "mock function not implemented for this prefix");
*last += 1;
prefix
}
#[test]
fn value_translate_works() {
let t = RuntimeGenesisConfig::default().build_storage().unwrap();
TestExternalities::new(t).execute_with(|| {
type Value = self::pezframe_system::Value<Runtime>;
// put the old value `1111u32` in the storage.
let key = Value::storage_value_final_key();
unhashed::put_raw(&key, &1111u32.encode());
// translate
let translate_fn = |old: Option<u32>| -> Option<(u64, u64)> {
old.map(|o| (o.into(), (o * 2).into()))
};
let res = Value::translate(translate_fn);
debug_assert!(res.is_ok());
// new storage should be `(1111, 1111 * 2)`
assert_eq!(Value::get(), (1111, 2222));
})
}
#[test]
fn map_translate_works() {
let t = RuntimeGenesisConfig::default().build_storage().unwrap();
TestExternalities::new(t).execute_with(|| {
type NumberMap = self::pezframe_system::NumberMap<Runtime>;
// start with a map of u32 -> u64.
for i in 0u32..100u32 {
unhashed::put(&NumberMap::hashed_key_for(&i), &(i as u64));
}
assert_eq!(
NumberMap::iter().collect::<Vec<_>>(),
(0..100).map(|x| (x as u32, x as u64)).collect::<Vec<_>>(),
);
// do translation.
NumberMap::translate(
|k: u32, v: u64| if k % 2 == 0 { Some(((k as u64) << 32) | v) } else { None },
);
assert_eq!(
NumberMap::iter().collect::<Vec<_>>(),
(0..50u32)
.map(|x| x * 2)
.map(|x| (x, ((x as u64) << 32) | x as u64))
.collect::<Vec<_>>(),
);
})
}
#[test]
fn try_mutate_works() {
let t = RuntimeGenesisConfig::default().build_storage().unwrap();
TestExternalities::new(t).execute_with(|| {
type Value = self::pezframe_system::Value<Runtime>;
type NumberMap = self::pezframe_system::NumberMap<Runtime>;
type DoubleMap = self::pezframe_system::DoubleMap<Runtime>;
assert_eq!(Value::get(), (0, 0));
assert_eq!(NumberMap::get(0), 0);
assert_eq!(DoubleMap::get(0, 0), 0);
// `assert_noop` ensures that the state does not change
assert_noop!(
Value::try_mutate(|value| -> Result<(), &'static str> {
*value = (2, 2);
Err("don't change value")
}),
"don't change value"
);
assert_noop!(
NumberMap::try_mutate(0, |value| -> Result<(), &'static str> {
*value = 4;
Err("don't change value")
}),
"don't change value"
);
assert_noop!(
DoubleMap::try_mutate(0, 0, |value| -> Result<(), &'static str> {
*value = 6;
Err("don't change value")
}),
"don't change value"
);
// Showing this explicitly for clarity
assert_eq!(Value::get(), (0, 0));
assert_eq!(NumberMap::get(0), 0);
assert_eq!(DoubleMap::get(0, 0), 0);
assert_ok!(Value::try_mutate(|value| -> Result<(), &'static str> {
*value = (2, 2);
Ok(())
}));
assert_ok!(NumberMap::try_mutate(0, |value| -> Result<(), &'static str> {
*value = 4;
Ok(())
}));
assert_ok!(DoubleMap::try_mutate(0, 0, |value| -> Result<(), &'static str> {
*value = 6;
Ok(())
}));
assert_eq!(Value::get(), (2, 2));
assert_eq!(NumberMap::get(0), 4);
assert_eq!(DoubleMap::get(0, 0), 6);
});
}
}
+628
View File
@@ -0,0 +1,628 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Generator for `StorageNMap` used by `decl_storage` and storage types.
//!
//! By default each key value is stored at:
//! ```nocompile
//! Twox128(pezpallet_prefix) ++ Twox128(storage_prefix)
//! ++ Hasher1(encode(key1)) ++ Hasher2(encode(key2)) ++ ... ++ HasherN(encode(keyN))
//! ```
//!
//! # Warning
//!
//! If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
//! `blake2_256` must be used. Otherwise, other values in storage with the same prefix can
//! be compromised.
use crate::{
storage::{
self, storage_prefix,
types::{
EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, KeyGenerator,
ReversibleKeyGenerator, TupleToEncodedIter,
},
unhashed, KeyPrefixIterator, PrefixIterator, StorageAppend,
},
Never,
};
use alloc::vec::Vec;
use codec::{Decode, Encode, EncodeLike, FullCodec};
/// Generator for `StorageNMap` used by `decl_storage` and storage types.
///
/// By default each key value is stored at:
/// ```nocompile
/// Twox128(pezpallet_prefix) ++ Twox128(storage_prefix)
/// ++ Hasher1(encode(key1)) ++ Hasher2(encode(key2)) ++ ... ++ HasherN(encode(keyN))
/// ```
///
/// # Warning
///
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
/// `blake2_256` must be used. Otherwise, other values in storage with the same prefix can
/// be compromised.
pub trait StorageNMap<K: KeyGenerator, V: FullCodec> {
/// The type that get/take returns.
type Query;
/// Pallet prefix. Used for generating final key.
fn pezpallet_prefix() -> &'static [u8];
/// Storage prefix. Used for generating final key.
fn storage_prefix() -> &'static [u8];
/// The full prefix; just the hash of `pezpallet_prefix` concatenated to the hash of
/// `storage_prefix`.
fn prefix_hash() -> [u8; 32];
/// Convert an optional value retrieved from storage to the type queried.
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
/// Convert a query to an optional value into storage.
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
/// Generate a partial key used in top storage.
fn storage_n_map_partial_key<KP>(key: KP) -> Vec<u8>
where
K: HasKeyPrefix<KP>,
{
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key_hashed = <K as HasKeyPrefix<KP>>::partial_key(key);
let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key_hashed.as_ref());
final_key
}
/// Generate the full key used in top storage.
fn storage_n_map_final_key<KG, KArg>(key: KArg) -> Vec<u8>
where
KG: KeyGenerator,
KArg: EncodeLikeTuple<KG::KArg> + TupleToEncodedIter,
{
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key_hashed = KG::final_key(key);
let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key_hashed.as_ref());
final_key
}
}
impl<K, V, G> storage::StorageNMap<K, V> for G
where
K: KeyGenerator,
V: FullCodec,
G: StorageNMap<K, V>,
{
type Query = G::Query;
fn hashed_key_for<KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter>(key: KArg) -> Vec<u8> {
Self::storage_n_map_final_key::<K, _>(key)
}
fn contains_key<KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter>(key: KArg) -> bool {
unhashed::exists(&Self::storage_n_map_final_key::<K, _>(key))
}
fn get<KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter>(key: KArg) -> Self::Query {
G::from_optional_value_to_query(unhashed::get(&Self::storage_n_map_final_key::<K, _>(key)))
}
fn try_get<KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter>(key: KArg) -> Result<V, ()> {
unhashed::get(&Self::storage_n_map_final_key::<K, _>(key)).ok_or(())
}
fn set<KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter>(key: KArg, q: Self::Query) {
match G::from_query_to_optional_value(q) {
Some(v) => Self::insert(key, v),
None => Self::remove(key),
}
}
fn take<KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter>(key: KArg) -> Self::Query {
let final_key = Self::storage_n_map_final_key::<K, _>(key);
let value = unhashed::take(&final_key);
G::from_optional_value_to_query(value)
}
fn swap<KOther, KArg1, KArg2>(key1: KArg1, key2: KArg2)
where
KOther: KeyGenerator,
KArg1: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
KArg2: EncodeLikeTuple<KOther::KArg> + TupleToEncodedIter,
{
let final_x_key = Self::storage_n_map_final_key::<K, _>(key1);
let final_y_key = Self::storage_n_map_final_key::<KOther, _>(key2);
let v1 = unhashed::get_raw(&final_x_key);
if let Some(val) = unhashed::get_raw(&final_y_key) {
unhashed::put_raw(&final_x_key, &val);
} else {
unhashed::kill(&final_x_key);
}
if let Some(val) = v1 {
unhashed::put_raw(&final_y_key, &val);
} else {
unhashed::kill(&final_y_key);
}
}
fn insert<KArg, VArg>(key: KArg, val: VArg)
where
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
VArg: EncodeLike<V>,
{
unhashed::put(&Self::storage_n_map_final_key::<K, _>(key), &val);
}
fn remove<KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter>(key: KArg) {
unhashed::kill(&Self::storage_n_map_final_key::<K, _>(key));
}
fn remove_prefix<KP>(partial_key: KP, limit: Option<u32>) -> pezsp_io::KillStorageResult
where
K: HasKeyPrefix<KP>,
{
unhashed::clear_prefix(&Self::storage_n_map_partial_key(partial_key), limit, None).into()
}
fn clear_prefix<KP>(
partial_key: KP,
limit: u32,
maybe_cursor: Option<&[u8]>,
) -> pezsp_io::MultiRemovalResults
where
K: HasKeyPrefix<KP>,
{
unhashed::clear_prefix(
&Self::storage_n_map_partial_key(partial_key),
Some(limit),
maybe_cursor,
)
}
fn contains_prefix<KP>(partial_key: KP) -> bool
where
K: HasKeyPrefix<KP>,
{
unhashed::contains_prefixed_key(&Self::storage_n_map_partial_key(partial_key))
}
fn iter_prefix_values<KP>(partial_key: KP) -> PrefixIterator<V>
where
K: HasKeyPrefix<KP>,
{
let prefix = Self::storage_n_map_partial_key(partial_key);
PrefixIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |_raw_key, mut raw_value| V::decode(&mut raw_value),
phantom: Default::default(),
}
}
fn mutate<KArg, R, F>(key: KArg, f: F) -> R
where
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
F: FnOnce(&mut Self::Query) -> R,
{
Self::try_mutate(key, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
fn try_mutate<KArg, R, E, F>(key: KArg, f: F) -> Result<R, E>
where
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
F: FnOnce(&mut Self::Query) -> Result<R, E>,
{
let final_key = Self::storage_n_map_final_key::<K, _>(key);
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
let ret = f(&mut val);
if ret.is_ok() {
match G::from_query_to_optional_value(val) {
Some(ref val) => unhashed::put(final_key.as_ref(), val),
None => unhashed::kill(final_key.as_ref()),
}
}
ret
}
fn mutate_exists<KArg, R, F>(key: KArg, f: F) -> R
where
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
F: FnOnce(&mut Option<V>) -> R,
{
Self::try_mutate_exists(key, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
fn try_mutate_exists<KArg, R, E, F>(key: KArg, f: F) -> Result<R, E>
where
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
F: FnOnce(&mut Option<V>) -> Result<R, E>,
{
let final_key = Self::storage_n_map_final_key::<K, _>(key);
let mut val = unhashed::get(final_key.as_ref());
let ret = f(&mut val);
if ret.is_ok() {
match val {
Some(ref val) => unhashed::put(final_key.as_ref(), val),
None => unhashed::kill(final_key.as_ref()),
}
}
ret
}
fn append<Item, EncodeLikeItem, KArg>(key: KArg, item: EncodeLikeItem)
where
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
V: StorageAppend<Item>,
{
let final_key = Self::storage_n_map_final_key::<K, _>(key);
pezsp_io::storage::append(&final_key, item.encode());
}
fn migrate_keys<KArg>(key: KArg, hash_fns: K::HArg) -> Option<V>
where
KArg: EncodeLikeTuple<K::KArg> + TupleToEncodedIter,
{
let old_key = {
let storage_prefix = storage_prefix(Self::pezpallet_prefix(), Self::storage_prefix());
let key_hashed = K::migrate_key(&key, hash_fns);
let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
final_key.extend_from_slice(&storage_prefix);
final_key.extend_from_slice(key_hashed.as_ref());
final_key
};
unhashed::take(old_key.as_ref()).inspect(|value| {
unhashed::put(Self::storage_n_map_final_key::<K, _>(key).as_ref(), &value);
})
}
}
impl<K: ReversibleKeyGenerator, V: FullCodec, G: StorageNMap<K, V>>
storage::IterableStorageNMap<K, V> for G
{
type KeyIterator = KeyPrefixIterator<K::Key>;
type Iterator = PrefixIterator<(K::Key, V)>;
fn iter_prefix<KP>(kp: KP) -> PrefixIterator<(<K as HasKeyPrefix<KP>>::Suffix, V)>
where
K: HasReversibleKeyPrefix<KP>,
{
let prefix = G::storage_n_map_partial_key(kp);
PrefixIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: |raw_key_without_prefix, mut raw_value| {
let partial_key = K::decode_partial_key(raw_key_without_prefix)?;
Ok((partial_key, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
fn iter_prefix_from<KP>(
kp: KP,
starting_raw_key: Vec<u8>,
) -> PrefixIterator<(<K as HasKeyPrefix<KP>>::Suffix, V)>
where
K: HasReversibleKeyPrefix<KP>,
{
let mut iter = Self::iter_prefix(kp);
iter.set_last_raw_key(starting_raw_key);
iter
}
fn iter_key_prefix<KP>(kp: KP) -> KeyPrefixIterator<<K as HasKeyPrefix<KP>>::Suffix>
where
K: HasReversibleKeyPrefix<KP>,
{
let prefix = G::storage_n_map_partial_key(kp);
KeyPrefixIterator {
prefix: prefix.clone(),
previous_key: prefix,
drain: false,
closure: K::decode_partial_key,
}
}
fn iter_key_prefix_from<KP>(
kp: KP,
starting_raw_key: Vec<u8>,
) -> KeyPrefixIterator<<K as HasKeyPrefix<KP>>::Suffix>
where
K: HasReversibleKeyPrefix<KP>,
{
let mut iter = Self::iter_key_prefix(kp);
iter.set_last_raw_key(starting_raw_key);
iter
}
fn drain_prefix<KP>(kp: KP) -> PrefixIterator<(<K as HasKeyPrefix<KP>>::Suffix, V)>
where
K: HasReversibleKeyPrefix<KP>,
{
let mut iter = Self::iter_prefix(kp);
iter.drain = true;
iter
}
fn iter() -> Self::Iterator {
Self::iter_from(G::prefix_hash().to_vec())
}
fn iter_from(starting_raw_key: Vec<u8>) -> Self::Iterator {
let prefix = G::prefix_hash().to_vec();
Self::Iterator {
prefix,
previous_key: starting_raw_key,
drain: false,
closure: |raw_key_without_prefix, mut raw_value| {
let (final_key, _) = K::decode_final_key(raw_key_without_prefix)?;
Ok((final_key, V::decode(&mut raw_value)?))
},
phantom: Default::default(),
}
}
fn iter_keys() -> Self::KeyIterator {
Self::iter_keys_from(G::prefix_hash().to_vec())
}
fn iter_keys_from(starting_raw_key: Vec<u8>) -> Self::KeyIterator {
let prefix = G::prefix_hash().to_vec();
Self::KeyIterator {
prefix,
previous_key: starting_raw_key,
drain: false,
closure: |raw_key_without_prefix| {
let (final_key, _) = K::decode_final_key(raw_key_without_prefix)?;
Ok(final_key)
},
}
}
fn drain() -> Self::Iterator {
let mut iterator = Self::iter();
iterator.drain = true;
iterator
}
fn translate<O: Decode, F: FnMut(K::Key, O) -> Option<V>>(mut f: F) {
let prefix = G::prefix_hash().to_vec();
let mut previous_key = prefix.clone();
while let Some(next) =
pezsp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix))
{
previous_key = next;
let value = match unhashed::get::<O>(&previous_key) {
Some(value) => value,
None => {
log::error!("Invalid translate: fail to decode old value");
continue;
},
};
let final_key = match K::decode_final_key(&previous_key[prefix.len()..]) {
Ok((final_key, _)) => final_key,
Err(_) => {
log::error!("Invalid translate: fail to decode key");
continue;
},
};
match f(final_key, value) {
Some(new) => unhashed::put::<V>(&previous_key, &new),
None => unhashed::kill(&previous_key),
}
}
}
}
/// Test iterators for StorageNMap
#[cfg(test)]
mod test_iterators {
use crate::{
hash::StorageHasher,
storage::{
generator::{tests::*, StorageNMap},
unhashed,
},
};
use alloc::vec;
use codec::Encode;
#[test]
fn n_map_iter_from() {
pezsp_io::TestExternalities::default().execute_with(|| {
use crate::{hash::Identity, storage::Key as NMapKey};
#[crate::storage_alias]
type MyNMap = StorageNMap<
MyModule,
(NMapKey<Identity, u64>, NMapKey<Identity, u64>, NMapKey<Identity, u64>),
u64,
>;
MyNMap::insert((1, 1, 1), 11);
MyNMap::insert((1, 1, 2), 21);
MyNMap::insert((1, 1, 3), 31);
MyNMap::insert((1, 2, 1), 12);
MyNMap::insert((1, 2, 2), 22);
MyNMap::insert((1, 2, 3), 32);
MyNMap::insert((1, 3, 1), 13);
MyNMap::insert((1, 3, 2), 23);
MyNMap::insert((1, 3, 3), 33);
MyNMap::insert((2, 0, 0), 200);
type Key = (NMapKey<Identity, u64>, NMapKey<Identity, u64>, NMapKey<Identity, u64>);
let starting_raw_key = MyNMap::storage_n_map_final_key::<Key, _>((1, 2, 2));
let iter = MyNMap::iter_key_prefix_from((1,), starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![(2, 3), (3, 1), (3, 2), (3, 3)]);
let starting_raw_key = MyNMap::storage_n_map_final_key::<Key, _>((1, 3, 1));
let iter = MyNMap::iter_prefix_from((1, 3), starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![(2, 23), (3, 33)]);
let starting_raw_key = MyNMap::storage_n_map_final_key::<Key, _>((1, 3, 2));
let iter = MyNMap::iter_keys_from(starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![(1, 3, 3), (2, 0, 0)]);
let starting_raw_key = MyNMap::storage_n_map_final_key::<Key, _>((1, 3, 3));
let iter = MyNMap::iter_from(starting_raw_key);
assert_eq!(iter.collect::<Vec<_>>(), vec![((2, 0, 0), 200)]);
});
}
#[test]
fn n_map_double_map_identical_key() {
pezsp_io::TestExternalities::default().execute_with(|| {
use crate::hash::{Blake2_128Concat, Twox64Concat};
type NMap = self::pezframe_system::NMap<Runtime>;
NMap::insert((1, 2), 50);
let key_hash = NMap::hashed_key_for((1, 2));
{
#[crate::storage_alias]
type NMap = StorageDoubleMap<System, Blake2_128Concat, u16, Twox64Concat, u32, u64>;
assert_eq!(NMap::get(1, 2), Some(50));
assert_eq!(NMap::hashed_key_for(1, 2), key_hash);
}
});
}
#[test]
fn n_map_reversible_reversible_iteration() {
pezsp_io::TestExternalities::default().execute_with(|| {
type NMap = self::pezframe_system::NMap<Runtime>;
// All map iterator
let prefix = NMap::prefix_hash().to_vec();
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
NMap::insert((i as u16, i as u32), i as u64);
}
assert_eq!(
NMap::iter().collect::<Vec<_>>(),
vec![((3, 3), 3), ((0, 0), 0), ((2, 2), 2), ((1, 1), 1)],
);
assert_eq!(NMap::iter_keys().collect::<Vec<_>>(), vec![(3, 3), (0, 0), (2, 2), (1, 1)]);
assert_eq!(NMap::iter_values().collect::<Vec<_>>(), vec![3, 0, 2, 1]);
assert_eq!(
NMap::drain().collect::<Vec<_>>(),
vec![((3, 3), 3), ((0, 0), 0), ((2, 2), 2), ((1, 1), 1)],
);
assert_eq!(NMap::iter().collect::<Vec<_>>(), vec![]);
assert_eq!(unhashed::get(&key_before_prefix(prefix.clone())), Some(1u64));
assert_eq!(unhashed::get(&key_after_prefix(prefix.clone())), Some(1u64));
// Prefix iterator
let k1 = 3 << 8;
let prefix = NMap::storage_n_map_partial_key((k1,));
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
NMap::insert((k1, i as u32), i as u64);
}
assert_eq!(
NMap::iter_prefix((k1,)).collect::<Vec<_>>(),
vec![(1, 1), (2, 2), (0, 0), (3, 3)],
);
assert_eq!(NMap::iter_key_prefix((k1,)).collect::<Vec<_>>(), vec![1, 2, 0, 3]);
assert_eq!(NMap::iter_prefix_values((k1,)).collect::<Vec<_>>(), vec![1, 2, 0, 3]);
assert_eq!(
NMap::drain_prefix((k1,)).collect::<Vec<_>>(),
vec![(1, 1), (2, 2), (0, 0), (3, 3)],
);
assert_eq!(NMap::iter_prefix((k1,)).collect::<Vec<_>>(), vec![]);
assert_eq!(unhashed::get(&key_before_prefix(prefix.clone())), Some(1u64));
assert_eq!(unhashed::get(&key_after_prefix(prefix.clone())), Some(1u64));
// Translate
let prefix = NMap::prefix_hash().to_vec();
unhashed::put(&key_before_prefix(prefix.clone()), &1u64);
unhashed::put(&key_after_prefix(prefix.clone()), &1u64);
for i in 0..4 {
NMap::insert((i as u16, i as u32), i as u64);
}
// Wrong key1
unhashed::put(&[prefix.clone(), vec![1, 2, 3]].concat(), &3u64.encode());
// Wrong key2
unhashed::put(
&[prefix.clone(), crate::Blake2_128Concat::hash(&1u16.encode())].concat(),
&3u64.encode(),
);
// Wrong value
unhashed::put(
&[
prefix.clone(),
crate::Blake2_128Concat::hash(&1u16.encode()),
crate::Twox64Concat::hash(&2u32.encode()),
]
.concat(),
&vec![1],
);
NMap::translate(|(_k1, _k2), v: u64| Some(v * 2));
assert_eq!(
NMap::iter().collect::<Vec<_>>(),
vec![((3, 3), 6), ((0, 0), 0), ((2, 2), 4), ((1, 1), 2)],
);
})
}
}
@@ -0,0 +1,161 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
storage::{self, unhashed, StorageAppend},
Never,
};
use codec::{Decode, Encode, EncodeLike, FullCodec};
/// Generator for `StorageValue` used by `decl_storage`.
///
/// By default value is stored at:
/// ```nocompile
/// Twox128(pezpallet_prefix) ++ Twox128(storage_prefix)
/// ```
pub trait StorageValue<T: FullCodec> {
/// The type that get/take returns.
type Query;
/// Pallet prefix. Used for generating final key.
fn pezpallet_prefix() -> &'static [u8];
/// Storage prefix. Used for generating final key.
fn storage_prefix() -> &'static [u8];
/// Convert an optional value retrieved from storage to the type queried.
fn from_optional_value_to_query(v: Option<T>) -> Self::Query;
/// Convert a query to an optional value into storage.
fn from_query_to_optional_value(v: Self::Query) -> Option<T>;
/// Generate the full key used in top storage.
fn storage_value_final_key() -> [u8; 32];
}
impl<T: FullCodec, G: StorageValue<T>> storage::StorageValue<T> for G {
type Query = G::Query;
fn hashed_key() -> [u8; 32] {
Self::storage_value_final_key()
}
fn exists() -> bool {
unhashed::exists(&Self::storage_value_final_key())
}
fn get() -> Self::Query {
let value = unhashed::get(&Self::storage_value_final_key());
G::from_optional_value_to_query(value)
}
fn try_get() -> Result<T, ()> {
unhashed::get(&Self::storage_value_final_key()).ok_or(())
}
fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()> {
let key = Self::storage_value_final_key();
// attempt to get the length directly.
let maybe_old = unhashed::get_raw(&key)
.map(|old_data| O::decode(&mut &old_data[..]).map_err(|_| ()))
.transpose()?;
let maybe_new = f(maybe_old);
if let Some(new) = maybe_new.as_ref() {
new.using_encoded(|d| unhashed::put_raw(&key, d));
} else {
unhashed::kill(&key);
}
Ok(maybe_new)
}
fn put<Arg: EncodeLike<T>>(val: Arg) {
unhashed::put(&Self::storage_value_final_key(), &val)
}
fn set(maybe_val: Self::Query) {
if let Some(val) = G::from_query_to_optional_value(maybe_val) {
unhashed::put(&Self::storage_value_final_key(), &val)
} else {
unhashed::kill(&Self::storage_value_final_key())
}
}
fn mutate<R, F: FnOnce(&mut G::Query) -> R>(f: F) -> R {
Self::try_mutate(|v| Ok::<R, Never>(f(v))).expect("`Never` can not be constructed; qed")
}
fn try_mutate<R, E, F: FnOnce(&mut G::Query) -> Result<R, E>>(f: F) -> Result<R, E> {
let mut val = G::get();
let ret = f(&mut val);
if ret.is_ok() {
match G::from_query_to_optional_value(val) {
Some(ref val) => G::put(val),
None => G::kill(),
}
}
ret
}
fn mutate_exists<R, F>(f: F) -> R
where
F: FnOnce(&mut Option<T>) -> R,
{
Self::try_mutate_exists(|v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
fn try_mutate_exists<R, E, F>(f: F) -> Result<R, E>
where
F: FnOnce(&mut Option<T>) -> Result<R, E>,
{
let mut val = G::from_query_to_optional_value(Self::get());
let ret = f(&mut val);
if ret.is_ok() {
match val {
Some(ref val) => Self::put(val),
None => Self::kill(),
}
}
ret
}
fn kill() {
unhashed::kill(&Self::storage_value_final_key())
}
fn take() -> G::Query {
let key = Self::storage_value_final_key();
let value = unhashed::get(&key);
if value.is_some() {
unhashed::kill(&key)
}
G::from_optional_value_to_query(value)
}
fn append<Item, EncodeLikeItem>(item: EncodeLikeItem)
where
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
T: StorageAppend<Item>,
{
let key = Self::storage_value_final_key();
pezsp_io::storage::append(&key, item.encode());
}
}
@@ -0,0 +1,156 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Operation on runtime storage using hashed keys.
use super::unhashed;
use alloc::vec::Vec;
use codec::{Decode, Encode};
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<T>
where
T: Decode + Sized,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::get(hash(key).as_ref())
}
/// Return the value of the item in storage under `key`, or the type's default if there is no
/// explicit entry.
pub fn get_or_default<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> T
where
T: Decode + Sized + Default,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::get_or_default(hash(key).as_ref())
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry.
pub fn get_or<T, HashFn, R>(hash: &HashFn, key: &[u8], default_value: T) -> T
where
T: Decode + Sized,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::get_or(hash(key).as_ref(), default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry.
pub fn get_or_else<T, F, HashFn, R>(hash: &HashFn, key: &[u8], default_value: F) -> T
where
T: Decode + Sized,
F: FnOnce() -> T,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::get_or_else(hash(key).as_ref(), default_value)
}
/// Put `value` in storage under `key`.
pub fn put<T, HashFn, R>(hash: &HashFn, key: &[u8], value: &T)
where
T: Encode,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::put(hash(key).as_ref(), value)
}
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
pub fn take<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<T>
where
T: Decode + Sized,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::take(hash(key).as_ref())
}
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
/// the default for its type.
pub fn take_or_default<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> T
where
T: Decode + Sized + Default,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::take_or_default(hash(key).as_ref())
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or<T, HashFn, R>(hash: &HashFn, key: &[u8], default_value: T) -> T
where
T: Decode + Sized,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::take_or(hash(key).as_ref(), default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or_else<T, F, HashFn, R>(hash: &HashFn, key: &[u8], default_value: F) -> T
where
T: Decode + Sized,
F: FnOnce() -> T,
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::take_or_else(hash(key).as_ref(), default_value)
}
/// Check to see if `key` has an explicit entry in storage.
pub fn exists<HashFn, R>(hash: &HashFn, key: &[u8]) -> bool
where
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::exists(hash(key).as_ref())
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill<HashFn, R>(hash: &HashFn, key: &[u8])
where
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::kill(hash(key).as_ref())
}
/// Get a Vec of bytes from storage.
pub fn get_raw<HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<Vec<u8>>
where
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::get_raw(hash(key).as_ref())
}
/// Put a raw byte slice into storage.
pub fn put_raw<HashFn, R>(hash: &HashFn, key: &[u8], value: &[u8])
where
HashFn: Fn(&[u8]) -> R,
R: AsRef<[u8]>,
{
unhashed::put_raw(hash(key).as_ref(), value)
}
@@ -0,0 +1,517 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Some utilities for helping access storage with arbitrary key types.
use crate::{
hash::ReversibleStorageHasher,
storage::{storage_prefix, unhashed},
StorageHasher, Twox128,
};
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode};
use super::PrefixIterator;
/// Utility to iterate through raw items in storage.
pub struct StorageIterator<T> {
prefix: Vec<u8>,
previous_key: Vec<u8>,
drain: bool,
_phantom: ::core::marker::PhantomData<T>,
}
impl<T> StorageIterator<T> {
/// Construct iterator to iterate over map items in `module` for the map called `item`.
#[deprecated(note = "Will be removed after July 2023; Please use the storage_iter or \
storage_iter_with_suffix functions instead")]
pub fn new(module: &[u8], item: &[u8]) -> Self {
#[allow(deprecated)]
Self::with_suffix(module, item, &[][..])
}
/// Construct iterator to iterate over map items in `module` for the map called `item`.
#[deprecated(note = "Will be removed after July 2023; Please use the storage_iter or \
storage_iter_with_suffix functions instead")]
pub fn with_suffix(module: &[u8], item: &[u8], suffix: &[u8]) -> Self {
let mut prefix = Vec::new();
let storage_prefix = storage_prefix(module, item);
prefix.extend_from_slice(&storage_prefix);
prefix.extend_from_slice(suffix);
let previous_key = prefix.clone();
Self { prefix, previous_key, drain: false, _phantom: Default::default() }
}
/// Mutate this iterator into a draining iterator; items iterated are removed from storage.
pub fn drain(mut self) -> Self {
self.drain = true;
self
}
}
impl<T: Decode + Sized> Iterator for StorageIterator<T> {
type Item = (Vec<u8>, T);
fn next(&mut self) -> Option<(Vec<u8>, T)> {
loop {
let maybe_next = pezsp_io::storage::next_key(&self.previous_key)
.filter(|n| n.starts_with(&self.prefix));
break match maybe_next {
Some(next) => {
self.previous_key = next.clone();
let maybe_value = pezframe_support::storage::unhashed::get::<T>(&next);
match maybe_value {
Some(value) => {
if self.drain {
pezframe_support::storage::unhashed::kill(&next);
}
Some((self.previous_key[self.prefix.len()..].to_vec(), value))
},
None => continue,
}
},
None => None,
};
}
}
}
/// Utility to iterate through raw items in storage.
pub struct StorageKeyIterator<K, T, H: ReversibleStorageHasher> {
prefix: Vec<u8>,
previous_key: Vec<u8>,
drain: bool,
_phantom: ::core::marker::PhantomData<(K, T, H)>,
}
impl<K, T, H: ReversibleStorageHasher> StorageKeyIterator<K, T, H> {
/// Construct iterator to iterate over map items in `module` for the map called `item`.
#[deprecated(note = "Will be removed after July 2023; Please use the storage_key_iter or \
storage_key_iter_with_suffix functions instead")]
pub fn new(module: &[u8], item: &[u8]) -> Self {
#[allow(deprecated)]
Self::with_suffix(module, item, &[][..])
}
/// Construct iterator to iterate over map items in `module` for the map called `item`.
#[deprecated(note = "Will be removed after July 2023; Please use the storage_key_iter or \
storage_key_iter_with_suffix functions instead")]
pub fn with_suffix(module: &[u8], item: &[u8], suffix: &[u8]) -> Self {
let mut prefix = Vec::new();
let storage_prefix = storage_prefix(module, item);
prefix.extend_from_slice(&storage_prefix);
prefix.extend_from_slice(suffix);
let previous_key = prefix.clone();
Self { prefix, previous_key, drain: false, _phantom: Default::default() }
}
/// Mutate this iterator into a draining iterator; items iterated are removed from storage.
pub fn drain(mut self) -> Self {
self.drain = true;
self
}
}
impl<K: Decode + Sized, T: Decode + Sized, H: ReversibleStorageHasher> Iterator
for StorageKeyIterator<K, T, H>
{
type Item = (K, T);
fn next(&mut self) -> Option<(K, T)> {
loop {
let maybe_next = pezsp_io::storage::next_key(&self.previous_key)
.filter(|n| n.starts_with(&self.prefix));
break match maybe_next {
Some(next) => {
self.previous_key = next.clone();
let mut key_material = H::reverse(&next[self.prefix.len()..]);
match K::decode(&mut key_material) {
Ok(key) => {
let maybe_value = pezframe_support::storage::unhashed::get::<T>(&next);
match maybe_value {
Some(value) => {
if self.drain {
pezframe_support::storage::unhashed::kill(&next);
}
Some((key, value))
},
None => continue,
}
},
Err(_) => continue,
}
},
None => None,
};
}
}
}
/// Construct iterator to iterate over map items in `module` for the map called `item`.
pub fn storage_iter<T: Decode + Sized>(module: &[u8], item: &[u8]) -> PrefixIterator<(Vec<u8>, T)> {
storage_iter_with_suffix(module, item, &[][..])
}
/// Construct iterator to iterate over map items in `module` for the map called `item`.
pub fn storage_iter_with_suffix<T: Decode + Sized>(
module: &[u8],
item: &[u8],
suffix: &[u8],
) -> PrefixIterator<(Vec<u8>, T)> {
let mut prefix = Vec::new();
let storage_prefix = storage_prefix(module, item);
prefix.extend_from_slice(&storage_prefix);
prefix.extend_from_slice(suffix);
let previous_key = prefix.clone();
let closure = |raw_key_without_prefix: &[u8], mut raw_value: &[u8]| {
let value = T::decode(&mut raw_value)?;
Ok((raw_key_without_prefix.to_vec(), value))
};
PrefixIterator { prefix, previous_key, drain: false, closure, phantom: Default::default() }
}
/// Construct iterator to iterate over map items in `module` for the map called `item`.
pub fn storage_key_iter<K: Decode + Sized, T: Decode + Sized, H: ReversibleStorageHasher>(
module: &[u8],
item: &[u8],
) -> PrefixIterator<(K, T)> {
storage_key_iter_with_suffix::<K, T, H>(module, item, &[][..])
}
/// Construct iterator to iterate over map items in `module` for the map called `item`.
pub fn storage_key_iter_with_suffix<
K: Decode + Sized,
T: Decode + Sized,
H: ReversibleStorageHasher,
>(
module: &[u8],
item: &[u8],
suffix: &[u8],
) -> PrefixIterator<(K, T)> {
let mut prefix = Vec::new();
let storage_prefix = storage_prefix(module, item);
prefix.extend_from_slice(&storage_prefix);
prefix.extend_from_slice(suffix);
let previous_key = prefix.clone();
let closure = |raw_key_without_prefix: &[u8], mut raw_value: &[u8]| {
let mut key_material = H::reverse(raw_key_without_prefix);
let key = K::decode(&mut key_material)?;
let value = T::decode(&mut raw_value)?;
Ok((key, value))
};
PrefixIterator { prefix, previous_key, drain: false, closure, phantom: Default::default() }
}
/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`.
pub fn have_storage_value(module: &[u8], item: &[u8], hash: &[u8]) -> bool {
get_storage_value::<()>(module, item, hash).is_some()
}
/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`.
pub fn get_storage_value<T: Decode + Sized>(module: &[u8], item: &[u8], hash: &[u8]) -> Option<T> {
let mut key = vec![0u8; 32 + hash.len()];
let storage_prefix = storage_prefix(module, item);
key[0..32].copy_from_slice(&storage_prefix);
key[32..].copy_from_slice(hash);
pezframe_support::storage::unhashed::get::<T>(&key)
}
/// Take a particular value in storage by the `module`, the map's `item` name and the key `hash`.
pub fn take_storage_value<T: Decode + Sized>(module: &[u8], item: &[u8], hash: &[u8]) -> Option<T> {
let mut key = vec![0u8; 32 + hash.len()];
let storage_prefix = storage_prefix(module, item);
key[0..32].copy_from_slice(&storage_prefix);
key[32..].copy_from_slice(hash);
pezframe_support::storage::unhashed::take::<T>(&key)
}
/// Put a particular value into storage by the `module`, the map's `item` name and the key `hash`.
pub fn put_storage_value<T: Encode>(module: &[u8], item: &[u8], hash: &[u8], value: T) {
let mut key = vec![0u8; 32 + hash.len()];
let storage_prefix = storage_prefix(module, item);
key[0..32].copy_from_slice(&storage_prefix);
key[32..].copy_from_slice(hash);
pezframe_support::storage::unhashed::put(&key, &value);
}
/// Remove all items under a storage prefix by the `module`, the map's `item` name and the key
/// `hash`.
#[deprecated = "Use `clear_storage_prefix` instead"]
pub fn remove_storage_prefix(module: &[u8], item: &[u8], hash: &[u8]) {
let mut key = vec![0u8; 32 + hash.len()];
let storage_prefix = storage_prefix(module, item);
key[0..32].copy_from_slice(&storage_prefix);
key[32..].copy_from_slice(hash);
let _ = pezframe_support::storage::unhashed::clear_prefix(&key, None, None);
}
/// Attempt to remove all values under a storage prefix by the `module`, the map's `item` name and
/// the key `hash`.
///
/// All values in the client overlay will be deleted, if `maybe_limit` is `Some` then up to
/// that number of values are deleted from the client backend by seeking and reading that number of
/// storage values plus one. If `maybe_limit` is `None` then all values in the client backend are
/// deleted. This is potentially unsafe since it's an unbounded operation.
///
/// ## Cursors
///
/// The `maybe_cursor` parameter should be `None` for the first call to initial removal.
/// If the resultant `maybe_cursor` is `Some`, then another call is required to complete the
/// removal operation. This value must be passed in as the subsequent call's `maybe_cursor`
/// parameter. If the resultant `maybe_cursor` is `None`, then the operation is complete and no
/// items remain in storage provided that no items were added between the first calls and the
/// final call.
pub fn clear_storage_prefix(
module: &[u8],
item: &[u8],
hash: &[u8],
maybe_limit: Option<u32>,
maybe_cursor: Option<&[u8]>,
) -> pezsp_io::MultiRemovalResults {
let mut key = vec![0u8; 32 + hash.len()];
let storage_prefix = storage_prefix(module, item);
key[0..32].copy_from_slice(&storage_prefix);
key[32..].copy_from_slice(hash);
pezframe_support::storage::unhashed::clear_prefix(&key, maybe_limit, maybe_cursor)
}
/// Take a particular item in storage by the `module`, the map's `item` name and the key `hash`.
pub fn take_storage_item<K: Encode + Sized, T: Decode + Sized, H: StorageHasher>(
module: &[u8],
item: &[u8],
key: K,
) -> Option<T> {
take_storage_value(module, item, key.using_encoded(H::hash).as_ref())
}
/// Move a storage from a pallet prefix to another pallet prefix.
///
/// Keys used in pallet storages always start with:
/// `concat(twox_128(pezpallet_name), twox_128(storage_name))`.
///
/// This function will remove all value for which the key start with
/// `concat(twox_128(old_pallet_name), twox_128(storage_name))` and insert them at the key with
/// the start replaced by `concat(twox_128(new_pallet_name), twox_128(storage_name))`.
///
/// # Example
///
/// If a pallet named "my_example" has 2 storages named "Foo" and "Bar" and the pallet is renamed
/// "my_new_example_name", a migration can be:
/// ```
/// # use pezframe_support::storage::migration::move_storage_from_pallet;
/// # pezsp_io::TestExternalities::new_empty().execute_with(|| {
/// move_storage_from_pallet(b"Foo", b"my_example", b"my_new_example_name");
/// move_storage_from_pallet(b"Bar", b"my_example", b"my_new_example_name");
/// # })
/// ```
pub fn move_storage_from_pallet(
storage_name: &[u8],
old_pallet_name: &[u8],
new_pallet_name: &[u8],
) {
let new_prefix = storage_prefix(new_pallet_name, storage_name);
let old_prefix = storage_prefix(old_pallet_name, storage_name);
move_prefix(&old_prefix, &new_prefix);
if let Some(value) = unhashed::get_raw(&old_prefix) {
unhashed::put_raw(&new_prefix, &value);
unhashed::kill(&old_prefix);
}
}
/// Move all storages from a pallet prefix to another pallet prefix.
///
/// Keys used in pallet storages always start with:
/// `concat(twox_128(pezpallet_name), twox_128(storage_name))`.
///
/// This function will remove all value for which the key start with `twox_128(old_pallet_name)`
/// and insert them at the key with the start replaced by `twox_128(new_pallet_name)`.
///
/// NOTE: The value at the key `twox_128(old_pallet_name)` is not moved.
///
/// # Example
///
/// If a pallet named "my_example" has some storages and the pallet is renamed
/// "my_new_example_name", a migration can be:
/// ```
/// # use pezframe_support::storage::migration::move_pallet;
/// # pezsp_io::TestExternalities::new_empty().execute_with(|| {
/// move_pallet(b"my_example", b"my_new_example_name");
/// # })
/// ```
pub fn move_pallet(old_pallet_name: &[u8], new_pallet_name: &[u8]) {
move_prefix(&Twox128::hash(old_pallet_name), &Twox128::hash(new_pallet_name))
}
/// Move all `(key, value)` after some prefix to the another prefix
///
/// This function will remove all value for which the key start with `from_prefix`
/// and insert them at the key with the start replaced by `to_prefix`.
///
/// NOTE: The value at the key `from_prefix` is not moved.
pub fn move_prefix(from_prefix: &[u8], to_prefix: &[u8]) {
if from_prefix == to_prefix {
return;
}
let iter = PrefixIterator::<_> {
prefix: from_prefix.to_vec(),
previous_key: from_prefix.to_vec(),
drain: true,
closure: |key, value| Ok((key.to_vec(), value.to_vec())),
phantom: Default::default(),
};
for (key, value) in iter {
let full_key = [to_prefix, &key].concat();
unhashed::put_raw(&full_key, &value);
}
}
#[cfg(test)]
mod tests {
use super::{
move_pallet, move_prefix, move_storage_from_pallet, storage_iter, storage_key_iter,
};
use crate::{
hash::StorageHasher,
pezpallet_prelude::{StorageMap, StorageValue, Twox128, Twox64Concat},
};
use pezsp_io::TestExternalities;
struct OldPalletStorageValuePrefix;
impl pezframe_support::traits::StorageInstance for OldPalletStorageValuePrefix {
const STORAGE_PREFIX: &'static str = "foo_value";
fn pezpallet_prefix() -> &'static str {
"my_old_pallet"
}
}
type OldStorageValue = StorageValue<OldPalletStorageValuePrefix, u32>;
struct OldPalletStorageMapPrefix;
impl pezframe_support::traits::StorageInstance for OldPalletStorageMapPrefix {
const STORAGE_PREFIX: &'static str = "foo_map";
fn pezpallet_prefix() -> &'static str {
"my_old_pallet"
}
}
type OldStorageMap = StorageMap<OldPalletStorageMapPrefix, Twox64Concat, u32, u32>;
struct NewPalletStorageValuePrefix;
impl pezframe_support::traits::StorageInstance for NewPalletStorageValuePrefix {
const STORAGE_PREFIX: &'static str = "foo_value";
fn pezpallet_prefix() -> &'static str {
"my_new_pallet"
}
}
type NewStorageValue = StorageValue<NewPalletStorageValuePrefix, u32>;
struct NewPalletStorageMapPrefix;
impl pezframe_support::traits::StorageInstance for NewPalletStorageMapPrefix {
const STORAGE_PREFIX: &'static str = "foo_map";
fn pezpallet_prefix() -> &'static str {
"my_new_pallet"
}
}
type NewStorageMap = StorageMap<NewPalletStorageMapPrefix, Twox64Concat, u32, u32>;
#[test]
fn test_move_prefix() {
TestExternalities::new_empty().execute_with(|| {
OldStorageValue::put(3);
OldStorageMap::insert(1, 2);
OldStorageMap::insert(3, 4);
move_prefix(&Twox128::hash(b"my_old_pallet"), &Twox128::hash(b"my_new_pallet"));
assert_eq!(OldStorageValue::get(), None);
assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
assert_eq!(NewStorageValue::get(), Some(3));
assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
})
}
#[test]
fn test_move_storage() {
TestExternalities::new_empty().execute_with(|| {
OldStorageValue::put(3);
OldStorageMap::insert(1, 2);
OldStorageMap::insert(3, 4);
move_storage_from_pallet(b"foo_map", b"my_old_pallet", b"my_new_pallet");
assert_eq!(OldStorageValue::get(), Some(3));
assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
assert_eq!(NewStorageValue::get(), None);
assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
move_storage_from_pallet(b"foo_value", b"my_old_pallet", b"my_new_pallet");
assert_eq!(OldStorageValue::get(), None);
assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
assert_eq!(NewStorageValue::get(), Some(3));
assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
})
}
#[test]
fn test_move_pallet() {
TestExternalities::new_empty().execute_with(|| {
OldStorageValue::put(3);
OldStorageMap::insert(1, 2);
OldStorageMap::insert(3, 4);
move_pallet(b"my_old_pallet", b"my_new_pallet");
assert_eq!(OldStorageValue::get(), None);
assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
assert_eq!(NewStorageValue::get(), Some(3));
assert_eq!(NewStorageMap::iter().collect::<Vec<_>>(), vec![(1, 2), (3, 4)]);
})
}
#[test]
fn test_storage_iter() {
TestExternalities::new_empty().execute_with(|| {
OldStorageValue::put(3);
OldStorageMap::insert(1, 2);
OldStorageMap::insert(3, 4);
assert_eq!(
storage_key_iter::<i32, i32, Twox64Concat>(b"my_old_pallet", b"foo_map")
.collect::<Vec<_>>(),
vec![(1, 2), (3, 4)],
);
assert_eq!(
storage_iter(b"my_old_pallet", b"foo_map")
.drain()
.map(|t| t.1)
.collect::<Vec<i32>>(),
vec![2, 4],
);
assert_eq!(OldStorageMap::iter().collect::<Vec<_>>(), vec![]);
// Empty because storage iterator skips over the entry under the first key
assert_eq!(storage_iter::<i32>(b"my_old_pallet", b"foo_value").drain().next(), None);
assert_eq!(OldStorageValue::get(), Some(3));
});
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,170 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Feature gated since it can panic.
#![cfg(any(feature = "std", feature = "runtime-benchmarks", feature = "try-runtime", test))]
//! Contains the [`crate::StorageNoopGuard`] for conveniently asserting
//! that no storage mutation has been made by a whole code block.
/// Asserts that no storage changes took place between con- and destruction of [`Self`].
///
/// This is easier than wrapping the whole code-block inside a `assert_storage_noop!`.
///
/// # Example
///
/// ```should_panic
/// use pezframe_support::{StorageNoopGuard, storage::unhashed::put};
///
/// pezsp_io::TestExternalities::default().execute_with(|| {
/// let _guard = pezframe_support::StorageNoopGuard::default();
/// put(b"key", b"value");
/// // Panics since there are storage changes.
/// });
/// ```
#[must_use]
pub struct StorageNoopGuard<'a> {
storage_root: alloc::vec::Vec<u8>,
error_message: &'a str,
}
impl<'a> Default for StorageNoopGuard<'a> {
fn default() -> Self {
Self {
storage_root: pezsp_io::storage::root(pezsp_runtime::StateVersion::V1),
error_message: "`StorageNoopGuard` detected an attempted storage change.",
}
}
}
impl<'a> StorageNoopGuard<'a> {
/// Alias to `default()`.
pub fn new() -> Self {
Self::default()
}
/// Creates a new [`StorageNoopGuard`] with a custom error message.
pub fn from_error_message(error_message: &'a str) -> Self {
Self { storage_root: pezsp_io::storage::root(pezsp_runtime::StateVersion::V1), error_message }
}
/// Sets a custom error message for a [`StorageNoopGuard`].
pub fn set_error_message(&mut self, error_message: &'a str) {
self.error_message = error_message;
}
}
impl<'a> Drop for StorageNoopGuard<'a> {
fn drop(&mut self) {
// No need to double panic, eg. inside a test assertion failure.
#[cfg(feature = "std")]
if std::thread::panicking() {
return;
}
assert_eq!(
pezsp_io::storage::root(pezsp_runtime::StateVersion::V1),
self.storage_root,
"{}",
self.error_message,
);
}
}
#[cfg(test)]
mod tests {
use pezsp_io::TestExternalities;
use super::*;
#[test]
#[should_panic(expected = "`StorageNoopGuard` detected an attempted storage change.")]
fn storage_noop_guard_panics_on_changed() {
TestExternalities::default().execute_with(|| {
let _guard = StorageNoopGuard::default();
pezframe_support::storage::unhashed::put(b"key", b"value");
});
}
#[test]
fn storage_noop_guard_works_on_unchanged() {
TestExternalities::default().execute_with(|| {
let _guard = StorageNoopGuard::default();
pezframe_support::storage::unhashed::put(b"key", b"value");
pezframe_support::storage::unhashed::kill(b"key");
});
}
#[test]
#[should_panic(expected = "`StorageNoopGuard` detected an attempted storage change.")]
fn storage_noop_guard_panics_on_early_drop() {
TestExternalities::default().execute_with(|| {
let guard = StorageNoopGuard::default();
pezframe_support::storage::unhashed::put(b"key", b"value");
std::mem::drop(guard);
pezframe_support::storage::unhashed::kill(b"key");
});
}
#[test]
fn storage_noop_guard_works_on_changed_forget() {
TestExternalities::default().execute_with(|| {
let guard = StorageNoopGuard::default();
pezframe_support::storage::unhashed::put(b"key", b"value");
std::mem::forget(guard);
});
}
#[test]
#[should_panic(expected = "Something else")]
fn storage_noop_guard_does_not_double_panic() {
TestExternalities::default().execute_with(|| {
let _guard = StorageNoopGuard::default();
pezframe_support::storage::unhashed::put(b"key", b"value");
panic!("Something else");
});
}
#[test]
#[should_panic(expected = "`StorageNoopGuard` found unexpected storage changes.")]
fn storage_noop_guard_panics_created_from_error_message() {
TestExternalities::default().execute_with(|| {
let _guard = StorageNoopGuard::from_error_message(
"`StorageNoopGuard` found unexpected storage changes.",
);
pezframe_support::storage::unhashed::put(b"key", b"value");
});
}
#[test]
#[should_panic(expected = "`StorageNoopGuard` found unexpected storage changes.")]
fn storage_noop_guard_panics_with_set_error_message() {
TestExternalities::default().execute_with(|| {
let mut guard = StorageNoopGuard::default();
guard.set_error_message("`StorageNoopGuard` found unexpected storage changes.");
pezframe_support::storage::unhashed::put(b"key", b"value");
});
}
#[test]
#[should_panic(expected = "`StorageNoopGuard` detected an attempted storage change.")]
fn storage_noop_guard_panics_new_alias() {
TestExternalities::default().execute_with(|| {
let _guard = StorageNoopGuard::new();
pezframe_support::storage::unhashed::put(b"key", b"value");
});
}
}
@@ -0,0 +1,666 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{BoundedBTreeMap, BoundedBTreeSet, BoundedVec, WeakBoundedVec};
use alloc::vec::Vec;
use codec::Decode;
/// Provides the sealed trait `StreamIter`.
mod private {
use super::*;
/// Used as marker trait for types that support stream iteration.
pub trait StreamIter {
/// The actual iterator implementation.
type Iterator: core::iter::Iterator;
/// Create the stream iterator for the value found at `key`.
fn stream_iter(key: Vec<u8>) -> Self::Iterator;
}
impl<T: codec::Decode> StreamIter for Vec<T> {
type Iterator = ScaleContainerStreamIter<T>;
fn stream_iter(key: Vec<u8>) -> Self::Iterator {
ScaleContainerStreamIter::new(key)
}
}
impl<T: codec::Decode> StreamIter for alloc::collections::btree_set::BTreeSet<T> {
type Iterator = ScaleContainerStreamIter<T>;
fn stream_iter(key: Vec<u8>) -> Self::Iterator {
ScaleContainerStreamIter::new(key)
}
}
impl<K: codec::Decode, V: codec::Decode> StreamIter
for alloc::collections::btree_map::BTreeMap<K, V>
{
type Iterator = ScaleContainerStreamIter<(K, V)>;
fn stream_iter(key: Vec<u8>) -> Self::Iterator {
ScaleContainerStreamIter::new(key)
}
}
impl<T: codec::Decode, S> StreamIter for BoundedVec<T, S> {
type Iterator = ScaleContainerStreamIter<T>;
fn stream_iter(key: Vec<u8>) -> Self::Iterator {
ScaleContainerStreamIter::new(key)
}
}
impl<T: codec::Decode, S> StreamIter for WeakBoundedVec<T, S> {
type Iterator = ScaleContainerStreamIter<T>;
fn stream_iter(key: Vec<u8>) -> Self::Iterator {
ScaleContainerStreamIter::new(key)
}
}
impl<K: codec::Decode, V: codec::Decode, S> StreamIter for BoundedBTreeMap<K, V, S> {
type Iterator = ScaleContainerStreamIter<(K, V)>;
fn stream_iter(key: Vec<u8>) -> Self::Iterator {
ScaleContainerStreamIter::new(key)
}
}
impl<T: codec::Decode, S> StreamIter for BoundedBTreeSet<T, S> {
type Iterator = ScaleContainerStreamIter<T>;
fn stream_iter(key: Vec<u8>) -> Self::Iterator {
ScaleContainerStreamIter::new(key)
}
}
}
/// An iterator that streams values directly from storage.
///
/// Requires that `T` implements the sealed trait `StreamIter`.
///
/// Instead of loading the entire `T` into memory, the iterator only loads a certain number of bytes
/// into memory to decode the next `T::Item`. The iterator implementation is allowed to have some
/// internal buffer to reduce the number of storage reads. The iterator should have an almost
/// constant memory usage over its lifetime. If at some point there is a decoding error, the
/// iterator will return `None` to signal that the iterator is finished.
pub trait StorageStreamIter<T: private::StreamIter> {
/// Create the streaming iterator.
fn stream_iter() -> T::Iterator;
}
impl<T: private::StreamIter + codec::FullCodec, StorageValue: super::StorageValue<T>>
StorageStreamIter<T> for StorageValue
{
fn stream_iter() -> T::Iterator {
T::stream_iter(Self::hashed_key().into())
}
}
/// A streaming iterator implementation for SCALE container types.
///
/// SCALE container types follow the same type of encoding `Compact<u32>(len) ++ data`.
/// This type provides an [`Iterator`](core::iter::Iterator) implementation that decodes
/// one item after another with each call to [`next`](Self::next). The bytes representing
/// the container are also not read at once into memory and instead being read in chunks. As long
/// as individual items are smaller than these chunks the memory usage of this iterator should
/// be constant. On decoding errors [`next`](Self::next) will return `None` to signal that the
/// iterator is finished.
pub struct ScaleContainerStreamIter<T> {
marker: core::marker::PhantomData<T>,
input: StorageInput,
length: u32,
read: u32,
}
impl<T> ScaleContainerStreamIter<T> {
/// Creates a new instance of the stream iterator.
///
/// - `key`: Storage key of the container in the state.
///
/// Same as [`Self::new_try`], but logs a potential error and sets the length to `0`.
pub fn new(key: Vec<u8>) -> Self {
let mut input = StorageInput::new(key);
let length = if input.exists() {
match codec::Compact::<u32>::decode(&mut input) {
Ok(length) => length.0,
Err(e) => {
// TODO #3700: error should be handleable.
log::error!(
target: "runtime::storage",
"Corrupted state at `{:?}`: failed to decode element count: {:?}",
input.key,
e,
);
0
},
}
} else {
0
};
Self { marker: core::marker::PhantomData, input, length, read: 0 }
}
/// Creates a new instance of the stream iterator.
///
/// - `key`: Storage key of the container in the state.
///
/// Returns an error if the length of the container fails to decode.
pub fn new_try(key: Vec<u8>) -> Result<Self, codec::Error> {
let mut input = StorageInput::new(key);
let length = if input.exists() { codec::Compact::<u32>::decode(&mut input)?.0 } else { 0 };
Ok(Self { marker: core::marker::PhantomData, input, length, read: 0 })
}
}
impl<T: codec::Decode> core::iter::Iterator for ScaleContainerStreamIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.read >= self.length {
return None;
}
match codec::Decode::decode(&mut self.input) {
Ok(r) => {
self.read += 1;
Some(r)
},
Err(e) => {
log::error!(
target: "runtime::storage",
"Corrupted state at `{:?}`: failed to decode element {} (out of {} in total): {:?}",
self.input.key,
self.read,
self.length,
e,
);
self.read = self.length;
None
},
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let left = (self.length - self.read) as usize;
(left, Some(left))
}
}
/// The size of the internal buffer used by [`StorageInput`].
///
/// This internal buffer is used to speed up implementation as reading from the
/// state for every access is too slow.
const STORAGE_INPUT_BUFFER_CAPACITY: usize = 2 * 1024;
/// Implementation of [`codec::Input`] using [`pezsp_io::storage::read`].
///
/// Keeps an internal buffer with a size of [`STORAGE_INPUT_BUFFER_CAPACITY`]. All read accesses
/// are tried to be served by this buffer. If the buffer doesn't hold enough bytes to fulfill the
/// current read access, the buffer is re-filled from the state. A read request that is bigger than
/// the internal buffer is directly forwarded to the state to reduce the number of reads from the
/// state.
struct StorageInput {
key: Vec<u8>,
offset: u32,
total_length: u32,
exists: bool,
buffer: Vec<u8>,
buffer_pos: usize,
}
impl StorageInput {
/// Create a new instance of the input.
///
/// - `key`: The storage key of the storage item that this input will read.
fn new(key: Vec<u8>) -> Self {
let mut buffer = alloc::vec![0; STORAGE_INPUT_BUFFER_CAPACITY];
unsafe {
buffer.set_len(buffer.capacity());
}
let (total_length, exists) =
if let Some(total_length) = pezsp_io::storage::read(&key, &mut buffer, 0) {
(total_length, true)
} else {
(0, false)
};
if (total_length as usize) < buffer.len() {
unsafe {
buffer.set_len(total_length as usize);
}
}
Self { total_length, offset: buffer.len() as u32, key, exists, buffer, buffer_pos: 0 }
}
/// Fill the internal buffer from the state.
fn fill_buffer(&mut self) -> Result<(), codec::Error> {
self.buffer.copy_within(self.buffer_pos.., 0);
let present_bytes = self.buffer.len() - self.buffer_pos;
self.buffer_pos = 0;
unsafe {
self.buffer.set_len(self.buffer.capacity());
}
if let Some(length_minus_offset) =
pezsp_io::storage::read(&self.key, &mut self.buffer[present_bytes..], self.offset)
{
let bytes_read =
core::cmp::min(length_minus_offset as usize, self.buffer.len() - present_bytes);
let buffer_len = present_bytes + bytes_read;
unsafe {
self.buffer.set_len(buffer_len);
}
self.ensure_total_length_did_not_change(length_minus_offset)?;
self.offset += bytes_read as u32;
Ok(())
} else {
// The value was deleted, let's ensure we don't read anymore.
self.stop_reading();
Err("Value doesn't exist in the state?".into())
}
}
/// Returns if the value to read exists in the state.
fn exists(&self) -> bool {
self.exists
}
/// Reads directly into the given slice `into`.
///
/// Should be used when `into.len() > self.buffer.capacity()` to reduce the number of reads from
/// the state.
#[inline(never)]
fn read_big_item(&mut self, into: &mut [u8]) -> Result<(), codec::Error> {
let num_cached = self.buffer.len() - self.buffer_pos;
let (out_already_read, mut out_remaining) = into.split_at_mut(num_cached);
out_already_read.copy_from_slice(&self.buffer[self.buffer_pos..]);
self.buffer_pos = 0;
unsafe {
self.buffer.set_len(0);
}
if let Some(length_minus_offset) =
pezsp_io::storage::read(&self.key, &mut out_remaining, self.offset)
{
if (length_minus_offset as usize) < out_remaining.len() {
return Err("Not enough data to fill the buffer".into());
}
self.ensure_total_length_did_not_change(length_minus_offset)?;
self.offset += out_remaining.len() as u32;
Ok(())
} else {
// The value was deleted, let's ensure we don't read anymore.
self.stop_reading();
Err("Value doesn't exist in the state?".into())
}
}
/// Ensures that the expected total length of the value did not change.
///
/// On error ensures that further reading is prohibited.
fn ensure_total_length_did_not_change(
&mut self,
length_minus_offset: u32,
) -> Result<(), codec::Error> {
if self.total_length == self.offset + length_minus_offset {
Ok(())
} else {
// The value total length changed, let's ensure we don't read anymore.
self.stop_reading();
Err("Storage value changed while it is being read!".into())
}
}
/// Ensure that we are stop reading from this value in the state.
///
/// Should be used when there happened an unrecoverable error while reading.
fn stop_reading(&mut self) {
self.offset = self.total_length;
self.buffer_pos = 0;
unsafe {
self.buffer.set_len(0);
}
}
}
impl codec::Input for StorageInput {
fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
Ok(Some(self.total_length.saturating_sub(
self.offset.saturating_sub((self.buffer.len() - self.buffer_pos) as u32),
) as usize))
}
fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> {
// If there is still data left to be read from the state.
if self.offset < self.total_length {
if into.len() > self.buffer.capacity() {
return self.read_big_item(into);
} else if self.buffer_pos + into.len() > self.buffer.len() {
self.fill_buffer()?;
}
}
// Guard against `fill_buffer` not reading enough data or just not having enough data
// anymore.
if into.len() + self.buffer_pos > self.buffer.len() {
return Err("Not enough data to fill the buffer".into());
}
let end = self.buffer_pos + into.len();
into.copy_from_slice(&self.buffer[self.buffer_pos..end]);
self.buffer_pos = end;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use codec::{Compact, CompactLen, Encode, Input};
#[crate::storage_alias]
pub type TestVecU32 = StorageValue<Test, Vec<u32>>;
#[crate::storage_alias]
pub type TestVecVecU8 = StorageValue<Test, Vec<Vec<u8>>>;
#[test]
fn remaining_len_works() {
pezsp_io::TestExternalities::default().execute_with(|| {
let data: Vec<u32> = vec![1, 2, 3, 4, 5];
TestVecU32::put(&data);
let mut input = StorageInput::new(TestVecU32::hashed_key().into());
assert_eq!(
5 * std::mem::size_of::<u32>() + Compact::<u32>::compact_len(&5) as usize,
input.remaining_len().ok().flatten().unwrap()
);
assert_eq!(5, Compact::<u32>::decode(&mut input).unwrap().0);
assert_eq!(
5 * std::mem::size_of::<u32>(),
input.remaining_len().ok().flatten().unwrap()
);
for i in &data {
assert_eq!(*i, u32::decode(&mut input).unwrap());
assert_eq!(
(5 - *i as usize) * std::mem::size_of::<u32>(),
input.remaining_len().ok().flatten().unwrap()
);
}
let data: Vec<Vec<u8>> = vec![
vec![0; 20],
vec![1; STORAGE_INPUT_BUFFER_CAPACITY * 2],
vec![2; STORAGE_INPUT_BUFFER_CAPACITY * 2],
vec![3; 30],
vec![4; 30],
vec![5; STORAGE_INPUT_BUFFER_CAPACITY * 2],
vec![6; 30],
];
TestVecVecU8::put(&data);
let mut input = StorageInput::new(TestVecVecU8::hashed_key().into());
let total_data_len = data
.iter()
.map(|v| v.len() + Compact::<u32>::compact_len(&(v.len() as u32)) as usize)
.sum::<usize>();
assert_eq!(
total_data_len + Compact::<u32>::compact_len(&(data.len() as u32)) as usize,
input.remaining_len().ok().flatten().unwrap()
);
assert_eq!(data.len(), Compact::<u32>::decode(&mut input).unwrap().0 as usize);
assert_eq!(total_data_len, input.remaining_len().ok().flatten().unwrap());
let mut remaining_len = total_data_len;
for i in data {
assert_eq!(i, Vec::<u8>::decode(&mut input).unwrap());
remaining_len -= i.len() + Compact::<u32>::compact_len(&(i.len() as u32)) as usize;
assert_eq!(remaining_len, input.remaining_len().ok().flatten().unwrap());
}
})
}
#[test]
fn detects_value_total_length_change() {
pezsp_io::TestExternalities::default().execute_with(|| {
let test_data: Vec<Vec<Vec<u8>>> = vec![
vec![vec![0; 20], vec![1; STORAGE_INPUT_BUFFER_CAPACITY * 2]],
vec![
vec![0; STORAGE_INPUT_BUFFER_CAPACITY - 1],
vec![1; STORAGE_INPUT_BUFFER_CAPACITY - 1],
],
];
for data in test_data {
TestVecVecU8::put(&data);
let mut input = StorageInput::new(TestVecVecU8::hashed_key().into());
Compact::<u32>::decode(&mut input).unwrap();
Vec::<u8>::decode(&mut input).unwrap();
TestVecVecU8::append(vec![1, 2, 3]);
assert!(Vec::<u8>::decode(&mut input)
.unwrap_err()
.to_string()
.contains("Storage value changed while it is being read"));
// Reading a second time should now prevent reading at all.
assert!(Vec::<u8>::decode(&mut input)
.unwrap_err()
.to_string()
.contains("Not enough data to fill the buffer"));
}
})
}
#[test]
fn stream_read_test() {
pezsp_io::TestExternalities::default().execute_with(|| {
let data: Vec<u32> = vec![1, 2, 3, 4, 5];
TestVecU32::put(&data);
assert_eq!(data, TestVecU32::stream_iter().collect::<Vec<_>>());
let data: Vec<Vec<u8>> = vec![vec![0; 3000], vec![1; 2500]];
TestVecVecU8::put(&data);
assert_eq!(data, TestVecVecU8::stream_iter().collect::<Vec<_>>());
})
}
#[test]
fn reading_big_intermediate_value() {
pezsp_io::TestExternalities::default().execute_with(|| {
let data: Vec<Vec<u8>> =
vec![vec![0; 20], vec![1; STORAGE_INPUT_BUFFER_CAPACITY * 2], vec![2; 30]];
TestVecVecU8::put(&data);
assert_eq!(data, TestVecVecU8::stream_iter().collect::<Vec<_>>());
let data: Vec<Vec<u8>> = vec![
vec![0; 20],
vec![1; STORAGE_INPUT_BUFFER_CAPACITY * 2],
vec![2; STORAGE_INPUT_BUFFER_CAPACITY * 2],
vec![3; 30],
vec![4; 30],
vec![5; STORAGE_INPUT_BUFFER_CAPACITY * 2],
vec![6; 30],
];
TestVecVecU8::put(&data);
assert_eq!(data, TestVecVecU8::stream_iter().collect::<Vec<_>>());
})
}
#[test]
fn reading_more_data_as_in_the_state_is_detected() {
pezsp_io::TestExternalities::default().execute_with(|| {
let data: Vec<Vec<u8>> = vec![vec![0; 20], vec![1; STORAGE_INPUT_BUFFER_CAPACITY * 2]];
TestVecVecU8::put(&data);
let mut input = StorageInput::new(TestVecVecU8::hashed_key().into());
Compact::<u32>::decode(&mut input).unwrap();
Vec::<u8>::decode(&mut input).unwrap();
let mut buffer = vec![0; STORAGE_INPUT_BUFFER_CAPACITY * 4];
assert!(input
.read(&mut buffer)
.unwrap_err()
.to_string()
.contains("Not enough data to fill the buffer"));
})
}
#[test]
fn reading_invalid_data_from_state() {
pezsp_io::TestExternalities::default().execute_with(|| {
let data: Vec<u32> = vec![1, 2, 3, 4, 5];
let mut data_encoded = data.encode();
data_encoded.truncate(data_encoded.len() - 2);
pezsp_io::storage::set(&TestVecU32::hashed_key(), &data_encoded);
assert_eq!(
data.iter().copied().take(data.len() - 1).collect::<Vec<_>>(),
TestVecU32::stream_iter().collect::<Vec<_>>()
);
let data_encoded = data.encode()[2..].to_vec();
pezsp_io::storage::set(&TestVecU32::hashed_key(), &data_encoded);
assert!(TestVecU32::stream_iter().collect::<Vec<_>>().is_empty());
let data: Vec<Vec<u8>> = vec![vec![0; 20], vec![1; STORAGE_INPUT_BUFFER_CAPACITY * 2]];
let mut data_encoded = data.encode();
data_encoded.truncate(data_encoded.len() - 100);
pezsp_io::storage::set(&TestVecVecU8::hashed_key(), &data_encoded);
assert_eq!(
data.iter().cloned().take(1).collect::<Vec<_>>(),
TestVecVecU8::stream_iter().collect::<Vec<_>>()
);
})
}
#[test]
fn reading_with_fill_buffer() {
pezsp_io::TestExternalities::default().execute_with(|| {
const BUFFER_SIZE: usize = 300;
// Ensure that the capacity isn't dividable by `300`.
assert!(STORAGE_INPUT_BUFFER_CAPACITY % BUFFER_SIZE != 0, "Please update buffer size");
// Create some items where the last item is partially in the inner buffer so that
// we need to fill the buffer to read the entire item.
let data: Vec<Vec<u8>> = (0..=(STORAGE_INPUT_BUFFER_CAPACITY / BUFFER_SIZE))
.into_iter()
.map(|i| vec![i as u8; BUFFER_SIZE])
.collect::<Vec<Vec<u8>>>();
TestVecVecU8::put(&data);
assert_eq!(data, TestVecVecU8::stream_iter().collect::<Vec<_>>());
let mut input = StorageInput::new(TestVecVecU8::hashed_key().into());
Compact::<u32>::decode(&mut input).unwrap();
(0..data.len() - 1).into_iter().for_each(|_| {
Vec::<u8>::decode(&mut input).unwrap();
});
// Try reading a more data than there should be left.
let mut result_buffer = vec![0; BUFFER_SIZE * 2];
assert!(input
.read(&mut result_buffer)
.unwrap_err()
.to_string()
.contains("Not enough data to fill the buffer"));
})
}
#[test]
fn detect_value_deleted_in_state() {
pezsp_io::TestExternalities::default().execute_with(|| {
let data: Vec<Vec<u8>> = vec![vec![0; 20], vec![1; STORAGE_INPUT_BUFFER_CAPACITY * 2]];
TestVecVecU8::put(&data);
let mut input = StorageInput::new(TestVecVecU8::hashed_key().into());
TestVecVecU8::kill();
Compact::<u32>::decode(&mut input).unwrap();
Vec::<u8>::decode(&mut input).unwrap();
assert!(Vec::<u8>::decode(&mut input)
.unwrap_err()
.to_string()
.contains("Value doesn't exist in the state?"));
const BUFFER_SIZE: usize = 300;
// Ensure that the capacity isn't dividable by `300`.
assert!(STORAGE_INPUT_BUFFER_CAPACITY % BUFFER_SIZE != 0, "Please update buffer size");
// Create some items where the last item is partially in the inner buffer so that
// we need to fill the buffer to read the entire item.
let data: Vec<Vec<u8>> = (0..=(STORAGE_INPUT_BUFFER_CAPACITY / BUFFER_SIZE))
.into_iter()
.map(|i| vec![i as u8; BUFFER_SIZE])
.collect::<Vec<Vec<u8>>>();
TestVecVecU8::put(&data);
let mut input = StorageInput::new(TestVecVecU8::hashed_key().into());
TestVecVecU8::kill();
Compact::<u32>::decode(&mut input).unwrap();
(0..data.len() - 1).into_iter().for_each(|_| {
Vec::<u8>::decode(&mut input).unwrap();
});
assert!(Vec::<u8>::decode(&mut input)
.unwrap_err()
.to_string()
.contains("Value doesn't exist in the state?"));
})
}
}
@@ -0,0 +1,319 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Provides functionality around the transaction storage.
//!
//! Transactional storage provides functionality to run an entire code block
//! in a storage transaction. This means that either the entire changes to the
//! storage are committed or everything is thrown away. This simplifies the
//! writing of functionality that may bail at any point of operation. Otherwise
//! you would need to first verify all storage accesses and then do the storage
//! modifications.
//!
//! [`with_transaction`] provides a way to run a given closure in a transactional context.
use pezsp_io::storage::{commit_transaction, rollback_transaction, start_transaction};
use pezsp_runtime::{DispatchError, TransactionOutcome, TransactionalError};
/// The type that is being used to store the current number of active layers.
pub type Layer = u32;
/// The key that is holds the current number of active layers.
///
/// Encodes to `0x3a7472616e73616374696f6e5f6c6576656c3a`.
pub const TRANSACTION_LEVEL_KEY: &[u8] = b":transaction_level:";
/// The maximum number of nested layers.
pub const TRANSACTIONAL_LIMIT: Layer = 255;
/// Returns the current number of nested transactional layers.
fn get_transaction_level() -> Layer {
crate::storage::unhashed::get_or_default::<Layer>(TRANSACTION_LEVEL_KEY)
}
/// Set the current number of nested transactional layers.
fn set_transaction_level(level: Layer) {
crate::storage::unhashed::put::<Layer>(TRANSACTION_LEVEL_KEY, &level);
}
/// Kill the transactional layers storage.
fn kill_transaction_level() {
crate::storage::unhashed::kill(TRANSACTION_LEVEL_KEY);
}
/// Increments the transaction level. Returns an error if levels go past the limit.
///
/// Returns a guard that when dropped decrements the transaction level automatically.
fn inc_transaction_level() -> Result<StorageLayerGuard, ()> {
let existing_levels = get_transaction_level();
if existing_levels >= TRANSACTIONAL_LIMIT {
return Err(());
}
// Cannot overflow because of check above.
set_transaction_level(existing_levels + 1);
Ok(StorageLayerGuard)
}
fn dec_transaction_level() {
let existing_levels = get_transaction_level();
if existing_levels == 0 {
log::warn!(
"We are underflowing with calculating transactional levels. Not great, but let's not panic...",
);
} else if existing_levels == 1 {
// Don't leave any trace of this storage item.
kill_transaction_level();
} else {
// Cannot underflow because of checks above.
set_transaction_level(existing_levels - 1);
}
}
struct StorageLayerGuard;
impl Drop for StorageLayerGuard {
fn drop(&mut self) {
dec_transaction_level()
}
}
/// Check if the current call is within a transactional layer.
pub fn is_transactional() -> bool {
get_transaction_level() > 0
}
/// Execute the supplied function in a new storage transaction.
///
/// All changes to storage performed by the supplied function are discarded if the returned
/// outcome is `TransactionOutcome::Rollback`.
///
/// Transactions can be nested up to `TRANSACTIONAL_LIMIT` times; more than that will result in an
/// error.
///
/// Commits happen to the parent transaction.
pub fn with_transaction<T, E, F>(f: F) -> Result<T, E>
where
E: From<DispatchError>,
F: FnOnce() -> TransactionOutcome<Result<T, E>>,
{
// This needs to happen before `start_transaction` below.
// Otherwise we may rollback the increase, then decrease as the guard goes out of scope
// and then end in some bad state.
let _guard = inc_transaction_level().map_err(|()| TransactionalError::LimitReached.into())?;
start_transaction();
match f() {
TransactionOutcome::Commit(res) => {
commit_transaction();
res
},
TransactionOutcome::Rollback(res) => {
rollback_transaction();
res
},
}
}
/// Same as [`with_transaction`] but casts any internal error to `()`.
///
/// This rids `E` of the `From<DispatchError>` bound that is required by `with_transaction`.
pub fn with_transaction_opaque_err<T, E, F>(f: F) -> Result<Result<T, E>, ()>
where
F: FnOnce() -> TransactionOutcome<Result<T, E>>,
{
with_transaction(move || -> TransactionOutcome<Result<Result<T, E>, DispatchError>> {
match f() {
TransactionOutcome::Commit(res) => TransactionOutcome::Commit(Ok(res)),
TransactionOutcome::Rollback(res) => TransactionOutcome::Rollback(Ok(res)),
}
})
.map_err(|_| ())
}
/// Same as [`with_transaction`] but without a limit check on nested transactional layers.
///
/// This is mostly for backwards compatibility before there was a transactional layer limit.
/// It is recommended to only use [`with_transaction`] to avoid users from generating too many
/// transactional layers.
pub fn with_transaction_unchecked<R, F>(f: F) -> R
where
F: FnOnce() -> TransactionOutcome<R>,
{
// This needs to happen before `start_transaction` below.
// Otherwise we may rollback the increase, then decrease as the guard goes out of scope
// and then end in some bad state.
let maybe_guard = inc_transaction_level();
if maybe_guard.is_err() {
log::warn!(
"The transactional layer limit has been reached, and new transactional layers are being
spawned with `with_transaction_unchecked`. This could be caused by someone trying to
attack your chain, and you should investigate usage of `with_transaction_unchecked` and
potentially migrate to `with_transaction`, which enforces a transactional limit.",
);
}
start_transaction();
match f() {
TransactionOutcome::Commit(res) => {
commit_transaction();
res
},
TransactionOutcome::Rollback(res) => {
rollback_transaction();
res
},
}
}
/// Execute the supplied function, adding a new storage layer.
///
/// This is the same as `with_transaction`, but assuming that any function returning an `Err` should
/// rollback, and any function returning `Ok` should commit. This provides a cleaner API to the
/// developer who wants this behavior.
pub fn with_storage_layer<T, E, F>(f: F) -> Result<T, E>
where
E: From<DispatchError>,
F: FnOnce() -> Result<T, E>,
{
with_transaction(|| {
let r = f();
if r.is_ok() {
TransactionOutcome::Commit(r)
} else {
TransactionOutcome::Rollback(r)
}
})
}
/// Execute the supplied function, ensuring we are at least in one storage layer.
///
/// If we are already in a storage layer, we just execute the provided closure.
/// If we are not, we execute the closure within a [`with_storage_layer`].
pub fn in_storage_layer<T, E, F>(f: F) -> Result<T, E>
where
E: From<DispatchError>,
F: FnOnce() -> Result<T, E>,
{
if is_transactional() {
f()
} else {
with_storage_layer(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_noop, assert_ok};
use pezsp_io::TestExternalities;
use pezsp_runtime::DispatchResult;
#[test]
fn is_transactional_should_return_false() {
TestExternalities::default().execute_with(|| {
assert!(!is_transactional());
});
}
#[test]
fn is_transactional_should_not_error_in_with_transaction() {
TestExternalities::default().execute_with(|| {
assert_ok!(with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert!(is_transactional());
TransactionOutcome::Commit(Ok(()))
}));
assert_noop!(
with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert!(is_transactional());
TransactionOutcome::Rollback(Err("revert".into()))
}),
"revert"
);
});
}
fn recursive_transactional(num: u32) -> DispatchResult {
if num == 0 {
return Ok(());
}
with_transaction(|| -> TransactionOutcome<DispatchResult> {
let res = recursive_transactional(num - 1);
TransactionOutcome::Commit(res)
})
}
#[test]
fn transaction_limit_should_work() {
TestExternalities::default().execute_with(|| {
assert_eq!(get_transaction_level(), 0);
assert_ok!(with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert_eq!(get_transaction_level(), 1);
TransactionOutcome::Commit(Ok(()))
}));
assert_ok!(with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert_eq!(get_transaction_level(), 1);
let res = with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert_eq!(get_transaction_level(), 2);
TransactionOutcome::Commit(Ok(()))
});
TransactionOutcome::Commit(res)
}));
assert_ok!(recursive_transactional(255));
assert_noop!(
recursive_transactional(256),
pezsp_runtime::TransactionalError::LimitReached
);
assert_eq!(get_transaction_level(), 0);
});
}
#[test]
fn in_storage_layer_works() {
TestExternalities::default().execute_with(|| {
assert_eq!(get_transaction_level(), 0);
let res = in_storage_layer(|| -> DispatchResult {
assert_eq!(get_transaction_level(), 1);
in_storage_layer(|| -> DispatchResult {
// We are still in the same layer :)
assert_eq!(get_transaction_level(), 1);
Ok(())
})
});
assert_ok!(res);
let res = in_storage_layer(|| -> DispatchResult {
assert_eq!(get_transaction_level(), 1);
in_storage_layer(|| -> DispatchResult {
// We are still in the same layer :)
assert_eq!(get_transaction_level(), 1);
Err("epic fail".into())
})
});
assert_noop!(res, "epic fail");
});
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+282
View File
@@ -0,0 +1,282 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Storage key type.
use crate::hash::{ReversibleStorageHasher, StorageHasher};
use alloc::{boxed::Box, vec::Vec};
use codec::{Encode, EncodeLike, FullCodec, MaxEncodedLen};
use paste::paste;
use scale_info::StaticTypeInfo;
/// A type used exclusively by storage maps as their key type.
///
/// The final key generated has the following form:
/// ```nocompile
/// Hasher1(encode(key1))
/// ++ Hasher2(encode(key2))
/// ++ ...
/// ++ HasherN(encode(keyN))
/// ```
pub struct Key<Hasher, KeyType>(core::marker::PhantomData<(Hasher, KeyType)>);
/// A trait that contains the current key as an associated type.
pub trait KeyGenerator {
type Key: EncodeLike<Self::Key> + StaticTypeInfo;
type KArg: Encode + EncodeLike<Self::KArg>;
type HashFn: FnOnce(&[u8]) -> Vec<u8>;
type HArg;
const HASHER_METADATA: &'static [pezsp_metadata_ir::StorageHasherIR];
/// Given a `key` tuple, calculate the final key by encoding each element individually and
/// hashing them using the corresponding hasher in the `KeyGenerator`.
fn final_key<KArg: EncodeLikeTuple<Self::KArg> + TupleToEncodedIter>(key: KArg) -> Vec<u8>;
/// Given a `key` tuple, migrate the keys from using the old hashers as given by `hash_fns`
/// to using the newer hashers as specified by this `KeyGenerator`.
fn migrate_key<KArg: EncodeLikeTuple<Self::KArg> + TupleToEncodedIter>(
key: &KArg,
hash_fns: Self::HArg,
) -> Vec<u8>;
}
/// The maximum length used by the key in storage.
pub trait KeyGeneratorMaxEncodedLen: KeyGenerator {
fn key_max_encoded_len() -> usize;
}
/// A trait containing methods that are only implemented on the Key struct instead of the entire
/// tuple.
pub trait KeyGeneratorInner: KeyGenerator {
type Hasher: StorageHasher;
/// Hash a given `encoded` byte slice using the `KeyGenerator`'s associated `StorageHasher`.
fn final_hash(encoded: &[u8]) -> Vec<u8>;
}
impl<H: StorageHasher, K: FullCodec + StaticTypeInfo> KeyGenerator for Key<H, K> {
type Key = K;
type KArg = (K,);
type HashFn = Box<dyn FnOnce(&[u8]) -> Vec<u8>>;
type HArg = (Self::HashFn,);
const HASHER_METADATA: &'static [pezsp_metadata_ir::StorageHasherIR] = &[H::METADATA];
fn final_key<KArg: EncodeLikeTuple<Self::KArg> + TupleToEncodedIter>(key: KArg) -> Vec<u8> {
H::hash(&key.to_encoded_iter().next().expect("should have at least one element!"))
.as_ref()
.to_vec()
}
fn migrate_key<KArg: EncodeLikeTuple<Self::KArg> + TupleToEncodedIter>(
key: &KArg,
hash_fns: Self::HArg,
) -> Vec<u8> {
(hash_fns.0)(&key.to_encoded_iter().next().expect("should have at least one element!"))
}
}
impl<H: StorageHasher, K: FullCodec + MaxEncodedLen + StaticTypeInfo> KeyGeneratorMaxEncodedLen
for Key<H, K>
{
fn key_max_encoded_len() -> usize {
H::max_len::<K>()
}
}
impl<H: StorageHasher, K: FullCodec + StaticTypeInfo> KeyGeneratorInner for Key<H, K> {
type Hasher = H;
fn final_hash(encoded: &[u8]) -> Vec<u8> {
H::hash(encoded).as_ref().to_vec()
}
}
#[impl_trait_for_tuples::impl_for_tuples(1, 18)]
#[tuple_types_custom_trait_bound(KeyGeneratorInner)]
impl KeyGenerator for Tuple {
for_tuples!( type Key = ( #(Tuple::Key),* ); );
for_tuples!( type KArg = ( #(Tuple::Key),* ); );
for_tuples!( type HArg = ( #(Tuple::HashFn),* ); );
type HashFn = Box<dyn FnOnce(&[u8]) -> Vec<u8>>;
const HASHER_METADATA: &'static [pezsp_metadata_ir::StorageHasherIR] =
&[for_tuples!( #(Tuple::Hasher::METADATA),* )];
fn final_key<KArg: EncodeLikeTuple<Self::KArg> + TupleToEncodedIter>(key: KArg) -> Vec<u8> {
let mut final_key = Vec::new();
let mut iter = key.to_encoded_iter();
for_tuples!(
#(
let next_encoded = iter.next().expect("KArg number should be equal to Key number");
final_key.extend_from_slice(&Tuple::final_hash(&next_encoded));
)*
);
final_key
}
fn migrate_key<KArg: EncodeLikeTuple<Self::KArg> + TupleToEncodedIter>(
key: &KArg,
hash_fns: Self::HArg,
) -> Vec<u8> {
let mut migrated_key = Vec::new();
let mut iter = key.to_encoded_iter();
for_tuples!(
#(
let next_encoded = iter.next().expect("KArg number should be equal to Key number");
migrated_key.extend_from_slice(&(hash_fns.Tuple)(&next_encoded));
)*
);
migrated_key
}
}
#[impl_trait_for_tuples::impl_for_tuples(1, 18)]
#[tuple_types_custom_trait_bound(KeyGeneratorInner + KeyGeneratorMaxEncodedLen)]
impl KeyGeneratorMaxEncodedLen for Tuple {
fn key_max_encoded_len() -> usize {
let mut len = 0usize;
for_tuples!(
#(
len = len.saturating_add(Tuple::key_max_encoded_len());
)*
);
len
}
}
/// Marker trait to indicate that each element in the tuple encodes like the corresponding element
/// in another tuple.
///
/// This trait is sealed.
pub trait EncodeLikeTuple<T>: crate::storage::private::Sealed {}
macro_rules! impl_encode_like_tuples {
($($elem:ident),+) => {
paste! {
impl<$($elem: Encode,)+ $([<$elem $elem>]: Encode + EncodeLike<$elem>,)+>
EncodeLikeTuple<($($elem,)+)> for
($([<$elem $elem>],)+) {}
impl<$($elem: Encode,)+ $([<$elem $elem>]: Encode + EncodeLike<$elem>,)+>
EncodeLikeTuple<($($elem,)+)> for
&($([<$elem $elem>],)+) {}
}
};
}
impl_encode_like_tuples!(A);
impl_encode_like_tuples!(A, B);
impl_encode_like_tuples!(A, B, C);
impl_encode_like_tuples!(A, B, C, D);
impl_encode_like_tuples!(A, B, C, D, E);
impl_encode_like_tuples!(A, B, C, D, E, F);
impl_encode_like_tuples!(A, B, C, D, E, F, G);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J, K);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, O);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, O, P);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, O, P, Q);
impl_encode_like_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, O, P, Q, R);
impl<'a, T: EncodeLike<U> + EncodeLikeTuple<U>, U: Encode> EncodeLikeTuple<U>
for codec::Ref<'a, T, U>
{
}
/// Trait to indicate that a tuple can be converted into an iterator of a vector of encoded bytes.
pub trait TupleToEncodedIter {
fn to_encoded_iter(&self) -> alloc::vec::IntoIter<Vec<u8>>;
}
#[impl_trait_for_tuples::impl_for_tuples(1, 18)]
#[tuple_types_custom_trait_bound(Encode)]
impl TupleToEncodedIter for Tuple {
fn to_encoded_iter(&self) -> alloc::vec::IntoIter<Vec<u8>> {
[for_tuples!( #(self.Tuple.encode()),* )].to_vec().into_iter()
}
}
impl<T: TupleToEncodedIter> TupleToEncodedIter for &T {
fn to_encoded_iter(&self) -> alloc::vec::IntoIter<Vec<u8>> {
(*self).to_encoded_iter()
}
}
impl<'a, T: EncodeLike<U> + TupleToEncodedIter, U: Encode> TupleToEncodedIter
for codec::Ref<'a, T, U>
{
fn to_encoded_iter(&self) -> alloc::vec::IntoIter<Vec<u8>> {
use core::ops::Deref as _;
self.deref().to_encoded_iter()
}
}
/// A trait that indicates the hashers for the keys generated are all reversible.
pub trait ReversibleKeyGenerator: KeyGenerator {
type ReversibleHasher;
fn decode_final_key(key_material: &[u8]) -> Result<(Self::Key, &[u8]), codec::Error>;
}
impl<H: ReversibleStorageHasher, K: FullCodec + StaticTypeInfo> ReversibleKeyGenerator
for Key<H, K>
{
type ReversibleHasher = H;
fn decode_final_key(key_material: &[u8]) -> Result<(Self::Key, &[u8]), codec::Error> {
let mut current_key_material = Self::ReversibleHasher::reverse(key_material);
let key = K::decode(&mut current_key_material)?;
Ok((key, current_key_material))
}
}
#[impl_trait_for_tuples::impl_for_tuples(2, 18)]
#[tuple_types_custom_trait_bound(ReversibleKeyGenerator + KeyGeneratorInner)]
impl ReversibleKeyGenerator for Tuple {
for_tuples!( type ReversibleHasher = ( #(Tuple::ReversibleHasher),* ); );
fn decode_final_key(key_material: &[u8]) -> Result<(Self::Key, &[u8]), codec::Error> {
let mut current_key_material = key_material;
Ok((
(for_tuples! {
#({
let (key, material) = Tuple::decode_final_key(current_key_material)?;
current_key_material = material;
key
}),*
}),
current_key_material,
))
}
}
/// Trait indicating whether a KeyGenerator has the prefix P.
pub trait HasKeyPrefix<P>: KeyGenerator {
type Suffix;
fn partial_key(prefix: P) -> Vec<u8>;
}
/// Trait indicating whether a ReversibleKeyGenerator has the prefix P.
pub trait HasReversibleKeyPrefix<P>: ReversibleKeyGenerator + HasKeyPrefix<P> {
fn decode_partial_key(key_material: &[u8]) -> Result<Self::Suffix, codec::Error>;
}
pezframe_support_procedural::impl_key_prefix_for_tuples!();
@@ -0,0 +1,845 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Storage map type. Implements StorageMap, StorageIterableMap, StoragePrefixedMap traits and their
//! methods directly.
use crate::{
storage::{
types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
KeyLenOf, StorageAppend, StorageDecodeLength, StoragePrefixedMap, StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
StorageHasher, Twox128,
};
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen};
use pezframe_support::storage::StorageDecodeNonDedupLength;
use pezsp_arithmetic::traits::SaturatedConversion;
use pezsp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
/// A type representing a *map* in storage. A *storage map* is a mapping of keys to values of a
/// given type stored on-chain.
///
/// For general information regarding the `#[pallet::storage]` attribute, refer to
/// [`crate::pezpallet_macros::storage`].
///
/// # Example
///
/// ```
/// #[pezframe_support::pallet]
/// mod pallet {
/// # use pezframe_support::pezpallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: pezframe_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// /// A kitchen-sink StorageMap, with all possible additional attributes.
/// #[pallet::storage]
/// #[pallet::getter(fn foo)]
/// #[pallet::storage_prefix = "OtherFoo"]
/// #[pallet::unbounded]
/// pub type Foo<T> = StorageMap<
/// _,
/// Blake2_128Concat,
/// u32,
/// u32,
/// ValueQuery
/// >;
///
/// /// Alternative named syntax.
/// #[pallet::storage]
/// pub type Bar<T> = StorageMap<
/// Hasher = Blake2_128Concat,
/// Key = u32,
/// Value = u32,
/// QueryKind = ValueQuery
/// >;
/// }
/// ```
pub struct StorageMap<
Prefix,
Hasher,
Key,
Value,
QueryKind = OptionQuery,
OnEmpty = GetDefault,
MaxValues = GetDefault,
>(core::marker::PhantomData<(Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues)>);
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> Get<u32>
for KeyLenOf<StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec + MaxEncodedLen,
{
fn get() -> u32 {
// The `max_len` of the key hash plus the pallet prefix and storage prefix (which both are
// hashed with `Twox128`).
let z = Hasher::max_len::<Key>() + Twox128::max_len::<()>() * 2;
z as u32
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::storage::generator::StorageMap<Key, Value>
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
type Query = QueryKind::Query;
type Hasher = Hasher;
fn pezpallet_prefix() -> &'static [u8] {
Prefix::pezpallet_prefix().as_bytes()
}
fn storage_prefix() -> &'static [u8] {
Prefix::STORAGE_PREFIX.as_bytes()
}
fn prefix_hash() -> [u8; 32] {
Prefix::prefix_hash()
}
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
QueryKind::from_optional_value_to_query(v)
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
QueryKind::from_query_to_optional_value(v)
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> StoragePrefixedMap<Value>
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn pezpallet_prefix() -> &'static [u8] {
<Self as crate::storage::generator::StorageMap<Key, Value>>::pezpallet_prefix()
}
fn storage_prefix() -> &'static [u8] {
<Self as crate::storage::generator::StorageMap<Key, Value>>::storage_prefix()
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Get the storage key used to fetch a value corresponding to a specific key.
pub fn hashed_key_for<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Vec<u8> {
<Self as crate::storage::StorageMap<Key, Value>>::hashed_key_for(key)
}
/// Does the value (explicitly) exist in storage?
pub fn contains_key<KeyArg: EncodeLike<Key>>(key: KeyArg) -> bool {
<Self as crate::storage::StorageMap<Key, Value>>::contains_key(key)
}
/// Load the value associated with the given key from the map.
pub fn get<KeyArg: EncodeLike<Key>>(key: KeyArg) -> QueryKind::Query {
<Self as crate::storage::StorageMap<Key, Value>>::get(key)
}
/// Try to get the value for the given key from the map.
///
/// Returns `Ok` if it exists, `Err` if not.
pub fn try_get<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Result<Value, ()> {
<Self as crate::storage::StorageMap<Key, Value>>::try_get(key)
}
/// Swap the values of two keys.
pub fn swap<KeyArg1: EncodeLike<Key>, KeyArg2: EncodeLike<Key>>(key1: KeyArg1, key2: KeyArg2) {
<Self as crate::storage::StorageMap<Key, Value>>::swap(key1, key2)
}
/// Store or remove the value to be associated with `key` so that `get` returns the `query`.
pub fn set<KeyArg: EncodeLike<Key>>(key: KeyArg, q: QueryKind::Query) {
<Self as crate::storage::StorageMap<Key, Value>>::set(key, q)
}
/// Store a value to be associated with the given key from the map.
pub fn insert<KeyArg: EncodeLike<Key>, ValArg: EncodeLike<Value>>(key: KeyArg, val: ValArg) {
<Self as crate::storage::StorageMap<Key, Value>>::insert(key, val)
}
/// Remove the value under a key.
pub fn remove<KeyArg: EncodeLike<Key>>(key: KeyArg) {
<Self as crate::storage::StorageMap<Key, Value>>::remove(key)
}
/// Mutate the value under a key.
pub fn mutate<KeyArg: EncodeLike<Key>, R, F: FnOnce(&mut QueryKind::Query) -> R>(
key: KeyArg,
f: F,
) -> R {
<Self as crate::storage::StorageMap<Key, Value>>::mutate(key, f)
}
/// Mutate the item, only if an `Ok` value is returned.
pub fn try_mutate<KeyArg, R, E, F>(key: KeyArg, f: F) -> Result<R, E>
where
KeyArg: EncodeLike<Key>,
F: FnOnce(&mut QueryKind::Query) -> Result<R, E>,
{
<Self as crate::storage::StorageMap<Key, Value>>::try_mutate(key, f)
}
/// Mutate the value under a key iff it exists. Do nothing and return the default value if not.
pub fn mutate_extant<KeyArg: EncodeLike<Key>, R: Default, F: FnOnce(&mut Value) -> R>(
key: KeyArg,
f: F,
) -> R {
<Self as crate::storage::StorageMap<Key, Value>>::mutate_extant(key, f)
}
/// Mutate the value under a key. Deletes the item if mutated to a `None`.
pub fn mutate_exists<KeyArg: EncodeLike<Key>, R, F: FnOnce(&mut Option<Value>) -> R>(
key: KeyArg,
f: F,
) -> R {
<Self as crate::storage::StorageMap<Key, Value>>::mutate_exists(key, f)
}
/// Mutate the item, only if an `Ok` value is returned. Deletes the item if mutated to a `None`.
/// `f` will always be called with an option representing if the storage item exists (`Some<V>`)
/// or if the storage item does not exist (`None`), independent of the `QueryType`.
pub fn try_mutate_exists<KeyArg, R, E, F>(key: KeyArg, f: F) -> Result<R, E>
where
KeyArg: EncodeLike<Key>,
F: FnOnce(&mut Option<Value>) -> Result<R, E>,
{
<Self as crate::storage::StorageMap<Key, Value>>::try_mutate_exists(key, f)
}
/// Take the value under a key.
pub fn take<KeyArg: EncodeLike<Key>>(key: KeyArg) -> QueryKind::Query {
<Self as crate::storage::StorageMap<Key, Value>>::take(key)
}
/// Append the given items to the value in the storage.
///
/// `Value` is required to implement `codec::EncodeAppend`.
///
/// # Warning
///
/// If the storage item is not encoded properly, the storage will be overwritten
/// and set to `[item]`. Any default value set for the storage item will be ignored
/// on overwrite.
pub fn append<Item, EncodeLikeItem, EncodeLikeKey>(key: EncodeLikeKey, item: EncodeLikeItem)
where
EncodeLikeKey: EncodeLike<Key>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageAppend<Item>,
{
<Self as crate::storage::StorageMap<Key, Value>>::append(key, item)
}
/// Read the length of the storage value without decoding the entire value under the
/// given `key`.
///
/// `Value` is required to implement [`StorageDecodeLength`].
///
/// If the value does not exists or it fails to decode the length, `None` is returned.
/// Otherwise `Some(len)` is returned.
///
/// # Warning
///
/// `None` does not mean that `get()` does not return a value. The default value is completely
/// ignored by this function.
pub fn decode_len<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Option<usize>
where
Value: StorageDecodeLength,
{
<Self as crate::storage::StorageMap<Key, Value>>::decode_len(key)
}
/// Read the length of the storage value without decoding the entire value.
///
/// `Value` is required to implement [`StorageDecodeNonDedupLength`].
///
/// If the value does not exists or it fails to decode the length, `None` is returned.
/// Otherwise `Some(len)` is returned.
///
/// # Warning
///
/// - `None` does not mean that `get()` does not return a value. The default value is
/// completely
/// ignored by this function.
///
/// - The value returned is the non-deduplicated length of the underlying Vector in storage.This
/// means that any duplicate items are included.
pub fn decode_non_dedup_len<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Option<usize>
where
Value: StorageDecodeNonDedupLength,
{
<Self as crate::storage::StorageMap<Key, Value>>::decode_non_dedup_len(key)
}
/// Migrate an item with the given `key` from a defunct `OldHasher` to the current hasher.
///
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
pub fn migrate_key<OldHasher: crate::hash::StorageHasher, KeyArg: EncodeLike<Key>>(
key: KeyArg,
) -> Option<Value> {
<Self as crate::storage::StorageMap<Key, Value>>::migrate_key::<OldHasher, _>(key)
}
/// Remove all values of the storage in the overlay and up to `limit` in the backend.
///
/// All values in the client overlay will be deleted, if there is some `limit` then up to
/// `limit` values are deleted from the client backend, if `limit` is none then all values in
/// the client backend are deleted.
///
/// # Note
///
/// Calling this multiple times per block with a `limit` set leads always to the same keys being
/// removed and the same result being returned. This happens because the keys to delete in the
/// overlay are not taken into account when deleting keys in the backend.
#[deprecated = "Use `clear` instead"]
pub fn remove_all(limit: Option<u32>) -> pezsp_io::KillStorageResult {
#[allow(deprecated)]
<Self as crate::storage::StoragePrefixedMap<Value>>::remove_all(limit)
}
/// Attempt to remove all items from the map.
///
/// Returns [`MultiRemovalResults`](pezsp_io::MultiRemovalResults) to inform about the result. Once
/// the resultant `maybe_cursor` field is `None`, then no further items remain to be deleted.
///
/// NOTE: After the initial call for any given map, it is important that no further items
/// are inserted into the map. If so, then the map may not be empty when the resultant
/// `maybe_cursor` is `None`.
///
/// # Limit
///
/// A `limit` must always be provided through in order to cap the maximum
/// amount of deletions done in a single call. This is one fewer than the
/// maximum number of backend iterations which may be done by this operation and as such
/// represents the maximum number of backend deletions which may happen. A `limit` of zero
/// implies that no keys will be deleted, though there may be a single iteration done.
///
/// # Cursor
///
/// A *cursor* may be passed in to this operation with `maybe_cursor`. `None` should only be
/// passed once (in the initial call) for any given storage map. Subsequent calls
/// operating on the same map should always pass `Some`, and this should be equal to the
/// previous call result's `maybe_cursor` field.
pub fn clear(limit: u32, maybe_cursor: Option<&[u8]>) -> pezsp_io::MultiRemovalResults {
<Self as crate::storage::StoragePrefixedMap<Value>>::clear(limit, maybe_cursor)
}
/// Iter over all value of the storage.
///
/// NOTE: If a value failed to decode because storage is corrupted then it is skipped.
pub fn iter_values() -> crate::storage::PrefixIterator<Value> {
<Self as crate::storage::StoragePrefixedMap<Value>>::iter_values()
}
/// Translate the values of all elements by a function `f`, in the map in no particular order.
///
/// By returning `None` from `f` for an element, you'll remove it from the map.
///
/// NOTE: If a value fail to decode because storage is corrupted then it is skipped.
///
/// # Warning
///
/// This function must be used with care, before being updated the storage still contains the
/// old type, thus other calls (such as `get`) will fail at decoding it.
///
/// # Usage
///
/// This would typically be called inside the module implementation of on_runtime_upgrade.
pub fn translate_values<OldValue: Decode, F: FnMut(OldValue) -> Option<Value>>(f: F) {
<Self as crate::storage::StoragePrefixedMap<Value>>::translate_values(f)
}
/// Try and append the given item to the value in the storage.
///
/// Is only available if `Value` of the storage implements [`StorageTryAppend`].
pub fn try_append<KArg, Item, EncodeLikeItem>(key: KArg, item: EncodeLikeItem) -> Result<(), ()>
where
KArg: EncodeLike<Key> + Clone,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageTryAppend<Item>,
{
<Self as crate::storage::TryAppendMap<Key, Value, Item>>::try_append(key, item)
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
/// Enumerate all elements in the map in no particular order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter() -> crate::storage::PrefixIterator<(Key, Value)> {
<Self as crate::storage::IterableStorageMap<Key, Value>>::iter()
}
/// Enumerate all elements in the map after a specified `starting_raw_key` in no
/// particular order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter_from(starting_raw_key: Vec<u8>) -> crate::storage::PrefixIterator<(Key, Value)> {
<Self as crate::storage::IterableStorageMap<Key, Value>>::iter_from(starting_raw_key)
}
/// Enumerate all elements in the map after a specified `starting_key` in no
/// particular order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter_from_key(
starting_key: impl EncodeLike<Key>,
) -> crate::storage::PrefixIterator<(Key, Value)> {
Self::iter_from(Self::hashed_key_for(starting_key))
}
/// Enumerate all keys in the map in no particular order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter_keys() -> crate::storage::KeyPrefixIterator<Key> {
<Self as crate::storage::IterableStorageMap<Key, Value>>::iter_keys()
}
/// Enumerate all keys in the map after a specified `starting_raw_key` in no particular
/// order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter_keys_from(starting_raw_key: Vec<u8>) -> crate::storage::KeyPrefixIterator<Key> {
<Self as crate::storage::IterableStorageMap<Key, Value>>::iter_keys_from(starting_raw_key)
}
/// Enumerate all keys in the map after a specified `starting_key` in no particular
/// order.
///
/// If you alter the map while doing this, you'll get undefined results.
pub fn iter_keys_from_key(
starting_key: impl EncodeLike<Key>,
) -> crate::storage::KeyPrefixIterator<Key> {
Self::iter_keys_from(Self::hashed_key_for(starting_key))
}
/// Remove all elements from the map and iterate through them in no particular order.
///
/// If you add elements to the map while doing this, you'll get undefined results.
pub fn drain() -> crate::storage::PrefixIterator<(Key, Value)> {
<Self as crate::storage::IterableStorageMap<Key, Value>>::drain()
}
/// Translate the values of all elements by a function `f`, in the map in no particular order.
///
/// By returning `None` from `f` for an element, you'll remove it from the map.
///
/// NOTE: If a value fails to decode because storage is corrupted, then it will log an error and
/// be skipped in production, or panic in development.
pub fn translate<O: Decode, F: FnMut(Key, O) -> Option<Value>>(f: F) {
<Self as crate::storage::IterableStorageMap<Key, Value>>::translate(f)
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> StorageEntryMetadataBuilder
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec + scale_info::StaticTypeInfo,
Value: FullCodec + scale_info::StaticTypeInfo,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn build_metadata(
deprecation_status: pezsp_metadata_ir::ItemDeprecationInfoIR,
docs: Vec<&'static str>,
entries: &mut Vec<StorageEntryMetadataIR>,
) {
let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs };
let entry = StorageEntryMetadataIR {
name: Prefix::STORAGE_PREFIX,
modifier: QueryKind::METADATA,
ty: StorageEntryTypeIR::Map {
hashers: vec![Hasher::METADATA],
key: scale_info::meta_type::<Key>(),
value: scale_info::meta_type::<Value>(),
},
default: OnEmpty::get().encode(),
docs,
deprecation_info: deprecation_status,
};
entries.push(entry);
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> crate::traits::StorageInfoTrait
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec + MaxEncodedLen,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pezpallet_name: Self::pezpallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::final_prefix().to_vec(),
max_values: MaxValues::get(),
max_size: Some(
Hasher::max_len::<Key>()
.saturating_add(Value::max_encoded_len())
.saturated_into(),
),
}]
}
}
/// It doesn't require to implement `MaxEncodedLen` and give no information for `max_size`.
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::traits::PartialStorageInfoTrait
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn partial_storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pezpallet_name: Self::pezpallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::final_prefix().to_vec(),
max_values: MaxValues::get(),
max_size: None,
}]
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
hash::*,
storage::{types::ValueQuery, IterableStorageMap},
};
use pezsp_io::{hashing::twox_128, TestExternalities};
use pezsp_metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR};
struct Prefix;
impl StorageInstance for Prefix {
fn pezpallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "foo";
}
struct ADefault;
impl crate::traits::Get<u32> for ADefault {
fn get() -> u32 {
97
}
}
#[test]
fn keylenof_works() {
// Works with Blake2_128Concat.
type A = StorageMap<Prefix, Blake2_128Concat, u32, u32>;
let size = 16 * 2 // Two Twox128
+ 16 + 4; // Blake2_128Concat = hash + key
assert_eq!(KeyLenOf::<A>::get(), size);
// Works with Blake2_256.
type B = StorageMap<Prefix, Blake2_256, u32, u32>;
let size = 16 * 2 // Two Twox128
+ 32; // Blake2_256
assert_eq!(KeyLenOf::<B>::get(), size);
// Works with Twox64Concat.
type C = StorageMap<Prefix, Twox64Concat, u32, u32>;
let size = 16 * 2 // Two Twox128
+ 8 + 4; // Twox64Concat = hash + key
assert_eq!(KeyLenOf::<C>::get(), size);
}
#[test]
fn test() {
type A = StorageMap<Prefix, Blake2_128Concat, u16, u32, OptionQuery>;
type AValueQueryWithAnOnEmpty =
StorageMap<Prefix, Blake2_128Concat, u16, u32, ValueQuery, ADefault>;
type B = StorageMap<Prefix, Blake2_256, u16, u32, ValueQuery>;
type C = StorageMap<Prefix, Blake2_128Concat, u16, u8, ValueQuery>;
type WithLen = StorageMap<Prefix, Blake2_128Concat, u16, Vec<u32>>;
TestExternalities::default().execute_with(|| {
let mut k: Vec<u8> = vec![];
k.extend(&twox_128(b"test"));
k.extend(&twox_128(b"foo"));
k.extend(&3u16.blake2_128_concat());
assert_eq!(A::hashed_key_for(3).to_vec(), k);
assert_eq!(A::contains_key(3), false);
assert_eq!(A::get(3), None);
assert_eq!(AValueQueryWithAnOnEmpty::get(3), 97);
A::insert(3, 10);
assert_eq!(A::contains_key(3), true);
assert_eq!(A::get(3), Some(10));
assert_eq!(A::try_get(3), Ok(10));
assert_eq!(AValueQueryWithAnOnEmpty::get(3), 10);
A::swap(3, 2);
assert_eq!(A::contains_key(3), false);
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(3), None);
assert_eq!(A::try_get(3), Err(()));
assert_eq!(AValueQueryWithAnOnEmpty::get(3), 97);
assert_eq!(A::get(2), Some(10));
assert_eq!(AValueQueryWithAnOnEmpty::get(2), 10);
A::remove(2);
assert_eq!(A::contains_key(2), false);
assert_eq!(A::get(2), None);
AValueQueryWithAnOnEmpty::mutate(2, |v| *v = *v * 2);
AValueQueryWithAnOnEmpty::mutate(2, |v| *v = *v * 2);
assert_eq!(AValueQueryWithAnOnEmpty::contains_key(2), true);
assert_eq!(AValueQueryWithAnOnEmpty::get(2), 97 * 4);
A::remove(2);
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, |v| {
*v = *v * 2;
Ok(())
});
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, |v| {
*v = *v * 2;
Ok(())
});
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(2), Some(97 * 4));
A::remove(2);
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, |v| {
*v = *v * 2;
Err(())
});
assert_eq!(A::contains_key(2), false);
A::remove(2);
AValueQueryWithAnOnEmpty::mutate_exists(2, |v| {
assert!(v.is_none());
*v = Some(10);
});
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(2), Some(10));
AValueQueryWithAnOnEmpty::mutate_exists(2, |v| {
*v = Some(v.unwrap() * 10);
});
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(2), Some(100));
A::remove(2);
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, |v| {
assert!(v.is_none());
*v = Some(10);
Ok(())
});
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(2), Some(10));
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, |v| {
*v = Some(v.unwrap() * 10);
Ok(())
});
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(2), Some(100));
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, |v| {
*v = Some(v.unwrap() * 10);
Err(())
});
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(2), Some(100));
A::insert(2, 10);
assert_eq!(A::take(2), Some(10));
assert_eq!(A::contains_key(2), false);
assert_eq!(AValueQueryWithAnOnEmpty::take(2), 97);
assert_eq!(A::contains_key(2), false);
// Set non-existing.
B::set(30, 100);
assert_eq!(B::contains_key(30), true);
assert_eq!(B::get(30), 100);
assert_eq!(B::try_get(30), Ok(100));
// Set existing.
B::set(30, 101);
assert_eq!(B::contains_key(30), true);
assert_eq!(B::get(30), 101);
assert_eq!(B::try_get(30), Ok(101));
// Set non-existing.
A::set(30, Some(100));
assert_eq!(A::contains_key(30), true);
assert_eq!(A::get(30), Some(100));
assert_eq!(A::try_get(30), Ok(100));
// Set existing.
A::set(30, Some(101));
assert_eq!(A::contains_key(30), true);
assert_eq!(A::get(30), Some(101));
assert_eq!(A::try_get(30), Ok(101));
// Unset existing.
A::set(30, None);
assert_eq!(A::contains_key(30), false);
assert_eq!(A::get(30), None);
assert_eq!(A::try_get(30), Err(()));
// Unset non-existing.
A::set(31, None);
assert_eq!(A::contains_key(31), false);
assert_eq!(A::get(31), None);
assert_eq!(A::try_get(31), Err(()));
B::insert(2, 10);
assert_eq!(A::migrate_key::<Blake2_256, _>(2), Some(10));
assert_eq!(A::contains_key(2), true);
assert_eq!(A::get(2), Some(10));
A::insert(3, 10);
A::insert(4, 10);
let _ = A::clear(u32::max_value(), None);
assert_eq!(A::contains_key(3), false);
assert_eq!(A::contains_key(4), false);
A::insert(3, 10);
A::insert(4, 10);
assert_eq!(A::iter_values().collect::<Vec<_>>(), vec![10, 10]);
C::insert(3, 10);
C::insert(4, 10);
A::translate_values::<u8, _>(|v| Some((v * 2).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 20), (3, 20)]);
A::insert(3, 10);
A::insert(4, 10);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 10), (3, 10)]);
assert_eq!(A::drain().collect::<Vec<_>>(), vec![(4, 10), (3, 10)]);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![]);
C::insert(3, 10);
C::insert(4, 10);
A::translate::<u8, _>(|k, v| Some((k * v as u16).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40), (3, 30)]);
let translate_next = |k: u16, v: u8| Some((v as u16 / k).into());
let k = A::translate_next::<u8, _>(None, translate_next);
let k = A::translate_next::<u8, _>(k, translate_next);
assert_eq!(None, A::translate_next::<u8, _>(k, translate_next));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 10), (3, 10)]);
let _ = A::translate_next::<u8, _>(None, |_, _| None);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(3, 10)]);
let mut entries = vec![];
A::build_metadata(
pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated,
vec![],
&mut entries,
);
AValueQueryWithAnOnEmpty::build_metadata(
pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated,
vec![],
&mut entries,
);
assert_eq!(
entries,
vec![
StorageEntryMetadataIR {
name: "foo",
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat],
key: scale_info::meta_type::<u16>(),
value: scale_info::meta_type::<u32>(),
},
default: Option::<u32>::None.encode(),
docs: vec![],
deprecation_info: pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated
},
StorageEntryMetadataIR {
name: "foo",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Blake2_128Concat],
key: scale_info::meta_type::<u16>(),
value: scale_info::meta_type::<u32>(),
},
default: 97u32.encode(),
docs: vec![],
deprecation_info: pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated
}
]
);
let _ = WithLen::clear(u32::max_value(), None);
assert_eq!(WithLen::decode_len(3), None);
WithLen::append(0, 10);
assert_eq!(WithLen::decode_len(0), Some(1));
})
}
}
@@ -0,0 +1,206 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Storage types to build abstraction on storage, they implements storage traits such as
//! StorageMap and others.
use alloc::vec::Vec;
use codec::FullCodec;
use pezsp_metadata_ir::{StorageEntryMetadataIR, StorageEntryModifierIR};
mod counted_map;
mod counted_nmap;
mod double_map;
mod key;
mod map;
mod nmap;
mod value;
pub use counted_map::{CountedStorageMap, CountedStorageMapInstance, Counter};
pub use counted_nmap::{CountedStorageNMap, CountedStorageNMapInstance};
pub use double_map::StorageDoubleMap;
pub use key::{
EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, Key, KeyGenerator,
KeyGeneratorMaxEncodedLen, ReversibleKeyGenerator, TupleToEncodedIter,
};
pub use map::StorageMap;
pub use nmap::StorageNMap;
pub use value::StorageValue;
/// Trait implementing how the storage optional value is converted into the queried type.
///
/// It is implemented most notable by:
///
/// * [`OptionQuery`] which converts an optional value to an optional value, used when querying
/// storage returns an optional value.
/// * [`ResultQuery`] which converts an optional value to a result value, used when querying storage
/// returns a result value.
/// * [`ValueQuery`] which converts an optional value to a value, used when querying storage returns
/// a value.
///
/// ## Example
#[doc = docify::embed!("src/storage/types/mod.rs", value_query_examples)]
pub trait QueryKindTrait<Value, OnEmpty> {
/// Metadata for the storage kind.
const METADATA: StorageEntryModifierIR;
/// Type returned on query
type Query: FullCodec + 'static;
/// Convert an optional value (i.e. some if trie contains the value or none otherwise) to the
/// query.
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query;
/// Convert a query to an optional value.
fn from_query_to_optional_value(v: Self::Query) -> Option<Value>;
}
/// Implements [`QueryKindTrait`] with `Query` type being `Option<_>`.
///
/// NOTE: it doesn't support a generic `OnEmpty`. This means only `None` can be returned when no
/// value is found. To use another `OnEmpty` implementation, `ValueQuery` can be used instead.
pub struct OptionQuery;
impl<Value> QueryKindTrait<Value, crate::traits::GetDefault> for OptionQuery
where
Value: FullCodec + 'static,
{
const METADATA: StorageEntryModifierIR = StorageEntryModifierIR::Optional;
type Query = Option<Value>;
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
// NOTE: OnEmpty is fixed to GetDefault, thus it returns `None` on no value.
v
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
v
}
}
/// Implements [`QueryKindTrait`] with `Query` type being `Result<Value, PalletError>`.
pub struct ResultQuery<Error>(core::marker::PhantomData<Error>);
impl<Value, Error, OnEmpty> QueryKindTrait<Value, OnEmpty> for ResultQuery<Error>
where
Value: FullCodec + 'static,
Error: FullCodec + 'static,
OnEmpty: crate::traits::Get<Result<Value, Error>>,
{
const METADATA: StorageEntryModifierIR = StorageEntryModifierIR::Optional;
type Query = Result<Value, Error>;
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
match v {
Some(v) => Ok(v),
None => OnEmpty::get(),
}
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
v.ok()
}
}
/// Implements [`QueryKindTrait`] with `Query` type being `Value`.
pub struct ValueQuery;
impl<Value, OnEmpty> QueryKindTrait<Value, OnEmpty> for ValueQuery
where
Value: FullCodec + 'static,
OnEmpty: crate::traits::Get<Value>,
{
const METADATA: StorageEntryModifierIR = StorageEntryModifierIR::Default;
type Query = Value;
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
v.unwrap_or_else(|| OnEmpty::get())
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
Some(v)
}
}
/// Build the metadata of a storage.
///
/// Implemented by each of the storage types: value, map, countedmap, doublemap and nmap.
pub trait StorageEntryMetadataBuilder {
/// Build into `entries` the storage metadata entries of a storage given some `docs`.
fn build_metadata(
deprecation_status: pezsp_metadata_ir::ItemDeprecationInfoIR,
doc: Vec<&'static str>,
entries: &mut Vec<StorageEntryMetadataIR>,
);
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
storage::types::ValueQuery,
traits::{Get, StorageInstance},
};
use pezsp_io::TestExternalities;
struct Prefix;
impl StorageInstance for Prefix {
fn pezpallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "foo";
}
#[docify::export]
#[test]
pub fn value_query_examples() {
/// Custom default impl to be used with `ValueQuery`.
struct UniverseSecret;
impl Get<u32> for UniverseSecret {
fn get() -> u32 {
42
}
}
/// Custom default impl to be used with `ResultQuery`.
struct GetDefaultForResult;
impl Get<Result<u32, ()>> for GetDefaultForResult {
fn get() -> Result<u32, ()> {
Err(())
}
}
type A = StorageValue<Prefix, u32, ValueQuery>;
type B = StorageValue<Prefix, u32, OptionQuery>;
type C = StorageValue<Prefix, u32, ResultQuery<()>, GetDefaultForResult>;
type D = StorageValue<Prefix, u32, ValueQuery, UniverseSecret>;
TestExternalities::default().execute_with(|| {
// normal value query returns default
assert_eq!(A::get(), 0);
// option query returns none
assert_eq!(B::get(), None);
// result query returns error
assert_eq!(C::get(), Err(()));
// value query with custom on empty returns 42
assert_eq!(D::get(), 42);
});
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,461 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Storage value type. Implements StorageValue trait and its method directly.
use crate::{
storage::{
generator::StorageValue as StorageValueT,
types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
StorageAppend, StorageDecodeLength, StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
};
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen};
use pezframe_support::storage::StorageDecodeNonDedupLength;
use pezsp_arithmetic::traits::SaturatedConversion;
use pezsp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
/// A type representing a *value* in storage. A *storage value* is a single value of a given type
/// stored on-chain.
///
/// For general information regarding the `#[pallet::storage]` attribute, refer to
/// [`crate::pezpallet_macros::storage`].
///
/// # Example
///
/// ```
/// #[pezframe_support::pallet]
/// mod pallet {
/// # use pezframe_support::pezpallet_prelude::*;
/// # #[pallet::config]
/// # pub trait Config: pezframe_system::Config {}
/// # #[pallet::pallet]
/// # pub struct Pallet<T>(_);
/// /// A kitchen-sink StorageValue, with all possible additional attributes.
/// #[pallet::storage]
/// #[pallet::getter(fn foo)]
/// #[pallet::storage_prefix = "OtherFoo"]
/// #[pallet::unbounded]
/// pub type Foo<T> = StorageValue<_, u32,ValueQuery>;
///
/// /// Named alternative syntax.
/// #[pallet::storage]
/// pub type Bar<T> = StorageValue<
/// Value = u32,
/// QueryKind = ValueQuery
/// >;
/// }
/// ```
pub struct StorageValue<Prefix, Value, QueryKind = OptionQuery, OnEmpty = GetDefault>(
core::marker::PhantomData<(Prefix, Value, QueryKind, OnEmpty)>,
);
impl<Prefix, Value, QueryKind, OnEmpty> crate::storage::generator::StorageValue<Value>
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
{
type Query = QueryKind::Query;
fn pezpallet_prefix() -> &'static [u8] {
Prefix::pezpallet_prefix().as_bytes()
}
fn storage_prefix() -> &'static [u8] {
Prefix::STORAGE_PREFIX.as_bytes()
}
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
QueryKind::from_optional_value_to_query(v)
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
QueryKind::from_query_to_optional_value(v)
}
fn storage_value_final_key() -> [u8; 32] {
Prefix::prefix_hash()
}
}
impl<Prefix, Value, QueryKind, OnEmpty> StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
/// Get the storage key.
pub fn hashed_key() -> [u8; 32] {
<Self as crate::storage::StorageValue<Value>>::hashed_key()
}
/// Does the value (explicitly) exist in storage?
pub fn exists() -> bool {
<Self as crate::storage::StorageValue<Value>>::exists()
}
/// Load the value from the provided storage instance.
pub fn get() -> QueryKind::Query {
<Self as crate::storage::StorageValue<Value>>::get()
}
/// Try to get the underlying value from the provided storage instance; `Ok` if it exists,
/// `Err` if not.
pub fn try_get() -> Result<Value, ()> {
<Self as crate::storage::StorageValue<Value>>::try_get()
}
/// Translate a value from some previous type (`O`) to the current type.
///
/// `f: F` is the translation function.
///
/// Returns `Err` if the storage item could not be interpreted as the old type, and Ok, along
/// with the new value if it could.
///
/// NOTE: This operates from and to `Option<_>` types; no effort is made to respect the default
/// value of the original type.
///
/// # Warning
///
/// This function must be used with care, before being updated the storage still contains the
/// old type, thus other calls (such as `get`) will fail at decoding it.
///
/// # Usage
///
/// This would typically be called inside the module implementation of on_runtime_upgrade,
/// while ensuring **no usage of this storage are made before the call to
/// `on_runtime_upgrade`**. (More precisely prior initialized modules doesn't make use of this
/// storage).
pub fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<Value>>(
f: F,
) -> Result<Option<Value>, ()> {
<Self as crate::storage::StorageValue<Value>>::translate(f)
}
/// Store a value under this key into the provided storage instance.
pub fn put<Arg: EncodeLike<Value>>(val: Arg) {
<Self as crate::storage::StorageValue<Value>>::put(val)
}
/// Store a value under this key into the provided storage instance.
///
/// this uses the query type rather than the underlying value.
pub fn set(val: QueryKind::Query) {
<Self as crate::storage::StorageValue<Value>>::set(val)
}
/// Mutate the value
pub fn mutate<R, F: FnOnce(&mut QueryKind::Query) -> R>(f: F) -> R {
<Self as crate::storage::StorageValue<Value>>::mutate(f)
}
/// Mutate the value under a key iff it exists. Do nothing and return the default value if not.
pub fn mutate_extant<R: Default, F: FnOnce(&mut Value) -> R>(f: F) -> R {
<Self as crate::storage::StorageValue<Value>>::mutate_extant(f)
}
/// Mutate the value if closure returns `Ok`
pub fn try_mutate<R, E, F: FnOnce(&mut QueryKind::Query) -> Result<R, E>>(
f: F,
) -> Result<R, E> {
<Self as crate::storage::StorageValue<Value>>::try_mutate(f)
}
/// Mutate the value. Deletes the item if mutated to a `None`.
pub fn mutate_exists<R, F: FnOnce(&mut Option<Value>) -> R>(f: F) -> R {
<Self as crate::storage::StorageValue<Value>>::mutate_exists(f)
}
/// Mutate the value if closure returns `Ok`. Deletes the item if mutated to a `None`.
pub fn try_mutate_exists<R, E, F: FnOnce(&mut Option<Value>) -> Result<R, E>>(
f: F,
) -> Result<R, E> {
<Self as crate::storage::StorageValue<Value>>::try_mutate_exists(f)
}
/// Clear the storage value.
pub fn kill() {
<Self as crate::storage::StorageValue<Value>>::kill()
}
/// Take a value from storage, removing it afterwards.
pub fn take() -> QueryKind::Query {
<Self as crate::storage::StorageValue<Value>>::take()
}
/// Append the given item to the value in the storage.
///
/// `Value` is required to implement [`StorageAppend`].
///
/// # Warning
///
/// If the storage item is not encoded properly, the storage item will be overwritten
/// and set to `[item]`. Any default value set for the storage item will be ignored
/// on overwrite.
pub fn append<Item, EncodeLikeItem>(item: EncodeLikeItem)
where
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageAppend<Item>,
{
<Self as crate::storage::StorageValue<Value>>::append(item)
}
/// Read the length of the storage value without decoding the entire value.
///
/// `Value` is required to implement [`StorageDecodeLength`].
///
/// If the value does not exists or it fails to decode the length, `None` is returned.
/// Otherwise `Some(len)` is returned.
///
/// # Warning
///
/// `None` does not mean that `get()` does not return a value. The default value is completely
/// ignored by this function.
pub fn decode_len() -> Option<usize>
where
Value: StorageDecodeLength,
{
<Self as crate::storage::StorageValue<Value>>::decode_len()
}
/// Read the length of the storage value without decoding the entire value.
///
/// `Value` is required to implement [`StorageDecodeNonDedupLength`].
///
/// If the value does not exists or it fails to decode the length, `None` is returned.
/// Otherwise `Some(len)` is returned.
///
/// # Warning
///
/// - `None` does not mean that `get()` does not return a value. The default value is
/// completely
/// ignored by this function.
///
/// - The value returned is the non-deduplicated length of the underlying Vector in storage.This
/// means that any duplicate items are included.
pub fn decode_non_dedup_len() -> Option<usize>
where
Value: StorageDecodeNonDedupLength,
{
<Self as crate::storage::StorageValue<Value>>::decode_non_dedup_len()
}
/// Try and append the given item to the value in the storage.
///
/// Is only available if `Value` of the storage implements [`StorageTryAppend`].
pub fn try_append<Item, EncodeLikeItem>(item: EncodeLikeItem) -> Result<(), ()>
where
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageTryAppend<Item>,
{
<Self as crate::storage::TryAppendValue<Value, Item>>::try_append(item)
}
}
impl<Prefix, Value, QueryKind, OnEmpty> StorageEntryMetadataBuilder
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec + scale_info::StaticTypeInfo,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
fn build_metadata(
deprecation_status: pezsp_metadata_ir::ItemDeprecationInfoIR,
docs: Vec<&'static str>,
entries: &mut Vec<StorageEntryMetadataIR>,
) {
let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs };
let entry = StorageEntryMetadataIR {
name: Prefix::STORAGE_PREFIX,
modifier: QueryKind::METADATA,
ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<Value>()),
default: OnEmpty::get().encode(),
docs,
deprecation_info: deprecation_status,
};
entries.push(entry);
}
}
impl<Prefix, Value, QueryKind, OnEmpty> crate::traits::StorageInfoTrait
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
fn storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pezpallet_name: Self::pezpallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::hashed_key().to_vec(),
max_values: Some(1),
max_size: Some(Value::max_encoded_len().saturated_into()),
}]
}
}
/// It doesn't require to implement `MaxEncodedLen` and give no information for `max_size`.
impl<Prefix, Value, QueryKind, OnEmpty> crate::traits::PartialStorageInfoTrait
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
fn partial_storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pezpallet_name: Self::pezpallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::hashed_key().to_vec(),
max_values: Some(1),
max_size: None,
}]
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::storage::types::ValueQuery;
use pezsp_io::{hashing::twox_128, TestExternalities};
use pezsp_metadata_ir::StorageEntryModifierIR;
struct Prefix;
impl StorageInstance for Prefix {
fn pezpallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "foo";
}
struct ADefault;
impl crate::traits::Get<u32> for ADefault {
fn get() -> u32 {
97
}
}
#[test]
fn test() {
type A = StorageValue<Prefix, u32, OptionQuery>;
type AValueQueryWithAnOnEmpty = StorageValue<Prefix, u32, ValueQuery, ADefault>;
type B = StorageValue<Prefix, u16, ValueQuery>;
type WithLen = StorageValue<Prefix, Vec<u32>>;
TestExternalities::default().execute_with(|| {
assert_eq!(A::hashed_key().to_vec(), [twox_128(b"test"), twox_128(b"foo")].concat());
assert_eq!(A::exists(), false);
assert_eq!(A::get(), None);
assert_eq!(AValueQueryWithAnOnEmpty::get(), 97);
assert_eq!(A::try_get(), Err(()));
A::put(2);
assert_eq!(A::exists(), true);
assert_eq!(A::get(), Some(2));
assert_eq!(AValueQueryWithAnOnEmpty::get(), 2);
assert_eq!(A::try_get(), Ok(2));
assert_eq!(A::try_get(), Ok(2));
B::put(4);
A::translate::<u16, _>(|v| v.map(Into::into)).unwrap();
assert_eq!(A::try_get(), Ok(4));
A::set(None);
assert_eq!(A::try_get(), Err(()));
A::set(Some(2));
assert_eq!(A::try_get(), Ok(2));
A::mutate(|v| *v = Some(v.unwrap() * 2));
assert_eq!(A::try_get(), Ok(4));
A::set(Some(4));
let _: Result<(), ()> = A::try_mutate(|v| {
*v = Some(v.unwrap() * 2);
Ok(())
});
assert_eq!(A::try_get(), Ok(8));
let _: Result<(), ()> = A::try_mutate(|v| {
*v = Some(v.unwrap() * 2);
Err(())
});
assert_eq!(A::try_get(), Ok(8));
A::kill();
AValueQueryWithAnOnEmpty::mutate(|v| *v = *v * 2);
assert_eq!(AValueQueryWithAnOnEmpty::try_get(), Ok(97 * 2));
AValueQueryWithAnOnEmpty::kill();
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(|v| {
*v = *v * 2;
Ok(())
});
assert_eq!(AValueQueryWithAnOnEmpty::try_get(), Ok(97 * 2));
A::kill();
assert_eq!(A::try_get(), Err(()));
let mut entries = vec![];
A::build_metadata(
pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated,
vec![],
&mut entries,
);
AValueQueryWithAnOnEmpty::build_metadata(
pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated,
vec![],
&mut entries,
);
assert_eq!(
entries,
vec![
StorageEntryMetadataIR {
name: "foo",
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<u32>()),
default: Option::<u32>::None.encode(),
docs: vec![],
deprecation_info: pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated
},
StorageEntryMetadataIR {
name: "foo",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<u32>()),
default: 97u32.encode(),
docs: vec![],
deprecation_info: pezsp_metadata_ir::ItemDeprecationInfoIR::NotDeprecated
}
]
);
WithLen::kill();
assert_eq!(WithLen::decode_len(), None);
WithLen::append(3);
assert_eq!(WithLen::decode_len(), Some(1));
});
}
}
@@ -0,0 +1,179 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Operation on unhashed runtime storage.
use alloc::vec::Vec;
use codec::{Decode, Encode};
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T: Decode + Sized>(key: &[u8]) -> Option<T> {
pezsp_io::storage::get(key).and_then(|val| {
Decode::decode(&mut &val[..]).map(Some).unwrap_or_else(|e| {
// TODO #3700: error should be handleable.
log::error!(
target: "runtime::storage",
"Corrupted state at `{}`: {:?}",
array_bytes::bytes2hex("0x", key),
e,
);
None
})
})
}
/// Return the value of the item in storage under `key`, or the type's default if there is no
/// explicit entry.
pub fn get_or_default<T: Decode + Sized + Default>(key: &[u8]) -> T {
get(key).unwrap_or_default()
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry.
pub fn get_or<T: Decode + Sized>(key: &[u8], default_value: T) -> T {
get(key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry.
pub fn get_or_else<T: Decode + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
get(key).unwrap_or_else(default_value)
}
/// Put `value` in storage under `key`.
pub fn put<T: Encode + ?Sized>(key: &[u8], value: &T) {
value.using_encoded(|slice| pezsp_io::storage::set(key, slice));
}
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
pub fn take<T: Decode + Sized>(key: &[u8]) -> Option<T> {
let r = get(key);
if r.is_some() {
kill(key);
}
r
}
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
/// the default for its type.
pub fn take_or_default<T: Decode + Sized + Default>(key: &[u8]) -> T {
take(key).unwrap_or_default()
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or<T: Decode + Sized>(key: &[u8], default_value: T) -> T {
take(key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or_else<T: Decode + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
take(key).unwrap_or_else(default_value)
}
/// Check to see if `key` has an explicit entry in storage.
pub fn exists(key: &[u8]) -> bool {
pezsp_io::storage::exists(key)
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill(key: &[u8]) {
pezsp_io::storage::clear(key);
}
/// Ensure keys with the given `prefix` have no entries in storage.
#[deprecated = "Use `clear_prefix` instead"]
pub fn kill_prefix(prefix: &[u8], limit: Option<u32>) -> pezsp_io::KillStorageResult {
// TODO: Once the network has upgraded to include the new host functions, this code can be
// enabled.
// clear_prefix(prefix, limit).into()
pezsp_io::storage::clear_prefix(prefix, limit)
}
/// Partially clear the storage of all keys under a common `prefix`.
///
/// # Limit
///
/// A *limit* should always be provided through `maybe_limit`. This is one fewer than the
/// maximum number of backend iterations which may be done by this operation and as such
/// represents the maximum number of backend deletions which may happen. A *limit* of zero
/// implies that no keys will be deleted, though there may be a single iteration done.
///
/// The limit can be used to partially delete storage items in case it is too large or costly
/// to delete all in a single operation.
///
/// # Cursor
///
/// A *cursor* may be passed in to this operation with `maybe_cursor`. `None` should only be
/// passed once (in the initial call) for any attempt to clear storage. In general, subsequent calls
/// operating on the same prefix should pass `Some` and this value should be equal to the
/// previous call result's `maybe_cursor` field. The only exception to this is when you can
/// guarantee that the subsequent call is in a new block; in this case the previous call's result
/// cursor need not be passed in and a `None` may be passed instead. This exception may be useful
/// then making this call solely from a block-hook such as `on_initialize`.
///
/// Returns [`MultiRemovalResults`](pezsp_io::MultiRemovalResults) to inform about the result. Once the
/// resultant `maybe_cursor` field is `None`, then no further items remain to be deleted.
///
/// NOTE: After the initial call for any given child storage, it is important that no keys further
/// keys are inserted. If so, then they may or may not be deleted by subsequent calls.
///
/// # Note
///
/// Please note that keys which are residing in the overlay for the child are deleted without
/// counting towards the `limit`.
pub fn clear_prefix(
prefix: &[u8],
maybe_limit: Option<u32>,
_maybe_cursor: Option<&[u8]>,
) -> pezsp_io::MultiRemovalResults {
// TODO: Once the network has upgraded to include the new host functions, this code can be
// enabled.
// pezsp_io::storage::clear_prefix(prefix, maybe_limit, maybe_cursor)
use pezsp_io::{KillStorageResult::*, MultiRemovalResults};
#[allow(deprecated)]
let (maybe_cursor, i) = match kill_prefix(prefix, maybe_limit) {
AllRemoved(i) => (None, i),
SomeRemaining(i) => (Some(prefix.to_vec()), i),
};
MultiRemovalResults { maybe_cursor, backend: i, unique: i, loops: i }
}
/// Returns `true` if the storage contains any key, which starts with a certain prefix,
/// and is longer than said prefix.
/// This means that a key which equals the prefix will not be counted.
pub fn contains_prefixed_key(prefix: &[u8]) -> bool {
match pezsp_io::storage::next_key(prefix) {
Some(key) => key.starts_with(prefix),
None => false,
}
}
/// Get a Vec of bytes from storage.
pub fn get_raw(key: &[u8]) -> Option<Vec<u8>> {
pezsp_io::storage::get(key).map(|value| value.to_vec())
}
/// Put a raw byte slice into storage.
///
/// **WARNING**: If you set the storage of the Bizinikiwi Wasm (`well_known_keys::CODE`),
/// you should also call `pezframe_system::RuntimeUpgraded::put(true)` to trigger the
/// `on_runtime_upgrade` logic.
pub fn put_raw(key: &[u8], value: &[u8]) {
pezsp_io::storage::set(key, value)
}
@@ -0,0 +1,75 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Traits, types and structs to support putting a bounded vector into storage, as a raw value, map
//! or a double map.
use crate::{
storage::{StorageDecodeLength, StorageTryAppend},
traits::Get,
};
pub use pezsp_runtime::WeakBoundedVec;
impl<T, S> StorageDecodeLength for WeakBoundedVec<T, S> {}
impl<T, S: Get<u32>> StorageTryAppend<T> for WeakBoundedVec<T, S> {
fn bound() -> usize {
S::get() as usize
}
}
#[cfg(test)]
pub mod test {
use super::*;
use crate::Twox128;
use pezframe_support::traits::ConstU32;
use pezsp_io::TestExternalities;
#[crate::storage_alias]
type Foo = StorageValue<Prefix, WeakBoundedVec<u32, ConstU32<7>>>;
#[crate::storage_alias]
type FooMap = StorageMap<Prefix, Twox128, u32, WeakBoundedVec<u32, ConstU32<7>>>;
#[crate::storage_alias]
type FooDoubleMap =
StorageDoubleMap<Prefix, Twox128, u32, Twox128, u32, WeakBoundedVec<u32, ConstU32<7>>>;
#[test]
fn decode_len_works() {
TestExternalities::default().execute_with(|| {
let bounded: WeakBoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap();
Foo::put(bounded);
assert_eq!(Foo::decode_len().unwrap(), 3);
});
TestExternalities::default().execute_with(|| {
let bounded: WeakBoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap();
FooMap::insert(1, bounded);
assert_eq!(FooMap::decode_len(1).unwrap(), 3);
assert!(FooMap::decode_len(0).is_none());
assert!(FooMap::decode_len(2).is_none());
});
TestExternalities::default().execute_with(|| {
let bounded: WeakBoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap();
FooDoubleMap::insert(1, 1, bounded);
assert_eq!(FooDoubleMap::decode_len(1, 1).unwrap(), 3);
assert!(FooDoubleMap::decode_len(2, 1).is_none());
assert!(FooDoubleMap::decode_len(1, 2).is_none());
assert!(FooDoubleMap::decode_len(2, 2).is_none());
});
}
}