mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-27 22:11:06 +00:00
a670208a33
* Introduce first groundwork for Wasm executor. * Remove old Rust-runtime code. * Avoid commiting compled files. * Add runtime precompile. * Rename so module makes more sense. * Further renaming. * Ensure tests work. * Allow bringing in of externalities. - Add util functions/macros. - Add uncompacted runtime. - Add some external crates from pwasm-std for managing allocs/memory stuff. * Nice macros for imports. * Allow passing in of data through allocators. Make memcpy and malloc work. Basic allocator. * Can now pass in bytes to WasmExecutor. * Additional cleanup. * Switch usages of `OutData` to `u64` No need to be able to return bytes anymore. * convert to safe but extremely verbose type conversion. @rphmeier any more concise way of doing this? * Remove StaticExternalities distinction. * Remove another unused use. * Refactor wasm utils out * Remove extraneous copies that weren't really testing anything. * Try to use wasm 0.15 * Make it work! * Call-time externalities working. * Add basic externalities. * Fix grumbles and note unwraps to be sorted. * Test storage externality. Unforunately had to change signatures of externalities to avoid immutable function returning a reference. Not sure what to do about this... * Fix nits. * Compile collation logic. * Move back to refs. Yey. * Remove "object" id for storage access. * Fix test. * Fix up rest of tests. * remove unwrap. * Expose set/get code in externalities Also improve tests and add nice wrappers in rust-wasm. * Add validator set. * Introduce validator set into externalities and test. * Add another external function. * Remove code and validators; use storage for everything. * Introduce validators function. * Tests (and a fix) for the validators getter. * Allow calls into runtime to return data. * Remove unneeded trace. * Make runtime printing a bit nicer. * Create separate runtimes for testing and polkadot. * Remove commented code. * Use new path. * Refactor into shared support module. * Fix warning. * Remove unwraps. * Make macro a little less unhygenic. * Add wasm files.
270 lines
6.9 KiB
Rust
270 lines
6.9 KiB
Rust
// 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 keccak_hash;
|
|
|
|
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 {}
|
|
|
|
fn value_vec(mut value: usize, initial: Vec<u8>) -> Vec<u8> {
|
|
let mut acc = initial;
|
|
while value > 0 {
|
|
acc.push(value as u8);
|
|
value /= 256;
|
|
}
|
|
acc
|
|
}
|
|
|
|
/// Externalities: pinned to specific active address.
|
|
pub trait Externalities {
|
|
/// Externalities error type.
|
|
type Error: Error;
|
|
|
|
/// Read storage of current contract being called.
|
|
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>;
|
|
|
|
/// Set storage of current contract being called (effective immediately).
|
|
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>);
|
|
|
|
/// Get the current set of validators.
|
|
fn validators(&self) -> Result<Vec<&[u8]>, Self::Error> {
|
|
(0..self.storage(b"\0validator_count")?.into_iter()
|
|
.rev()
|
|
.fold(0, |acc, &i| (acc << 8) + (i as usize)))
|
|
.map(|i| self.storage(&value_vec(i, b"\0validator".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"\0code").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};
|
|
|
|
#[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 {
|
|
type Error = u8;
|
|
|
|
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> {
|
|
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);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn validators_call_works() {
|
|
let mut ext = TestExternalities::default();
|
|
|
|
assert_eq!(ext.validators(), Ok(vec![]));
|
|
|
|
ext.set_storage(b"\0validator_count".to_vec(), vec![]);
|
|
assert_eq!(ext.validators(), Ok(vec![]));
|
|
|
|
ext.set_storage(b"\0validator_count".to_vec(), vec![1]);
|
|
assert_eq!(ext.validators(), Ok(vec![&[][..]]));
|
|
|
|
ext.set_storage(b"\0validator".to_vec(), b"first".to_vec());
|
|
assert_eq!(ext.validators(), Ok(vec![&b"first"[..]]));
|
|
|
|
ext.set_storage(b"\0validator_count".to_vec(), vec![2]);
|
|
ext.set_storage(b"\0validator\x01".to_vec(), b"second".to_vec());
|
|
assert_eq!(ext.validators(), Ok(vec![&b"first"[..], &b"second"[..]]));
|
|
}
|
|
}
|