diff --git a/substrate/substrate/state-machine/src/backend.rs b/substrate/substrate/state-machine/src/backend.rs index 6174649c5c..2c019510ff 100644 --- a/substrate/substrate/state-machine/src/backend.rs +++ b/substrate/substrate/state-machine/src/backend.rs @@ -17,8 +17,7 @@ //! State machine backends. These manage the code and storage of contracts. use std::{error, fmt}; - -use super::{Update, MemoryState}; +use std::collections::HashMap; /// A state backend is used to read state data and can have changes committed /// to it. @@ -26,12 +25,12 @@ pub trait Backend { /// An error type when fetching data is not possible. type Error: super::Error; - /// Get keyed storage associated with specific address. - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; + /// Get keyed storage associated with specific address, or None if there is nothing associated. + fn storage(&self, key: &[u8]) -> Result, Self::Error>; /// Commit updates to the backend and get new state. fn commit(&mut self, changes: I) - where I: IntoIterator; + where I: IntoIterator, Option>)>; /// Get all key/value pairs into a Vec. fn pairs(&self) -> Vec<(&[u8], &[u8])>; @@ -54,37 +53,28 @@ impl error::Error for Void { /// In-memory backend. Fully recomputes tries on each commit but useful for /// tests. -#[derive(Debug, PartialEq, Default, Clone)] -pub struct InMemory { - inner: MemoryState, // keeps all the state in memory. -} - -impl InMemory { - /// Create a new instance from a given storage map. - pub fn from(storage: ::std::collections::HashMap, Vec>) -> Self { - InMemory { - inner: MemoryState { - storage - } - } - } -} +pub type InMemory = HashMap, Vec>; impl Backend for InMemory { type Error = Void; - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { - Ok(self.inner.storage(key).unwrap_or(&[])) + fn storage(&self, key: &[u8]) -> Result, Self::Error> { + Ok(self.get(key).map(AsRef::as_ref)) } fn commit(&mut self, changes: I) - where I: IntoIterator + where I: IntoIterator, Option>)> { - self.inner.update(changes); + for (key, val) in changes { + match val { + Some(v) => { self.insert(key, v); }, + None => { self.remove(&key); }, + } + } } fn pairs(&self) -> Vec<(&[u8], &[u8])> { - self.inner.storage.iter().map(|(k, v)| (&k[..], &v[..])).collect() + self.iter().map(|(k, v)| (&k[..], &v[..])).collect() } } diff --git a/substrate/substrate/state-machine/src/ext.rs b/substrate/substrate/state-machine/src/ext.rs index e0176ad41c..fc04d18f5b 100644 --- a/substrate/substrate/state-machine/src/ext.rs +++ b/substrate/substrate/state-machine/src/ext.rs @@ -17,6 +17,7 @@ //! Conrete externalities implementation. use std::{error, fmt}; +use std::collections::HashMap; use triehash::trie_root; use backend::Backend; use {Externalities, ExternalitiesError, OverlayedChanges}; @@ -58,17 +59,31 @@ pub struct Ext<'a, B: 'a> { pub backend: &'a B, } +#[cfg(test)] +impl<'a, B: 'a + Backend> Ext<'a, B> { + pub fn storage_pairs(&self) -> Vec<(Vec, Vec)> { + self.backend.pairs().iter() + .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) + .chain(self.overlay.committed.clone().into_iter()) + .chain(self.overlay.prospective.clone().into_iter()) + .collect::>() + .into_iter() + .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val))) + .collect() + } +} + impl<'a, B: 'a> Externalities for Ext<'a, B> where B: Backend { - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { + fn storage(&self, key: &[u8]) -> Result, ExternalitiesError> { match self.overlay.storage(key) { Some(x) => Ok(x), None => self.backend.storage(key).map_err(|_| ExternalitiesError), } } - fn set_storage(&mut self, key: Vec, value: Vec) { + fn place_storage(&mut self, key: Vec, value: Option>) { self.overlay.set_storage(key, value); } @@ -77,13 +92,12 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> } fn storage_root(&self) -> [u8; 32] { - let mut all_pairs = self.backend.pairs(); - all_pairs.extend( - self.overlay.committed.storage.iter() - .chain(self.overlay.prospective.storage.iter()) - .map(|(k, v)| (&k[..], &v[..])) - ); - - trie_root(all_pairs.into_iter().map(|(k, v)| (k.to_vec(), v.to_vec()))).0 + trie_root(self.backend.pairs().iter() + .map(|&(ref k, ref v)| (k.to_vec(), Some(v.to_vec()))) + .chain(self.overlay.committed.clone().into_iter()) + .chain(self.overlay.prospective.clone().into_iter()) + .collect::>() + .into_iter() + .filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))).0 } } diff --git a/substrate/substrate/state-machine/src/lib.rs b/substrate/substrate/state-machine/src/lib.rs index e6127beb1e..3bbe7632a8 100644 --- a/substrate/substrate/state-machine/src/lib.rs +++ b/substrate/substrate/state-machine/src/lib.rs @@ -32,6 +32,7 @@ extern crate triehash; extern crate byteorder; use std::collections::HashMap; +use std::collections::hash_map::Drain; use std::fmt; pub mod backend; @@ -42,73 +43,47 @@ pub use testing::TestExternalities; pub use ext::Ext; pub use backend::Backend; -/// Updates to be committed to the state. -pub type Update = (Vec, Vec); - -// in-memory section of the state. -#[derive(Debug, PartialEq, Default, Clone)] -struct MemoryState { - storage: HashMap, Vec>, -} - -impl MemoryState { - fn storage(&self, key: &[u8]) -> Option<&[u8]> { - self.storage.get(key).map(|v| &v[..]) - } - - fn set_storage(&mut self, key: Vec, val: Vec) { - self.storage.insert(key, val); - } - - fn update(&mut self, changes: I) where I: IntoIterator { - for (key, val) in changes { - if val.is_empty() { - self.storage.remove(&key); - } else { - self.storage.insert(key, val); - } - } - } -} - /// The overlayed changes to state to be queried on top of the backend. /// /// A transaction shares all prospective changes within an inner overlay /// that can be cleared. #[derive(Default)] pub struct OverlayedChanges { - prospective: MemoryState, - committed: MemoryState, + prospective: HashMap, Option>>, + committed: HashMap, Option>>, } impl OverlayedChanges { - fn storage(&self, key: &[u8]) -> Option<&[u8]> { - self.prospective.storage(key) - .or_else(|| self.committed.storage(key)) - .and_then(|v| if v.is_empty() { None } else { Some(v) }) + /// Returns a double-Option: None if the key is unknown (i.e. and the query should be refered + /// to the backend); Some(None) if the key has been deleted. Some(Some(...)) for a key whose + /// value has been set. + pub fn storage(&self, key: &[u8]) -> Option> { + self.prospective.get(key) + .or_else(|| self.committed.get(key)) + .map(|x| x.as_ref().map(AsRef::as_ref)) } - fn set_storage(&mut self, key: Vec, val: Vec) { - self.prospective.set_storage(key, val); + fn set_storage(&mut self, key: Vec, val: Option>) { + self.prospective.insert(key, val); } /// Discard prospective changes to state. pub fn discard_prospective(&mut self) { - self.prospective.storage.clear(); + self.prospective.clear(); } /// Commit prospective changes to state. pub fn commit_prospective(&mut self) { - if self.committed.storage.is_empty() { + if self.committed.is_empty() { ::std::mem::swap(&mut self.prospective, &mut self.committed); } else { - self.committed.update(self.prospective.storage.drain()); + self.committed.extend(self.prospective.drain()); } } /// Drain prospective changes to an iterator. - pub fn drain(&mut self) -> ::std::collections::hash_map::Drain, std::vec::Vec> { - self.committed.storage.drain() + pub fn drain(&mut self) -> Drain, Option>> { + self.committed.drain() } } @@ -133,10 +108,20 @@ impl fmt::Display for ExternalitiesError { /// Externalities: pinned to specific active address. pub trait Externalities { /// Read storage of current contract being called. - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError>; + fn storage(&self, key: &[u8]) -> Result, ExternalitiesError>; - /// Set storage of current contract being called (effective immediately). - fn set_storage(&mut self, key: Vec, value: Vec); + /// Set storage entry `key` of current contract being called (effective immediately). + fn set_storage(&mut self, key: Vec, value: Vec) { + self.place_storage(key, Some(value)); + } + + /// Clear a storage entry (`key`) of current contract being called (effective immediately). + fn clear_storage(&mut self, key: &[u8]) { + self.place_storage(key.to_vec(), None); + } + + /// Set or clear a storage entry (`key`) of current contract being called (effective immediately). + fn place_storage(&mut self, key: Vec, value: Option>); /// Get the identity of the chain. fn chain_id(&self) -> u64; @@ -172,7 +157,8 @@ pub fn execute( exec: &Exec, method: &str, call_data: &[u8], -) -> Result, Box> { +) -> Result, Box> +{ let result = { let mut externalities = ext::Ext { @@ -180,7 +166,7 @@ pub fn execute( overlay: &mut *overlay }; // make a copy. - let code = externalities.storage(b":code").unwrap_or(&[]).to_vec(); + let code = externalities.storage(b":code").map_err(|e| Box::new(e) as Box)?.unwrap_or(&[]).to_vec(); exec.call( &mut externalities, @@ -216,21 +202,24 @@ mod tests { assert!(overlayed.storage(&key).is_none()); - overlayed.set_storage(key.clone(), vec![1, 2, 3]); - assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); + overlayed.set_storage(key.clone(), Some(vec![1, 2, 3])); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); overlayed.commit_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - overlayed.set_storage(key.clone(), vec![]); - assert!(overlayed.storage(&key).is_none()); + overlayed.set_storage(key.clone(), Some(vec![])); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..])); + + overlayed.set_storage(key.clone(), None); + assert!(overlayed.storage(&key).unwrap().is_none()); overlayed.discard_prospective(); - assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..])); - overlayed.set_storage(key.clone(), vec![]); + overlayed.set_storage(key.clone(), None); overlayed.commit_prospective(); - assert!(overlayed.storage(&key).is_none()); + assert!(overlayed.storage(&key).unwrap().is_none()); } macro_rules! map { @@ -244,16 +233,19 @@ mod tests { let mut backend = InMemory::from(map![ b"doe".to_vec() => b"reindeer".to_vec(), b"dog".to_vec() => b"puppyXXX".to_vec(), - b"dogglesworth".to_vec() => b"catXXX".to_vec() + b"dogglesworth".to_vec() => b"catXXX".to_vec(), + b"doug".to_vec() => b"notadog".to_vec() ]); let mut overlay = OverlayedChanges { - committed: MemoryState { storage: map![ - b"dog".to_vec() => b"puppy".to_vec(), - b"dogglesworth".to_vec() => b"catYYY".to_vec() - ], }, - prospective: MemoryState { storage: map![ - b"dogglesworth".to_vec() => b"cat".to_vec() - ], }, + committed: map![ + b"dog".to_vec() => Some(b"puppy".to_vec()), + b"dogglesworth".to_vec() => Some(b"catYYY".to_vec()), + b"doug".to_vec() => Some(vec![]) + ], + prospective: map![ + b"dogglesworth".to_vec() => Some(b"cat".to_vec()), + b"doug".to_vec() => None + ], }; let ext = Ext { backend: &mut backend, diff --git a/substrate/substrate/state-machine/src/testing.rs b/substrate/substrate/state-machine/src/testing.rs index a85e1d3f4a..28661579ec 100644 --- a/substrate/substrate/state-machine/src/testing.rs +++ b/substrate/substrate/state-machine/src/testing.rs @@ -21,34 +21,24 @@ use super::{Externalities, ExternalitiesError}; use triehash::trie_root; /// Simple HashMap based Externalities impl. -#[derive(Debug, Default)] -pub struct TestExternalities { - /// The storage. - pub storage: HashMap, Vec>, -} - -impl TestExternalities { - /// Create a new instance with empty storage. - pub fn new() -> Self { - TestExternalities { - storage: HashMap::new(), - } - } -} +pub type TestExternalities = HashMap, Vec>; impl Externalities for TestExternalities { - fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError> { - Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) + fn storage(&self, key: &[u8]) -> Result, ExternalitiesError> { + Ok(self.get(key).map(AsRef::as_ref)) } - fn set_storage(&mut self, key: Vec, value: Vec) { - self.storage.insert(key, value); + fn place_storage(&mut self, key: Vec, maybe_value: Option>) { + match maybe_value { + Some(value) => { self.insert(key, value); } + None => { self.remove(&key); } + } } fn chain_id(&self) -> u64 { 42 } fn storage_root(&self) -> [u8; 32] { - trie_root(self.storage.clone()).0 + trie_root(self.clone()).0 } }