mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-17 06:41:02 +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.
|
//! 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user