mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-07 18:58:01 +00:00
All backend stuff now manages optional storage.
Also simplified a lot of the backend.
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user