All backend stuff now manages optional storage.

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