mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-09 18:47:59 +00:00
clean up workspaces a little
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! State machine backends. These manage the code and storage of contracts.
|
||||
|
||||
use std::{error, fmt};
|
||||
use primitives::hash::H256;
|
||||
use triehash::sec_trie_root;
|
||||
|
||||
use super::{Update, MemoryState};
|
||||
|
||||
/// Output of a commit.
|
||||
pub struct Committed {
|
||||
/// Root of the storage tree after changes committed.
|
||||
pub storage_tree_root: H256,
|
||||
}
|
||||
|
||||
/// A state backend is used to read state data and can have changes committed
|
||||
/// to it.
|
||||
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>;
|
||||
|
||||
/// Commit updates to the backend and get new state.
|
||||
fn commit<I>(&mut self, changes: I) -> Committed
|
||||
where I: IntoIterator<Item=Update>;
|
||||
}
|
||||
|
||||
/// Error impossible.
|
||||
// TODO: use `!` type when stabilized.
|
||||
#[derive(Debug)]
|
||||
pub enum Void {}
|
||||
|
||||
impl fmt::Display for Void {
|
||||
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Void {
|
||||
fn description(&self) -> &str { "unreachable error" }
|
||||
}
|
||||
|
||||
/// In-memory backend. Fully recomputes tries on each commit but useful for
|
||||
/// tests.
|
||||
#[derive(Default)]
|
||||
pub struct InMemory {
|
||||
inner: MemoryState, // keeps all the state in memory.
|
||||
}
|
||||
|
||||
impl Backend for InMemory {
|
||||
type Error = Void;
|
||||
|
||||
fn storage(&self, key: &[u8]) -> Result<&[u8], Void> {
|
||||
Ok(self.inner.storage(key).unwrap_or(&[]))
|
||||
}
|
||||
|
||||
fn commit<I>(&mut self, changes: I) -> Committed
|
||||
where I: IntoIterator<Item=Update>
|
||||
{
|
||||
self.inner.update(changes);
|
||||
|
||||
// fully recalculate trie roots.
|
||||
let storage_tree_root = H256(sec_trie_root(
|
||||
self.inner.storage.iter()
|
||||
.map(|(k, v)| (k.to_vec(), v.clone()))
|
||||
.collect()
|
||||
).0);
|
||||
|
||||
Committed {
|
||||
storage_tree_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: DB-based backend
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Conrete externalities implementation.
|
||||
|
||||
use std::{error, fmt};
|
||||
|
||||
use backend::Backend;
|
||||
use {Externalities, ExternalitiesError, OverlayedChanges};
|
||||
|
||||
/// Errors that can occur when interacting with the externalities.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Error<B, E> {
|
||||
/// Failure to load state data from the backend.
|
||||
#[allow(unused)]
|
||||
Backend(B),
|
||||
/// Failure to execute a function.
|
||||
#[allow(unused)]
|
||||
Executor(E),
|
||||
}
|
||||
|
||||
impl<B: fmt::Display, E: fmt::Display> fmt::Display for Error<B, E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Backend(ref e) => write!(f, "Storage backend error: {}", e),
|
||||
Error::Executor(ref e) => write!(f, "Sub-call execution error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: error::Error, E: error::Error> error::Error for Error<B, E> {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::Backend(..) => "backend error",
|
||||
Error::Executor(..) => "executor error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a read-only backend, call executor, and current overlayed changes.
|
||||
pub struct Ext<'a, B: 'a> {
|
||||
/// The overlayed changes to write to.
|
||||
pub overlay: &'a mut OverlayedChanges,
|
||||
/// The storage backend to read from.
|
||||
pub backend: &'a B,
|
||||
}
|
||||
|
||||
impl<'a, B: 'a> Externalities for Ext<'a, B>
|
||||
where B: Backend
|
||||
{
|
||||
fn storage(&self, key: &[u8]) -> Result<&[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>) {
|
||||
self.overlay.set_storage(key, value);
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> u64 {
|
||||
42
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Polkadot state machine implementation.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate polkadot_primitives as primitives;
|
||||
|
||||
extern crate hashdb;
|
||||
extern crate memorydb;
|
||||
|
||||
extern crate patricia_trie;
|
||||
extern crate triehash;
|
||||
|
||||
extern crate byteorder;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
use primitives::contract::{CallData};
|
||||
|
||||
pub mod backend;
|
||||
mod ext;
|
||||
|
||||
/// Updates to be committed to the state.
|
||||
pub enum Update {
|
||||
/// Set storage of object at given key -- empty is deletion.
|
||||
Storage(Vec<u8>, Vec<u8>),
|
||||
}
|
||||
|
||||
// in-memory section of the state.
|
||||
#[derive(Default)]
|
||||
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 update in changes {
|
||||
match update {
|
||||
Update::Storage(key, val) => {
|
||||
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,
|
||||
}
|
||||
|
||||
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) })
|
||||
}
|
||||
|
||||
fn set_storage(&mut self, key: Vec<u8>, val: Vec<u8>) {
|
||||
self.prospective.set_storage(key, val);
|
||||
}
|
||||
|
||||
/// Discard prospective changes to state.
|
||||
pub fn discard_prospective(&mut self) {
|
||||
self.prospective.storage.clear();
|
||||
}
|
||||
|
||||
/// Commit prospective changes to state.
|
||||
pub fn commit_prospective(&mut self) {
|
||||
let storage_updates = self.prospective.storage.drain()
|
||||
.map(|(key, value)| Update::Storage(key, value));
|
||||
|
||||
self.committed.update(storage_updates);
|
||||
}
|
||||
}
|
||||
|
||||
/// State Machine Error bound.
|
||||
///
|
||||
/// This should reflect WASM error type bound for future compatibility.
|
||||
pub trait Error: 'static + fmt::Debug + fmt::Display + Send {}
|
||||
impl<E> Error for E where E: 'static + fmt::Debug + fmt::Display + Send {}
|
||||
|
||||
/// Externalities Error.
|
||||
///
|
||||
/// Externalities are not really allowed to have errors, since it's assumed that dependent code
|
||||
/// would not be executed unless externalities were available. This is included for completeness,
|
||||
/// and as a transition away from the pre-existing framework.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct ExternalitiesError;
|
||||
|
||||
impl fmt::Display for ExternalitiesError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Externalities Error") }
|
||||
}
|
||||
|
||||
fn to_keyed_vec(value: u32, mut prepend: Vec<u8>) -> Vec<u8> {
|
||||
prepend.extend((0..::std::mem::size_of::<u32>()).into_iter().map(|i| (value >> (i * 8)) as u8));
|
||||
prepend
|
||||
}
|
||||
|
||||
/// Externalities: pinned to specific active address.
|
||||
pub trait Externalities {
|
||||
/// Read storage of current contract being called.
|
||||
fn storage(&self, key: &[u8]) -> Result<&[u8], ExternalitiesError>;
|
||||
|
||||
/// Set storage of current contract being called (effective immediately).
|
||||
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>);
|
||||
|
||||
/// Get the identity of the chain.
|
||||
fn chain_id(&self) -> u64;
|
||||
|
||||
/// Get the current set of authorities from storage.
|
||||
fn authorities(&self) -> Result<Vec<&[u8]>, ExternalitiesError> {
|
||||
(0..self.storage(b"con:aut:len")?.into_iter()
|
||||
.rev()
|
||||
.fold(0, |acc, &i| (acc << 8) + (i as u32)))
|
||||
.map(|i| self.storage(&to_keyed_vec(i, b"con:aut:".to_vec())))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Code execution engine.
|
||||
pub trait CodeExecutor: Sized {
|
||||
/// Externalities error type.
|
||||
type Error: Error;
|
||||
|
||||
/// Call a given method in the runtime.
|
||||
fn call<E: Externalities>(
|
||||
&self,
|
||||
ext: &mut E,
|
||||
code: &[u8],
|
||||
method: &str,
|
||||
data: &CallData,
|
||||
) -> Result<Vec<u8>, Self::Error>;
|
||||
}
|
||||
|
||||
/// Execute a call using the given state backend, overlayed changes, and call executor.
|
||||
///
|
||||
/// On an error, no prospective changes are written to the overlay.
|
||||
///
|
||||
/// Note: changes to code will be in place if this call is made again. For running partial
|
||||
/// blocks (e.g. a transaction at a time), ensure a differrent method is used.
|
||||
pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
|
||||
backend: &B,
|
||||
overlay: &mut OverlayedChanges,
|
||||
exec: &Exec,
|
||||
method: &str,
|
||||
call_data: &CallData,
|
||||
) -> Result<Vec<u8>, Box<Error>> {
|
||||
|
||||
let result = {
|
||||
let mut externalities = ext::Ext {
|
||||
backend,
|
||||
overlay: &mut *overlay
|
||||
};
|
||||
// make a copy.
|
||||
let code = externalities.storage(b":code").unwrap_or(&[]).to_vec();
|
||||
|
||||
exec.call(
|
||||
&mut externalities,
|
||||
&code,
|
||||
method,
|
||||
call_data,
|
||||
)
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(out) => {
|
||||
overlay.commit_prospective();
|
||||
Ok(out)
|
||||
}
|
||||
Err(e) => {
|
||||
overlay.discard_prospective();
|
||||
Err(Box::new(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use super::{OverlayedChanges, Externalities, ExternalitiesError};
|
||||
|
||||
#[test]
|
||||
fn overlayed_storage_works() {
|
||||
let mut overlayed = OverlayedChanges::default();
|
||||
|
||||
let key = vec![42, 69, 169, 142];
|
||||
|
||||
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.commit_prospective();
|
||||
assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]);
|
||||
|
||||
overlayed.set_storage(key.clone(), vec![]);
|
||||
assert!(overlayed.storage(&key).is_none());
|
||||
|
||||
overlayed.discard_prospective();
|
||||
assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]);
|
||||
|
||||
overlayed.set_storage(key.clone(), vec![]);
|
||||
overlayed.commit_prospective();
|
||||
assert!(overlayed.storage(&key).is_none());
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct TestExternalities {
|
||||
storage: 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 set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
|
||||
self.storage.insert(key, value);
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> u64 { 42 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorities_call_works() {
|
||||
let mut ext = TestExternalities::default();
|
||||
|
||||
assert_eq!(ext.authorities(), Ok(vec![]));
|
||||
|
||||
ext.set_storage(b"con:aut:len".to_vec(), vec![0u8; 4]);
|
||||
assert_eq!(ext.authorities(), Ok(vec![]));
|
||||
|
||||
ext.set_storage(b"con:aut:len".to_vec(), vec![1u8, 0, 0, 0]);
|
||||
assert_eq!(ext.authorities(), Ok(vec![&[][..]]));
|
||||
|
||||
ext.set_storage(b"con:aut:\0\0\0\0".to_vec(), b"first".to_vec());
|
||||
assert_eq!(ext.authorities(), Ok(vec![&b"first"[..]]));
|
||||
|
||||
ext.set_storage(b"con:aut:len".to_vec(), vec![2u8, 0, 0, 0]);
|
||||
ext.set_storage(b"con:aut:\x01\0\0\0".to_vec(), b"second".to_vec());
|
||||
assert_eq!(ext.authorities(), Ok(vec![&b"first"[..], &b"second"[..]]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user