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:
Guillaume Thiolliere
2020-11-03 11:08:36 +01:00
committed by GitHub
parent 7fcf0ff610
commit ff6b603d69
7 changed files with 1501 additions and 0 deletions
+8
View File
@@ -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));
});
}
}
+18
View File
@@ -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)