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];