mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 11:07:56 +00:00
Introduce storage types, to builds abstraction on storage (to be used in pallet attribute macro) (#7278)
* introduce storage types * fix line width * improve doc * typo * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * remove redundant function * some more doc improvment * disallow: some value on empty with option query tests are update to still test default value is used when it is supposed to be used. Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7fcf0ff610
commit
ff6b603d69
@@ -56,6 +56,7 @@ impl<T: Codec> Hashable for T {
|
||||
|
||||
/// Hasher to use to hash keys to insert to storage.
|
||||
pub trait StorageHasher: 'static {
|
||||
const METADATA: frame_metadata::StorageHasher;
|
||||
type Output: AsRef<[u8]>;
|
||||
fn hash(x: &[u8]) -> Self::Output;
|
||||
}
|
||||
@@ -73,6 +74,7 @@ pub trait ReversibleStorageHasher: StorageHasher {
|
||||
/// Store the key directly.
|
||||
pub struct Identity;
|
||||
impl StorageHasher for Identity {
|
||||
const METADATA: frame_metadata::StorageHasher = frame_metadata::StorageHasher::Identity;
|
||||
type Output = Vec<u8>;
|
||||
fn hash(x: &[u8]) -> Vec<u8> {
|
||||
x.to_vec()
|
||||
@@ -87,6 +89,7 @@ impl ReversibleStorageHasher for Identity {
|
||||
/// Hash storage keys with `concat(twox64(key), key)`
|
||||
pub struct Twox64Concat;
|
||||
impl StorageHasher for Twox64Concat {
|
||||
const METADATA: frame_metadata::StorageHasher = frame_metadata::StorageHasher::Twox64Concat;
|
||||
type Output = Vec<u8>;
|
||||
fn hash(x: &[u8]) -> Vec<u8> {
|
||||
twox_64(x)
|
||||
@@ -109,6 +112,7 @@ impl ReversibleStorageHasher for Twox64Concat {
|
||||
/// Hash storage keys with `concat(blake2_128(key), key)`
|
||||
pub struct Blake2_128Concat;
|
||||
impl StorageHasher for Blake2_128Concat {
|
||||
const METADATA: frame_metadata::StorageHasher = frame_metadata::StorageHasher::Blake2_128Concat;
|
||||
type Output = Vec<u8>;
|
||||
fn hash(x: &[u8]) -> Vec<u8> {
|
||||
blake2_128(x)
|
||||
@@ -131,6 +135,7 @@ impl ReversibleStorageHasher for Blake2_128Concat {
|
||||
/// Hash storage keys with blake2 128
|
||||
pub struct Blake2_128;
|
||||
impl StorageHasher for Blake2_128 {
|
||||
const METADATA: frame_metadata::StorageHasher = frame_metadata::StorageHasher::Blake2_128;
|
||||
type Output = [u8; 16];
|
||||
fn hash(x: &[u8]) -> [u8; 16] {
|
||||
blake2_128(x)
|
||||
@@ -140,6 +145,7 @@ impl StorageHasher for Blake2_128 {
|
||||
/// Hash storage keys with blake2 256
|
||||
pub struct Blake2_256;
|
||||
impl StorageHasher for Blake2_256 {
|
||||
const METADATA: frame_metadata::StorageHasher = frame_metadata::StorageHasher::Blake2_256;
|
||||
type Output = [u8; 32];
|
||||
fn hash(x: &[u8]) -> [u8; 32] {
|
||||
blake2_256(x)
|
||||
@@ -149,6 +155,7 @@ impl StorageHasher for Blake2_256 {
|
||||
/// Hash storage keys with twox 128
|
||||
pub struct Twox128;
|
||||
impl StorageHasher for Twox128 {
|
||||
const METADATA: frame_metadata::StorageHasher = frame_metadata::StorageHasher::Twox128;
|
||||
type Output = [u8; 16];
|
||||
fn hash(x: &[u8]) -> [u8; 16] {
|
||||
twox_128(x)
|
||||
@@ -158,6 +165,7 @@ impl StorageHasher for Twox128 {
|
||||
/// Hash storage keys with twox 256
|
||||
pub struct Twox256;
|
||||
impl StorageHasher for Twox256 {
|
||||
const METADATA: frame_metadata::StorageHasher = frame_metadata::StorageHasher::Twox256;
|
||||
type Output = [u8; 32];
|
||||
fn hash(x: &[u8]) -> [u8; 32] {
|
||||
twox_256(x)
|
||||
|
||||
@@ -29,6 +29,7 @@ pub mod child;
|
||||
#[doc(hidden)]
|
||||
pub mod generator;
|
||||
pub mod migration;
|
||||
pub mod types;
|
||||
|
||||
#[cfg(all(feature = "std", any(test, debug_assertions)))]
|
||||
mod debug_helper {
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2020 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 StorageDoubleMap, StorageIterableDoubleMap,
|
||||
//! StoragePrefixedDoubleMap traits and their methods directly.
|
||||
|
||||
use codec::{FullCodec, Decode, EncodeLike, Encode};
|
||||
use crate::{
|
||||
storage::{
|
||||
StorageAppend, StorageDecodeLength,
|
||||
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
|
||||
},
|
||||
traits::{GetDefault, StorageInstance},
|
||||
};
|
||||
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
/// A type that allow to store values for `(key1, key2)` couple. Similar to `StorageMap` but allow
|
||||
/// to iterate and remove value associated to first key.
|
||||
///
|
||||
/// Each value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(<Prefix::Pallet as PalletInfo>::name())
|
||||
/// ++ Twox128(Prefix::STORAGE_PREFIX)
|
||||
/// ++ Hasher1(encode(key1))
|
||||
/// ++ Hasher2(encode(key2))
|
||||
/// ```
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the key1s (or key2s) are not trusted (e.g. can be set by a user), a cryptographic `hasher`
|
||||
/// such as `blake2_128_concat` must be used for Hasher1 (resp. Hasher2). Otherwise, other values
|
||||
/// in storage can be compromised.
|
||||
pub struct StorageDoubleMap<
|
||||
Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind=OptionQuery, OnEmpty=GetDefault
|
||||
>(
|
||||
core::marker::PhantomData<(Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty)>
|
||||
);
|
||||
|
||||
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
crate::storage::generator::StorageDoubleMap<Key1, Key2, Value> for
|
||||
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher1: crate::hash::StorageHasher,
|
||||
Hasher2: crate::hash::StorageHasher,
|
||||
Key1: FullCodec,
|
||||
Key2: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
|
||||
{
|
||||
type Query = QueryKind::Query;
|
||||
type Hasher1 = Hasher1;
|
||||
type Hasher2 = Hasher2;
|
||||
fn module_prefix() -> &'static [u8] {
|
||||
<Prefix::PalletInfo as crate::traits::PalletInfo>::name::<Prefix::Pallet>()
|
||||
.expect("Every active pallet has a name in the runtime; qed").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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
crate::storage::StoragePrefixedMap<Value> for
|
||||
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher1: crate::hash::StorageHasher,
|
||||
Hasher2: crate::hash::StorageHasher,
|
||||
Key1: FullCodec,
|
||||
Key2: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
|
||||
{
|
||||
fn module_prefix() -> &'static [u8] {
|
||||
<Self as crate::storage::generator::StorageDoubleMap<Key1, Key2, Value>>::module_prefix()
|
||||
}
|
||||
fn storage_prefix() -> &'static [u8] {
|
||||
<Self as crate::storage::generator::StorageDoubleMap<Key1, Key2, Value>>::storage_prefix()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher1: crate::hash::StorageHasher,
|
||||
Hasher2: crate::hash::StorageHasher,
|
||||
Key1: FullCodec,
|
||||
Key2: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
|
||||
{
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
pub fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::hashed_key_for(k1, k2)
|
||||
}
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
pub fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::contains_key(k1, k2)
|
||||
}
|
||||
|
||||
/// Load the value associated with the given key from the double map.
|
||||
pub fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> QueryKind::Query
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::get(k1, k2)
|
||||
}
|
||||
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
pub fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> QueryKind::Query
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::take(k1, k2)
|
||||
}
|
||||
|
||||
/// Swap the values of two key-pairs.
|
||||
pub fn swap<XKArg1, XKArg2, YKArg1, YKArg2>(x_k1: XKArg1, x_k2: XKArg2, y_k1: YKArg1, y_k2: YKArg2)
|
||||
where
|
||||
XKArg1: EncodeLike<Key1>,
|
||||
XKArg2: EncodeLike<Key2>,
|
||||
YKArg1: EncodeLike<Key1>,
|
||||
YKArg2: EncodeLike<Key2>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::swap(x_k1, x_k2, y_k1, y_k2)
|
||||
}
|
||||
|
||||
/// Store a value to be associated with the given keys from the double map.
|
||||
pub fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
VArg: EncodeLike<Value>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::insert(k1, k2, val)
|
||||
}
|
||||
|
||||
/// Remove the value under the given keys.
|
||||
pub fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::remove(k1, k2)
|
||||
}
|
||||
|
||||
/// Remove all values under the first key.
|
||||
pub fn remove_prefix<KArg1>(k1: KArg1) where KArg1: ?Sized + EncodeLike<Key1> {
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::remove_prefix(k1)
|
||||
}
|
||||
|
||||
/// Iterate over values that share the first key.
|
||||
pub fn iter_prefix_values<KArg1>(k1: KArg1) -> crate::storage::PrefixIterator<Value>
|
||||
where KArg1: ?Sized + EncodeLike<Key1>
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::iter_prefix_values(k1)
|
||||
}
|
||||
|
||||
/// Mutate the value under the given keys.
|
||||
pub fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
F: FnOnce(&mut QueryKind::Query) -> R,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::mutate(k1, k2, f)
|
||||
}
|
||||
|
||||
/// Mutate the value under the given keys when the closure returns `Ok`.
|
||||
pub fn try_mutate<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
F: FnOnce(&mut QueryKind::Query) -> Result<R, E>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::try_mutate(k1, k2, f)
|
||||
}
|
||||
|
||||
/// Mutate the value under the given keys. Deletes the item if mutated to a `None`.
|
||||
pub fn mutate_exists<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
F: FnOnce(&mut Option<Value>) -> R,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::mutate_exists(k1, k2, f)
|
||||
}
|
||||
|
||||
/// Mutate the item, only if an `Ok` value is returned. Deletes the item if mutated to a `None`.
|
||||
pub fn try_mutate_exists<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
F: FnOnce(&mut Option<Value>) -> Result<R, E>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::try_mutate_exists(k1, k2, f)
|
||||
}
|
||||
|
||||
/// 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 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, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
item: EncodeLikeItem,
|
||||
) where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
Value: StorageAppend<Item>,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::append(k1, k2, item)
|
||||
}
|
||||
|
||||
/// Read the length of the storage value without decoding the entire value under the
|
||||
/// given `key1` and `key2`.
|
||||
///
|
||||
/// `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 completly
|
||||
/// ignored by this function.
|
||||
pub fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Option<usize>
|
||||
where
|
||||
KArg1: EncodeLike<Key1>,
|
||||
KArg2: EncodeLike<Key2>,
|
||||
Value: StorageDecodeLength,
|
||||
{
|
||||
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::decode_len(key1, key2)
|
||||
}
|
||||
|
||||
/// Migrate an item with the given `key1` and `key2` from defunct `OldHasher1` and
|
||||
/// `OldHasher2` to the current hashers.
|
||||
///
|
||||
/// If the key doesn't exist, then it's a no-op. If it does, then it returns its value.
|
||||
pub fn migrate_keys<
|
||||
OldHasher1: crate::StorageHasher,
|
||||
OldHasher2: crate::StorageHasher,
|
||||
KeyArg1: EncodeLike<Key1>,
|
||||
KeyArg2: EncodeLike<Key2>,
|
||||
>(key1: KeyArg1, key2: KeyArg2) -> Option<Value> {
|
||||
<
|
||||
Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>
|
||||
>::migrate_keys::<OldHasher1, OldHasher2, _, _>(key1, key2)
|
||||
}
|
||||
|
||||
/// Remove all value of the storage.
|
||||
pub fn remove_all() {
|
||||
<Self as crate::storage::StoragePrefixedMap<Value>>::remove_all()
|
||||
}
|
||||
|
||||
/// Iter over all value of the storage.
|
||||
///
|
||||
/// NOTE: If a value failed to decode becaues 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: Fn(OldValue) -> Option<Value>>(f: F) {
|
||||
<Self as crate::storage::StoragePrefixedMap<Value>>::translate_values(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher1: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
|
||||
Hasher2: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
|
||||
Key1: FullCodec,
|
||||
Key2: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
|
||||
{
|
||||
/// Enumerate all elements in the map with first key `k1` in no particular order.
|
||||
///
|
||||
/// If you add or remove values whose first key is `k1` to the map while doing this, you'll get
|
||||
/// undefined results.
|
||||
pub fn iter_prefix(k1: impl EncodeLike<Key1>) -> crate::storage::PrefixIterator<(Key2, Value)> {
|
||||
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_prefix(k1)
|
||||
}
|
||||
|
||||
/// Remove all elements from the map with first key `k1` and iterate through them in no
|
||||
/// particular order.
|
||||
///
|
||||
/// If you add elements with first key `k1` to the map while doing this, you'll get undefined
|
||||
/// results.
|
||||
pub fn drain_prefix(k1: impl EncodeLike<Key1>) -> crate::storage::PrefixIterator<(Key2, Value)> {
|
||||
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::drain_prefix(k1)
|
||||
}
|
||||
|
||||
/// Enumerate all elements in the map in no particular order.
|
||||
///
|
||||
/// If you add or remove values to the map while doing this, you'll get undefined results.
|
||||
pub fn iter() -> crate::storage::PrefixIterator<(Key1, Key2, Value)> {
|
||||
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter()
|
||||
}
|
||||
|
||||
/// 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<(Key1, Key2, Value)> {
|
||||
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, 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 fail to decode because storage is corrupted then it is skipped.
|
||||
pub fn translate<O: Decode, F: Fn(Key1, Key2, O) -> Option<Value>>(f: F) {
|
||||
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::translate(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Part of storage metadata for a storage double map.
|
||||
///
|
||||
/// NOTE: Generic hashers is supported.
|
||||
pub trait StorageDoubleMapMetadata {
|
||||
const MODIFIER: StorageEntryModifier;
|
||||
const NAME: &'static str;
|
||||
const DEFAULT: DefaultByteGetter;
|
||||
const HASHER1: frame_metadata::StorageHasher;
|
||||
const HASHER2: frame_metadata::StorageHasher;
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty> StorageDoubleMapMetadata
|
||||
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty> where
|
||||
Prefix: StorageInstance,
|
||||
Hasher1: crate::hash::StorageHasher,
|
||||
Hasher2: crate::hash::StorageHasher,
|
||||
Key1: FullCodec,
|
||||
Key2: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static
|
||||
{
|
||||
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
|
||||
const HASHER1: frame_metadata::StorageHasher = Hasher1::METADATA;
|
||||
const HASHER2: frame_metadata::StorageHasher = Hasher2::METADATA;
|
||||
const NAME: &'static str = Prefix::STORAGE_PREFIX;
|
||||
const DEFAULT: DefaultByteGetter =
|
||||
DefaultByteGetter(&OnEmptyGetter::<QueryKind::Query, OnEmpty>(core::marker::PhantomData));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use sp_io::{TestExternalities, hashing::twox_128};
|
||||
use crate::hash::*;
|
||||
use crate::storage::types::ValueQuery;
|
||||
use frame_metadata::StorageEntryModifier;
|
||||
|
||||
struct Prefix;
|
||||
impl StorageInstance for Prefix {
|
||||
type Pallet = ();
|
||||
type PalletInfo = ();
|
||||
const STORAGE_PREFIX: &'static str = "foo";
|
||||
}
|
||||
|
||||
struct ADefault;
|
||||
impl crate::traits::Get<u32> for ADefault {
|
||||
fn get() -> u32 {
|
||||
97
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
type A = StorageDoubleMap<
|
||||
Prefix, Blake2_128Concat, u16, Twox64Concat, u8, u32, OptionQuery
|
||||
>;
|
||||
type AValueQueryWithAnOnEmpty = StorageDoubleMap<
|
||||
Prefix, Blake2_128Concat, u16, Twox64Concat, u8, u32, ValueQuery, ADefault
|
||||
>;
|
||||
type B = StorageDoubleMap<Prefix, Blake2_256, u16, Twox128, u8, u32, ValueQuery>;
|
||||
type C = StorageDoubleMap<Prefix, Blake2_128Concat, u16, Twox64Concat, u8, u8, ValueQuery>;
|
||||
type WithLen = StorageDoubleMap<Prefix, Blake2_128Concat, u16, Twox64Concat, u8, 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());
|
||||
k.extend(&30u8.twox_64_concat());
|
||||
assert_eq!(A::hashed_key_for(3, 30).to_vec(), k);
|
||||
|
||||
assert_eq!(A::contains_key(3, 30), false);
|
||||
assert_eq!(A::get(3, 30), None);
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::get(3, 30), 97);
|
||||
|
||||
A::insert(3, 30, 10);
|
||||
assert_eq!(A::contains_key(3, 30), true);
|
||||
assert_eq!(A::get(3, 30), Some(10));
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::get(3, 30), 10);
|
||||
|
||||
A::swap(3, 30, 2, 20);
|
||||
assert_eq!(A::contains_key(3, 30), false);
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(3, 30), None);
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::get(3, 30), 97);
|
||||
assert_eq!(A::get(2, 20), Some(10));
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::get(2, 20), 10);
|
||||
|
||||
A::remove(2, 20);
|
||||
assert_eq!(A::contains_key(2, 20), false);
|
||||
assert_eq!(A::get(2, 20), None);
|
||||
|
||||
AValueQueryWithAnOnEmpty::mutate(2, 20, |v| *v = *v * 2);
|
||||
AValueQueryWithAnOnEmpty::mutate(2, 20, |v| *v = *v * 2);
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(97 * 4));
|
||||
|
||||
A::remove(2, 20);
|
||||
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, 20, |v| {
|
||||
*v = *v * 2; Ok(())
|
||||
});
|
||||
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, 20, |v| {
|
||||
*v = *v * 2; Ok(())
|
||||
});
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(97 * 4));
|
||||
|
||||
A::remove(2, 20);
|
||||
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, 20, |v| {
|
||||
*v = *v * 2; Err(())
|
||||
});
|
||||
assert_eq!(A::contains_key(2, 20), false);
|
||||
|
||||
A::remove(2, 20);
|
||||
AValueQueryWithAnOnEmpty::mutate_exists(2, 20, |v| {
|
||||
assert!(v.is_none());
|
||||
*v = Some(10);
|
||||
});
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(10));
|
||||
AValueQueryWithAnOnEmpty::mutate_exists(2, 20, |v| {
|
||||
*v = Some(v.unwrap() * 10);
|
||||
});
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(100));
|
||||
|
||||
A::remove(2, 20);
|
||||
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, 20, |v| {
|
||||
assert!(v.is_none());
|
||||
*v = Some(10);
|
||||
Ok(())
|
||||
});
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(10));
|
||||
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, 20, |v| {
|
||||
*v = Some(v.unwrap() * 10);
|
||||
Ok(())
|
||||
});
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(100));
|
||||
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, 20, |v| {
|
||||
*v = Some(v.unwrap() * 10);
|
||||
Err(())
|
||||
});
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(100));
|
||||
|
||||
|
||||
A::insert(2, 20, 10);
|
||||
assert_eq!(A::take(2, 20), Some(10));
|
||||
assert_eq!(A::contains_key(2, 20), false);
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::take(2, 20), 97);
|
||||
assert_eq!(A::contains_key(2, 20), false);
|
||||
|
||||
B::insert(2, 20, 10);
|
||||
assert_eq!(A::migrate_keys::<Blake2_256, Twox128, _, _>(2, 20), Some(10));
|
||||
assert_eq!(A::contains_key(2, 20), true);
|
||||
assert_eq!(A::get(2, 20), Some(10));
|
||||
|
||||
A::insert(3, 30, 10);
|
||||
A::insert(4, 40, 10);
|
||||
A::remove_all();
|
||||
assert_eq!(A::contains_key(3, 30), false);
|
||||
assert_eq!(A::contains_key(4, 40), false);
|
||||
|
||||
A::insert(3, 30, 10);
|
||||
A::insert(4, 40, 10);
|
||||
assert_eq!(A::iter_values().collect::<Vec<_>>(), vec![10, 10]);
|
||||
|
||||
C::insert(3, 30, 10);
|
||||
C::insert(4, 40, 10);
|
||||
A::translate_values::<u8,_>(|v| Some((v * 2).into()));
|
||||
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40, 20), (3, 30, 20)]);
|
||||
|
||||
A::insert(3, 30, 10);
|
||||
A::insert(4, 40, 10);
|
||||
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40, 10), (3, 30, 10)]);
|
||||
assert_eq!(A::drain().collect::<Vec<_>>(), vec![(4, 40, 10), (3, 30, 10)]);
|
||||
assert_eq!(A::iter().collect::<Vec<_>>(), vec![]);
|
||||
|
||||
C::insert(3, 30, 10);
|
||||
C::insert(4, 40, 10);
|
||||
A::translate::<u8,_>(|k1, k2, v| Some((k1 * k2 as u16 * v as u16).into()));
|
||||
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40, 1600), (3, 30, 900)]);
|
||||
|
||||
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
|
||||
assert_eq!(A::HASHER1, frame_metadata::StorageHasher::Blake2_128Concat);
|
||||
assert_eq!(A::HASHER2, frame_metadata::StorageHasher::Twox64Concat);
|
||||
assert_eq!(
|
||||
AValueQueryWithAnOnEmpty::HASHER1,
|
||||
frame_metadata::StorageHasher::Blake2_128Concat
|
||||
);
|
||||
assert_eq!(
|
||||
AValueQueryWithAnOnEmpty::HASHER2,
|
||||
frame_metadata::StorageHasher::Twox64Concat
|
||||
);
|
||||
assert_eq!(A::NAME, "foo");
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::DEFAULT.0.default_byte(), 97u32.encode());
|
||||
assert_eq!(A::DEFAULT.0.default_byte(), Option::<u32>::None.encode());
|
||||
|
||||
WithLen::remove_all();
|
||||
assert_eq!(WithLen::decode_len(3, 30), None);
|
||||
WithLen::append(0, 100, 10);
|
||||
assert_eq!(WithLen::decode_len(0, 100), Some(1));
|
||||
|
||||
A::insert(3, 30, 11);
|
||||
A::insert(3, 31, 12);
|
||||
A::insert(4, 40, 13);
|
||||
A::insert(4, 41, 14);
|
||||
assert_eq!(A::iter_prefix_values(3).collect::<Vec<_>>(), vec![12, 11]);
|
||||
assert_eq!(A::iter_prefix(3).collect::<Vec<_>>(), vec![(31, 12), (30, 11)]);
|
||||
assert_eq!(A::iter_prefix_values(4).collect::<Vec<_>>(), vec![13, 14]);
|
||||
assert_eq!(A::iter_prefix(4).collect::<Vec<_>>(), vec![(40, 13), (41, 14)]);
|
||||
|
||||
A::remove_prefix(3);
|
||||
assert_eq!(A::iter_prefix(3).collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(A::iter_prefix(4).collect::<Vec<_>>(), vec![(40, 13), (41, 14)]);
|
||||
|
||||
assert_eq!(A::drain_prefix(4).collect::<Vec<_>>(), vec![(40, 13), (41, 14)]);
|
||||
assert_eq!(A::iter_prefix(4).collect::<Vec<_>>(), vec![]);
|
||||
assert_eq!(A::drain_prefix(4).collect::<Vec<_>>(), vec![]);
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2020 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 codec::{FullCodec, Decode, EncodeLike, Encode};
|
||||
use crate::{
|
||||
storage::{
|
||||
StorageAppend, StorageDecodeLength,
|
||||
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
|
||||
},
|
||||
traits::{GetDefault, StorageInstance},
|
||||
};
|
||||
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// A type that allow to store value for given key. Allowing to insert/remove/iterate on values.
|
||||
///
|
||||
/// Each value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(<Prefix::Pallet as PalletInfo>::name())
|
||||
/// ++ Twox128(Prefix::STORAGE_PREFIX)
|
||||
/// ++ Hasher1(encode(key))
|
||||
/// ```
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_128_concat` must be used. Otherwise, other values in storage can be compromised.
|
||||
pub struct StorageMap<Prefix, Hasher, Key, Value, QueryKind=OptionQuery, OnEmpty=GetDefault>(
|
||||
core::marker::PhantomData<(Prefix, Hasher, Key, Value, QueryKind, OnEmpty)>
|
||||
);
|
||||
|
||||
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
|
||||
crate::storage::generator::StorageMap<Key, Value>
|
||||
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher: crate::hash::StorageHasher,
|
||||
Key: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
|
||||
{
|
||||
type Query = QueryKind::Query;
|
||||
type Hasher = Hasher;
|
||||
fn module_prefix() -> &'static [u8] {
|
||||
<Prefix::PalletInfo as crate::traits::PalletInfo>::name::<Prefix::Pallet>()
|
||||
.expect("Every active pallet has a name in the runtime; qed").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)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> crate::storage::StoragePrefixedMap<Value> for
|
||||
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher: crate::hash::StorageHasher,
|
||||
Key: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
|
||||
{
|
||||
fn module_prefix() -> &'static [u8] {
|
||||
<Self as crate::storage::generator::StorageMap<Key, Value>>::module_prefix()
|
||||
}
|
||||
fn storage_prefix() -> &'static [u8] {
|
||||
<Self as crate::storage::generator::StorageMap<Key, Value>>::storage_prefix()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
|
||||
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher: crate::hash::StorageHasher,
|
||||
Key: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
|
||||
{
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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 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. 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`.
|
||||
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 completly
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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 value of the storage.
|
||||
pub fn remove_all() {
|
||||
<Self as crate::storage::StoragePrefixedMap<Value>>::remove_all()
|
||||
}
|
||||
|
||||
/// Iter over all value of the storage.
|
||||
///
|
||||
/// NOTE: If a value failed to decode becaues 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: Fn(OldValue) -> Option<Value>>(f: F) {
|
||||
<Self as crate::storage::StoragePrefixedMap<Value>>::translate_values(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
|
||||
StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty>
|
||||
where
|
||||
Prefix: StorageInstance,
|
||||
Hasher: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
|
||||
Key: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
|
||||
{
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// 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 fail to decode because storage is corrupted then it is skipped.
|
||||
pub fn translate<O: Decode, F: Fn(Key, O) -> Option<Value>>(f: F) {
|
||||
<Self as crate::storage::IterableStorageMap<Key, Value>>::translate(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Part of storage metadata for a storage map.
|
||||
///
|
||||
/// NOTE: Generic hasher is supported.
|
||||
pub trait StorageMapMetadata {
|
||||
const MODIFIER: StorageEntryModifier;
|
||||
const NAME: &'static str;
|
||||
const DEFAULT: DefaultByteGetter;
|
||||
const HASHER: frame_metadata::StorageHasher;
|
||||
}
|
||||
|
||||
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> StorageMapMetadata
|
||||
for StorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty> where
|
||||
Prefix: StorageInstance,
|
||||
Hasher: crate::hash::StorageHasher,
|
||||
Key: FullCodec,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
|
||||
{
|
||||
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
|
||||
const HASHER: frame_metadata::StorageHasher = Hasher::METADATA;
|
||||
const NAME: &'static str = Prefix::STORAGE_PREFIX;
|
||||
const DEFAULT: DefaultByteGetter =
|
||||
DefaultByteGetter(&OnEmptyGetter::<QueryKind::Query, OnEmpty>(core::marker::PhantomData));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use sp_io::{TestExternalities, hashing::twox_128};
|
||||
use crate::hash::*;
|
||||
use crate::storage::types::ValueQuery;
|
||||
use frame_metadata::StorageEntryModifier;
|
||||
|
||||
struct Prefix;
|
||||
impl StorageInstance for Prefix {
|
||||
type Pallet = ();
|
||||
type PalletInfo = ();
|
||||
const STORAGE_PREFIX: &'static str = "foo";
|
||||
}
|
||||
|
||||
struct ADefault;
|
||||
impl crate::traits::Get<u32> for ADefault {
|
||||
fn get() -> u32 {
|
||||
97
|
||||
}
|
||||
}
|
||||
|
||||
#[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!(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!(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);
|
||||
|
||||
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);
|
||||
A::remove_all();
|
||||
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)]);
|
||||
|
||||
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
|
||||
assert_eq!(A::HASHER, frame_metadata::StorageHasher::Blake2_128Concat);
|
||||
assert_eq!(
|
||||
AValueQueryWithAnOnEmpty::HASHER,
|
||||
frame_metadata::StorageHasher::Blake2_128Concat
|
||||
);
|
||||
assert_eq!(A::NAME, "foo");
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::DEFAULT.0.default_byte(), 97u32.encode());
|
||||
assert_eq!(A::DEFAULT.0.default_byte(), Option::<u32>::None.encode());
|
||||
|
||||
WithLen::remove_all();
|
||||
assert_eq!(WithLen::decode_len(3), None);
|
||||
WithLen::append(0, 10);
|
||||
assert_eq!(WithLen::decode_len(0), Some(1));
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2020 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 codec::FullCodec;
|
||||
use frame_metadata::{DefaultByte, StorageEntryModifier};
|
||||
|
||||
mod value;
|
||||
mod map;
|
||||
mod double_map;
|
||||
|
||||
pub use value::{StorageValue, StorageValueMetadata};
|
||||
pub use map::{StorageMap, StorageMapMetadata};
|
||||
pub use double_map::{StorageDoubleMap, StorageDoubleMapMetadata};
|
||||
|
||||
/// Trait implementing how the storage optional value is converted into the queried type.
|
||||
///
|
||||
/// It is implemented by:
|
||||
/// * `OptionQuery` which convert an optional value to an optional value, user when querying
|
||||
/// storage will get an optional value.
|
||||
/// * `ValueQuery` which convert an optional value to a value, user when querying storage will get
|
||||
/// a value.
|
||||
pub trait QueryKindTrait<Value, OnEmpty> {
|
||||
/// Metadata for the storage kind.
|
||||
const METADATA: StorageEntryModifier;
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
|
||||
/// Implement QueryKindTrait with query being `Option<Value>`
|
||||
///
|
||||
/// 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: StorageEntryModifier = StorageEntryModifier::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
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement QueryKindTrait with query being `Value`
|
||||
pub struct ValueQuery;
|
||||
impl<Value, OnEmpty> QueryKindTrait<Value, OnEmpty> for ValueQuery
|
||||
where
|
||||
Value: FullCodec + 'static,
|
||||
OnEmpty: crate::traits::Get<Value>,
|
||||
{
|
||||
const METADATA: StorageEntryModifier = StorageEntryModifier::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)
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper struct which implements DefaultByte using `Get<Value>` and encode it.
|
||||
struct OnEmptyGetter<Value, OnEmpty>(core::marker::PhantomData<(Value, OnEmpty)>);
|
||||
impl<Value: FullCodec, OnEmpty: crate::traits::Get<Value>> DefaultByte
|
||||
for OnEmptyGetter<Value, OnEmpty>
|
||||
{
|
||||
fn default_byte(&self) -> sp_std::vec::Vec<u8> {
|
||||
OnEmpty::get().encode()
|
||||
}
|
||||
}
|
||||
unsafe impl <Value, OnEmpty: crate::traits::Get<Value>> Send for OnEmptyGetter<Value, OnEmpty> {}
|
||||
unsafe impl <Value, OnEmpty: crate::traits::Get<Value>> Sync for OnEmptyGetter<Value, OnEmpty> {}
|
||||
@@ -0,0 +1,282 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2020 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 codec::{FullCodec, Decode, EncodeLike, Encode};
|
||||
use crate::{
|
||||
storage::{
|
||||
StorageAppend, StorageDecodeLength,
|
||||
types::{OptionQuery, QueryKindTrait, OnEmptyGetter},
|
||||
},
|
||||
traits::{GetDefault, StorageInstance},
|
||||
};
|
||||
use frame_metadata::{DefaultByteGetter, StorageEntryModifier};
|
||||
|
||||
/// A type that allow to store a value.
|
||||
///
|
||||
/// Each value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(<Prefix::Pallet as PalletInfo>::name()) ++ Twox128(Prefix::STORAGE_PREFIX)
|
||||
/// ```
|
||||
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: crate::traits::Get<QueryKind::Query> + 'static,
|
||||
{
|
||||
type Query = QueryKind::Query;
|
||||
fn module_prefix() -> &'static [u8] {
|
||||
<Prefix::PalletInfo as crate::traits::PalletInfo>::name::<Prefix::Pallet>()
|
||||
.expect("Every active pallet has a name in the runtime; qed").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)
|
||||
}
|
||||
}
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
/// 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 completly
|
||||
/// ignored by this function.
|
||||
pub fn decode_len() -> Option<usize> where Value: StorageDecodeLength {
|
||||
<Self as crate::storage::StorageValue<Value>>::decode_len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Part of storage metadata for storage value.
|
||||
pub trait StorageValueMetadata {
|
||||
const MODIFIER: StorageEntryModifier;
|
||||
const NAME: &'static str;
|
||||
const DEFAULT: DefaultByteGetter;
|
||||
}
|
||||
|
||||
impl<Prefix, Value, QueryKind, OnEmpty> StorageValueMetadata
|
||||
for StorageValue<Prefix, Value, QueryKind, OnEmpty> where
|
||||
Prefix: StorageInstance,
|
||||
Value: FullCodec,
|
||||
QueryKind: QueryKindTrait<Value, OnEmpty>,
|
||||
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
|
||||
{
|
||||
const MODIFIER: StorageEntryModifier = QueryKind::METADATA;
|
||||
const NAME: &'static str = Prefix::STORAGE_PREFIX;
|
||||
const DEFAULT: DefaultByteGetter =
|
||||
DefaultByteGetter(&OnEmptyGetter::<QueryKind::Query, OnEmpty>(core::marker::PhantomData));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use sp_io::{TestExternalities, hashing::twox_128};
|
||||
use crate::storage::types::ValueQuery;
|
||||
use frame_metadata::StorageEntryModifier;
|
||||
|
||||
struct Prefix;
|
||||
impl StorageInstance for Prefix {
|
||||
type Pallet = ();
|
||||
type PalletInfo = ();
|
||||
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(()));
|
||||
|
||||
assert_eq!(A::MODIFIER, StorageEntryModifier::Optional);
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::MODIFIER, StorageEntryModifier::Default);
|
||||
assert_eq!(A::NAME, "foo");
|
||||
assert_eq!(A::DEFAULT.0.default_byte(), Option::<u32>::None.encode());
|
||||
assert_eq!(AValueQueryWithAnOnEmpty::DEFAULT.0.default_byte(), 97u32.encode());
|
||||
|
||||
WithLen::kill();
|
||||
assert_eq!(WithLen::decode_len(), None);
|
||||
WithLen::append(3);
|
||||
assert_eq!(WithLen::decode_len(), Some(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1724,6 +1724,24 @@ pub trait Instance: 'static {
|
||||
const PREFIX: &'static str;
|
||||
}
|
||||
|
||||
/// An instance of a storage.
|
||||
///
|
||||
/// It is required the the couple `(PalletInfo::name<Pallet>(), STORAGE_PREFIX)` is unique.
|
||||
/// Any storage with same couple will collide.
|
||||
pub trait StorageInstance {
|
||||
type Pallet: 'static;
|
||||
type PalletInfo: PalletInfo;
|
||||
const STORAGE_PREFIX: &'static str;
|
||||
}
|
||||
|
||||
/// Implement Get by returning Default for any type that implements Default.
|
||||
pub struct GetDefault;
|
||||
impl<T: Default> crate::traits::Get<T> for GetDefault {
|
||||
fn get() -> T {
|
||||
T::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait similar to `Convert` to convert values from `B` an abstract balance type
|
||||
/// into u64 and back from u128. (This conversion is used in election and other places where complex
|
||||
/// calculation over balance type is needed)
|
||||
|
||||
Reference in New Issue
Block a user