diff --git a/substrate/runtime-support/src/lib.rs b/substrate/runtime-support/src/lib.rs
index 44cebcac5854d..6e946ce6c7988 100644
--- a/substrate/runtime-support/src/lib.rs
+++ b/substrate/runtime-support/src/lib.rs
@@ -20,9 +20,12 @@
extern crate substrate_runtime_std as rstd;
extern crate substrate_runtime_io as runtime_io;
-extern crate substrate_codec as codec;
extern crate substrate_primitives as primitives;
+#[doc(hidden)]
+pub extern crate substrate_codec as codec;
+pub use self::storage::generator::Storage as GenericStorage;
+
pub mod storage;
mod hashable;
diff --git a/substrate/runtime-support/src/storage/generator.rs b/substrate/runtime-support/src/storage/generator.rs
new file mode 100644
index 0000000000000..ce02a4e099264
--- /dev/null
+++ b/substrate/runtime-support/src/storage/generator.rs
@@ -0,0 +1,485 @@
+// Copyright 2017 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 .
+
+//! Strongly typed wrappers around values in storage.
+//!
+//! This crate exports a macro `storage_items!` and traits describing behavior of generated
+//! structs.
+//!
+//! Three kinds of data types are currently supported:
+//! - values
+//! - maps
+//! - lists
+//!
+//! # Examples:
+//!
+//! ```rust
+//! #[macro_use]
+//! extern crate substrate_runtime_support;
+//!
+//! type AuthorityId = [u8; 32];
+//! type Balance = u64;
+//! pub type SessionKey = [u8; 32];
+//!
+//! storage_items! {
+//! // public value
+//! pub Value: b"stored_key" => SessionKey;
+//! // private map.
+//! Balances: b"private_map:" => map [AuthorityId => Balance];
+//! // private list.
+//! Authorities: b"auth:" => list [AuthorityId];
+//! }
+//!
+//!# fn main() { }
+//! ```
+
+use codec;
+use rstd::vec::Vec;
+
+/// Abstraction around storage.
+pub trait Storage {
+ /// Load the bytes of a key from storage. Can panic if the type is incorrect.
+ fn load(&self, key: &[u8]) -> Option;
+
+ /// Put a value in under a key.
+ fn store(&self, key: &[u8], val: &T);
+
+ /// Remove the bytes of a key from storage.
+ fn kill(&self, key: &[u8]);
+
+ /// Take a value from storage, deleting it after reading.
+ fn take(&self, key: &[u8]) -> Option {
+ let value = self.load(key);
+ self.kill(key);
+ value
+ }
+}
+
+/// A strongly-typed value kept in storage.
+pub trait StorageValue {
+ /// Get the storage key.
+ fn key() -> &'static [u8];
+ /// Load the value from the provided storage instance.
+ fn load(storage: &S) -> Option;
+ /// Store a value under this key into the provded storage instance.
+ fn store(val: &T, storage: &S);
+ /// Clear the storage value.
+ fn kill(storage: &S);
+ /// Take a value from storage, removing it afterwards.
+ fn take(storage: &S) -> Option;
+}
+
+/// A strongly-typed list in storage.
+pub trait StorageList {
+ /// Get the prefix key in storage.
+ fn prefix() -> &'static [u8];
+
+ /// Get the key used to store the length field.
+ fn len_key() -> Vec;
+
+ /// Get the storage key used to fetch a value at a given index.
+ fn key_for(index: u32) -> Vec;
+
+ /// Read out all the items.
+ fn items(storage: &S) -> Vec;
+
+ /// Set the current set of items.
+ fn set_items(items: &[T], storage: &S);
+
+ /// Set the item at the given index.
+ fn set_item(index: u32, item: &T, storage: &S);
+
+ /// Load the value at given index. Returns `None` if the index is out-of-bounds.
+ fn get(index: u32, storage: &S) -> Option;
+
+ /// Load the length of the list
+ fn len(storage: &S) -> u32;
+
+ /// Clear the list.
+ fn clear(storage: &S);
+}
+
+/// A strongly-typed map in storage.
+pub trait StorageMap {
+ /// Get the prefix key in storage.
+ fn prefix() -> &'static [u8];
+
+ /// Get the storage key used to fetch a value corresponding to a specific key.
+ fn key_for(x: &K) -> Vec;
+
+ /// Load the value associated with the given key from the map.
+ fn get(key: &K, storage: &S) -> Option;
+
+ /// Store a value to be associated with the given key from the map.
+ fn insert(key: &K, val: &V, storage: &S);
+
+ /// Remove the value under a key.
+ fn remove(key: &K, storage: &S);
+
+ /// Take the value under a key.
+ fn take(key: &K, storage: &S) -> Option;
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! __storage_items_internal {
+ // generator for values.
+ (($($vis:tt)*) $name: ident: $key: expr => $ty:ty) => {
+ $($vis)* struct $name;
+
+ #[allow(unused)]
+ impl $name {
+ /// Get the storage key.
+ $($vis)* fn key() -> &'static [u8] {
+ $key
+ }
+
+ /// Load the value from the provided storage instance.
+ $($vis)* fn load(storage: &S) -> Option<$ty> {
+ storage.load($key)
+ }
+
+ /// Store a value under this key into the provded storage instance.
+ $($vis)* fn store(val: &$ty, storage: &S) {
+ storage.store($key, val)
+ }
+
+ /// Kill the value.
+ $($vis)* fn kill(storage: &S) {
+ storage.kill($key)
+ }
+
+ /// Take and remove the value from the provided storage instance.
+ $($vis)* fn take(storage: &S) -> Option<$ty> {
+ storage.take($key)
+ }
+ }
+
+ impl $crate::storage::generator::StorageValue<$ty> for $name {
+ fn key() -> &'static [u8] {
+ $key
+ }
+
+ fn load(storage: &S) -> Option<$ty> {
+ $name::load(storage)
+ }
+
+ fn store(val: &$ty, storage: &S) {
+ $name::store(val, storage)
+ }
+
+ fn kill(storage: &S) {
+ $name::kill(storage)
+ }
+
+ fn take(storage: &S) -> Option<$ty> {
+ $name::take(storage)
+ }
+ }
+ };
+ // generator for maps.
+ (($($vis:tt)*) $name: ident: $prefix: expr => map [$kty: ty => $ty:ty]) => {
+ $($vis)* struct $name;
+
+ #[allow(unused)]
+ impl $name {
+ /// Get the prefix key in storage.
+ $($vis)* fn prefix() -> &'static [u8] {
+ $prefix
+ }
+
+ /// Get the storage key used to fetch a value corresponding to a specific key.
+ $($vis)* fn key_for(x: &$kty) -> Vec {
+ let mut key = $prefix.to_vec();
+ key.extend($crate::codec::Slicable::encode(x));
+ key
+ }
+
+ /// Load the value associated with the given key from the map.
+ $($vis)* fn get(key: &$kty, storage: &S) -> Option<$ty> {
+ let key = $name::key_for(key);
+ storage.load(&key[..])
+ }
+
+ /// Store a value to be associated with the given key from the map.
+ $($vis)* fn insert(key: &$kty, val: &$ty, storage: &S) {
+ let key = $name::key_for(key);
+ storage.store(&key[..], val);
+ }
+
+ /// Remove the value from storage.
+ $($vis)* fn remove(key: &$kty, storage: &S) {
+ storage.kill(&$name::key_for(key)[..]);
+ }
+
+ /// Take the value, reading and removing it.
+ $($vis)* fn take(key: &$kty, storage: &S) -> Option<$ty> {
+ let key = $name::key_for(key);
+ storage.take(&key[..])
+ }
+ }
+
+ impl $crate::storage::generator::StorageMap<$kty, $ty> for $name {
+ fn prefix() -> &'static [u8] {
+ $prefix
+ }
+
+ fn key_for(x: &$kty) -> Vec {
+ $name::key_for(x)
+ }
+
+ fn get(key: &$kty, storage: &S) -> Option<$ty> {
+ $name::get(key, storage)
+ }
+
+ fn insert(key: &$kty, val: &$ty, storage: &S) {
+ $name::insert(key, val, storage)
+ }
+
+ fn remove(key: &$kty, storage: &S) {
+ $name::remove(key, storage)
+ }
+
+ fn take(key: &$kty, storage: &S) -> Option<$ty> {
+ $name::take(key, storage)
+ }
+ }
+ };
+ // generator for lists.
+ (($($vis:tt)*) $name: ident: $prefix: expr => list [$ty:ty]) => {
+ $($vis)* struct $name;
+
+ #[allow(unused)]
+ impl $name {
+ /// Get the prefix key in storage.
+ $($vis)* fn prefix() -> &'static [u8] {
+ $prefix
+ }
+
+ /// Get the key used to store the length field.
+ // TODO: concat macro should accept byte literals.
+ $($vis)* fn len_key() -> Vec {
+ let mut key = $prefix.to_vec();
+ key.extend(b"len");
+ key
+ }
+
+ /// Get the storage key used to fetch a value at a given index.
+ $($vis)* fn key_for(index: u32) -> Vec {
+ let mut key = $prefix.to_vec();
+ key.extend($crate::codec::Slicable::encode(&index));
+ key
+ }
+
+ /// Read out all the items.
+ $($vis)* fn items(storage: &S) -> Vec<$ty> {
+ (0..$name::len(storage))
+ .map(|i| $name::get(i, storage).expect("all items within length are set; qed"))
+ .collect()
+ }
+
+ /// Set the current set of items.
+ $($vis)* fn set_items(items: &[$ty], storage: &S) {
+ $name::set_len(items.len() as u32, storage);
+ items.iter()
+ .enumerate()
+ .for_each(|(i, item)| $name::set_item(i as u32, item, storage));
+ }
+
+ $($vis)* fn set_item(index: u32, item: &$ty, storage: &S) {
+ if index < $name::len(storage) {
+ storage.store(&$name::key_for(index)[..], item);
+ }
+ }
+
+ /// Load the value at given index. Returns `None` if the index is out-of-bounds.
+ $($vis)* fn get(index: u32, storage: &S) -> Option<$ty> {
+ storage.load(&$name::key_for(index)[..])
+ }
+
+ /// Load the length of the list.
+ $($vis)* fn len(storage: &S) -> u32 {
+ storage.load(&$name::len_key()).unwrap_or_default()
+ }
+
+ /// Clear the list.
+ $($vis)* fn clear(storage: &S) {
+ for i in 0..$name::len(storage) {
+ $name::clear_item(i, storage);
+ }
+
+ storage.kill(&$name::len_key()[..])
+ }
+
+ fn clear_item(index: u32, storage: &S) {
+ if index < $name::len(storage) {
+ storage.kill(&$name::key_for(index));
+ }
+ }
+
+ fn set_len(count: u32, storage: &S) {
+ (count..$name::len(storage)).for_each(|i| $name::clear_item(i, storage));
+ storage.store(&$name::len_key(), &count);
+ }
+ }
+
+ impl $crate::storage::generator::StorageList<$ty> for $name {
+ /// Get the prefix key in storage.
+ fn prefix() -> &'static [u8] {
+ $prefix
+ }
+
+ /// Get the key used to store the length field.
+ // TODO: concat macro should accept byte literals.
+ fn len_key() -> Vec {
+ $name::len_key()
+ }
+
+ /// Get the storage key used to fetch a value at a given index.
+ fn key_for(index: u32) -> Vec {
+ $name::key_for(index)
+ }
+
+ /// Read out all the items.
+ fn items(storage: &S) -> Vec<$ty> {
+ $name::items(storage)
+ }
+
+ /// Set the current set of items.
+ fn set_items(items: &[$ty], storage: &S) {
+ $name::set_items(items, storage)
+ }
+
+ fn set_item(index: u32, item: &$ty, storage: &S) {
+ $name::set_item(index, item, storage)
+ }
+
+ /// Load the value at given index. Returns `None` if the index is out-of-bounds.
+ fn get(index: u32, storage: &S) -> Option<$ty> {
+ $name::get(index, storage)
+ }
+
+ fn len(storage: &S) -> u32 {
+ $name::len(storage)
+ }
+
+ fn clear(storage: &S) {
+ $name::clear(storage)
+ }
+ }
+ };
+}
+
+/// Declares strongly-typed wrappers around codec-compatible types in storage.
+#[macro_export]
+macro_rules! storage_items {
+ // simple values
+ ($name: ident: $key: expr => $ty:ty; $($t:tt)*) => {
+ __storage_items_internal!(() $name: $key => $ty);
+ storage_items!($($t)*);
+ };
+ (pub $name: ident: $key: expr => $ty:ty; $($t:tt)*) => {
+ __storage_items_internal!((pub) $name: $key => $ty);
+ storage_items!($($t)*);
+ };
+ // maps
+ ($name: ident: $prefix: expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
+ __storage_items_internal!(() $name: $prefix => map [$kty => $ty]);
+ storage_items!($($t)*);
+ };
+ (pub $name: ident: $prefix: expr => map [$kty: ty => $ty:ty]; $($t:tt)*) => {
+ __storage_items_internal!((pub) $name: $prefix => map [$kty => $ty]);
+ storage_items!($($t)*);
+ };
+ // lists
+ ($name: ident: $prefix: expr => list [$ty:ty]; $($t:tt)*) => {
+ __storage_items_internal!(() $name: $prefix => list [$ty]);
+ storage_items!($($t)*);
+ };
+ (pub $name: ident: $prefix: expr => list [$ty:ty]; $($t:tt)*) => {
+ __storage_items_internal!((pub) $name: $prefix => list [$ty]);
+ storage_items!($($t)*);
+ };
+ () => ()
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::HashMap;
+ use std::cell::RefCell;
+ use codec::Slicable;
+ use super::*;
+
+ impl Storage for RefCell, Vec>> {
+ fn load(&self, key: &[u8]) -> Option {
+ self.borrow_mut().get(key).map(|v| T::decode(&mut &v[..]).unwrap())
+ }
+
+ fn store(&self, key: &[u8], val: &T) {
+ self.borrow_mut().insert(key.to_owned(), val.encode());
+ }
+
+ fn kill(&self, key: &[u8]) {
+ self.borrow_mut().remove(key);
+ }
+ }
+
+ storage_items! {
+ Value: b"a" => u32;
+ List: b"b:" => list [u64];
+ Map: b"c:" => map [u32 => [u8; 32]];
+ }
+
+ #[test]
+ fn value() {
+ let storage = RefCell::new(HashMap::new());
+ assert!(Value::load(&storage).is_none());
+ Value::store(&100_000, &storage);
+ assert_eq!(Value::load(&storage), Some(100_000));
+ Value::kill(&storage);
+ assert!(Value::load(&storage).is_none());
+ }
+
+ #[test]
+ fn list() {
+ let storage = RefCell::new(HashMap::new());
+ assert_eq!(List::len(&storage), 0);
+ assert!(List::items(&storage).is_empty());
+
+ List::set_items(&[0, 2, 4, 6, 8], &storage);
+ assert_eq!(List::items(&storage), &[0, 2, 4, 6, 8]);
+ assert_eq!(List::len(&storage), 5);
+
+ List::set_item(2, &10, &storage);
+ assert_eq!(List::items(&storage), &[0, 2, 10, 6, 8]);
+ assert_eq!(List::len(&storage), 5);
+
+ List::clear(&storage);
+ assert_eq!(List::len(&storage), 0);
+ assert!(List::items(&storage).is_empty());
+ }
+
+ #[test]
+ fn map() {
+ let storage = RefCell::new(HashMap::new());
+ assert!(Map::get(&5, &storage).is_none());
+ Map::insert(&5, &[1; 32], &storage);
+ assert_eq!(Map::get(&5, &storage), Some([1; 32]));
+ assert_eq!(Map::take(&5, &storage), Some([1; 32]));
+ assert!(Map::get(&5, &storage).is_none());
+ assert!(Map::get(&999, &storage).is_none());
+ }
+}
diff --git a/substrate/runtime-support/src/storage.rs b/substrate/runtime-support/src/storage/mod.rs
similarity index 73%
rename from substrate/runtime-support/src/storage.rs
rename to substrate/runtime-support/src/storage/mod.rs
index cdb52468cf5b6..fb4aa17487360 100644
--- a/substrate/runtime-support/src/storage.rs
+++ b/substrate/runtime-support/src/storage/mod.rs
@@ -21,6 +21,8 @@ use rstd::borrow::Borrow;
use runtime_io::{self, twox_128};
use codec::{Slicable, KeyedVec, Input};
+pub mod generator;
+
// TODO: consider using blake256 to avoid possible preimage attack.
struct IncrementalInput<'a> {
@@ -120,8 +122,178 @@ pub fn put_raw(key: &[u8], value: &[u8]) {
runtime_io::set_storage(&twox_128(key)[..], value)
}
+struct RuntimeStorage;
+
+impl ::GenericStorage for RuntimeStorage {
+ /// Load the bytes of a key from storage. Can panic if the type is incorrect.
+ fn load(&self, key: &[u8]) -> Option {
+ get(key)
+ }
+
+ /// Put a value in under a key.
+ fn store(&self, key: &[u8], val: &T) {
+ put(key, val)
+ }
+
+ /// Remove the bytes of a key from storage.
+ fn kill(&self, key: &[u8]) {
+ kill(key)
+ }
+
+ /// Take a value from storage, deleting it after reading.
+ fn take(&self, key: &[u8]) -> Option {
+ take(key)
+ }
+}
+
+/// A trait for working with macro-generated storage values under the substrate storage API.
+pub trait StorageValue {
+ /// Get the storage key.
+ fn key() -> &'static [u8];
+ /// Load the value from the provided storage instance.
+ fn load() -> Option;
+ /// Store a value under this key into the provded storage instance.
+ fn store(val: &T);
+ /// Clear the storage value.
+ fn kill();
+ /// Take a value from storage, removing it afterwards.
+ fn take() -> Option;
+}
+
+impl StorageValue for U where U: generator::StorageValue {
+ fn key() -> &'static [u8] {
+ >::key()
+ }
+ fn load() -> Option {
+ U::load(&RuntimeStorage)
+ }
+ fn store(val: &T) {
+ U::store(val, &RuntimeStorage)
+ }
+ fn kill() {
+ U::kill(&RuntimeStorage)
+ }
+ fn take() -> Option {
+ U::take(&RuntimeStorage)
+ }
+}
+
+/// A strongly-typed list in storage.
+pub trait StorageList {
+ /// Get the prefix key in storage.
+ fn prefix() -> &'static [u8];
+
+ /// Get the key used to store the length field.
+ fn len_key() -> Vec;
+
+ /// Get the storage key used to fetch a value at a given index.
+ fn key_for(index: u32) -> Vec;
+
+ /// Read out all the items.
+ fn items() -> Vec;
+
+ /// Set the current set of items.
+ fn set_items(items: &[T]);
+
+ /// Set the item at the given index.
+ fn set_item(index: u32, item: &T);
+
+ /// Load the value at given index. Returns `None` if the index is out-of-bounds.
+ fn get(index: u32) -> Option;
+
+ /// Load the length of the list
+ fn len() -> u32;
+
+ /// Clear the list.
+ fn clear();
+}
+
+impl StorageList for U where U: generator::StorageList {
+ fn prefix() -> &'static [u8] {
+ >::prefix()
+ }
+
+ fn len_key() -> Vec {
+ >::len_key()
+ }
+
+ fn key_for(index: u32) -> Vec {
+ >::key_for(index)
+ }
+
+ fn items() -> Vec {
+ U::items(&RuntimeStorage)
+ }
+
+ fn set_items(items: &[T]) {
+ U::set_items(items, &RuntimeStorage)
+ }
+
+ fn set_item(index: u32, item: &T) {
+ U::set_item(index, item, &RuntimeStorage)
+ }
+
+ fn get(index: u32) -> Option {
+ U::get(index, &RuntimeStorage)
+ }
+
+ fn len() -> u32 {
+ U::len(&RuntimeStorage)
+ }
+
+ fn clear() {
+ U::clear(&RuntimeStorage)
+ }
+}
+
+/// A strongly-typed map in storage.
+pub trait StorageMap {
+ /// Get the prefix key in storage.
+ fn prefix() -> &'static [u8];
+
+ /// Get the storage key used to fetch a value corresponding to a specific key.
+ fn key_for(x: &K) -> Vec;
+
+ /// Load the value associated with the given key from the map.
+ fn get(key: &K) -> Option;
+
+ /// Store a value to be associated with the given key from the map.
+ fn insert(key: &K, val: &V);
+
+ /// Remove the value under a key.
+ fn remove(key: &K);
+
+ /// Take the value under a key.
+ fn take(key: &K) -> Option;
+}
+
+impl StorageMap for U where U: generator::StorageMap {
+ fn prefix() -> &'static [u8] {
+ >::prefix()
+ }
+
+ fn key_for(item: &K) -> Vec {
+ >::key_for(item)
+ }
+
+ fn get(key: &K) -> Option {
+ U::get(key, &RuntimeStorage)
+ }
+
+ fn insert(key: &K, val: &V) {
+ U::insert(key, val, &RuntimeStorage)
+ }
+
+ fn remove(key: &K) {
+ U::remove(key, &RuntimeStorage)
+ }
+
+ fn take(key: &K) -> Option {
+ U::take(key, &RuntimeStorage)
+ }
+}
+
/// A trait to conveniently store a vector of storable data.
-// TODO: add iterator support
pub trait StorageVec {
type Item: Default + Sized + Slicable;
const PREFIX: &'static [u8];
@@ -266,7 +438,6 @@ pub mod unhashed {
}
/// A trait to conveniently store a vector of storable data.
- // TODO: add iterator support
pub trait StorageVec {
type Item: Default + Sized + Slicable;
const PREFIX: &'static [u8];