mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 04:41:02 +00:00
Rename Palette to FRAME (#4182)
* palette -> frame * PALETTE, Palette -> FRAME * Move folder pallete -> frame * Update docs/Structure.adoc Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com> * Update docs/README.adoc Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com> * Update README.adoc
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Operation on runtime child storages.
|
||||
//!
|
||||
//! This module is a currently only a variant of unhashed with additional `storage_key`.
|
||||
//! Note that `storage_key` must be unique and strong (strong in the sense of being long enough to
|
||||
//! avoid collision from a resistant hash function (which unique implies)).
|
||||
// NOTE: could replace unhashed by having only one kind of storage (root being null storage key (storage_key can become Option<&[u8]>).
|
||||
|
||||
use crate::rstd::prelude::*;
|
||||
use codec::{Codec, Encode, Decode};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T: Decode + Sized>(storage_key: &[u8], key: &[u8]) -> Option<T> {
|
||||
runtime_io::storage::child_get(storage_key, key).and_then(|v| {
|
||||
Decode::decode(&mut &v[..]).map(Some).unwrap_or_else(|_| {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!("ERROR: Corrupted state in child trie at {:?}/{:?}", storage_key, key);
|
||||
None
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or the type's default if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_default<T: Decode + Sized + Default>(storage_key: &[u8], key: &[u8]) -> T {
|
||||
get(storage_key, key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or<T: Decode + Sized>(storage_key: &[u8], key: &[u8], default_value: T) -> T {
|
||||
get(storage_key, key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_else<T: Decode + Sized, F: FnOnce() -> T>(
|
||||
storage_key: &[u8],
|
||||
key: &[u8],
|
||||
default_value: F,
|
||||
) -> T {
|
||||
get(storage_key, key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T: Encode>(storage_key: &[u8], key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::storage::child_set(storage_key, key, slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Decode + Sized>(storage_key: &[u8], key: &[u8]) -> Option<T> {
|
||||
let r = get(storage_key, key);
|
||||
if r.is_some() {
|
||||
kill(storage_key, key);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
|
||||
/// the default for its type.
|
||||
pub fn take_or_default<T: Codec + Sized + Default>(storage_key: &[u8], key: &[u8]) -> T {
|
||||
take(storage_key, key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or<T: Codec + Sized>(storage_key: &[u8],key: &[u8], default_value: T) -> T {
|
||||
take(storage_key, key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or_else<T: Codec + Sized, F: FnOnce() -> T>(
|
||||
storage_key: &[u8],
|
||||
key: &[u8],
|
||||
default_value: F,
|
||||
) -> T {
|
||||
take(storage_key, key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Check to see if `key` has an explicit entry in storage.
|
||||
pub fn exists(storage_key: &[u8], key: &[u8]) -> bool {
|
||||
runtime_io::storage::child_read(storage_key, key, &mut [0;0][..], 0).is_some()
|
||||
}
|
||||
|
||||
/// Remove all `storage_key` key/values
|
||||
pub fn kill_storage(storage_key: &[u8]) {
|
||||
runtime_io::storage::child_storage_kill(storage_key)
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill(storage_key: &[u8], key: &[u8]) {
|
||||
runtime_io::storage::child_clear(storage_key, key);
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw(storage_key: &[u8], key: &[u8]) -> Option<Vec<u8>> {
|
||||
runtime_io::storage::child_get(storage_key, key)
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw(storage_key: &[u8], key: &[u8], value: &[u8]) {
|
||||
runtime_io::storage::child_set(storage_key, key, value)
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use codec::{Ref, FullCodec, FullEncode, Encode, EncodeLike, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::StorageHasher};
|
||||
|
||||
/// Generator for `StorageDoubleMap` used by `decl_storage`.
|
||||
///
|
||||
/// # Mapping of keys to a storage path
|
||||
///
|
||||
/// The storage key (i.e. the key under which the `Value` will be stored) is created from two parts.
|
||||
/// The first part is a hash of a concatenation of the `key1_prefix` and `Key1`. And the second part
|
||||
/// is a hash of a `Key2`.
|
||||
///
|
||||
/// Thus value for (key1, key2) is stored at:
|
||||
/// ```nocompile
|
||||
/// Hasher1(key1_prefix ++ key1) ++ Hasher2(key2)
|
||||
/// ```
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the key1s are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used for Hasher1. Otherwise, other values in storage can be compromised.
|
||||
/// If the key2s are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used for Hasher2. Otherwise, other items in storage with the same first
|
||||
/// key can be compromised.
|
||||
pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Hasher for the first key.
|
||||
type Hasher1: StorageHasher;
|
||||
|
||||
/// Hasher for the second key.
|
||||
type Hasher2: StorageHasher;
|
||||
|
||||
/// Get the prefix for first key.
|
||||
fn key1_prefix() -> &'static [u8];
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the first part of the key used in top storage.
|
||||
fn storage_double_map_final_key1<KArg1>(k1: KArg1) -> <Self::Hasher1 as StorageHasher>::Output
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
{
|
||||
let mut final_key1 = Self::key1_prefix().to_vec();
|
||||
k1.encode_to(&mut final_key1);
|
||||
Self::Hasher1::hash(&final_key1)
|
||||
}
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_double_map_final_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
let mut final_key = Self::storage_double_map_final_key1(k1).as_ref().to_vec();
|
||||
final_key.extend_from_slice(k2.using_encoded(Self::Hasher2::hash).as_ref());
|
||||
final_key
|
||||
}
|
||||
}
|
||||
|
||||
impl<K1, K2, V, G> storage::StorageDoubleMap<K1, K2, V> for G
|
||||
where
|
||||
K1: FullEncode,
|
||||
K2: FullEncode,
|
||||
V: FullCodec,
|
||||
G: StorageDoubleMap<K1, K2, V>,
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
Self::storage_double_map_final_key(k1, k2)
|
||||
}
|
||||
|
||||
fn exists<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
unhashed::exists(&Self::storage_double_map_final_key(k1, k2))
|
||||
}
|
||||
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
G::from_optional_value_to_query(unhashed::get(&Self::storage_double_map_final_key(k1, k2)))
|
||||
}
|
||||
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(k1, k2);
|
||||
|
||||
let value = unhashed::take(&final_key);
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
VArg: EncodeLike<V>,
|
||||
{
|
||||
unhashed::put(&Self::storage_double_map_final_key(k1, k2), &val.borrow())
|
||||
}
|
||||
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
{
|
||||
unhashed::kill(&Self::storage_double_map_final_key(k1, k2))
|
||||
}
|
||||
|
||||
fn remove_prefix<KArg1>(k1: KArg1) where KArg1: EncodeLike<K1> {
|
||||
unhashed::kill_prefix(Self::storage_double_map_final_key1(k1).as_ref())
|
||||
}
|
||||
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
F: FnOnce(&mut Self::Query) -> R,
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(k1, k2);
|
||||
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), val),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn append<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator
|
||||
{
|
||||
let final_key = Self::storage_double_map_final_key(k1, k2);
|
||||
|
||||
let encoded_value = unhashed::get_raw(&final_key)
|
||||
.unwrap_or_else(|| {
|
||||
match G::from_query_to_optional_value(G::from_optional_value_to_query(None)) {
|
||||
Some(value) => value.encode(),
|
||||
None => vec![],
|
||||
}
|
||||
});
|
||||
|
||||
let new_val = V::append_or_new(
|
||||
encoded_value,
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
unhashed::put_raw(&final_key, &new_val);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator
|
||||
{
|
||||
Self::append(Ref::from(&k1), Ref::from(&k2), items.clone())
|
||||
.unwrap_or_else(|_| Self::insert(k1, k2, items));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,497 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{FullCodec, Encode, Decode, EncodeLike, Ref};
|
||||
use crate::{storage::{self, unhashed}, hash::StorageHasher, traits::Len};
|
||||
use rstd::marker::PhantomData;
|
||||
|
||||
/// Generator for `StorageLinkedMap` used by `decl_storage`.
|
||||
///
|
||||
/// # Mapping of keys to a storage path
|
||||
///
|
||||
/// The key for the head of the map is stored at one fixed path:
|
||||
/// ```nocompile
|
||||
/// Hasher(head_key)
|
||||
/// ```
|
||||
///
|
||||
/// For each key, the value stored under that key is appended with a
|
||||
/// [`Linkage`](struct.Linkage.html) (which hold previous and next key) at the path:
|
||||
/// ```nocompile
|
||||
/// Hasher(prefix ++ key)
|
||||
/// ```
|
||||
///
|
||||
/// Enumeration is done by getting the head of the linked map and then iterating getting the
|
||||
/// value and linkage stored at the key until the found linkage has no next key.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used. Otherwise, other values in storage can be compromised.
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Hasher used to insert into storage.
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// The family of key formats used for this map.
|
||||
type KeyFormat: KeyFormat<Hasher=Self::Hasher>;
|
||||
|
||||
/// Prefix used to prepend each key.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// The head key of the linked-map.
|
||||
fn head_key() -> &'static [u8] {
|
||||
<Self::KeyFormat as KeyFormat>::head_key()
|
||||
}
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<KeyArg>(key: KeyArg) -> <Self::Hasher as StorageHasher>::Output
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_key::<KeyArg>(Self::prefix(), &key)
|
||||
}
|
||||
|
||||
/// Generate the hashed key for head
|
||||
fn storage_linked_map_final_head_key() -> <Self::Hasher as StorageHasher>::Output {
|
||||
<Self::KeyFormat as KeyFormat>::storage_linked_map_final_head_key()
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-abstracted key format used for a family of linked-map types.
|
||||
pub trait KeyFormat {
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// Key used to store linked map head.
|
||||
fn head_key() -> &'static [u8];
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_linked_map_final_key<K>(prefix: &[u8], key: &K)
|
||||
-> <Self::Hasher as StorageHasher>::Output
|
||||
where
|
||||
K: Encode,
|
||||
{
|
||||
let mut final_key = prefix.to_vec();
|
||||
key.encode_to(&mut final_key);
|
||||
<Self::Hasher as StorageHasher>::hash(&final_key)
|
||||
}
|
||||
|
||||
fn storage_linked_map_final_head_key()
|
||||
-> <Self::Hasher as StorageHasher>::Output
|
||||
{
|
||||
<Self::Hasher as StorageHasher>::hash(Self::head_key())
|
||||
}
|
||||
}
|
||||
|
||||
/// Linkage data of an element (it's successor and predecessor)
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct Linkage<Key> {
|
||||
/// Previous element key in storage (None for the first element)
|
||||
pub previous: Option<Key>,
|
||||
/// Next element key in storage (None for the last element)
|
||||
pub next: Option<Key>,
|
||||
}
|
||||
|
||||
impl<Key> Default for Linkage<Key> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
previous: None,
|
||||
next: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encode like a linkage.
|
||||
#[derive(Encode)]
|
||||
struct EncodeLikeLinkage<PKey: EncodeLike<Key>, NKey: EncodeLike<Key>, Key: Encode> {
|
||||
// Previous element key in storage (None for the first element)
|
||||
previous: Option<PKey>,
|
||||
// Next element key in storage (None for the last element)
|
||||
next: Option<NKey>,
|
||||
// The key of the linkage this type encode to
|
||||
phantom: core::marker::PhantomData<Key>,
|
||||
}
|
||||
|
||||
/// A key-value pair iterator for enumerable map.
|
||||
pub struct Enumerator<K, V, F> {
|
||||
next: Option<K>,
|
||||
prefix: &'static [u8],
|
||||
_phantom: PhantomData<(V, F)>,
|
||||
}
|
||||
|
||||
impl<K, V, F> Enumerator<K, V, F> {
|
||||
/// Create an explicit enumerator for testing.
|
||||
#[cfg(test)]
|
||||
pub fn from_head(head: K, prefix: &'static [u8]) -> Self {
|
||||
Enumerator {
|
||||
next: Some(head),
|
||||
prefix,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, F> Iterator for Enumerator<K, V, F>
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
type Item = (K, V);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.next.take()?;
|
||||
|
||||
let (val, linkage): (V, Linkage<K>) = {
|
||||
let next_full_key = F::storage_linked_map_final_key(self.prefix, &next);
|
||||
match read_with_linkage::<K, V>(next_full_key.as_ref()) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::head_key(), next_full_key.as_ref(),
|
||||
);
|
||||
return None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.next = linkage.next;
|
||||
Some((next, val))
|
||||
}
|
||||
}
|
||||
|
||||
/// Update linkage when this element is removed.
|
||||
///
|
||||
/// Takes care of updating previous and next elements points
|
||||
/// as well as updates head if the element is first or last.
|
||||
fn remove_linkage<K, V, F>(linkage: Linkage<K>, prefix: &[u8])
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
let next_key = linkage.next.as_ref()
|
||||
.map(|k| F::storage_linked_map_final_key(prefix, k))
|
||||
.map(|x| x.as_ref().to_vec());
|
||||
let prev_key = linkage.previous.as_ref()
|
||||
.map(|k| F::storage_linked_map_final_key(prefix, k))
|
||||
.map(|x| x.as_ref().to_vec());
|
||||
|
||||
if let Some(prev_key) = prev_key {
|
||||
// Retrieve previous element and update `next`
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(prev_key.as_ref()) {
|
||||
res.1.next = linkage.next;
|
||||
unhashed::put(prev_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
previous value doesn't exist at {:?}",
|
||||
F::head_key(), prev_key,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// we were first so let's update the head
|
||||
write_head::<&K, K, F>(linkage.next.as_ref());
|
||||
}
|
||||
if let Some(next_key) = next_key {
|
||||
// Update previous of next element
|
||||
if let Some(mut res) = read_with_linkage::<K, V>(next_key.as_ref()) {
|
||||
res.1.previous = linkage.previous;
|
||||
unhashed::put(next_key.as_ref(), &res);
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
next value doesn't exist at {:?}",
|
||||
F::head_key(), next_key,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the contained data and its linkage.
|
||||
pub(super) fn read_with_linkage<K, V>(key: &[u8]) -> Option<(V, Linkage<K>)>
|
||||
where
|
||||
K: Decode,
|
||||
V: Decode,
|
||||
{
|
||||
unhashed::get(key)
|
||||
}
|
||||
|
||||
/// Generate linkage for newly inserted element.
|
||||
///
|
||||
/// Takes care of updating head and previous head's pointer.
|
||||
pub(super) fn new_head_linkage<KeyArg, K, V, F>(key: KeyArg, prefix: &[u8]) -> Linkage<K>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
if let Some(head) = read_head::<K, F>() {
|
||||
// update previous head predecessor
|
||||
{
|
||||
let head_key = F::storage_linked_map_final_key(prefix, &head);
|
||||
if let Some((data, linkage)) = read_with_linkage::<K, V>(head_key.as_ref()) {
|
||||
let new_linkage = EncodeLikeLinkage::<_, _, K> {
|
||||
previous: Some(Ref::from(&key)),
|
||||
next: linkage.next.as_ref(),
|
||||
phantom: Default::default(),
|
||||
};
|
||||
unhashed::put(head_key.as_ref(), &(data, new_linkage));
|
||||
} else {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!(
|
||||
"ERROR: Corrupted state: linked map head_key={:?}: \
|
||||
head value doesn't exist at {:?}",
|
||||
F::head_key(), head_key.as_ref(),
|
||||
);
|
||||
// Thus we consider we are first - update the head and produce empty linkage
|
||||
|
||||
write_head::<_, _, F>(Some(key));
|
||||
return Linkage::default();
|
||||
}
|
||||
}
|
||||
// update to current head
|
||||
write_head::<_, _, F>(Some(key));
|
||||
// return linkage with pointer to previous head
|
||||
let mut linkage = Linkage::default();
|
||||
linkage.next = Some(head);
|
||||
linkage
|
||||
} else {
|
||||
// we are first - update the head and produce empty linkage
|
||||
write_head::<_, _, F>(Some(key));
|
||||
Linkage::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Read current head pointer.
|
||||
pub(crate) fn read_head<K, F>() -> Option<K>
|
||||
where
|
||||
K: Decode,
|
||||
F: KeyFormat,
|
||||
{
|
||||
unhashed::get(F::storage_linked_map_final_head_key().as_ref())
|
||||
}
|
||||
|
||||
/// Overwrite current head pointer.
|
||||
///
|
||||
/// If `None` is given head is removed from storage.
|
||||
pub(super) fn write_head<KeyArg, K, F>(head: Option<KeyArg>)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
K: FullCodec,
|
||||
F: KeyFormat,
|
||||
{
|
||||
match head.as_ref() {
|
||||
Some(head) => unhashed::put(F::storage_linked_map_final_head_key().as_ref(), head),
|
||||
None => unhashed::kill(F::storage_linked_map_final_head_key().as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, G> storage::StorageLinkedMap<K, V> for G
|
||||
where
|
||||
K: FullCodec,
|
||||
V: FullCodec,
|
||||
G: StorageLinkedMap<K, V>,
|
||||
{
|
||||
type Query = G::Query;
|
||||
|
||||
type Enumerator = Enumerator<K, V, G::KeyFormat>;
|
||||
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
||||
unhashed::exists(Self::storage_linked_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let val = unhashed::get(Self::storage_linked_map_final_key(key).as_ref());
|
||||
G::from_optional_value_to_query(val)
|
||||
}
|
||||
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
||||
let prefix = Self::prefix();
|
||||
let final_key1 = Self::storage_linked_map_final_key(Ref::from(&key1));
|
||||
let final_key2 = Self::storage_linked_map_final_key(Ref::from(&key2));
|
||||
let full_value_1 = read_with_linkage::<K, V>(final_key1.as_ref());
|
||||
let full_value_2 = read_with_linkage::<K, V>(final_key2.as_ref());
|
||||
|
||||
match (full_value_1, full_value_2) {
|
||||
// Just keep linkage in order and only swap values.
|
||||
(Some((value1, linkage1)), Some((value2, linkage2))) => {
|
||||
unhashed::put(final_key1.as_ref(), &(value2, linkage1));
|
||||
unhashed::put(final_key2.as_ref(), &(value1, linkage2));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(Some((value, _linkage)), None) => {
|
||||
Self::remove(key1);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key2, prefix);
|
||||
unhashed::put(final_key2.as_ref(), &(value, linkage));
|
||||
}
|
||||
// Remove key and insert the new one.
|
||||
(None, Some((value, _linkage))) => {
|
||||
Self::remove(key2);
|
||||
let linkage = new_head_linkage::<_, _, V, G::KeyFormat>(key1, prefix);
|
||||
unhashed::put(final_key1.as_ref(), &(value, linkage));
|
||||
}
|
||||
// No-op.
|
||||
(None, None) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
let linkage = match read_with_linkage::<_, V>(final_key.as_ref()) {
|
||||
// overwrite but reuse existing linkage
|
||||
Some((_data, linkage)) => linkage,
|
||||
// create new linkage
|
||||
None => new_head_linkage::<_, _, V, G::KeyFormat>(key, Self::prefix()),
|
||||
};
|
||||
unhashed::put(final_key.as_ref(), &(val, linkage))
|
||||
}
|
||||
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
|
||||
G::take(key);
|
||||
}
|
||||
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
||||
let final_key = Self::storage_linked_map_final_key(Ref::from(&key));
|
||||
|
||||
let (mut val, _linkage) = read_with_linkage::<K, V>(final_key.as_ref())
|
||||
.map(|(data, linkage)| (G::from_optional_value_to_query(Some(data)), Some(linkage)))
|
||||
.unwrap_or_else(|| (G::from_optional_value_to_query(None), None));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => G::insert(key, val),
|
||||
None => G::remove(key),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let final_key = Self::storage_linked_map_final_key(key);
|
||||
|
||||
let full_value: Option<(V, Linkage<K>)> = unhashed::take(final_key.as_ref());
|
||||
|
||||
let value = full_value.map(|(data, linkage)| {
|
||||
remove_linkage::<K, V, G::KeyFormat>(linkage, Self::prefix());
|
||||
data
|
||||
});
|
||||
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn enumerate() -> Self::Enumerator {
|
||||
Enumerator::<_, _, G::KeyFormat> {
|
||||
next: read_head::<_, G::KeyFormat>(),
|
||||
prefix: Self::prefix(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn head() -> Option<K> {
|
||||
read_head::<_, G::KeyFormat>()
|
||||
}
|
||||
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len
|
||||
{
|
||||
let key = Self::storage_linked_map_final_key(key);
|
||||
if let Some(v) = unhashed::get_raw(key.as_ref()) {
|
||||
<V as codec::DecodeLength>::len(&v).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V
|
||||
{
|
||||
let head_key = read_head::<K2, G::KeyFormat>().ok_or(None)?;
|
||||
let prefix = G::prefix();
|
||||
|
||||
let mut last_key = None;
|
||||
let mut current_key = head_key.clone();
|
||||
|
||||
write_head::<&K, K, G::KeyFormat>(Some(&translate_key(head_key)));
|
||||
|
||||
let translate_linkage = |old: Linkage<K2>| -> Linkage<K> {
|
||||
Linkage {
|
||||
previous: old.previous.map(&translate_key),
|
||||
next: old.next.map(&translate_key),
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let old_raw_key = G::KeyFormat::storage_linked_map_final_key(prefix, ¤t_key);
|
||||
let x = unhashed::take(old_raw_key.as_ref());
|
||||
let (val, linkage): (V2, Linkage<K2>) = match x {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
// we failed to read value and linkage. Update the last key's linkage
|
||||
// to end the map early, since it's impossible to iterate further.
|
||||
if let Some(last_key) = last_key {
|
||||
let last_raw_key = G::storage_linked_map_final_key(&last_key);
|
||||
if let Some((val, mut linkage))
|
||||
= read_with_linkage::<K, V>(last_raw_key.as_ref())
|
||||
{
|
||||
// defensive: should always happen, since it was just written
|
||||
// in the last iteration of the loop.
|
||||
linkage.next = None;
|
||||
unhashed::put(last_raw_key.as_ref(), &(&val, &linkage));
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Some(current_key));
|
||||
}
|
||||
};
|
||||
let next = linkage.next.clone();
|
||||
|
||||
let val = translate_val(val);
|
||||
let linkage = translate_linkage(linkage);
|
||||
|
||||
// and write in the value and linkage under the new key.
|
||||
let new_key = translate_key(current_key.clone());
|
||||
let new_raw_key = G::storage_linked_map_final_key(&new_key);
|
||||
unhashed::put(new_raw_key.as_ref(), &(&val, &linkage));
|
||||
|
||||
match next {
|
||||
None => break,
|
||||
Some(next) => {
|
||||
last_key = Some(new_key);
|
||||
current_key = next
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use rstd::prelude::*;
|
||||
use rstd::borrow::Borrow;
|
||||
use codec::{FullCodec, FullEncode, Encode, EncodeLike, Ref, EncodeAppend};
|
||||
use crate::{storage::{self, unhashed}, hash::StorageHasher, traits::Len};
|
||||
|
||||
/// Generator for `StorageMap` used by `decl_storage`.
|
||||
///
|
||||
/// For each key value is stored at:
|
||||
/// ```nocompile
|
||||
/// Hasher(prefix ++ key)
|
||||
/// ```
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// If the keys are not trusted (e.g. can be set by a user), a cryptographic `hasher` such as
|
||||
/// `blake2_256` must be used. Otherwise, other values in storage can be compromised.
|
||||
pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Hasher used to insert into storage.
|
||||
type Hasher: StorageHasher;
|
||||
|
||||
/// Prefix used to prepend each key.
|
||||
fn prefix() -> &'static [u8];
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<V>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<V>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_map_final_key<KeyArg>(key: KeyArg) -> <Self::Hasher as StorageHasher>::Output
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
{
|
||||
let mut final_key = Self::prefix().to_vec();
|
||||
key.borrow().encode_to(&mut final_key);
|
||||
Self::Hasher::hash(&final_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V> for G {
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key_for<KeyArg: EncodeLike<K>>(key: KeyArg) -> Vec<u8> {
|
||||
Self::storage_map_final_key(key).as_ref().to_vec()
|
||||
}
|
||||
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2) {
|
||||
let k1 = Self::storage_map_final_key(key1);
|
||||
let k2 = Self::storage_map_final_key(key2);
|
||||
|
||||
let v1 = unhashed::get_raw(k1.as_ref());
|
||||
if let Some(val) = unhashed::get_raw(k2.as_ref()) {
|
||||
unhashed::put_raw(k1.as_ref(), &val);
|
||||
} else {
|
||||
unhashed::kill(k1.as_ref())
|
||||
}
|
||||
if let Some(val) = v1 {
|
||||
unhashed::put_raw(k2.as_ref(), &val);
|
||||
} else {
|
||||
unhashed::kill(k2.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool {
|
||||
unhashed::exists(Self::storage_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
G::from_optional_value_to_query(unhashed::get(Self::storage_map_final_key(key).as_ref()))
|
||||
}
|
||||
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||
unhashed::put(Self::storage_map_final_key(key).as_ref(), &val.borrow())
|
||||
}
|
||||
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
|
||||
unhashed::kill(Self::storage_map_final_key(key).as_ref())
|
||||
}
|
||||
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R {
|
||||
let final_key = Self::storage_map_final_key(key);
|
||||
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let key = Self::storage_map_final_key(key);
|
||||
let value = unhashed::take(key.as_ref());
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn append<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let key = Self::storage_map_final_key(key);
|
||||
let encoded_value = unhashed::get_raw(key.as_ref())
|
||||
.unwrap_or_else(|| {
|
||||
match G::from_query_to_optional_value(G::from_optional_value_to_query(None)) {
|
||||
Some(value) => value.encode(),
|
||||
None => vec![],
|
||||
}
|
||||
});
|
||||
|
||||
let new_val = V::append_or_new(
|
||||
encoded_value,
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
unhashed::put_raw(key.as_ref(), &new_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
Self::append(Ref::from(&key), items.clone())
|
||||
.unwrap_or_else(|_| Self::insert(key, items));
|
||||
}
|
||||
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len
|
||||
{
|
||||
let key = Self::storage_map_final_key(key);
|
||||
if let Some(v) = unhashed::get_raw(key.as_ref()) {
|
||||
<V as codec::DecodeLength>::len(&v).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Generators are a set of trait on which storage traits are implemented.
|
||||
//!
|
||||
//! (i.e. implementing the generator for StorageValue on a type will automatically derive the
|
||||
//! implementation of StorageValue for this type).
|
||||
//!
|
||||
//! They are used by `decl_storage`.
|
||||
|
||||
mod linked_map;
|
||||
mod map;
|
||||
mod double_map;
|
||||
mod value;
|
||||
|
||||
pub use linked_map::{StorageLinkedMap, Enumerator, Linkage, KeyFormat as LinkedMapKeyFormat};
|
||||
pub use map::StorageMap;
|
||||
pub use double_map::StorageDoubleMap;
|
||||
pub use value::StorageValue;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code)]
|
||||
mod tests {
|
||||
use runtime_io::TestExternalities;
|
||||
use codec::{Encode, Decode};
|
||||
use crate::storage::{unhashed, generator::{StorageValue, StorageLinkedMap}};
|
||||
|
||||
struct Runtime {}
|
||||
pub trait Trait {
|
||||
type Origin;
|
||||
type BlockNumber;
|
||||
}
|
||||
|
||||
impl Trait for Runtime {
|
||||
type Origin = u32;
|
||||
type BlockNumber = u32;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)]
|
||||
struct NumberNumber {
|
||||
a: u32,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
crate::decl_storage! {
|
||||
trait Store for Module<T: Trait> as Runtime {
|
||||
Value get(fn value) config(): (u64, u64);
|
||||
NumberMap: linked_map NumberNumber => u64;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_translate_works() {
|
||||
let t = GenesisConfig::default().build_storage().unwrap();
|
||||
TestExternalities::new(t).execute_with(|| {
|
||||
// put the old value `1111u32` in the storage.
|
||||
let key = Value::storage_value_final_key();
|
||||
unhashed::put_raw(&key, &1111u32.encode());
|
||||
|
||||
// translate
|
||||
let translate_fn = |old: Option<u32>| -> Option<(u64, u64)> {
|
||||
old.map(|o| (o.into(), (o*2).into()))
|
||||
};
|
||||
let _ = Value::translate(translate_fn);
|
||||
|
||||
// new storage should be `(1111, 1111 * 2)`
|
||||
assert_eq!(Value::get(), (1111, 2222));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_map_translate_works() {
|
||||
use super::linked_map::{self, Enumerator, KeyFormat};
|
||||
|
||||
type Format = <NumberMap as StorageLinkedMap<NumberNumber, u64>>::KeyFormat;
|
||||
|
||||
let t = GenesisConfig::default().build_storage().unwrap();
|
||||
TestExternalities::new(t).execute_with(|| {
|
||||
let prefix = NumberMap::prefix();
|
||||
|
||||
// start with a map of u32 -> u32.
|
||||
for i in 0u32..100u32 {
|
||||
let final_key = <Format as KeyFormat>::storage_linked_map_final_key(
|
||||
prefix, &i,
|
||||
);
|
||||
|
||||
let linkage = linked_map::new_head_linkage::<_, u32, u32, Format>(&i, prefix);
|
||||
unhashed::put(final_key.as_ref(), &(&i, linkage));
|
||||
}
|
||||
|
||||
let head = linked_map::read_head::<u32, Format>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Enumerator::<u32, u32, Format>::from_head(head, prefix).collect::<Vec<_>>(),
|
||||
(0..100).rev().map(|x| (x, x)).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// do translation.
|
||||
NumberMap::translate(
|
||||
|k: u32| NumberNumber { a: k, b: k },
|
||||
|v: u32| (v as u64) << 32 | v as u64,
|
||||
).unwrap();
|
||||
|
||||
assert!(linked_map::read_head::<NumberNumber, Format>().is_some());
|
||||
assert_eq!(
|
||||
NumberMap::enumerate().collect::<Vec<_>>(),
|
||||
(0..100u32).rev().map(|x| (
|
||||
NumberNumber { a: x, b: x },
|
||||
(x as u64) << 32 | x as u64,
|
||||
)).collect::<Vec<_>>(),
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use rstd::prelude::*;
|
||||
use codec::{FullCodec, Encode, EncodeAppend, EncodeLike, Decode};
|
||||
use crate::{storage::{self, unhashed}, hash::{Twox128, StorageHasher}, traits::Len};
|
||||
|
||||
/// Generator for `StorageValue` used by `decl_storage`.
|
||||
///
|
||||
/// Value is stored at:
|
||||
/// ```nocompile
|
||||
/// Twox128(unhashed_key)
|
||||
/// ```
|
||||
pub trait StorageValue<T: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
/// Unhashed key used in storage
|
||||
fn unhashed_key() -> &'static [u8];
|
||||
|
||||
/// Convert an optional value retrieved from storage to the type queried.
|
||||
fn from_optional_value_to_query(v: Option<T>) -> Self::Query;
|
||||
|
||||
/// Convert a query to an optional value into storage.
|
||||
fn from_query_to_optional_value(v: Self::Query) -> Option<T>;
|
||||
|
||||
/// Generate the full key used in top storage.
|
||||
fn storage_value_final_key() -> [u8; 16] {
|
||||
Twox128::hash(Self::unhashed_key())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FullCodec, G: StorageValue<T>> storage::StorageValue<T> for G {
|
||||
type Query = G::Query;
|
||||
|
||||
fn hashed_key() -> [u8; 16] {
|
||||
Self::storage_value_final_key()
|
||||
}
|
||||
|
||||
fn exists() -> bool {
|
||||
unhashed::exists(&Self::storage_value_final_key())
|
||||
}
|
||||
|
||||
fn get() -> Self::Query {
|
||||
let value = unhashed::get(&Self::storage_value_final_key());
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()> {
|
||||
let key = Self::storage_value_final_key();
|
||||
|
||||
// attempt to get the length directly.
|
||||
let maybe_old = match unhashed::get_raw(&key) {
|
||||
Some(old_data) => Some(O::decode(&mut &old_data[..]).map_err(|_| ())?),
|
||||
None => None,
|
||||
};
|
||||
let maybe_new = f(maybe_old);
|
||||
if let Some(new) = maybe_new.as_ref() {
|
||||
new.using_encoded(|d| unhashed::put_raw(&key, d));
|
||||
} else {
|
||||
unhashed::kill(&key);
|
||||
}
|
||||
Ok(maybe_new)
|
||||
}
|
||||
|
||||
fn put<Arg: EncodeLike<T>>(val: Arg) {
|
||||
unhashed::put(&Self::storage_value_final_key(), &val)
|
||||
}
|
||||
|
||||
fn kill() {
|
||||
unhashed::kill(&Self::storage_value_final_key())
|
||||
}
|
||||
|
||||
fn mutate<R, F: FnOnce(&mut G::Query) -> R>(f: F) -> R {
|
||||
let mut val = G::get();
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => G::put(val),
|
||||
None => G::kill(),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take() -> G::Query {
|
||||
let key = Self::storage_value_final_key();
|
||||
let value = unhashed::get(&key);
|
||||
if value.is_some() {
|
||||
unhashed::kill(&key)
|
||||
}
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<Items, Item, EncodeLikeItem>(items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
let key = Self::storage_value_final_key();
|
||||
let encoded_value = unhashed::get_raw(&key)
|
||||
.unwrap_or_else(|| {
|
||||
match G::from_query_to_optional_value(G::from_optional_value_to_query(None)) {
|
||||
Some(value) => value.encode(),
|
||||
None => vec![],
|
||||
}
|
||||
});
|
||||
|
||||
let new_val = T::append_or_new(
|
||||
encoded_value,
|
||||
items,
|
||||
).map_err(|_| "Could not append given item")?;
|
||||
unhashed::put_raw(&key, &new_val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely append the given items to the value in the storage. If a codec error occurs, then the
|
||||
/// old (presumably corrupt) value is replaced with the given `items`.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append_or_put<Items, Item, EncodeLikeItem>(items: Items) where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<T>,
|
||||
Items::IntoIter: ExactSizeIterator
|
||||
{
|
||||
Self::append(items.clone()).unwrap_or_else(|_| Self::put(items));
|
||||
}
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len() -> Result<usize, &'static str> where T: codec::DecodeLength, T: Len {
|
||||
let key = Self::storage_value_final_key();
|
||||
|
||||
// attempt to get the length directly.
|
||||
if let Some(k) = unhashed::get_raw(&key) {
|
||||
<T as codec::DecodeLength>::len(&k).map_err(|e| e.what())
|
||||
} else {
|
||||
let len = G::from_query_to_optional_value(G::from_optional_value_to_query(None))
|
||||
.map(|v| v.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Operation on runtime storage using hashed keys.
|
||||
|
||||
use super::unhashed;
|
||||
use rstd::prelude::*;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<T>
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or the type's default if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_default<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> T
|
||||
where
|
||||
T: Decode + Sized + Default,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_or_default(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or<T, HashFn, R>(hash: &HashFn, key: &[u8], default_value: T) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_or(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_else<T, F, HashFn, R>(hash: &HashFn, key: &[u8], default_value: F) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
F: FnOnce() -> T,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_or_else(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T, HashFn, R>(hash: &HashFn, key: &[u8], value: &T)
|
||||
where
|
||||
T: Encode,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::put(&hash(key).as_ref(), value)
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<T>
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
|
||||
/// the default for its type.
|
||||
pub fn take_or_default<T, HashFn, R>(hash: &HashFn, key: &[u8]) -> T
|
||||
where
|
||||
T: Decode + Sized + Default,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take_or_default(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or<T, HashFn, R>(hash: &HashFn, key: &[u8], default_value: T) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take_or(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or_else<T, F, HashFn, R>(hash: &HashFn, key: &[u8], default_value: F) -> T
|
||||
where
|
||||
T: Decode + Sized,
|
||||
F: FnOnce() -> T,
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::take_or_else(&hash(key).as_ref(), default_value)
|
||||
}
|
||||
|
||||
/// Check to see if `key` has an explicit entry in storage.
|
||||
pub fn exists<HashFn, R>(hash: &HashFn, key: &[u8]) -> bool
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::exists(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill<HashFn, R>(hash: &HashFn, key: &[u8])
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::kill(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw<HashFn, R>(hash: &HashFn, key: &[u8]) -> Option<Vec<u8>>
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::get_raw(&hash(key).as_ref())
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw<HashFn, R>(hash: &HashFn, key: &[u8], value: &[u8])
|
||||
where
|
||||
HashFn: Fn(&[u8]) -> R,
|
||||
R: AsRef<[u8]>,
|
||||
{
|
||||
unhashed::put_raw(&hash(key).as_ref(), value)
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Stuff to do with the runtime's storage.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{FullCodec, FullEncode, Encode, EncodeAppend, EncodeLike, Decode};
|
||||
use crate::traits::Len;
|
||||
|
||||
pub mod unhashed;
|
||||
pub mod hashed;
|
||||
pub mod child;
|
||||
pub mod generator;
|
||||
|
||||
/// A trait for working with macro-generated storage values under the substrate storage API.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageValue`]
|
||||
pub trait StorageValue<T: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the storage key.
|
||||
fn hashed_key() -> [u8; 16];
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists() -> bool;
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
fn get() -> Self::Query;
|
||||
|
||||
/// 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_initialize, while
|
||||
/// ensuring **no usage of this storage are made before the call to `on_initialize`**. (More
|
||||
/// precisely prior initialized modules doesn't make use of this storage).
|
||||
fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()>;
|
||||
|
||||
/// Store a value under this key into the provided storage instance.
|
||||
fn put<Arg: EncodeLike<T>>(val: Arg);
|
||||
|
||||
/// Mutate the value
|
||||
fn mutate<R, F: FnOnce(&mut Self::Query) -> R>(f: F) -> R;
|
||||
|
||||
/// Clear the storage value.
|
||||
fn kill();
|
||||
|
||||
/// Take a value from storage, removing it afterwards.
|
||||
fn take() -> Self::Query;
|
||||
|
||||
/// Append the given item to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `codec::EncodeAppend`.
|
||||
fn append<Items, Item, EncodeLikeItem>(items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `T` is required to implement `Codec::EncodeAppend`.
|
||||
///
|
||||
/// Upon any failure, it replaces `items` as the new value (assuming that the previous stored
|
||||
/// data is simply corrupt and no longer usable).
|
||||
///
|
||||
/// ### WARNING
|
||||
///
|
||||
/// use with care; if your use-case is not _exactly_ as what this function is doing,
|
||||
/// you should use append and sensibly handle failure within the runtime code if it happens.
|
||||
fn append_or_put<Items, Item, EncodeLikeItem>(items: Items) where
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
T: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<T>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
fn decode_len() -> Result<usize, &'static str>
|
||||
where T: codec::DecodeLength + Len;
|
||||
}
|
||||
|
||||
/// A strongly-typed map in storage.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageMap`]
|
||||
pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn hashed_key_for<KeyArg: EncodeLike<K>>(key: KeyArg) -> Vec<u8>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Swap the values of two keys.
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2);
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg);
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg);
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `V` is required to implement `codec::EncodeAppend`.
|
||||
fn append<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Safely append the given items to the value in the storage. If a codec error occurs, then the
|
||||
/// old (presumably corrupt) value is replaced with the given `items`.
|
||||
///
|
||||
/// `V` is required to implement `codec::EncodeAppend`.
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items)
|
||||
where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
}
|
||||
|
||||
/// A strongly-typed linked map in storage.
|
||||
///
|
||||
/// Similar to `StorageMap` but allows to enumerate other elements and doesn't implement append.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageLinkedMap`]
|
||||
pub trait StorageLinkedMap<K: FullCodec, V: FullCodec> {
|
||||
/// The type that get/take return.
|
||||
type Query;
|
||||
|
||||
/// The type that iterates over all `(key, value)`.
|
||||
type Enumerator: Iterator<Item = (K, V)>;
|
||||
|
||||
/// Does the value (explicitly) exist in storage?
|
||||
fn exists<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool;
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Swap the values of two keys.
|
||||
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2);
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg);
|
||||
|
||||
/// Remove the value under a key.
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg);
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Return current head element.
|
||||
fn head() -> Option<K>;
|
||||
|
||||
/// Enumerate all elements in the map.
|
||||
fn enumerate() -> Self::Enumerator;
|
||||
|
||||
/// Read the length of the value in a fast way, without decoding the entire value.
|
||||
///
|
||||
/// `T` is required to implement `Codec::DecodeLength`.
|
||||
///
|
||||
/// Note that `0` is returned as the default value if no encoded value exists at the given key.
|
||||
/// Therefore, this function cannot be used as a sign of _existence_. use the `::exists()`
|
||||
/// function for this purpose.
|
||||
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Result<usize, &'static str>
|
||||
where V: codec::DecodeLength + Len;
|
||||
|
||||
/// Translate the keys and values from some previous `(K2, V2)` to the current type.
|
||||
///
|
||||
/// `TK` translates keys from the old type, and `TV` translates values.
|
||||
///
|
||||
/// Returns `Err` if the map could not be interpreted as the old type, and Ok if it could.
|
||||
/// The `Err` contains the first key which could not be migrated, or `None` if the
|
||||
/// head of the list could not be read.
|
||||
///
|
||||
/// # 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_initialize, while
|
||||
/// ensuring **no usage of this storage are made before the call to `on_initialize`**. (More
|
||||
/// precisely prior initialized modules doesn't make use of this storage).
|
||||
fn translate<K2, V2, TK, TV>(translate_key: TK, translate_val: TV) -> Result<(), Option<K2>>
|
||||
where K2: FullCodec + Clone, V2: Decode, TK: Fn(K2) -> K, TV: Fn(V2) -> V;
|
||||
}
|
||||
|
||||
/// An implementation of a map with a two keys.
|
||||
///
|
||||
/// It provides an important ability to efficiently remove all entries
|
||||
/// that have a common first key.
|
||||
///
|
||||
/// Details on implementation can be found at
|
||||
/// [`generator::StorageDoubleMap`]
|
||||
pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
|
||||
/// The type that get/take returns.
|
||||
type Query;
|
||||
|
||||
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn exists<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Self::Query
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
VArg: EncodeLike<V>;
|
||||
|
||||
fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>;
|
||||
|
||||
fn remove_prefix<KArg1>(k1: KArg1) where KArg1: ?Sized + EncodeLike<K1>;
|
||||
|
||||
fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
F: FnOnce(&mut Self::Query) -> R;
|
||||
|
||||
fn append<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KArg1, KArg2>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: Items,
|
||||
)
|
||||
where
|
||||
KArg1: EncodeLike<K1>,
|
||||
KArg2: EncodeLike<K2>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
V: EncodeAppend<Item=Item>,
|
||||
Items: IntoIterator<Item=EncodeLikeItem> + Clone + EncodeLike<V>,
|
||||
Items::IntoIter: ExactSizeIterator;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Operation on unhashed runtime storage.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
|
||||
pub fn get<T: Decode + Sized>(key: &[u8]) -> Option<T> {
|
||||
runtime_io::storage::get(key).and_then(|val| {
|
||||
Decode::decode(&mut &val[..]).map(Some).unwrap_or_else(|_| {
|
||||
// TODO #3700: error should be handleable.
|
||||
runtime_print!("ERROR: Corrupted state at {:?}", key);
|
||||
None
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or the type's default if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_default<T: Decode + Sized + Default>(key: &[u8]) -> T {
|
||||
get(key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or<T: Decode + Sized>(key: &[u8], default_value: T) -> T {
|
||||
get(key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry.
|
||||
pub fn get_or_else<T: Decode + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
|
||||
get(key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Put `value` in storage under `key`.
|
||||
pub fn put<T: Encode + ?Sized>(key: &[u8], value: &T) {
|
||||
value.using_encoded(|slice| runtime_io::storage::set(key, slice));
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
|
||||
pub fn take<T: Decode + Sized>(key: &[u8]) -> Option<T> {
|
||||
let r = get(key);
|
||||
if r.is_some() {
|
||||
kill(key);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
|
||||
/// the default for its type.
|
||||
pub fn take_or_default<T: Decode + Sized + Default>(key: &[u8]) -> T {
|
||||
take(key).unwrap_or_else(Default::default)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or<T: Decode + Sized>(key: &[u8], default_value: T) -> T {
|
||||
take(key).unwrap_or(default_value)
|
||||
}
|
||||
|
||||
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
|
||||
/// explicit entry. Ensure there is no explicit entry on return.
|
||||
pub fn take_or_else<T: Decode + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
|
||||
take(key).unwrap_or_else(default_value)
|
||||
}
|
||||
|
||||
/// Check to see if `key` has an explicit entry in storage.
|
||||
pub fn exists(key: &[u8]) -> bool {
|
||||
runtime_io::storage::read(key, &mut [0;0][..], 0).is_some()
|
||||
}
|
||||
|
||||
/// Ensure `key` has no explicit entry in storage.
|
||||
pub fn kill(key: &[u8]) {
|
||||
runtime_io::storage::clear(key);
|
||||
}
|
||||
|
||||
/// Ensure keys with the given `prefix` have no entries in storage.
|
||||
pub fn kill_prefix(prefix: &[u8]) {
|
||||
runtime_io::storage::clear_prefix(prefix);
|
||||
}
|
||||
|
||||
/// Get a Vec of bytes from storage.
|
||||
pub fn get_raw(key: &[u8]) -> Option<Vec<u8>> {
|
||||
runtime_io::storage::get(key)
|
||||
}
|
||||
|
||||
/// Put a raw byte slice into storage.
|
||||
pub fn put_raw(key: &[u8], value: &[u8]) {
|
||||
runtime_io::storage::set(key, value)
|
||||
}
|
||||
Reference in New Issue
Block a user