use sp_std::prelude::*;
use codec::{FullCodec, FullEncode, Encode, EncodeLike, Decode};
use crate::hash::{Twox128, StorageHasher};
use sp_runtime::generic::{Digest, DigestItem};
pub use sp_runtime::TransactionOutcome;
pub mod unhashed;
pub mod hashed;
pub mod child;
#[doc(hidden)]
pub mod generator;
pub mod migration;
pub fn with_transaction<R>(f: impl FnOnce() -> TransactionOutcome<R>) -> R {
use sp_io::storage::{
start_transaction, commit_transaction, rollback_transaction,
};
use TransactionOutcome::*;
start_transaction();
match f() {
Commit(res) => { commit_transaction(); res },
Rollback(res) => { rollback_transaction(); res },
}
}
pub trait StorageValue<T: FullCodec> {
type Query;
fn hashed_key() -> [u8; 32];
fn exists() -> bool;
fn get() -> Self::Query;
fn try_get() -> Result<T, ()>;
fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()>;
fn put<Arg: EncodeLike<T>>(val: Arg);
fn set(val: Self::Query);
fn mutate<R, F: FnOnce(&mut Self::Query) -> R>(f: F) -> R;
fn try_mutate<R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>(f: F) -> Result<R, E>;
fn kill();
fn take() -> Self::Query;
fn append<Item, EncodeLikeItem>(item: EncodeLikeItem)
where
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
T: StorageAppend<Item>;
fn decode_len() -> Option<usize> where T: StorageDecodeLength {
T::decode_len(&Self::hashed_key())
}
}
pub trait StorageMap<K: FullEncode, V: FullCodec> {
type Query;
fn hashed_key_for<KeyArg: EncodeLike<K>>(key: KeyArg) -> Vec<u8>;
fn contains_key<KeyArg: EncodeLike<K>>(key: KeyArg) -> bool;
fn get<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
fn swap<KeyArg1: EncodeLike<K>, KeyArg2: EncodeLike<K>>(key1: KeyArg1, key2: KeyArg2);
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg);
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg);
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
fn try_mutate<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>(
key: KeyArg,
f: F,
) -> Result<R, E>;
fn mutate_exists<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Option<V>) -> R>(key: KeyArg, f: F) -> R;
fn try_mutate_exists<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Option<V>) -> Result<R, E>>(
key: KeyArg,
f: F,
) -> Result<R, E>;
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
fn append<Item, EncodeLikeItem, EncodeLikeKey>(key: EncodeLikeKey, item: EncodeLikeItem)
where
EncodeLikeKey: EncodeLike<K>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
V: StorageAppend<Item>;
fn decode_len<KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<usize>
where V: StorageDecodeLength,
{
V::decode_len(&Self::hashed_key_for(key))
}
fn migrate_key<OldHasher: StorageHasher, KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V>;
fn migrate_key_from_blake<KeyArg: EncodeLike<K>>(key: KeyArg) -> Option<V> {
Self::migrate_key::<crate::hash::Blake2_256, KeyArg>(key)
}
}
pub trait IterableStorageMap<K: FullEncode, V: FullCodec>: StorageMap<K, V> {
type Iterator: Iterator<Item = (K, V)>;
fn iter() -> Self::Iterator;
fn drain() -> Self::Iterator;
fn translate<O: Decode, F: Fn(K, O) -> Option<V>>(f: F);
}
pub trait IterableStorageDoubleMap<
K1: FullCodec,
K2: FullCodec,
V: FullCodec
>: StorageDoubleMap<K1, K2, V> {
type PrefixIterator: Iterator<Item = (K2, V)>;
type Iterator: Iterator<Item = (K1, K2, V)>;
fn iter_prefix(k1: impl EncodeLike<K1>) -> Self::PrefixIterator;
fn drain_prefix(k1: impl EncodeLike<K1>) -> Self::PrefixIterator;
fn iter() -> Self::Iterator;
fn drain() -> Self::Iterator;
fn translate<O: Decode, F: Fn(K1, K2, O) -> Option<V>>(f: F);
}
pub trait StorageDoubleMap<K1: FullEncode, K2: FullEncode, V: FullCodec> {
type Query;
fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>;
fn contains_key<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 swap<XKArg1, XKArg2, YKArg1, YKArg2>(x_k1: XKArg1, x_k2: XKArg2, y_k1: YKArg1, y_k2: YKArg2)
where
XKArg1: EncodeLike<K1>,
XKArg2: EncodeLike<K2>,
YKArg1: EncodeLike<K1>,
YKArg2: EncodeLike<K2>;
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 iter_prefix_values<KArg1>(k1: KArg1) -> PrefixIterator<V>
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 try_mutate<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
F: FnOnce(&mut Self::Query) -> Result<R, E>;
fn mutate_exists<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
F: FnOnce(&mut Option<V>) -> R;
fn try_mutate_exists<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
F: FnOnce(&mut Option<V>) -> Result<R, E>;
fn append<Item, EncodeLikeItem, KArg1, KArg2>(
k1: KArg1,
k2: KArg2,
item: EncodeLikeItem,
) where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
V: StorageAppend<Item>;
fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Option<usize>
where
KArg1: EncodeLike<K1>,
KArg2: EncodeLike<K2>,
V: StorageDecodeLength,
{
V::decode_len(&Self::hashed_key_for(key1, key2))
}
fn migrate_keys<
OldHasher1: StorageHasher,
OldHasher2: StorageHasher,
KeyArg1: EncodeLike<K1>,
KeyArg2: EncodeLike<K2>,
>(key1: KeyArg1, key2: KeyArg2) -> Option<V>;
}
pub struct PrefixIterator<T> {
prefix: Vec<u8>,
previous_key: Vec<u8>,
drain: bool,
closure: fn(&[u8], &[u8]) -> Result<T, codec::Error>,
}
impl<T> Iterator for PrefixIterator<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
loop {
let maybe_next = sp_io::storage::next_key(&self.previous_key)
.filter(|n| n.starts_with(&self.prefix));
break match maybe_next {
Some(next) => {
self.previous_key = next;
let raw_value = match unhashed::get_raw(&self.previous_key) {
Some(raw_value) => raw_value,
None => {
crate::debug::error!(
"next_key returned a key with no value at {:?}",
self.previous_key
);
continue
}
};
if self.drain {
unhashed::kill(&self.previous_key)
}
let raw_key_without_prefix = &self.previous_key[self.prefix.len()..];
let item = match (self.closure)(raw_key_without_prefix, &raw_value[..]) {
Ok(item) => item,
Err(e) => {
crate::debug::error!(
"(key, value) failed to decode at {:?}: {:?}",
self.previous_key, e
);
continue
}
};
Some(item)
}
None => None,
}
}
}
}
pub trait StoragePrefixedMap<Value: FullCodec> {
fn module_prefix() -> &'static [u8];
fn storage_prefix() -> &'static [u8];
fn final_prefix() -> [u8; 32] {
let mut final_key = [0u8; 32];
final_key[0..16].copy_from_slice(&Twox128::hash(Self::module_prefix()));
final_key[16..32].copy_from_slice(&Twox128::hash(Self::storage_prefix()));
final_key
}
fn remove_all() {
sp_io::storage::clear_prefix(&Self::final_prefix())
}
fn iter_values() -> PrefixIterator<Value> {
let prefix = Self::final_prefix();
PrefixIterator {
prefix: prefix.to_vec(),
previous_key: prefix.to_vec(),
drain: false,
closure: |_raw_key, mut raw_value| Value::decode(&mut raw_value),
}
}
fn translate_values<OldValue: Decode, F: Fn(OldValue) -> Option<Value>>(f: F) {
let prefix = Self::final_prefix();
let mut previous_key = prefix.clone().to_vec();
while let Some(next) = sp_io::storage::next_key(&previous_key)
.filter(|n| n.starts_with(&prefix))
{
previous_key = next;
let maybe_value = unhashed::get::<OldValue>(&previous_key);
match maybe_value {
Some(value) => match f(value) {
Some(new) => unhashed::put::<Value>(&previous_key, &new),
None => unhashed::kill(&previous_key),
},
None => {
crate::debug::error!(
"old key failed to decode at {:?}",
previous_key
);
continue
},
}
}
}
}
pub trait StorageAppend<Item: Encode>: private::Sealed {}
pub trait StorageDecodeLength: private::Sealed + codec::DecodeLength {
fn decode_len(key: &[u8]) -> Option<usize> {
let mut data = [0u8; 5];
let len = sp_io::storage::read(key, &mut data, 0)?;
let len = data.len().min(len as usize);
<Self as codec::DecodeLength>::len(&data[..len]).ok()
}
}
mod private {
use super::*;
pub trait Sealed {}
impl<T: Encode> Sealed for Vec<T> {}
impl<Hash: Encode> Sealed for Digest<Hash> {}
}
impl<T: Encode> StorageAppend<T> for Vec<T> {}
impl<T: Encode> StorageDecodeLength for Vec<T> {}
impl<Hash: Encode> StorageAppend<DigestItem<Hash>> for Digest<Hash> {}
#[cfg(test)]
mod test {
use super::*;
use sp_core::hashing::twox_128;
use sp_io::TestExternalities;
use generator::StorageValue as _;
#[test]
fn prefixed_map_works() {
TestExternalities::default().execute_with(|| {
struct MyStorage;
impl StoragePrefixedMap<u64> for MyStorage {
fn module_prefix() -> &'static [u8] {
b"MyModule"
}
fn storage_prefix() -> &'static [u8] {
b"MyStorage"
}
}
let key_before = {
let mut k = MyStorage::final_prefix();
let last = k.iter_mut().last().unwrap();
*last = last.checked_sub(1).unwrap();
k
};
let key_after = {
let mut k = MyStorage::final_prefix();
let last = k.iter_mut().last().unwrap();
*last = last.checked_add(1).unwrap();
k
};
unhashed::put(&key_before[..], &32u64);
unhashed::put(&key_after[..], &33u64);
let k = [twox_128(b"MyModule"), twox_128(b"MyStorage")].concat();
assert_eq!(MyStorage::final_prefix().to_vec(), k);
assert!(MyStorage::iter_values().collect::<Vec<_>>().is_empty());
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u64);
unhashed::put(&[&k[..], &vec![1, 1][..]].concat(), &2u64);
unhashed::put(&[&k[..], &vec![8][..]].concat(), &3u64);
unhashed::put(&[&k[..], &vec![10][..]].concat(), &4u64);
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2, 3, 4]);
MyStorage::remove_all();
assert!(MyStorage::iter_values().collect::<Vec<_>>().is_empty());
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u32);
unhashed::put(&[&k[..], &vec![8][..]].concat(), &2u32);
assert!(MyStorage::iter_values().collect::<Vec<_>>().is_empty());
MyStorage::translate_values(|v: u32| Some(v as u64));
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2]);
MyStorage::remove_all();
unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u128);
unhashed::put(&[&k[..], &vec![1, 1][..]].concat(), &2u64);
unhashed::put(&[&k[..], &vec![8][..]].concat(), &3u128);
unhashed::put(&[&k[..], &vec![10][..]].concat(), &4u32);
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2, 3]);
MyStorage::translate_values(|v: u128| Some(v as u64));
assert_eq!(MyStorage::iter_values().collect::<Vec<_>>(), vec![1, 2, 3]);
MyStorage::remove_all();
assert_eq!(unhashed::get(&key_before[..]), Some(32u64));
assert_eq!(unhashed::get(&key_after[..]), Some(33u64));
});
}
#[test]
fn digest_storage_append_works_as_expected() {
TestExternalities::default().execute_with(|| {
struct Storage;
impl generator::StorageValue<Digest<u32>> for Storage {
type Query = Digest<u32>;
fn module_prefix() -> &'static [u8] {
b"MyModule"
}
fn storage_prefix() -> &'static [u8] {
b"Storage"
}
fn from_optional_value_to_query(v: Option<Digest<u32>>) -> Self::Query {
v.unwrap()
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Digest<u32>> {
Some(v)
}
}
Storage::append(DigestItem::ChangesTrieRoot(1));
Storage::append(DigestItem::Other(Vec::new()));
let value = unhashed::get_raw(&Storage::storage_value_final_key()).unwrap();
let expected = Digest {
logs: vec![DigestItem::ChangesTrieRoot(1), DigestItem::Other(Vec::new())],
};
assert_eq!(Digest::decode(&mut &value[..]).unwrap(), expected);
});
}
}