From 67863eeb0f98b5bd6e9af8a9f776cfa99ecb40f3 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Mar 2018 18:29:53 +0100 Subject: [PATCH 1/7] initial draft of the macro --- Cargo.lock | 7 + Cargo.toml | 1 + substrate/storage-wrapper/Cargo.toml | 7 + substrate/storage-wrapper/src/lib.rs | 245 +++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 substrate/storage-wrapper/Cargo.toml create mode 100644 substrate/storage-wrapper/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a16f065c4bc96..87d8a4f20af5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1651,6 +1651,13 @@ dependencies = [ "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-storage-wrapper" +version = "0.1.0" +dependencies = [ + "substrate-codec 0.1.0", +] + [[package]] name = "substrate-test-runtime" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 019cf90f8cb9d..139a6aa794ec1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "substrate/runtime-support", "substrate/serializer", "substrate/state-machine", + "substrate/storage-wrapper", "substrate/test-runtime", ] exclude = [ diff --git a/substrate/storage-wrapper/Cargo.toml b/substrate/storage-wrapper/Cargo.toml new file mode 100644 index 0000000000000..e097d923642d1 --- /dev/null +++ b/substrate/storage-wrapper/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "substrate-storage-wrapper" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +substrate-codec = { path = "../codec" } diff --git a/substrate/storage-wrapper/src/lib.rs b/substrate/storage-wrapper/src/lib.rs new file mode 100644 index 0000000000000..27f4ac11d4c96 --- /dev/null +++ b/substrate/storage-wrapper/src/lib.rs @@ -0,0 +1,245 @@ +// 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 . + +#[doc(hidden)] +pub extern crate substrate_codec as codec; + +/// 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_from(storage: &S) -> Option; + /// Store a value under this key into the provded storage instance. + fn store_into(val: &T, storage: &S); +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __storage_items_internal { + // generator for values. + (($($vis:tt)*) $name: ident: $key: expr => $ty:ty) => { + $($vis)* struct $name; + + impl $name { + /// Get the storage key. + $($vis)* fn key() -> &'static [u8] { + $key + } + + /// Load the value from the provided storage instance. + $($vis)* fn load_from(storage: &S) -> Option<$ty> { + storage.load($key) + } + + /// Store a value under this key into the provded storage instance. + $($vis)* fn store_into(val: &$ty, storage: &S) { + storage.store($key, val) + } + } + + impl $crate::StorageValue<$ty> for $name { + fn key() -> &'static [u8] { + $key + } + + fn load_from(storage: &S) -> Option<$ty> { + $name::load_from(storage) + } + + fn store_into(val: &$ty, storage: &S) { + $name::store_into(val, storage) + } + } + }; + // generator for maps. + (($($vis:tt)*) $name: ident: $prefix: expr => map [$kty: ty => $ty:ty]) => { + $($vis)* struct $name; + + 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(&index)); + key + } + + /// Load the value associated with the given key from the map. + $($vis)* fn load_from(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 store_into(key: &$kty, val: &$ty, storage: &S) { + let key = $name::key_for(key); + storage.store(&key[..], val); + } + } + }; + // generator for lists. + (($($vis:tt)*) $name: ident: $prefix: expr => list [$ty:ty]) => { + $($vis)* struct $name; + + 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); + } + } + + $($vis)* fn clear_item(index: u32, storage: &S) { + if index < $name::len(storage) { + storage.kill(&$name::key_for(index)); + } + } + + /// Load the value at given index. Returns `None` if `len + $($vis)* fn get(index: u32, storage: &S) -> Option<$ty> { + storage.load(&$name::key_for(index)[..]) + } + + $($vis)* fn len(storage: &S) -> u32 { + storage.load(&$name::len_key()).unwrap_or_default() + } + + 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); + } + } + }; +} + +/// Declares wrappers around stored data. +/// +/// storage_items! { +/// Authorities: ":auth:" => list [AuthorityId]; // list +/// Balances: ":bal:" => map [AccountId => Balance]; // map +/// Code: ":code" => Vec; // creates a single value. stored under that key. +/// ... +/// } +/// +/// Authorities::len_key() -> &'static [u8]; +/// Authorities::load_len(&Storage) -> u32 +/// Authorities::key_for(n) -> Vec; +/// Authorities::load_from(&Storage, n) -> Option; +/// // ... KeyedVec-like API +/// +/// Code::load_from(&Storage) -> Option>; // assumes a `Decodable` trait where the `<[u8] as Decodable>::Decoded = Vec` +/// Code::store_in(&mut Storage, &[u8]); +/// Code::key() -> &'static [u8]; // for low-level usage. +#[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 super::*; + + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} From b2df562024a0ea924d140365f6da8391c77f2d36 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Mar 2018 19:40:22 +0100 Subject: [PATCH 2/7] traits for all storage types --- substrate/storage-wrapper/src/lib.rs | 236 +++++++++++++++++++++++---- 1 file changed, 207 insertions(+), 29 deletions(-) diff --git a/substrate/storage-wrapper/src/lib.rs b/substrate/storage-wrapper/src/lib.rs index 27f4ac11d4c96..e2b8b8c2aeefa 100644 --- a/substrate/storage-wrapper/src/lib.rs +++ b/substrate/storage-wrapper/src/lib.rs @@ -14,6 +14,38 @@ // 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_storage_wrapper; +//! +//! 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() { } +//! ``` + #[doc(hidden)] pub extern crate substrate_codec as codec; @@ -44,6 +76,53 @@ pub trait StorageValue { fn load_from(storage: &S) -> Option; /// Store a value under this key into the provded storage instance. fn store_into(val: &T, storage: &S); + /// Clear the storage value. + fn kill(storage: &S); +} + +/// 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 load_from(key: &K, storage: &S) -> Option; + + /// Store a value to be associated with the given key from the map. + fn store_into(key: &K, val: &V, storage: &S); } #[macro_export] @@ -53,6 +132,7 @@ macro_rules! __storage_items_internal { (($($vis:tt)*) $name: ident: $key: expr => $ty:ty) => { $($vis)* struct $name; + #[allow(unused)] impl $name { /// Get the storage key. $($vis)* fn key() -> &'static [u8] { @@ -68,6 +148,11 @@ macro_rules! __storage_items_internal { $($vis)* fn store_into(val: &$ty, storage: &S) { storage.store($key, val) } + + /// Kill the value. + $($vis)* fn kill(storage: &S) { + storage.kill($key) + } } impl $crate::StorageValue<$ty> for $name { @@ -82,12 +167,17 @@ macro_rules! __storage_items_internal { fn store_into(val: &$ty, storage: &S) { $name::store_into(val, storage) } + + fn kill(storage: &S) { + $name::kill(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] { @@ -97,7 +187,7 @@ macro_rules! __storage_items_internal { /// 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(&index)); + key.extend($crate::codec::Slicable::encode(x)); key } @@ -113,11 +203,30 @@ macro_rules! __storage_items_internal { storage.store(&key[..], val); } } + + impl $crate::StorageMap<$kty, $ty> for $name { + fn prefix() -> &'static [u8] { + $prefix + } + + fn key_for(x: &$kty) -> Vec { + $name::key_for(x) + } + + fn load_from(key: &$kty, storage: &S) -> Option<$ty> { + $name::load_from(key, storage) + } + + fn store_into(key: &$kty, val: &$ty, storage: &S) { + $name::store_into(key, val, 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] { @@ -160,47 +269,85 @@ macro_rules! __storage_items_internal { } } - $($vis)* fn clear_item(index: u32, storage: &S) { - if index < $name::len(storage) { - storage.kill(&$name::key_for(index)); - } - } - - /// Load the value at given index. Returns `None` if `len + /// 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::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 wrappers around stored data. -/// -/// storage_items! { -/// Authorities: ":auth:" => list [AuthorityId]; // list -/// Balances: ":bal:" => map [AccountId => Balance]; // map -/// Code: ":code" => Vec; // creates a single value. stored under that key. -/// ... -/// } -/// -/// Authorities::len_key() -> &'static [u8]; -/// Authorities::load_len(&Storage) -> u32 -/// Authorities::key_for(n) -> Vec; -/// Authorities::load_from(&Storage, n) -> Option; -/// // ... KeyedVec-like API -/// -/// Code::load_from(&Storage) -> Option>; // assumes a `Decodable` trait where the `<[u8] as Decodable>::Decoded = Vec` -/// Code::store_in(&mut Storage, &[u8]); -/// Code::key() -> &'static [u8]; // for low-level usage. +/// Declares strongly-typed wrappers around codec-compatible types in storage. #[macro_export] macro_rules! storage_items { // simple values @@ -236,10 +383,41 @@ macro_rules! storage_items { #[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]; + } + #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } + fn value() { + let storage = RefCell::new(HashMap::new()); + assert!(Value::load_from(&storage).is_none()); + Value::store_into(&100_000, &storage); + assert_eq!(Value::load_from(&storage), Some(100_000)); + Value::kill(&storage); + assert!(Value::load_from(&storage).is_none()); + } + + #[test] + fn list() { + + } } From cdf2103e92aa788a7ba0b505241cb5e0698c64e5 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Mar 2018 19:44:12 +0100 Subject: [PATCH 3/7] test for list wrapper --- substrate/storage-wrapper/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/substrate/storage-wrapper/src/lib.rs b/substrate/storage-wrapper/src/lib.rs index e2b8b8c2aeefa..92ec167735aba 100644 --- a/substrate/storage-wrapper/src/lib.rs +++ b/substrate/storage-wrapper/src/lib.rs @@ -418,6 +418,20 @@ mod tests { #[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()); } } From 984146556a27747d9a5a9621b6c5b5d2542cafa7 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 6 Mar 2018 11:43:02 +0100 Subject: [PATCH 4/7] map API --- substrate/storage-wrapper/src/lib.rs | 88 +++++++++++++++++++++------- 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/substrate/storage-wrapper/src/lib.rs b/substrate/storage-wrapper/src/lib.rs index 92ec167735aba..e2e5082a2ffd7 100644 --- a/substrate/storage-wrapper/src/lib.rs +++ b/substrate/storage-wrapper/src/lib.rs @@ -73,11 +73,13 @@ pub trait StorageValue { /// Get the storage key. fn key() -> &'static [u8]; /// Load the value from the provided storage instance. - fn load_from(storage: &S) -> Option; + fn load(storage: &S) -> Option; /// Store a value under this key into the provded storage instance. - fn store_into(val: &T, storage: &S); + 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. @@ -119,10 +121,16 @@ pub trait StorageMap { fn key_for(x: &K) -> Vec; /// Load the value associated with the given key from the map. - fn load_from(key: &K, storage: &S) -> Option; + fn get(key: &K, storage: &S) -> Option; /// Store a value to be associated with the given key from the map. - fn store_into(key: &K, val: &V, storage: &S); + 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] @@ -140,12 +148,12 @@ macro_rules! __storage_items_internal { } /// Load the value from the provided storage instance. - $($vis)* fn load_from(storage: &S) -> Option<$ty> { + $($vis)* fn load(storage: &S) -> Option<$ty> { storage.load($key) } /// Store a value under this key into the provded storage instance. - $($vis)* fn store_into(val: &$ty, storage: &S) { + $($vis)* fn store(val: &$ty, storage: &S) { storage.store($key, val) } @@ -153,6 +161,11 @@ macro_rules! __storage_items_internal { $($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::StorageValue<$ty> for $name { @@ -160,17 +173,21 @@ macro_rules! __storage_items_internal { $key } - fn load_from(storage: &S) -> Option<$ty> { - $name::load_from(storage) + fn load(storage: &S) -> Option<$ty> { + $name::load(storage) } - fn store_into(val: &$ty, storage: &S) { - $name::store_into(val, 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. @@ -192,16 +209,27 @@ macro_rules! __storage_items_internal { } /// Load the value associated with the given key from the map. - $($vis)* fn load_from(key: &$kty, storage: &S) -> Option<$ty> { + $($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 store_into(key: &$kty, val: &$ty, storage: &S) { + $($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::StorageMap<$kty, $ty> for $name { @@ -213,12 +241,20 @@ macro_rules! __storage_items_internal { $name::key_for(x) } - fn load_from(key: &$kty, storage: &S) -> Option<$ty> { - $name::load_from(key, storage) + 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 store_into(key: &$kty, val: &$ty, storage: &S) { - $name::store_into(key, val, storage) + fn take(key: &$kty, storage: &S) -> Option<$ty> { + $name::take(key, storage) } } }; @@ -404,16 +440,17 @@ mod tests { 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_from(&storage).is_none()); - Value::store_into(&100_000, &storage); - assert_eq!(Value::load_from(&storage), Some(100_000)); + 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_from(&storage).is_none()); + assert!(Value::load(&storage).is_none()); } #[test] @@ -434,4 +471,15 @@ mod tests { 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()); + } } From 06c3d95fd9c702377fbe9f861f7c54a9178ecfae Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 6 Mar 2018 14:51:01 +0100 Subject: [PATCH 5/7] move generator to runtime-support --- Cargo.lock | 7 -- Cargo.toml | 1 - polkadot/runtime/src/runtime/consensus.rs | 2 +- substrate/runtime-support/src/lib.rs | 5 +- .../src/storage/generator.rs} | 72 +++++++++---------- .../src/{storage.rs => storage/mod.rs} | 53 +++----------- substrate/storage-wrapper/Cargo.toml | 7 -- 7 files changed, 49 insertions(+), 98 deletions(-) rename substrate/{storage-wrapper/src/lib.rs => runtime-support/src/storage/generator.rs} (82%) rename substrate/runtime-support/src/{storage.rs => storage/mod.rs} (89%) delete mode 100644 substrate/storage-wrapper/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 87d8a4f20af5c..a16f065c4bc96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1651,13 +1651,6 @@ dependencies = [ "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "substrate-storage-wrapper" -version = "0.1.0" -dependencies = [ - "substrate-codec 0.1.0", -] - [[package]] name = "substrate-test-runtime" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 139a6aa794ec1..019cf90f8cb9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ members = [ "substrate/runtime-support", "substrate/serializer", "substrate/state-machine", - "substrate/storage-wrapper", "substrate/test-runtime", ] exclude = [ diff --git a/polkadot/runtime/src/runtime/consensus.rs b/polkadot/runtime/src/runtime/consensus.rs index 7d50f6abd418f..aa52f641ec573 100644 --- a/polkadot/runtime/src/runtime/consensus.rs +++ b/polkadot/runtime/src/runtime/consensus.rs @@ -17,7 +17,7 @@ //! Conensus module for runtime; manages the authority set ready for the native code. use rstd::prelude::*; -use runtime_support::storage::unhashed::StorageVec; +use runtime_support::storage::StorageVec; use polkadot_primitives::SessionKey; struct AuthorityStorageVec {} 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/storage-wrapper/src/lib.rs b/substrate/runtime-support/src/storage/generator.rs similarity index 82% rename from substrate/storage-wrapper/src/lib.rs rename to substrate/runtime-support/src/storage/generator.rs index e2e5082a2ffd7..ce02a4e099264 100644 --- a/substrate/storage-wrapper/src/lib.rs +++ b/substrate/runtime-support/src/storage/generator.rs @@ -28,7 +28,7 @@ //! //! ```rust //! #[macro_use] -//! extern crate substrate_storage_wrapper; +//! extern crate substrate_runtime_support; //! //! type AuthorityId = [u8; 32]; //! type Balance = u64; @@ -46,8 +46,8 @@ //!# fn main() { } //! ``` -#[doc(hidden)] -pub extern crate substrate_codec as codec; +use codec; +use rstd::vec::Vec; /// Abstraction around storage. pub trait Storage { @@ -148,44 +148,44 @@ macro_rules! __storage_items_internal { } /// Load the value from the provided storage instance. - $($vis)* fn load(storage: &S) -> Option<$ty> { + $($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) { + $($vis)* fn store(val: &$ty, storage: &S) { storage.store($key, val) } /// Kill the value. - $($vis)* fn kill(storage: &S) { + $($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> { + $($vis)* fn take(storage: &S) -> Option<$ty> { storage.take($key) } } - impl $crate::StorageValue<$ty> for $name { + impl $crate::storage::generator::StorageValue<$ty> for $name { fn key() -> &'static [u8] { $key } - fn load(storage: &S) -> Option<$ty> { + fn load(storage: &S) -> Option<$ty> { $name::load(storage) } - fn store(val: &$ty, storage: &S) { + fn store(val: &$ty, storage: &S) { $name::store(val, storage) } - fn kill(storage: &S) { + fn kill(storage: &S) { $name::kill(storage) } - fn take(storage: &S) -> Option<$ty> { + fn take(storage: &S) -> Option<$ty> { $name::take(storage) } } @@ -209,30 +209,30 @@ macro_rules! __storage_items_internal { } /// Load the value associated with the given key from the map. - $($vis)* fn get(key: &$kty, storage: &S) -> Option<$ty> { + $($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) { + $($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) { + $($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> { + $($vis)* fn take(key: &$kty, storage: &S) -> Option<$ty> { let key = $name::key_for(key); storage.take(&key[..]) } } - impl $crate::StorageMap<$kty, $ty> for $name { + impl $crate::storage::generator::StorageMap<$kty, $ty> for $name { fn prefix() -> &'static [u8] { $prefix } @@ -241,19 +241,19 @@ macro_rules! __storage_items_internal { $name::key_for(x) } - fn get(key: &$kty, storage: &S) -> Option<$ty> { + fn get(key: &$kty, storage: &S) -> Option<$ty> { $name::get(key, storage) } - fn insert(key: &$kty, val: &$ty, storage: &S) { + fn insert(key: &$kty, val: &$ty, storage: &S) { $name::insert(key, val, storage) } - fn remove(key: &$kty, storage: &S) { + fn remove(key: &$kty, storage: &S) { $name::remove(key, storage) } - fn take(key: &$kty, storage: &S) -> Option<$ty> { + fn take(key: &$kty, storage: &S) -> Option<$ty> { $name::take(key, storage) } } @@ -285,38 +285,38 @@ macro_rules! __storage_items_internal { } /// Read out all the items. - $($vis)* fn items(storage: &S) -> Vec<$ty> { + $($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) { + $($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) { + $($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> { + $($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 { + $($vis)* fn len(storage: &S) -> u32 { storage.load(&$name::len_key()).unwrap_or_default() } /// Clear the list. - $($vis)* fn clear(storage: &S) { + $($vis)* fn clear(storage: &S) { for i in 0..$name::len(storage) { $name::clear_item(i, storage); } @@ -324,19 +324,19 @@ macro_rules! __storage_items_internal { storage.kill(&$name::len_key()[..]) } - fn clear_item(index: u32, storage: &S) { + 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) { + 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::StorageList<$ty> for $name { + impl $crate::storage::generator::StorageList<$ty> for $name { /// Get the prefix key in storage. fn prefix() -> &'static [u8] { $prefix @@ -354,29 +354,29 @@ macro_rules! __storage_items_internal { } /// Read out all the items. - fn items(storage: &S) -> Vec<$ty> { + fn items(storage: &S) -> Vec<$ty> { $name::items(storage) } /// Set the current set of items. - fn set_items(items: &[$ty], storage: &S) { + fn set_items(items: &[$ty], storage: &S) { $name::set_items(items, storage) } - fn set_item(index: u32, item: &$ty, storage: &S) { + 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> { + fn get(index: u32, storage: &S) -> Option<$ty> { $name::get(index, storage) } - fn len(storage: &S) -> u32 { + fn len(storage: &S) -> u32 { $name::len(storage) } - fn clear(storage: &S) { + fn clear(storage: &S) { $name::clear(storage) } } diff --git a/substrate/runtime-support/src/storage.rs b/substrate/runtime-support/src/storage/mod.rs similarity index 89% rename from substrate/runtime-support/src/storage.rs rename to substrate/runtime-support/src/storage/mod.rs index 0960ecb0ba284..cd91ed6abd8e1 100644 --- a/substrate/runtime-support/src/storage.rs +++ b/substrate/runtime-support/src/storage/mod.rs @@ -20,6 +20,8 @@ use rstd::prelude::*; 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> { @@ -119,8 +121,12 @@ pub fn put_raw(key: &[u8], value: &[u8]) { runtime_io::set_storage(&twox_128(key)[..], value) } +/// A trait for working with `storage-wrapper` values under the substrate storage API. +pub trait StorageValue { + +} + /// 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]; @@ -163,7 +169,7 @@ pub trait StorageVec { } pub mod unhashed { - use super::{runtime_io, Slicable, KeyedVec, Vec, IncrementalInput}; + use super::{runtime_io, Slicable, Vec, IncrementalInput}; /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { @@ -245,49 +251,6 @@ pub mod unhashed { pub fn put_raw(key: &[u8], value: &[u8]) { runtime_io::set_storage(key, value) } - - /// 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]; - - /// Get the current set of items. - fn items() -> Vec { - (0..Self::count()).into_iter().map(Self::item).collect() - } - - /// Set the current set of items. - fn set_items(items: &[Self::Item]) { - Self::set_count(items.len() as u32); - items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i)); - } - - fn set_item(index: u32, item: &Self::Item) { - if index < Self::count() { - put(&index.to_keyed_vec(Self::PREFIX), item); - } - } - - fn clear_item(index: u32) { - if index < Self::count() { - kill(&index.to_keyed_vec(Self::PREFIX)); - } - } - - fn item(index: u32) -> Self::Item { - get_or_default(&index.to_keyed_vec(Self::PREFIX)) - } - - fn set_count(count: u32) { - (count..Self::count()).for_each(Self::clear_item); - put(&b"len".to_keyed_vec(Self::PREFIX), &count); - } - - fn count() -> u32 { - get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) - } - } } #[cfg(test)] diff --git a/substrate/storage-wrapper/Cargo.toml b/substrate/storage-wrapper/Cargo.toml deleted file mode 100644 index e097d923642d1..0000000000000 --- a/substrate/storage-wrapper/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "substrate-storage-wrapper" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -substrate-codec = { path = "../codec" } From 2a9b1f1037c3c89037bb1ce1ca0679861921b32e Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 6 Mar 2018 15:03:44 +0100 Subject: [PATCH 6/7] specialize traits further for runtime storage --- substrate/runtime-support/src/storage/mod.rs | 170 ++++++++++++++++++- 1 file changed, 168 insertions(+), 2 deletions(-) diff --git a/substrate/runtime-support/src/storage/mod.rs b/substrate/runtime-support/src/storage/mod.rs index cd91ed6abd8e1..3bc11f60cac20 100644 --- a/substrate/runtime-support/src/storage/mod.rs +++ b/substrate/runtime-support/src/storage/mod.rs @@ -121,9 +121,175 @@ pub fn put_raw(key: &[u8], value: &[u8]) { runtime_io::set_storage(&twox_128(key)[..], value) } -/// A trait for working with `storage-wrapper` values under the substrate storage API. -pub trait StorageValue { +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. From 22417662d451a031bd5ef2a21062cb1b0e50ff4a Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 6 Mar 2018 16:47:21 +0100 Subject: [PATCH 7/7] restore storage vec for unhashed --- polkadot/runtime/src/runtime/consensus.rs | 2 +- substrate/runtime-support/src/storage/mod.rs | 44 +++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/polkadot/runtime/src/runtime/consensus.rs b/polkadot/runtime/src/runtime/consensus.rs index aa52f641ec573..7d50f6abd418f 100644 --- a/polkadot/runtime/src/runtime/consensus.rs +++ b/polkadot/runtime/src/runtime/consensus.rs @@ -17,7 +17,7 @@ //! Conensus module for runtime; manages the authority set ready for the native code. use rstd::prelude::*; -use runtime_support::storage::StorageVec; +use runtime_support::storage::unhashed::StorageVec; use polkadot_primitives::SessionKey; struct AuthorityStorageVec {} diff --git a/substrate/runtime-support/src/storage/mod.rs b/substrate/runtime-support/src/storage/mod.rs index 3bc11f60cac20..e74faf175a316 100644 --- a/substrate/runtime-support/src/storage/mod.rs +++ b/substrate/runtime-support/src/storage/mod.rs @@ -335,7 +335,7 @@ pub trait StorageVec { } pub mod unhashed { - use super::{runtime_io, Slicable, Vec, IncrementalInput}; + use super::{runtime_io, Slicable, Vec, KeyedVec, IncrementalInput}; /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { @@ -417,6 +417,48 @@ pub mod unhashed { pub fn put_raw(key: &[u8], value: &[u8]) { runtime_io::set_storage(key, value) } + + /// A trait to conveniently store a vector of storable data. + pub trait StorageVec { + type Item: Default + Sized + Slicable; + const PREFIX: &'static [u8]; + + /// Get the current set of items. + fn items() -> Vec { + (0..Self::count()).into_iter().map(Self::item).collect() + } + + /// Set the current set of items. + fn set_items(items: &[Self::Item]) { + Self::set_count(items.len() as u32); + items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i)); + } + + fn set_item(index: u32, item: &Self::Item) { + if index < Self::count() { + put(&index.to_keyed_vec(Self::PREFIX), item); + } + } + + fn clear_item(index: u32) { + if index < Self::count() { + kill(&index.to_keyed_vec(Self::PREFIX)); + } + } + + fn item(index: u32) -> Self::Item { + get_or_default(&index.to_keyed_vec(Self::PREFIX)) + } + + fn set_count(count: u32) { + (count..Self::count()).for_each(Self::clear_item); + put(&b"len".to_keyed_vec(Self::PREFIX), &count); + } + + fn count() -> u32 { + get_or_default(&b"len".to_keyed_vec(Self::PREFIX)) + } + } } #[cfg(test)]