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.
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<Option<&[u8]>, Self::Error>;
/// Commit updates to the backend and get new state.
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.
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<u8>, Vec<u8>>) -> Self {
InMemory {
inner: MemoryState {
storage
}
}
}
}
pub type InMemory = HashMap<Vec<u8>, Vec<u8>>;
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<Option<&[u8]>, Self::Error> {
Ok(self.get(key).map(AsRef::as_ref))
}
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])> {
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.
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<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>
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) {
Some(x) => Ok(x),
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);
}
@@ -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::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))).0
}
}
+55 -63
View File
@@ -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<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.
///
/// 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<Vec<u8>, Option<Vec<u8>>>,
committed: HashMap<Vec<u8>, Option<Vec<u8>>>,
}
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<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>) {
self.prospective.set_storage(key, val);
fn set_storage(&mut self, key: Vec<u8>, val: Option<Vec<u8>>) {
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<u8>, std::vec::Vec<u8>> {
self.committed.storage.drain()
pub fn drain(&mut self) -> Drain<Vec<u8>, Option<Vec<u8>>> {
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<Option<&[u8]>, ExternalitiesError>;
/// Set storage of current contract being called (effective immediately).
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>);
/// Set storage entry `key` of current contract being called (effective immediately).
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.
fn chain_id(&self) -> u64;
@@ -172,7 +157,8 @@ pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
exec: &Exec,
method: &str,
call_data: &[u8],
) -> Result<Vec<u8>, Box<Error>> {
) -> Result<Vec<u8>, Box<Error>>
{
let result = {
let mut externalities = ext::Ext {
@@ -180,7 +166,7 @@ pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
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<Error>)?.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,
@@ -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<u8>, Vec<u8>>,
}
impl TestExternalities {
/// Create a new instance with empty storage.
pub fn new() -> Self {
TestExternalities {
storage: HashMap::new(),
}
}
}
pub type TestExternalities = HashMap<Vec<u8>, Vec<u8>>;
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<Option<&[u8]>, ExternalitiesError> {
Ok(self.get(key).map(AsRef::as_ref))
}
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.storage.insert(key, value);
fn place_storage(&mut self, key: Vec<u8>, maybe_value: Option<Vec<u8>>) {
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
}
}