From 4f5c5b6c176606b99640b7f64da7d23175bf31ed Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 5 Dec 2017 20:53:48 +0100 Subject: [PATCH 01/49] Introduce first groundwork for Wasm executor. --- Cargo.lock | 32 ++- Cargo.toml | 3 + client/src/lib.rs | 9 +- contracts/Cargo.toml | 1 + contracts/src/auth.rs | 2 +- contracts/src/balances.rs | 2 +- contracts/src/error.rs | 6 + contracts/src/lib.rs | 22 +- .../src/{executor.rs => rust_executor.rs} | 70 +------ contracts/src/validator_set.rs | 2 +- contracts/src/wasm_executor.rs | 100 +++++++++ primitives/src/contract.rs | 4 + rpc/src/state/mod.rs | 16 +- runtime/Cargo.lock | 4 + runtime/Cargo.toml | 9 + runtime/build.sh | 4 + runtime/init.sh | 6 + runtime/src/lib.rs | 16 ++ state_machine/src/backend.rs | 34 ++-- state_machine/src/ext.rs | 122 +++-------- state_machine/src/lib.rs | 190 ++++++++---------- 21 files changed, 338 insertions(+), 316 deletions(-) rename contracts/src/{executor.rs => rust_executor.rs} (55%) create mode 100644 contracts/src/wasm_executor.rs create mode 100644 runtime/Cargo.lock create mode 100644 runtime/Cargo.toml create mode 100755 runtime/build.sh create mode 100755 runtime/init.sh create mode 100644 runtime/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2956ffe63d1d9..dac6fe5d14399 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,13 +1,3 @@ -[root] -name = "polkadot-validator" -version = "0.1.0" -dependencies = [ - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-primitives 0.1.0", - "polkadot-serializer 0.1.0", - "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "aho-corasick" version = "0.6.3" @@ -552,6 +542,16 @@ dependencies = [ "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-wasm" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.4.8" @@ -641,6 +641,7 @@ version = "0.1.0" dependencies = [ "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", "polkadot-serializer 0.1.0", "polkadot-state-machine 0.1.0", @@ -706,6 +707,16 @@ dependencies = [ "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "polkadot-validator" +version = "0.1.0" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-primitives 0.1.0", + "polkadot-serializer 0.1.0", + "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pretty_assertions" version = "0.4.0" @@ -1163,6 +1174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" "checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum parity-wasm 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57f3b2611a0c890a290b408be079dcbb10291b3b101bcd058032c55fb2c6dadc" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595" "checksum patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1e2f638d79aba5c4a71a4f373df6e3cd702250a53b7f0ed4da1e2a7be9737ae" diff --git a/Cargo.toml b/Cargo.toml index 84e987c436ece..86bba6f302fe4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,6 @@ members = [ "state_machine", "validator", ] +exclude = [ + "runtime" +] diff --git a/client/src/lib.rs b/client/src/lib.rs index 642dab79e2d0e..4c47c7dc7f04b 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -27,7 +27,7 @@ extern crate error_chain; pub mod error; use primitives::{block, Address, H256}; -use primitives::contract::{CallData, OutData, StorageData}; +use primitives::contract::{CallData, OutData, StorageKey, StorageData}; use state_machine::backend::Backend; use self::error::ResultExt; @@ -72,15 +72,15 @@ impl Client where } /// Return single storage entry of contract under given address in state in a block of given hash. - pub fn storage(&self, hash: &block::HeaderHash, address: &Address, key: &H256) -> error::Result { + pub fn storage(&self, hash: &block::HeaderHash, object: u64, key: &StorageKey) -> error::Result { self.state_at(hash)? - .storage(address, key) + .storage(object, &key.0) .map(|x| StorageData(x.to_vec())) .chain_err(|| error::ErrorKind::Backend) } /// Execute a call to a contract on top of state in a block of given hash. - pub fn call(&self, hash: &block::HeaderHash, address: &Address, method: &str, call_data: &CallData) -> error::Result { + pub fn call(&self, hash: &block::HeaderHash, method: &str, call_data: &CallData) -> error::Result { let state = self.state_at(hash)?; let mut changes = state_machine::OverlayedChanges::default(); @@ -88,7 +88,6 @@ impl Client where &state, &mut changes, &self.executor, - address, method, call_data, )?) diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 84d52c87f83b7..68dfc7810bb49 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -10,6 +10,7 @@ polkadot-serializer = { path = "../serializer", version = "0.1" } polkadot-state-machine = { path = "../state_machine" , version = "0.1" } serde = "1.0" serde_derive = "1.0" +parity-wasm = "0.17.0" [dev-dependencies] assert_matches = "1.1" diff --git a/contracts/src/auth.rs b/contracts/src/auth.rs index a244526ddd83c..93acc647ea799 100644 --- a/contracts/src/auth.rs +++ b/contracts/src/auth.rs @@ -18,7 +18,7 @@ use primitives::Address; use state_machine::StaticExternalities; use error::Result; -use executor::RustExecutor; +use rust_executor::RustExecutor; /// Data and some sort of Authentication Data type DataAndAuth = (Vec, Vec); diff --git a/contracts/src/balances.rs b/contracts/src/balances.rs index d5e3fd6b662fb..187e447f393dd 100644 --- a/contracts/src/balances.rs +++ b/contracts/src/balances.rs @@ -19,7 +19,7 @@ use primitives::uint::U256; use state_machine::{Externalities, StaticExternalities}; use error::Result; -use executor::RustExecutor; +use rust_executor::RustExecutor; #[derive(Debug, Serialize, Deserialize)] pub struct Transfer { diff --git a/contracts/src/error.rs b/contracts/src/error.rs index 04e3144ed66ba..13f302a2831ea 100644 --- a/contracts/src/error.rs +++ b/contracts/src/error.rs @@ -42,5 +42,11 @@ error_chain! { description("externalities failure"), display("Externalities error: {}", e), } + + /// Runtime failed. + Runtime { + description("runtime failure"), + display("Runtime error"), + } } } diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs index 6226edf4fcc8a..5318d80b112c4 100644 --- a/contracts/src/lib.rs +++ b/contracts/src/lib.rs @@ -17,6 +17,13 @@ //! Temporary crate for contracts implementations. //! //! This will be replaced with WASM contracts stored on-chain. +//! ** NOTE *** +//! This is entirely deprecated with the idea of a single-module Wasm module for state transition. +//! The dispatch table should be replaced with the specific functions needed: +//! - execute_block(bytes) +//! - init_block(PrevBlock?) -> InProgressBlock +//! - add_transaction(InProgressBlock) -> InProgressBlock +//! I leave it as is for now as it might be removed before this is ever done. #![warn(missing_docs)] @@ -24,24 +31,27 @@ extern crate polkadot_primitives as primitives; extern crate polkadot_serializer as serializer; extern crate polkadot_state_machine as state_machine; extern crate serde; +extern crate parity_wasm; #[macro_use] extern crate error_chain; -#[macro_use] -extern crate serde_derive; +//#[macro_use] +//extern crate serde_derive; #[cfg(test)] #[macro_use] extern crate assert_matches; - +/* mod auth; mod balances; mod validator_set; +pub mod rust_executor; +*/ +mod wasm_executor; pub mod error; -pub mod executor; /// Creates new RustExecutor for contracts. -pub fn executor() -> executor::RustExecutor { - executor::RustExecutor::default() +pub fn executor() -> wasm_executor::WasmExecutor { + wasm_executor::WasmExecutor::default() } diff --git a/contracts/src/executor.rs b/contracts/src/rust_executor.rs similarity index 55% rename from contracts/src/executor.rs rename to contracts/src/rust_executor.rs index dde20b42576fe..6e5f55ab23e91 100644 --- a/contracts/src/executor.rs +++ b/contracts/src/rust_executor.rs @@ -36,43 +36,9 @@ pub struct RustExecutor { validator_set: validator_set::Contract, } -impl RustExecutor { - const AUTH: u8 = 1; - const BALANCES: u8 = 2; - const VALIDATOR_SET: u8 = 3; -} - impl CodeExecutor for RustExecutor { type Error = Error; - fn call_static>( - &self, - ext: &E, - code: &[u8], - method: &str, - data: &CallData, - ) -> Result { - ensure!(code.len() == 1, ErrorKind::InvalidCode(code.to_vec())); - - Ok(OutData(match code[0] { - Self::AUTH => match method { - "check_auth" => ser(&self.auth.check_auth(ext, de(&data.0)?)?), - m => bail!(ErrorKind::MethodNotFound(m.to_owned())), - }, - Self::BALANCES => match method { - "balance_of" => ser(&self.balance.balance_of(ext, de(&data.0)?)?), - "next_nonce" => ser(&self.balance.next_nonce(ext, de(&data.0)?)?), - "transfer_preconditions" => ser(&self.balance.transfer_preconditions(ext, de(&data.0)?)?), - m => bail!(ErrorKind::MethodNotFound(m.to_owned())), - }, - Self::VALIDATOR_SET => match method { - "validator_set" => ser(&self.validator_set.validator_set(ext, de(&data.0)?)?), - m => bail!(ErrorKind::MethodNotFound(m.to_owned())), - }, - c => bail!(ErrorKind::InvalidCode(vec![c])), - })) - } - fn call>( &self, ext: &mut E, @@ -82,12 +48,14 @@ impl CodeExecutor for RustExecutor { ) -> Result { ensure!(code.len() == 1, ErrorKind::InvalidCode(code.to_vec())); - Ok(OutData(match code[0] { - Self::BALANCES=> match method { - "transfer" => ser(&self.balance.transfer(ext, de(&data.0)?)?), - m => bail!(ErrorKind::MethodNotFound(m.to_owned())), - }, - c => bail!(ErrorKind::InvalidCode(vec![c])), + Ok(OutData(match method { + "check_auth" => ser(&self.auth.check_auth(ext, de(&data.0)?)?), + "balance_of" => ser(&self.balance.balance_of(ext, de(&data.0)?)?), + "next_nonce" => ser(&self.balance.next_nonce(ext, de(&data.0)?)?), + "transfer_preconditions" => ser(&self.balance.transfer_preconditions(ext, de(&data.0)?)?), + "validator_set" => ser(&self.validator_set.validator_set(ext, de(&data.0)?)?), + "transfer" => ser(&self.balance.transfer(ext, de(&data.0)?)?), + m => bail!(ErrorKind::MethodNotFound(m.to_owned())), })) } } @@ -122,26 +90,6 @@ mod tests { } } - #[test] - fn should_fail_for_empty_or_unknown_code() { - // given - let mut ext = TestExternalities::default(); - let executor = RustExecutor::default(); - - assert_matches!( - *executor.call(&mut ext, &[], "any", &CallData(vec![])).unwrap_err().kind(), - ErrorKind::InvalidCode(ref code) if code.is_empty() - ); - assert_matches!( - *executor.call(&mut ext, &[1, 2], "any", &CallData(vec![])).unwrap_err().kind(), - ErrorKind::InvalidCode(ref code) if code.len() == 2 - ); - assert_matches!( - *executor.call(&mut ext, &[255,], "any", &CallData(vec![])).unwrap_err().kind(), - ErrorKind::InvalidCode(_) - ); - } - #[test] fn should_fail_on_invalid_method() { // given @@ -149,7 +97,7 @@ mod tests { let executor = RustExecutor::default(); assert_matches!( - *executor.call(&mut ext, &[2], "any", &CallData(vec![])).unwrap_err().kind(), + *executor.call(&mut ext, "any", &CallData(vec![])).unwrap_err().kind(), ErrorKind::MethodNotFound(ref method) if &*method == "any" ); } diff --git a/contracts/src/validator_set.rs b/contracts/src/validator_set.rs index 7462781c6b73a..08088a30e8b12 100644 --- a/contracts/src/validator_set.rs +++ b/contracts/src/validator_set.rs @@ -18,7 +18,7 @@ use primitives::Address; use state_machine::StaticExternalities; use error::Result; -use executor::RustExecutor; +use rust_executor::RustExecutor; /// Harcoded validator set contract. #[derive(Debug, Default)] diff --git a/contracts/src/wasm_executor.rs b/contracts/src/wasm_executor.rs new file mode 100644 index 0000000000000..9cd1117dc1b96 --- /dev/null +++ b/contracts/src/wasm_executor.rs @@ -0,0 +1,100 @@ +// 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 . + +//! Rust implementation of Polkadot contracts. + +use parity_wasm; + +use primitives::contract::{CallData, OutData}; +//use serializer::{from_slice as de, to_vec as ser}; +use state_machine::{Externalities, CodeExecutor}; + +use error::{Error, ErrorKind, Result}; + +/// Dummy rust executor for contracts. +/// +/// Instead of actually executing the provided code it just +/// dispatches the calls to pre-defined hardcoded implementations in rust. +#[derive(Debug, Default)] +pub struct WasmExecutor { +} + +impl CodeExecutor for WasmExecutor { + type Error = Error; + + fn call( + &self, + ext: &mut E, + method: &str, + data: &CallData, + ) -> Result { + + // TODO: avoid copying code by requiring code to remain immutable through execution, + // splitting it off from potentially mutable externalities. + let code = match ext.code() { + Ok(e) => e.to_owned(), + Err(e) => Err(ErrorKind::Externalities(Box::new(e)))?, + }; + + use parity_wasm::ModuleInstanceInterface; + use parity_wasm::RuntimeValue::{I64}; + let program = parity_wasm::ProgramInstance::new(); + let module = parity_wasm::deserialize_buffer(code).expect("Failed to load module"); + let module = program.add_module("main", module, None).expect("Failed to initialize module"); + module.execute_export(method, vec![I64(data.0.len() as i64)].into()) + .map(|o| OutData(vec![1; if let Some(I64(l)) = o { l as usize } else { 0 }])) + .map_err(|_| ErrorKind::Runtime.into()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use primitives::Address; + use primitives::hash::H256; + + use parity_wasm; + + #[derive(Debug, Default)] + struct TestExternalities; + impl Externalities for TestExternalities { + fn set_storage(&mut self, _object: i64, _key: Vec, _value: Vec) { + unimplemented!() + } + } + + impl StaticExternalities for TestExternalities { + type Error = Error; + + fn storage(&self, _object: i64, _key: Vec) -> Result<&[u8]> { + unimplemented!() + } + } + + #[test] + fn should_run_wasm() { + use parity_wasm::ModuleInstanceInterface; + use parity_wasm::RuntimeValue::{I64}; + + let program = parity_wasm::ProgramInstance::new(); + let test_module = include_bytes!("../../target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = program.add_module("test", module, None).expect("Failed to initialize module"); + let argument: i64 = 42; + assert_eq!(Some(I64(argument * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); + } +} diff --git a/primitives/src/contract.rs b/primitives/src/contract.rs index 33504d1b03f6e..02411b73d790f 100644 --- a/primitives/src/contract.rs +++ b/primitives/src/contract.rs @@ -26,6 +26,10 @@ pub struct CallData(#[serde(with="bytes")] pub Vec); #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct OutData(#[serde(with="bytes")] pub Vec); +/// Contract storage key. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StorageKey(#[serde(with="bytes")] pub Vec); + /// Contract storage entry data. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct StorageData(#[serde(with="bytes")] pub Vec); diff --git a/rpc/src/state/mod.rs b/rpc/src/state/mod.rs index 3ba8758bd4ad7..b3ab79147eab6 100644 --- a/rpc/src/state/mod.rs +++ b/rpc/src/state/mod.rs @@ -22,8 +22,8 @@ mod error; mod tests; use client::{self, Client}; -use primitives::{block, Address, H256}; -use primitives::contract::{CallData, OutData, StorageData}; +use primitives::{block}; +use primitives::contract::{CallData, OutData, StorageKey, StorageData}; use state_machine; use self::error::Result; @@ -33,11 +33,11 @@ build_rpc_trait! { pub trait StateApi { /// Returns a storage entry. #[rpc(name = "state_getStorage")] - fn storage(&self, Address, H256, block::HeaderHash) -> Result; + fn storage(&self, u64, StorageKey, block::HeaderHash) -> Result; /// Call a contract. #[rpc(name = "state_call")] - fn call(&self, Address, String, CallData, block::HeaderHash) -> Result; + fn call(&self, String, CallData, block::HeaderHash) -> Result; } } @@ -45,11 +45,11 @@ impl StateApi for Client where B: client::Blockchain + Send + Sync + 'static, E: state_machine::CodeExecutor + Send + Sync + 'static, { - fn storage(&self, address: Address, key: H256, block: block::HeaderHash) -> Result { - Ok(self.storage(&block, &address, &key)?) + fn storage(&self, object: u64, key: StorageKey, block: block::HeaderHash) -> Result { + Ok(self.storage(&block, object, &key)?) } - fn call(&self, address: Address, method: String, data: CallData, block: block::HeaderHash) -> Result { - Ok(self.call(&block, &address, &method, &data)?) + fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result { + Ok(self.call(&block, &method, &data)?) } } diff --git a/runtime/Cargo.lock b/runtime/Cargo.lock new file mode 100644 index 0000000000000..54ac945ce8e19 --- /dev/null +++ b/runtime/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "runtime" +version = "0.1.0" + diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml new file mode 100644 index 0000000000000..2c97c09b56394 --- /dev/null +++ b/runtime/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "runtime" +version = "0.1.0" +authors = ["Parity Technologies "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] diff --git a/runtime/build.sh b/runtime/build.sh new file mode 100755 index 0000000000000..66b2d2f5494ee --- /dev/null +++ b/runtime/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cargo +nightly build --target=wasm32-unknown-unknown --release +wasm-gc target/wasm32-unknown-unknown/release/runtime.wasm target/wasm32-unknown-unknown/release/runtime.compact.wasm diff --git a/runtime/init.sh b/runtime/init.sh new file mode 100755 index 0000000000000..02a0059a87584 --- /dev/null +++ b/runtime/init.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +rustup update nightly +rustup target add wasm32-unknown-unknown --toolchain nightly +rustup update stable +cargo install --git https://github.com/alexcrichton/wasm-gc diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs new file mode 100644 index 0000000000000..d8975a452b526 --- /dev/null +++ b/runtime/src/lib.rs @@ -0,0 +1,16 @@ +#![no_std] +#![feature(lang_items)] + +#[lang = "panic_fmt"] +fn panic_fmt() -> ! { + loop {} +} + +#[no_mangle] +pub fn main(data_length: u64) -> u64 { + do_something(data_length) +} + +fn do_something(param: u64) -> u64 { + param * 2 +} diff --git a/state_machine/src/backend.rs b/state_machine/src/backend.rs index c7f694712e24e..78778531b9319 100644 --- a/state_machine/src/backend.rs +++ b/state_machine/src/backend.rs @@ -17,8 +17,7 @@ //! State machine backends. These manage the code and storage of contracts. use std::{error, fmt}; - -use primitives::Address; +use primitives::hash; use primitives::hash::H256; use triehash::sec_trie_root; @@ -27,7 +26,7 @@ use super::{Update, MemoryState}; /// Output of a commit. pub struct Committed { /// Root of the code tree after changes committed. - pub code_tree_root: H256, + pub code_hash: H256, /// Root of the storage tree after changes committed. pub storage_tree_root: H256, } @@ -38,11 +37,8 @@ pub trait Backend { /// An error type when fetching data is not possible. type Error: super::Error; - /// Get code associated with specific address. - fn code(&self, address: &Address) -> Result<&[u8], Self::Error>; - /// Get keyed storage associated with specific address. - fn storage(&self, address: &Address, key: &H256) -> Result<&[u8], Self::Error>; + fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error>; /// Commit updates to the backend and get new state. fn commit(&mut self, changes: I) -> Committed @@ -71,15 +67,13 @@ pub struct InMemory { inner: MemoryState, // keeps all the state in memory. } +const EMPTY_BYTES: [u8; 0] = []; + impl Backend for InMemory { type Error = Void; - fn code(&self, address: &Address) -> Result<&[u8], Void> { - Ok(self.inner.code(address).unwrap_or(&[])) - } - - fn storage(&self, address: &Address, key: &H256) -> Result<&[u8], Void> { - Ok(self.inner.storage(address, key).unwrap_or(&[])) + fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Void> { + Ok(self.inner.storage(object, key).unwrap_or(&[])) } fn commit(&mut self, changes: I) -> Committed @@ -88,22 +82,18 @@ impl Backend for InMemory { self.inner.update(changes); // fully recalculate trie roots. - - let storage_roots = self.inner.storage.iter().map(|(addr, storage)| { + use std::mem::transmute; + let storage_roots = self.inner.storage.iter().map(|(object, storage)| { let flat_trie = storage.iter().map(|(k, v)| (k.to_vec(), v.clone())).collect(); - (addr.to_vec(), sec_trie_root(flat_trie).to_vec()) + (unsafe { transmute::(object.to_be()) }.to_vec(), sec_trie_root(flat_trie).to_vec()) }).collect(); let storage_tree_root = H256(sec_trie_root(storage_roots).0); - let code_tree_root = sec_trie_root( - self.inner.code.iter().map(|(k, v)| (k.to_vec(), v.clone())).collect() - ); - - let code_tree_root = H256(code_tree_root.0); + let code_hash = hash(&self.inner.code().unwrap_or_else(|| &EMPTY_BYTES)); Committed { - code_tree_root, + code_hash, storage_tree_root, } } diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index 455fc4d22ef29..0207b7b3b1530 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -19,10 +19,7 @@ use std::{error, fmt}; use backend::Backend; -use primitives::Address; -use primitives::contract::{CallData, OutData}; -use primitives::hash::H256; -use {Externalities, CodeExecutor, StaticExternalities, OverlayedChanges}; +use {Externalities, StaticExternalities, OverlayedChanges}; /// Errors that can occur when interacting with the externalities. #[derive(Debug, Copy, Clone)] @@ -52,122 +49,61 @@ impl error::Error for Error { } /// Wraps a read-only backend, call executor, and current overlayed changes. -pub struct Ext<'a, B: 'a, E: 'a> { +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, - /// Contract code executor. - pub exec: &'a E, - /// Contract address. - pub local: Address, } -impl<'a, B: 'a, E: 'a> StaticExternalities for Ext<'a, B, E> - where B: Backend, E: CodeExecutor +impl<'a, B: 'a> StaticExternalities for Ext<'a, B> + where B: Backend { - type Error = Error; + type Error = B::Error; - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { - match self.overlay.storage(&self.local, key) { - Some(x) => Ok(x), - None => self.backend.storage(&self.local, key).map_err(Error::Backend) - } + fn code(&self) -> Result<&[u8], Self::Error> { + Ok(self.overlay.code()) } - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result { - let inner_ext = StaticExt { - backend: self.backend, - exec: self.exec, - local: address.clone(), - overlay: self.overlay, - }; - - let code = match self.overlay.code(address) { - Some(x) => x, - None => self.backend.code(address).map_err(Error::Backend)?, - }; - - self.exec.call_static( - &inner_ext, - code, - method, - data, - ).map_err(Error::Executor) + fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error> { + match self.overlay.storage(object, key) { + Some(x) => Ok(x), + None => self.backend.storage(object, key) + } } } -impl<'a, B: 'a, E: 'a> Externalities for Ext<'a, B, E> - where B: Backend, E: CodeExecutor +impl<'a, B: 'a> Externalities for Ext<'a, B> + where B: Backend { - fn set_storage(&mut self, key: H256, value: Vec) { - self.overlay.set_storage(self.local, key, value); + fn set_code(&mut self, code: Vec) { + self.overlay.set_code(code); } - fn call(&mut self, address: &Address, method: &str, data: &CallData) -> Result { - let code = { - let code = match self.overlay.code(address) { - Some(x) => x, - None => self.backend.code(address).map_err(Error::Backend)?, - }; - - code.to_owned() - }; - - let mut inner_ext = Ext { - backend: self.backend, - exec: self.exec, - local: address.clone(), - overlay: &mut *self.overlay, - }; - - self.exec.call( - &mut inner_ext, - &code[..], - method, - data, - ).map_err(Error::Executor) + fn set_storage(&mut self, object: u64, key: Vec, value: Vec) { + self.overlay.set_storage(object, key, value); } } // Static externalities -struct StaticExt<'a, B: 'a, E: 'a> { +struct StaticExt<'a, B: 'a> { overlay: &'a OverlayedChanges, backend: &'a B, - exec: &'a E, - local: Address, } -impl<'a, B: 'a, E: 'a> StaticExternalities for StaticExt<'a, B, E> - where B: Backend, E: CodeExecutor +impl<'a, B: 'a> StaticExternalities for StaticExt<'a, B> + where B: Backend { - type Error = Error; + type Error = B::Error; - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { - match self.overlay.storage(&self.local, key) { - Some(x) => Ok(x), - None => self.backend.storage(&self.local, key).map_err(Error::Backend) - } + fn code(&self) -> Result<&[u8], Self::Error> { + Ok(self.overlay.code()) } - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result { - let inner_ext = StaticExt { - backend: self.backend, - exec: self.exec, - local: address.clone(), - overlay: self.overlay, - }; - - let code = match self.overlay.code(address) { - Some(x) => x, - None => self.backend.code(address).map_err(Error::Backend)?, - }; - - self.exec.call_static( - &inner_ext, - code, - method, - data, - ).map_err(Error::Executor) + fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error> { + match self.overlay.storage(object, key) { + Some(x) => Ok(x), + None => self.backend.storage(object, key) + } } } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 9eb6039fa3210..31682c6dc5b67 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -30,46 +30,44 @@ extern crate triehash; use std::collections::HashMap; use std::fmt; -use primitives::Address; use primitives::contract::{CallData, OutData}; -use primitives::hash::H256; pub mod backend; mod ext; /// Updates to be committed to the state. pub enum Update { - /// Set storage of address at given key -- empty is deletion. - Storage(Address, H256, Vec), - /// Set code of address -- empty is deletion. - Code(Address, Vec), + /// Set storage of object at given key -- empty is deletion. + Storage(u64, Vec, Vec), + /// Set code -- empty is deletion. + Code(Vec), } // in-memory section of the state. #[derive(Default)] struct MemoryState { - code: HashMap>, - storage: HashMap>>, + code: Option>, // None is unchanged. + storage: HashMap, Vec>>, } impl MemoryState { - fn code(&self, address: &Address) -> Option<&[u8]> { - self.code.get(address).map(|v| &v[..]) + fn code(&self) -> Option<&[u8]> { + self.code.as_ref().map(|x| &**x) } - fn storage(&self, address: &Address, key: &H256) -> Option<&[u8]> { - self.storage.get(address) + fn storage(&self, object: u64, key: &[u8]) -> Option<&[u8]> { + self.storage.get(&object) .and_then(|m| m.get(key)) .map(|v| &v[..]) } #[allow(unused)] - fn set_code(&mut self, address: Address, code: Vec) { - self.code.insert(address, code); + fn set_code(&mut self, code: Vec) { + self.code = Some(code); } - fn set_storage(&mut self, address: Address, key: H256, val: Vec) { - self.storage.entry(address) + fn set_storage(&mut self, object: u64, key: Vec, val: Vec) { + self.storage.entry(object) .or_insert_with(HashMap::new) .insert(key, val); } @@ -77,27 +75,23 @@ impl MemoryState { fn update(&mut self, changes: I) where I: IntoIterator { for update in changes { match update { - Update::Storage(addr, key, val) => { + Update::Storage(object, key, val) => { if val.is_empty() { let mut empty = false; - if let Some(s) = self.storage.get_mut(&addr) { + if let Some(s) = self.storage.get_mut(&object) { s.remove(&key); empty = s.is_empty(); }; - if empty { self.storage.remove(&addr); } + if empty { self.storage.remove(&object); } } else { - self.storage.entry(addr) + self.storage.entry(object) .or_insert_with(HashMap::new) .insert(key, val); } } - Update::Code(addr, code) => { - if code.is_empty() { - self.code.remove(&addr); - } else { - self.code.insert(addr, code); - } + Update::Code(code) => { + self.code = Some(code); } } } @@ -114,42 +108,44 @@ pub struct OverlayedChanges { committed: MemoryState, } +const EMPTY_BYTES: [u8; 0] = []; + impl OverlayedChanges { - fn code(&self, address: &Address) -> Option<&[u8]> { - self.prospective.code(address) - .or_else(|| self.committed.code(address)) - .and_then(|v| if v.is_empty() { None } else { Some(v) }) + fn code(&self) -> &[u8] { + self.prospective.code() + .or_else(|| self.committed.code()) + .unwrap_or_else(|| &EMPTY_BYTES) } - fn storage(&self, address: &Address, key: &H256) -> Option<&[u8]> { - self.prospective.storage(address, key) - .or_else(|| self.committed.storage(address, key)) + fn storage(&self, object: u64, key: &[u8]) -> Option<&[u8]> { + self.prospective.storage(object, key) + .or_else(|| self.committed.storage(object, key)) .and_then(|v| if v.is_empty() { None } else { Some(v) }) } #[allow(unused)] - fn set_code(&mut self, address: Address, code: Vec) { - self.prospective.set_code(address, code); + fn set_code(&mut self, code: Vec) { + self.prospective.set_code(code); } - fn set_storage(&mut self, address: Address, key: H256, val: Vec) { - self.prospective.set_storage(address, key, val); + fn set_storage(&mut self, object: u64, key: Vec, val: Vec) { + self.prospective.set_storage(object, key, val); } /// Discard prospective changes to state. pub fn discard_prospective(&mut self) { - self.prospective.code.clear(); + self.prospective.code = None; self.prospective.storage.clear(); } /// Commit prospective changes to state. pub fn commit_prospective(&mut self) { - let code_updates = self.prospective.code.drain() - .map(|(addr, code)| Update::Code(addr, code)); + let code_updates = self.prospective.code.take().into_iter() + .map(|code| Update::Code(code)); let storage_updates = self.prospective.storage.drain() - .flat_map(|(addr, storages)| storages.into_iter().map(move |(k, v)| (addr, k, v))) - .map(|(addr, key, value)| Update::Storage(addr, key, value)); + .flat_map(|(object, storages)| storages.into_iter().map(move |(k, v)| (object, k, v))) + .map(|(object, key, value)| Update::Storage(object, key, value)); self.committed.update(code_updates.chain(storage_updates)); } @@ -162,56 +158,45 @@ pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} impl Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} /// Externalities: pinned to specific active address. -pub trait Externalities: StaticExternalities { - /// Read storage of current contract being called. - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { - StaticExternalities::storage(self, key) +pub trait Externalities: StaticExternalities { +/* // Get the current runtime. + fn code(&self) -> Result<&[u8], Self::Error> { + StaticExternalities::code(self) } +*/ + /// Set the new runtime. + fn set_code(&mut self, code: Vec); - /// Set storage of current contract being called. - fn set_storage(&mut self, key: H256, value: Vec); - - /// Make a sub-call to another contract. - fn call(&mut self, address: &Address, method: &str, data: &CallData) -> Result; - - /// Make a static (read-only) call to another contract. - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result { - StaticExternalities::call_static(self, address, method, data) +/* /// Read storage of current contract being called. + fn storage(&self, index: u64, key: &[u8]) -> Result<&[u8], Self::Error> { + StaticExternalities::storage(self, index, key) } +*/ + /// Set storage of current contract being called. + fn set_storage(&mut self, index: u64, key: Vec, value: Vec); } /// Static externalities: used only for read-only requests. -pub trait StaticExternalities { +pub trait StaticExternalities { /// Externalities error type. type Error: Error; - /// Read storage of current contract being called. - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error>; + /// Get the current runtime. + fn code(&self) -> Result<&[u8], Self::Error>; - /// Make a static (read-only) call to another contract. - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result; + /// Read storage of current contract being called. + fn storage(&self, index: u64, key: &[u8]) -> Result<&[u8], Self::Error>; } -/// Contract code executor. +/// Code execution engine. pub trait CodeExecutor: Sized { - /// Error type for contract execution. + /// Externalities error type. type Error: Error; - /// Execute a contract in read-only mode. - /// The execution is not allowed to modify the state. - fn call_static>( - &self, - ext: &E, - code: &[u8], - method: &str, - data: &CallData, - ) -> Result; - - /// Execute a contract. - fn call>( + /// Call a given method in the runtime. + fn call( &self, ext: &mut E, - code: &[u8], method: &str, data: &CallData, ) -> Result; @@ -224,26 +209,18 @@ pub fn execute( backend: &B, overlay: &mut OverlayedChanges, exec: &Exec, - address: &Address, method: &str, call_data: &CallData, ) -> Result> { - let code = match overlay.code(address) { - Some(x) => x.to_owned(), - None => backend.code(address).map_err(|e| Box::new(e) as _)?.to_owned(), - }; let result = { let mut externalities = ext::Ext { backend, - exec, - overlay: &mut *overlay, - local: *address, + overlay: &mut *overlay }; exec.call( &mut externalities, - &code[..], method, call_data, ) @@ -265,57 +242,54 @@ pub fn execute( mod tests { use super::OverlayedChanges; - use primitives::hash::H256; - use primitives::Address; - #[test] fn overlayed_storage_works() { let mut overlayed = OverlayedChanges::default(); - let key = H256::random(); - let addr = Address::random(); + let key = vec![42, 69, 169, 142]; + let object = 69; - assert!(overlayed.storage(&addr, &key).is_none()); + assert!(overlayed.storage(object, &key).is_none()); - overlayed.set_storage(addr, key, vec![1, 2, 3]); - assert_eq!(overlayed.storage(&addr, &key).unwrap(), &[1, 2, 3]); + overlayed.set_storage(object, key, vec![1, 2, 3]); + assert_eq!(overlayed.storage(object, &key).unwrap(), &[1, 2, 3]); overlayed.commit_prospective(); - assert_eq!(overlayed.storage(&addr, &key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(object, &key).unwrap(), &[1, 2, 3]); - overlayed.set_storage(addr, key, vec![]); - assert!(overlayed.storage(&addr, &key).is_none()); + overlayed.set_storage(object, key, vec![]); + assert!(overlayed.storage(object, &key).is_none()); overlayed.discard_prospective(); - assert_eq!(overlayed.storage(&addr, &key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(object, &key).unwrap(), &[1, 2, 3]); - overlayed.set_storage(addr, key, vec![]); + overlayed.set_storage(object, key, vec![]); overlayed.commit_prospective(); - assert!(overlayed.storage(&addr, &key).is_none()); + assert!(overlayed.storage(object, &key).is_none()); } #[test] fn overlayed_code_works() { let mut overlayed = OverlayedChanges::default(); - let addr = Address::random(); + let object = 69; - assert!(overlayed.code(&addr).is_none()); + assert!(overlayed.code(&object).is_none()); - overlayed.set_code(addr, vec![1, 2, 3]); - assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]); + overlayed.set_code(object, vec![1, 2, 3]); + assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); overlayed.commit_prospective(); - assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); - overlayed.set_code(addr, vec![]); - assert!(overlayed.code(&addr).is_none()); + overlayed.set_code(object, vec![]); + assert!(overlayed.code(&object).is_none()); overlayed.discard_prospective(); - assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); - overlayed.set_code(addr, vec![]); + overlayed.set_code(object, vec![]); overlayed.commit_prospective(); - assert!(overlayed.code(&addr).is_none()); + assert!(overlayed.code(&object).is_none()); } } From 6237848966eda37f51c795ab2b8c667ab9458e89 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 6 Dec 2017 10:01:03 +0100 Subject: [PATCH 02/49] Remove old Rust-runtime code. --- contracts/src/auth.rs | 38 ------------ contracts/src/balances.rs | 64 -------------------- contracts/src/lib.rs | 7 +-- contracts/src/rust_executor.rs | 104 --------------------------------- contracts/src/validator_set.rs | 31 ---------- 5 files changed, 1 insertion(+), 243 deletions(-) delete mode 100644 contracts/src/auth.rs delete mode 100644 contracts/src/balances.rs delete mode 100644 contracts/src/rust_executor.rs delete mode 100644 contracts/src/validator_set.rs diff --git a/contracts/src/auth.rs b/contracts/src/auth.rs deleted file mode 100644 index 93acc647ea799..0000000000000 --- a/contracts/src/auth.rs +++ /dev/null @@ -1,38 +0,0 @@ -// 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 . - -use primitives::Address; -use state_machine::StaticExternalities; - -use error::Result; -use rust_executor::RustExecutor; - -/// Data and some sort of Authentication Data -type DataAndAuth = (Vec, Vec); - -/// Authentication contract rust implementation. -#[derive(Debug, Default)] -pub struct Contract; -impl Contract { - /// Verify authentication data. - /// - /// Given Message and Authentication Data verifies it and returns: - /// 1. None in case it doesn't match (i.e. signature is invalid) - /// 2. A address who signed that Message. - pub fn check_auth>(&self, _ext: &E, _data: DataAndAuth) -> Result> { - unimplemented!() - } -} diff --git a/contracts/src/balances.rs b/contracts/src/balances.rs deleted file mode 100644 index 187e447f393dd..0000000000000 --- a/contracts/src/balances.rs +++ /dev/null @@ -1,64 +0,0 @@ -// 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 . - -use primitives::Address; -use primitives::uint::U256; -use state_machine::{Externalities, StaticExternalities}; - -use error::Result; -use rust_executor::RustExecutor; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Transfer { - /// Transfer value - value: U256, - /// Transfer destination - to: Address, - /// Replay protection - nonce: U256, - /// Data authorizing the transfer (we can derive sender from it) - authentication_data: Vec, -} - -/// Balances contract rust implementation. -#[derive(Debug, Default)] -pub struct Contract; -impl Contract { - /// Returns a balance of given address. - pub fn balance_of>(&self, _ext: &E, _data: Address) -> Result { - unimplemented!() - } - - /// Returns the next nonce to authorize the transfer from given address. - pub fn next_nonce>(&self, _ext: &E, _data: Address) -> Result { - unimplemented!() - } - - /// Checks preconditions for transfer. - /// Should verify: - /// - signature - /// - replay protection - /// - enough balance - pub fn transfer_preconditions>(&self, _db: &E, _data: Transfer) -> Result { - unimplemented!() - } - - /// Perform a transfer. - /// This should first make sure that precondtions are satisfied and later perform the transfer. - pub fn transfer>(&self, _ext: &mut E, _data: Transfer) -> Result { - unimplemented!() - } -} diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs index 5318d80b112c4..3c2ab759a08a8 100644 --- a/contracts/src/lib.rs +++ b/contracts/src/lib.rs @@ -41,12 +41,7 @@ extern crate error_chain; #[cfg(test)] #[macro_use] extern crate assert_matches; -/* -mod auth; -mod balances; -mod validator_set; -pub mod rust_executor; -*/ + mod wasm_executor; pub mod error; diff --git a/contracts/src/rust_executor.rs b/contracts/src/rust_executor.rs deleted file mode 100644 index 6e5f55ab23e91..0000000000000 --- a/contracts/src/rust_executor.rs +++ /dev/null @@ -1,104 +0,0 @@ -// 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 . - -//! Rust implementation of Polkadot contracts. - -use primitives::contract::{CallData, OutData}; -use serializer::{from_slice as de, to_vec as ser}; -use state_machine::{StaticExternalities, Externalities, CodeExecutor}; - -use error::{Error, ErrorKind, Result}; -use auth; -use balances; -use validator_set; - -/// Dummy rust executor for contracts. -/// -/// Instead of actually executing the provided code it just -/// dispatches the calls to pre-defined hardcoded implementations in rust. -#[derive(Debug, Default)] -pub struct RustExecutor { - auth: auth::Contract, - balance: balances::Contract, - validator_set: validator_set::Contract, -} - -impl CodeExecutor for RustExecutor { - type Error = Error; - - fn call>( - &self, - ext: &mut E, - code: &[u8], - method: &str, - data: &CallData, - ) -> Result { - ensure!(code.len() == 1, ErrorKind::InvalidCode(code.to_vec())); - - Ok(OutData(match method { - "check_auth" => ser(&self.auth.check_auth(ext, de(&data.0)?)?), - "balance_of" => ser(&self.balance.balance_of(ext, de(&data.0)?)?), - "next_nonce" => ser(&self.balance.next_nonce(ext, de(&data.0)?)?), - "transfer_preconditions" => ser(&self.balance.transfer_preconditions(ext, de(&data.0)?)?), - "validator_set" => ser(&self.validator_set.validator_set(ext, de(&data.0)?)?), - "transfer" => ser(&self.balance.transfer(ext, de(&data.0)?)?), - m => bail!(ErrorKind::MethodNotFound(m.to_owned())), - })) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use primitives::Address; - use primitives::hash::H256; - - #[derive(Debug, Default)] - struct TestExternalities; - impl Externalities for TestExternalities { - fn set_storage(&mut self, _key: H256, _value: Vec) { - unimplemented!() - } - - fn call(&mut self, _address: &Address, _method: &str, _data: &CallData) -> Result { - unimplemented!() - } - } - - impl StaticExternalities for TestExternalities { - type Error = Error; - - fn storage(&self, _key: &H256) -> Result<&[u8]> { - unimplemented!() - } - - fn call_static(&self, _address: &Address, _method: &str, _data: &CallData) -> Result { - unimplemented!() - } - } - - #[test] - fn should_fail_on_invalid_method() { - // given - let mut ext = TestExternalities::default(); - let executor = RustExecutor::default(); - - assert_matches!( - *executor.call(&mut ext, "any", &CallData(vec![])).unwrap_err().kind(), - ErrorKind::MethodNotFound(ref method) if &*method == "any" - ); - } -} diff --git a/contracts/src/validator_set.rs b/contracts/src/validator_set.rs deleted file mode 100644 index 08088a30e8b12..0000000000000 --- a/contracts/src/validator_set.rs +++ /dev/null @@ -1,31 +0,0 @@ -// 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 . - -use primitives::Address; -use state_machine::StaticExternalities; - -use error::Result; -use rust_executor::RustExecutor; - -/// Harcoded validator set contract. -#[derive(Debug, Default)] -pub struct Contract; -impl Contract { - /// Returns current validator set. - pub fn validator_set>(&self, _db: &E, _data: ()) -> Result> { - unimplemented!() - } -} From 293cf5da658e46f933f16c057106b04f5b484a55 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 6 Dec 2017 10:06:04 +0100 Subject: [PATCH 03/49] Avoid commiting compled files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a2f4b3c27a017..88a4517df4518 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target/ **/*.rs.bk *.swp +runtime/target/ From 66b636ef9ec53c2d2fc9b5450e2e9a9a415330b8 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 6 Dec 2017 10:07:53 +0100 Subject: [PATCH 04/49] Add runtime precompile. --- .../release/runtime.compact.wasm | Bin 0 -> 76 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm new file mode 100644 index 0000000000000000000000000000000000000000..86f120021a7d603922614ed7be4639a446ec2d45 GIT binary patch literal 76 zcmV~$u@QhE6hy&y@P$zl=)hLA5)B9$9W^l_v~G9V?J X>#(QZ4~$|O6`5J}r8J4fl4UYKiIfRs literal 0 HcmV?d00001 From c7e7456e67867eec074abcc7e34849abfd5dadc7 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 6 Dec 2017 10:16:52 +0100 Subject: [PATCH 05/49] Rename so module makes more sense. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- cli/Cargo.toml | 2 +- {contracts => executor}/Cargo.toml | 2 +- {contracts => executor}/src/error.rs | 0 {contracts => executor}/src/lib.rs | 0 {contracts => executor}/src/wasm_executor.rs | 19 +++++++++++++++---- rpc/Cargo.toml | 2 +- 8 files changed, 22 insertions(+), 11 deletions(-) rename {contracts => executor}/Cargo.toml (93%) rename {contracts => executor}/src/error.rs (100%) rename {contracts => executor}/src/lib.rs (100%) rename {contracts => executor}/src/wasm_executor.rs (83%) diff --git a/Cargo.lock b/Cargo.lock index dac6fe5d14399..8f9b2b920e5fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -621,7 +621,7 @@ dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-client 0.1.0", - "polkadot-contracts 0.1.0", + "polkadot-executor 0.1.0", "polkadot-primitives 0.1.0", "polkadot-rpc-servers 0.1.0", ] @@ -636,7 +636,7 @@ dependencies = [ ] [[package]] -name = "polkadot-contracts" +name = "polkadot-executor" version = "0.1.0" dependencies = [ "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -673,7 +673,7 @@ dependencies = [ "jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "polkadot-client 0.1.0", - "polkadot-contracts 0.1.0", + "polkadot-executor 0.1.0", "polkadot-primitives 0.1.0", "polkadot-state-machine 0.1.0", ] diff --git a/Cargo.toml b/Cargo.toml index 86bba6f302fe4..0b527d8b2a01e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ polkadot-cli = { path = "cli", version = "0.1" } [workspace] members = [ "client", - "contracts", + "executor", "primitives", "rpc", "rpc_servers", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f19977785ffb1..64305642bfaa9 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -10,6 +10,6 @@ env_logger = "0.4" error-chain = "0.11" log = "0.3" polkadot-client = { path = "../client", version = "0.1" } -polkadot-contracts = { path = "../contracts", version = "0.1" } +polkadot-executor = { path = "../executor", version = "0.1" } polkadot-primitives = { path = "../primitives", version = "0.1" } polkadot-rpc-servers = { path = "../rpc_servers", version = "0.1" } diff --git a/contracts/Cargo.toml b/executor/Cargo.toml similarity index 93% rename from contracts/Cargo.toml rename to executor/Cargo.toml index 68dfc7810bb49..58a791ac457ab 100644 --- a/contracts/Cargo.toml +++ b/executor/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "polkadot-contracts" +name = "polkadot-executor" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/contracts/src/error.rs b/executor/src/error.rs similarity index 100% rename from contracts/src/error.rs rename to executor/src/error.rs diff --git a/contracts/src/lib.rs b/executor/src/lib.rs similarity index 100% rename from contracts/src/lib.rs rename to executor/src/lib.rs diff --git a/contracts/src/wasm_executor.rs b/executor/src/wasm_executor.rs similarity index 83% rename from contracts/src/wasm_executor.rs rename to executor/src/wasm_executor.rs index 9cd1117dc1b96..c902fa922f277 100644 --- a/contracts/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -63,11 +63,8 @@ impl CodeExecutor for WasmExecutor { #[cfg(test)] mod tests { - use super::*; - use primitives::Address; - use primitives::hash::H256; - use parity_wasm; + use super::*; #[derive(Debug, Default)] struct TestExternalities; @@ -97,4 +94,18 @@ mod tests { let argument: i64 = 42; assert_eq!(Some(I64(argument * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); } + + + #[test] + fn should_provide_externalities() { + use parity_wasm::ModuleInstanceInterface; + use parity_wasm::RuntimeValue::{I64}; + + let program = parity_wasm::ProgramInstance::new(); + let test_module = include_bytes!("../../target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = program.add_module("test", module, None).expect("Failed to initialize module"); + let argument: i64 = 42; + assert_eq!(Some(I64(argument * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); + } } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index fb9a83583459f..66d11d94dd4a4 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -13,4 +13,4 @@ polkadot-state-machine = { path = "../state_machine", version = "0.1" } [dev-dependencies] assert_matches = "1.1" -polkadot-contracts = { path = "../contracts", version = "0.1" } +polkadot-executor = { path = "../executor", version = "0.1" } From 0cfd67e7245e2696954587aaf63e49b3bd1aae19 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 6 Dec 2017 10:17:55 +0100 Subject: [PATCH 06/49] Further renaming. --- cli/src/lib.rs | 4 ++-- rpc/src/lib.rs | 2 +- rpc/src/state/tests.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/src/lib.rs b/cli/src/lib.rs index dfd7080451208..73168b73b4138 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -20,7 +20,7 @@ extern crate env_logger; extern crate polkadot_client as client; -extern crate polkadot_contracts as contracts; +extern crate polkadot_executor as executor; extern crate polkadot_primitives as primitives; extern crate polkadot_rpc_servers as rpc; @@ -54,7 +54,7 @@ pub fn run(args: I) -> error::Result<()> where // Create client let blockchain = DummyBlockchain; - let executor = contracts::executor(); + let executor = executor::executor(); let client = client::Client::new(blockchain, executor); let address = "127.0.0.1:9933".parse().unwrap(); diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 762514f7b092a..5061ffb56d8ba 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -29,7 +29,7 @@ extern crate error_chain; extern crate jsonrpc_macros; #[cfg(test)] -extern crate polkadot_contracts; +extern crate polkadot_executor; #[cfg(test)] #[macro_use] extern crate assert_matches; diff --git a/rpc/src/state/tests.rs b/rpc/src/state/tests.rs index d3e7493786746..8c1a2adb0f3aa 100644 --- a/rpc/src/state/tests.rs +++ b/rpc/src/state/tests.rs @@ -15,14 +15,14 @@ // along with Polkadot. If not, see . use super::*; -use polkadot_contracts as contracts; +use polkadot_executor as executor; use self::error::{Error, ErrorKind}; use test_helpers::Blockchain; #[test] fn should_return_storage() { - let client = Client::new(Blockchain::default(), contracts::executor()); + let client = Client::new(Blockchain::default(), executor::executor()); assert_matches!( StateApi::storage(&client, 5.into(), 10.into(), 0.into()), From 3011edf5e5f30496fcb7915e158037060f7e0753 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 6 Dec 2017 10:24:28 +0100 Subject: [PATCH 07/49] Ensure tests work. --- executor/src/lib.rs | 2 +- executor/src/wasm_executor.rs | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 3c2ab759a08a8..8a0cabb95e5ee 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -39,7 +39,7 @@ extern crate error_chain; //extern crate serde_derive; #[cfg(test)] -#[macro_use] +//#[macro_use] extern crate assert_matches; mod wasm_executor; diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index c902fa922f277..8fabbdc43936b 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -63,21 +63,28 @@ impl CodeExecutor for WasmExecutor { #[cfg(test)] mod tests { - use parity_wasm; use super::*; + use state_machine::StaticExternalities; #[derive(Debug, Default)] struct TestExternalities; - impl Externalities for TestExternalities { - fn set_storage(&mut self, _object: i64, _key: Vec, _value: Vec) { + impl Externalities for TestExternalities { + fn set_code(&mut self, _code: Vec) { + unimplemented!() + } + fn set_storage(&mut self, _object: u64, _key: Vec, _value: Vec) { unimplemented!() } } - impl StaticExternalities for TestExternalities { + impl StaticExternalities for TestExternalities { type Error = Error; - fn storage(&self, _object: i64, _key: Vec) -> Result<&[u8]> { + fn code(&self) -> Result<&[u8]> { + unimplemented!() + } + + fn storage(&self, _object: u64, _key: &[u8]) -> Result<&[u8]> { unimplemented!() } } @@ -88,7 +95,7 @@ mod tests { use parity_wasm::RuntimeValue::{I64}; let program = parity_wasm::ProgramInstance::new(); - let test_module = include_bytes!("../../target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); let argument: i64 = 42; @@ -102,7 +109,7 @@ mod tests { use parity_wasm::RuntimeValue::{I64}; let program = parity_wasm::ProgramInstance::new(); - let test_module = include_bytes!("../../target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); let argument: i64 = 42; From a74b4fa55bedf05bc7e0e22885ad5bacffb52ab1 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 7 Dec 2017 12:32:53 +0100 Subject: [PATCH 08/49] Allow bringing in of externalities. - Add util functions/macros. - Add uncompacted runtime. - Add some external crates from pwasm-std for managing allocs/memory stuff. --- .gitignore | 3 +- executor/src/wasm_executor.rs | 105 ++++++++++++++++-- runtime/Cargo.lock | 15 +++ runtime/Cargo.toml | 4 +- runtime/pwasm-alloc/Cargo.toml | 18 +++ runtime/pwasm-alloc/README.md | 12 ++ runtime/pwasm-alloc/src/lib.rs | 30 +++++ runtime/pwasm-libc/Cargo.toml | 15 +++ runtime/pwasm-libc/README.md | 12 ++ runtime/pwasm-libc/src/lib.rs | 46 ++++++++ runtime/src/lib.rs | 21 +++- .../release/runtime.compact.wasm | Bin 76 -> 72 bytes .../release/runtime.wasm | Bin 0 -> 326 bytes 13 files changed, 266 insertions(+), 15 deletions(-) create mode 100644 runtime/pwasm-alloc/Cargo.toml create mode 100644 runtime/pwasm-alloc/README.md create mode 100644 runtime/pwasm-alloc/src/lib.rs create mode 100644 runtime/pwasm-libc/Cargo.toml create mode 100644 runtime/pwasm-libc/README.md create mode 100644 runtime/pwasm-libc/src/lib.rs create mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime.wasm diff --git a/.gitignore b/.gitignore index 88a4517df4518..cd789637f93b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target/ **/*.rs.bk *.swp -runtime/target/ +runtime/**/target/ +**/._* diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 8fabbdc43936b..470bb531637d5 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -24,6 +24,30 @@ use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; +pub trait ConvertibleToWasm { const VALUE_TYPE: parity_wasm::elements::ValueType; } +impl ConvertibleToWasm for i32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } +impl ConvertibleToWasm for u32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } +impl ConvertibleToWasm for i64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; } +impl ConvertibleToWasm for u64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; } +impl ConvertibleToWasm for f32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F32; } +impl ConvertibleToWasm for f64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F64; } +impl ConvertibleToWasm for isize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } +impl ConvertibleToWasm for usize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } +impl ConvertibleToWasm for *const T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } +impl ConvertibleToWasm for *mut T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } + +#[macro_export] +macro_rules! convert_args { + () => ([]); + ( $( $t:ty ),* ) => ( [ $( <$t> :: VALUE_TYPE, )* ] ); +} + +#[macro_export] +macro_rules! convert_fn { + ( $name:ident ( $($params:tt)* ) ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params)*), None) ); + ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some(<$returns>::VALUE_TYPE) ) ); +} + /// Dummy rust executor for contracts. /// /// Instead of actually executing the provided code it just @@ -64,6 +88,7 @@ impl CodeExecutor for WasmExecutor { mod tests { use super::*; + use std::collections::HashMap; use state_machine::StaticExternalities; #[derive(Debug, Default)] @@ -72,6 +97,7 @@ mod tests { fn set_code(&mut self, _code: Vec) { unimplemented!() } + fn set_storage(&mut self, _object: u64, _key: Vec, _value: Vec) { unimplemented!() } @@ -89,27 +115,86 @@ mod tests { } } + use std::result; + use std::sync::{Arc, Weak}; + use parity_wasm::interpreter::{CallerContext}; + use parity_wasm::interpreter::{UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; + use parity_wasm::interpreter::{RuntimeValue}; + + // user function executor + struct FunctionExecutor; + + fn imported(n: u64) { + println!("imported {:?}", n); + } + +// marshalled!(imported) + + impl UserFunctionExecutor for FunctionExecutor { + fn execute(&mut self, name: &str, context: CallerContext) -> result::Result, parity_wasm::interpreter::Error> { + match name { + "imported" => { + let n = context.value_stack.pop_as()?; + imported(n); + Ok(None) + } + _ => Err(parity_wasm::interpreter::Error::Trap(format!("not implemented: {}", name)).into()) + } + } + } + + const SIGNATURES: &'static [UserFunctionDescriptor] = &[ + convert_fn!(imported(u64)), + convert_fn!(ext_memcpy(*mut u8, *const u8, usize) -> *mut u8), + convert_fn!(ext_memmove(*mut u8, *const u8, usize) -> *mut u8), + convert_fn!(ext_memset(*mut u8, i32, usize) -> *mut u8), + convert_fn!(ext_malloc(usize) -> *mut u8), + convert_fn!(ext_free(*mut u8)), + ]; + + fn program_with_externals(externals: UserDefinedElements, module_name: &str) -> result::Result { + let program = parity_wasm::ProgramInstance::new(); + let instance = { + let module = parity_wasm::builder::module().build(); + let mut instance = parity_wasm::ModuleInstance::new(Weak::default(), module_name.into(), module)?; + instance.instantiate(None)?; + instance + }; + let other_instance = parity_wasm::interpreter::native_module(Arc::new(instance), externals)?; + program.insert_loaded_module(module_name, other_instance)?; + Ok(program) + } + #[test] - fn should_run_wasm() { + fn should_provide_externalities() { use parity_wasm::ModuleInstanceInterface; use parity_wasm::RuntimeValue::{I64}; - let program = parity_wasm::ProgramInstance::new(); - let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); - let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module("test", module, None).expect("Failed to initialize module"); - let argument: i64 = 42; - assert_eq!(Some(I64(argument * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); - } + let externals = UserDefinedElements { + executor: Some(FunctionExecutor{}), + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(SIGNATURES), + }; + + let program = program_with_externals(externals, "env").unwrap(); + + let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); + let module_code = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = program.add_module("test", module_code, None).expect("Failed to initialize module"); +// module.exports.push_function("add", &[ValueType::I64, ValueType::I64], Some(ValueType::I64), marshall!(|a: u64, b: u64| a + b)); + + let argument: u64 = 42; + assert_eq!(Some(I64((argument * 2) as i64)), module.execute_export("test", vec![I64(argument as i64)].into()).unwrap()); + } #[test] - fn should_provide_externalities() { + fn should_run_wasm() { use parity_wasm::ModuleInstanceInterface; use parity_wasm::RuntimeValue::{I64}; let program = parity_wasm::ProgramInstance::new(); - let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); let argument: i64 = 42; diff --git a/runtime/Cargo.lock b/runtime/Cargo.lock index 54ac945ce8e19..28a7ce9a3236d 100644 --- a/runtime/Cargo.lock +++ b/runtime/Cargo.lock @@ -1,4 +1,19 @@ +[[package]] +name = "pwasm-alloc" +version = "0.1.0" +dependencies = [ + "pwasm-libc 0.1.0", +] + +[[package]] +name = "pwasm-libc" +version = "0.1.0" + [[package]] name = "runtime" version = "0.1.0" +dependencies = [ + "pwasm-alloc 0.1.0", + "pwasm-libc 0.1.0", +] diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 2c97c09b56394..0a68a8ee96928 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" authors = ["Parity Technologies "] [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib"] [dependencies] +pwasm-libc = { path = "./pwasm-libc", version = "0.1" } +pwasm-alloc = { path = "./pwasm-alloc", version = "0.1" } diff --git a/runtime/pwasm-alloc/Cargo.toml b/runtime/pwasm-alloc/Cargo.toml new file mode 100644 index 0000000000000..e0a28f2c0ddb1 --- /dev/null +++ b/runtime/pwasm-alloc/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "pwasm-alloc" +version = "0.1.0" +authors = ["Nikolay Volf "] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/paritytech/pwasm-std" +homepage = "https://github.com/paritytech/pwasm-std" +documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/" +description = "Parity WebAssembly standard library internal allocator" +keywords = ["wasm", "parity", "webassembly", "blockchain"] +categories = ["no-std", "embedded"] + +[dependencies] +pwasm-libc = { path = "../pwasm-libc", version = "0.1" } + +[features] +strict = [] diff --git a/runtime/pwasm-alloc/README.md b/runtime/pwasm-alloc/README.md new file mode 100644 index 0000000000000..489d629bc10b5 --- /dev/null +++ b/runtime/pwasm-alloc/README.md @@ -0,0 +1,12 @@ +# pwasm-libc + +Parity WASM contracts standard library libc bindings + +[Documentation](https://paritytech.github.io/pwasm-std/pwasm_alloc/) + +# License + +`pwasm_alloc` is primarily distributed under the terms of both the MIT +license and the Apache License (Version 2.0), at your choice. + +See LICENSE-APACHE, and LICENSE-MIT for details. diff --git a/runtime/pwasm-alloc/src/lib.rs b/runtime/pwasm-alloc/src/lib.rs new file mode 100644 index 0000000000000..8bd73682057af --- /dev/null +++ b/runtime/pwasm-alloc/src/lib.rs @@ -0,0 +1,30 @@ +#![warn(missing_docs)] +#![cfg_attr(feature = "strict", deny(warnings))] +#![no_std] +#![crate_type = "rlib"] +#![feature(global_allocator)] +#![feature(alloc)] +#![feature(allocator_api)] + +//! Custom allocator crate for wasm + +extern crate alloc; +extern crate pwasm_libc; + +use alloc::heap::{Alloc, Layout, AllocErr}; + +/// Wasm allocator +pub struct WasmAllocator; + +unsafe impl<'a> Alloc for &'a WasmAllocator { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + Ok(pwasm_libc::malloc(layout.size())) + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, _layout: Layout) { + pwasm_libc::free(ptr) + } +} + +#[global_allocator] +static ALLOCATOR: WasmAllocator = WasmAllocator; diff --git a/runtime/pwasm-libc/Cargo.toml b/runtime/pwasm-libc/Cargo.toml new file mode 100644 index 0000000000000..d3ff1f1f320e6 --- /dev/null +++ b/runtime/pwasm-libc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "pwasm-libc" +version = "0.1.0" +authors = ["Sergey Pepyakin "] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/paritytech/pwasm-std" +homepage = "https://github.com/paritytech/pwasm-std" +documentation = "https://paritytech.github.io/pwasm-std/pwasm_std/" +description = "Parity WebAssembly standard library libc bindings" +keywords = ["wasm", "parity", "webassembly", "blockchain"] +categories = ["no-std", "embedded"] + +[features] +strict = [] diff --git a/runtime/pwasm-libc/README.md b/runtime/pwasm-libc/README.md new file mode 100644 index 0000000000000..b01c4a76707eb --- /dev/null +++ b/runtime/pwasm-libc/README.md @@ -0,0 +1,12 @@ +# pwasm-libc + +Parity WASM contracts standard library libc bindings + +[Documentation](https://paritytech.github.io/pwasm-std/pwasm_libc/) + +# License + +`pwasm-libc` is primarily distributed under the terms of both the MIT +license and the Apache License (Version 2.0), at your choice. + +See LICENSE-APACHE, and LICENSE-MIT for details. diff --git a/runtime/pwasm-libc/src/lib.rs b/runtime/pwasm-libc/src/lib.rs new file mode 100644 index 0000000000000..eed597e73b7b0 --- /dev/null +++ b/runtime/pwasm-libc/src/lib.rs @@ -0,0 +1,46 @@ +#![warn(missing_docs)] +#![cfg_attr(feature = "strict", deny(warnings))] +#![no_std] + +//! libc externs crate + +extern "C" { + fn ext_memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + fn ext_memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + fn ext_memset(dest: *mut u8, c: i32, n: usize) -> *mut u8; + fn ext_malloc(size: usize) -> *mut u8; + fn ext_free(ptr: *mut u8); +} + +// Declaring these function here prevents Emscripten from including it's own verisons +// into final binary. + +/// memcpy extern +#[no_mangle] +pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + ext_memcpy(dest, src, n) +} + +/// memmove extern +#[no_mangle] +pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + ext_memmove(dest, src, n) +} + +/// memset extern +#[no_mangle] +pub unsafe extern "C" fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 { + ext_memset(dest, c, n) +} + +/// malloc extern +#[no_mangle] +pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { + ext_malloc(size) +} + +/// free extern +#[no_mangle] +pub unsafe extern "C" fn free(ptr: *mut u8) { + ext_free(ptr); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d8975a452b526..191b60ed96a9c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,16 +1,31 @@ #![no_std] #![feature(lang_items)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] +extern crate alloc; +use alloc::boxed::Box; + +extern crate pwasm_libc; +extern crate pwasm_alloc; #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } -#[no_mangle] -pub fn main(data_length: u64) -> u64 { - do_something(data_length) +extern "C" { + fn imported(number: u64); } fn do_something(param: u64) -> u64 { param * 2 } + +/// Test some execution. +#[no_mangle] +pub fn test(data_length: u64) -> u64 { + unsafe { imported(data_length); } + let b = Box::new(1); + do_something(data_length) +} diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index 86f120021a7d603922614ed7be4639a446ec2d45..643770138543d320c775aa64da4d6a7533e3d4fb 100644 GIT binary patch delta 39 ucmebA;AKcG&Shj^U|?irOkkMEryn$j!~g$l%Dr%_6`czyJVgHv|X( delta 43 ycmeb9;bTZF&Shj^U|?ipOkk{=$g3g7$;i&2z~IE##?8&e$l%Dr%_6`czyJV^Jq0%a diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm new file mode 100644 index 0000000000000000000000000000000000000000..1c1237b59614ac4d0b0eb8913ecfa48f3fe81d51 GIT binary patch literal 326 zcmYL@%}T^D6ot=8(zMec#;vR3#y4;$SiyI2=L|Df5GJ;?WpFml&gV91g)T1VoR9m5 z!x#vF_r;zOsTlV<#w?suo;jm0-3eD03m1|%H&D^vz<>5ve* zi>zdwkub`%g6b>u-7v`sT9?)+r3ho#1BE8;%IDGs3Egm2Fq4cEY|i;YSthe=t}@CS zclQ6C+AMFn9Z|pAM>00>g?ed?=L6qv(6Hr`O5)MrmA|(RaCPzx8|Bqz^=6H;sljs< MI9C&`amK^r4|nB3X#fBK literal 0 HcmV?d00001 From 6dac2756bf64ce050ffb63d8d2a62722c37c4181 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 9 Dec 2017 19:31:44 +0100 Subject: [PATCH 09/49] Nice macros for imports. --- executor/src/wasm_executor.rs | 146 ++++++++++++------ runtime/src/lib.rs | 5 +- .../release/runtime.wasm | Bin 326 -> 349 bytes 3 files changed, 103 insertions(+), 48 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 470bb531637d5..72c439181a78c 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -24,30 +24,102 @@ use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; -pub trait ConvertibleToWasm { const VALUE_TYPE: parity_wasm::elements::ValueType; } -impl ConvertibleToWasm for i32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } -impl ConvertibleToWasm for u32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } -impl ConvertibleToWasm for i64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; } -impl ConvertibleToWasm for u64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; } -impl ConvertibleToWasm for f32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F32; } -impl ConvertibleToWasm for f64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F64; } -impl ConvertibleToWasm for isize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } -impl ConvertibleToWasm for usize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } -impl ConvertibleToWasm for *const T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } -impl ConvertibleToWasm for *mut T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; } +pub trait ConvertibleToWasm { const VALUE_TYPE: parity_wasm::elements::ValueType; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue; } +impl ConvertibleToWasm for i32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self) } } +impl ConvertibleToWasm for u32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for i64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self) } } +impl ConvertibleToWasm for u64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self as i64) } } +impl ConvertibleToWasm for f32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F32(self) } } +impl ConvertibleToWasm for f64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F64(self) } } +impl ConvertibleToWasm for isize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for usize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as u32 as i32) } } +impl ConvertibleToWasm for *const T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } +impl ConvertibleToWasm for *mut T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } #[macro_export] macro_rules! convert_args { - () => ([]); - ( $( $t:ty ),* ) => ( [ $( <$t> :: VALUE_TYPE, )* ] ); + () => ([]); + ( $( $t:ty ),* ) => ( [ $( <$t> :: VALUE_TYPE, )* ] ); } #[macro_export] macro_rules! convert_fn { - ( $name:ident ( $($params:tt)* ) ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params)*), None) ); - ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some(<$returns>::VALUE_TYPE) ) ); + ( $name:ident ( $( $params:ty ),* ) ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) ); + ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some(<$returns>::VALUE_TYPE) ) ); } +#[macro_export] +macro_rules! reverse_params { + // Entry point, use brackets to recursively reverse above. + ($body:tt, $self:ident, $context:ident, $( $names:ident : $params:ty ),*) => ( + reverse_params!($body $self $context [ $( $names : $params ),* ]); + ); + ($body:tt $self:ident $context:ident [] $( $names:ident : $params:ty ),*) => ({ + $( + let $names : $params = match $context.value_stack.pop_as() { + Ok(value) => value, + Err(error) => return Err(error.into()), + }; + )* + $body + }); + ($body:tt $self:ident $context:ident [ $name:ident : $param:ty $(, $names:ident : $params:ty )* ] $( $reversed_names:ident : $reversed_params:ty ),*) => ( + reverse_params!($body $self $context [ $( $names : $params ),* ] $name : $param $( , $reversed_names : $reversed_params )*); + ); +} + +#[macro_export] +macro_rules! marshall { + ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ + let r = reverse_params!($body, $self, $context, $( $names : $params ),*); + Ok(Some(r.to_runtime_value())) + }); + ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ + reverse_params!($body, $self, $context, $( $names : $params ),*); + Ok(None) + }) +} + +#[macro_export] +macro_rules! dispatch { + ( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( + fn execute(&mut self, name: &str, context: CallerContext) + -> result::Result, parity_wasm::interpreter::Error> { + let $objectname = self; + match name { + $( + stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body), + )* + n => Err(parity_wasm::interpreter::Error::Trap(format!("not implemented: {}", n)).into()) + } + } + ); +} + +#[macro_export] +macro_rules! signatures { + ( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => ( + const SIGNATURES: &'static [UserFunctionDescriptor] = &[ + $( + convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ), + )* + ]; + ); +} + +#[macro_export] +macro_rules! function_executor { + ( $objectname:ident : $structname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( + impl UserFunctionExecutor for $structname { + dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); + } + impl $structname { + signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); + } + ); +} + + /// Dummy rust executor for contracts. /// /// Instead of actually executing the provided code it just @@ -122,35 +194,19 @@ mod tests { use parity_wasm::interpreter::{RuntimeValue}; // user function executor - struct FunctionExecutor; - - fn imported(n: u64) { - println!("imported {:?}", n); + #[derive(Default)] + struct FunctionExecutor { + heap_end: u32 } -// marshalled!(imported) - - impl UserFunctionExecutor for FunctionExecutor { - fn execute(&mut self, name: &str, context: CallerContext) -> result::Result, parity_wasm::interpreter::Error> { - match name { - "imported" => { - let n = context.value_stack.pop_as()?; - imported(n); - Ok(None) - } - _ => Err(parity_wasm::interpreter::Error::Trap(format!("not implemented: {}", name)).into()) - } - } - } - - const SIGNATURES: &'static [UserFunctionDescriptor] = &[ - convert_fn!(imported(u64)), - convert_fn!(ext_memcpy(*mut u8, *const u8, usize) -> *mut u8), - convert_fn!(ext_memmove(*mut u8, *const u8, usize) -> *mut u8), - convert_fn!(ext_memset(*mut u8, i32, usize) -> *mut u8), - convert_fn!(ext_malloc(usize) -> *mut u8), - convert_fn!(ext_free(*mut u8)), - ]; + function_executor!(this: FunctionExecutor, + imported(n: u64, m: u64) => { println!("imported {:?}, {:?}", n, m) }, + ext_memcpy(dest: u32, src: u32, count: u32) -> u32 => { println!("memcpy {} from {}, {} bytes", dest, src, count); dest }, + ext_memmove(dest: u32, src: u32, count: u32) -> u32 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, + ext_memset(dest: u32, val: i32, count: u32) -> u32 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, + ext_malloc(count: u32) -> *mut u8 => { let r = this.heap_end; this.heap_end += count; println!("malloc {} bytes at {}", count, r); r }, + ext_free(addr: u32) => { println!("free {}", addr) } + ); fn program_with_externals(externals: UserDefinedElements, module_name: &str) -> result::Result { let program = parity_wasm::ProgramInstance::new(); @@ -171,9 +227,9 @@ mod tests { use parity_wasm::RuntimeValue::{I64}; let externals = UserDefinedElements { - executor: Some(FunctionExecutor{}), + executor: Some(FunctionExecutor::default()), globals: HashMap::new(), - functions: ::std::borrow::Cow::from(SIGNATURES), + functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), }; let program = program_with_externals(externals, "env").unwrap(); @@ -182,8 +238,6 @@ mod tests { let module_code = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module_code, None).expect("Failed to initialize module"); -// module.exports.push_function("add", &[ValueType::I64, ValueType::I64], Some(ValueType::I64), marshall!(|a: u64, b: u64| a + b)); - let argument: u64 = 42; assert_eq!(Some(I64((argument * 2) as i64)), module.execute_export("test", vec![I64(argument as i64)].into()).unwrap()); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 191b60ed96a9c..b64e46602a195 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -15,7 +15,7 @@ fn panic_fmt() -> ! { } extern "C" { - fn imported(number: u64); + fn imported(n: u64, m: u64); } fn do_something(param: u64) -> u64 { @@ -25,7 +25,8 @@ fn do_something(param: u64) -> u64 { /// Test some execution. #[no_mangle] pub fn test(data_length: u64) -> u64 { - unsafe { imported(data_length); } + unsafe { imported(1, 2); } let b = Box::new(1); + let c = Box::new(2); do_something(data_length) } diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm index 1c1237b59614ac4d0b0eb8913ecfa48f3fe81d51..9babbcca2591996bbadf076d6874625fc758688e 100644 GIT binary patch delta 91 zcmX@cbeBnvA+b1@k%57MQJOV@v7P~l8S4|6>+3-jQ{6<_BZhfwwv0^mPK-`W0<269 kjw}LU_KLw&*aDi+3-j<3zb5(h+RxjEwaP3<9i74vs7W Uj7p5IybKDAj*MnZlZ6?*02v_-iU0rr From 08f7b26cc5c1e83654a6723267611611740c1137 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 13:32:43 +0100 Subject: [PATCH 10/49] Allow passing in of data through allocators. Make memcpy and malloc work. Basic allocator. --- executor/src/wasm_executor.rs | 128 +++++++++++++----- runtime/Cargo.toml | 3 + runtime/src/lib.rs | 29 +++- .../release/runtime.compact.wasm | Bin 72 -> 377 bytes .../release/runtime.wasm | Bin 349 -> 464 bytes 5 files changed, 122 insertions(+), 38 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 72c439181a78c..d003feaa2023e 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -24,17 +24,17 @@ use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; -pub trait ConvertibleToWasm { const VALUE_TYPE: parity_wasm::elements::ValueType; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue; } -impl ConvertibleToWasm for i32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self) } } -impl ConvertibleToWasm for u32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } -impl ConvertibleToWasm for i64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self) } } -impl ConvertibleToWasm for u64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self as i64) } } -impl ConvertibleToWasm for f32 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F32(self) } } -impl ConvertibleToWasm for f64 { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F64(self) } } -impl ConvertibleToWasm for isize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } -impl ConvertibleToWasm for usize { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as u32 as i32) } } -impl ConvertibleToWasm for *const T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } -impl ConvertibleToWasm for *mut T { const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } +pub trait ConvertibleToWasm { const VALUE_TYPE: parity_wasm::elements::ValueType; type NativeType; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue; } +impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self) } } +impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for i64 { type NativeType = i64; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self) } } +impl ConvertibleToWasm for u64 { type NativeType = u64; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self as i64) } } +impl ConvertibleToWasm for f32 { type NativeType = f32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F32(self) } } +impl ConvertibleToWasm for f64 { type NativeType = f64; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F64(self) } } +impl ConvertibleToWasm for isize { type NativeType = i32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for usize { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as u32 as i32) } } +impl ConvertibleToWasm for *const T { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } +impl ConvertibleToWasm for *mut T { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } #[macro_export] macro_rules! convert_args { @@ -56,7 +56,7 @@ macro_rules! reverse_params { ); ($body:tt $self:ident $context:ident [] $( $names:ident : $params:ty ),*) => ({ $( - let $names : $params = match $context.value_stack.pop_as() { + let $names : <$params as ConvertibleToWasm>::NativeType = match $context.value_stack.pop_as() { Ok(value) => value, Err(error) => return Err(error.into()), }; @@ -71,7 +71,7 @@ macro_rules! reverse_params { #[macro_export] macro_rules! marshall { ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ - let r = reverse_params!($body, $self, $context, $( $names : $params ),*); + let r : <$returns as ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*); Ok(Some(r.to_runtime_value())) }); ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ @@ -188,24 +188,52 @@ mod tests { } use std::result; - use std::sync::{Arc, Weak}; - use parity_wasm::interpreter::{CallerContext}; - use parity_wasm::interpreter::{UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; - use parity_wasm::interpreter::{RuntimeValue}; + use std::sync::{Arc, Weak, Mutex}; + use std::mem::transmute; + use parity_wasm::interpreter::{CallerContext, MemoryInstance, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; // user function executor #[derive(Default)] struct FunctionExecutor { - heap_end: u32 + context: Arc>>, + } + + struct FEContext { + heap_end: u32, + memory: Arc, + } + + impl FEContext { + fn allocate(&mut self, size: u32) -> u32 { + let r = self.heap_end; + self.heap_end += size; + r + } + fn deallocate(&mut self, _offset: u32) { + } } function_executor!(this: FunctionExecutor, - imported(n: u64, m: u64) => { println!("imported {:?}, {:?}", n, m) }, - ext_memcpy(dest: u32, src: u32, count: u32) -> u32 => { println!("memcpy {} from {}, {} bytes", dest, src, count); dest }, - ext_memmove(dest: u32, src: u32, count: u32) -> u32 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, - ext_memset(dest: u32, val: i32, count: u32) -> u32 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, - ext_malloc(count: u32) -> *mut u8 => { let r = this.heap_end; this.heap_end += count; println!("malloc {} bytes at {}", count, r); r }, - ext_free(addr: u32) => { println!("free {}", addr) } + imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, + ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { + let mut context = this.context.lock().unwrap(); + context.as_mut().unwrap().memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); + println!("memcpy {} from {}, {} bytes", dest, src, count); + dest + }, + ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, + ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, + ext_malloc(size: usize) -> *mut u8 => { + let mut context = this.context.lock().unwrap(); + let r = context.as_mut().unwrap().allocate(size); + println!("malloc {} bytes at {}", size, r); + r + }, + ext_free(addr: *mut u8) => { + let mut context = this.context.lock().unwrap(); + context.as_mut().unwrap().deallocate(addr); + println!("free {}", addr) + } ); fn program_with_externals(externals: UserDefinedElements, module_name: &str) -> result::Result { @@ -221,13 +249,44 @@ mod tests { Ok(program) } + #[test] + fn should_pass_freeable_data() { + use parity_wasm::ModuleInstanceInterface; + use parity_wasm::RuntimeValue::{I32}; + + let fe_context = Arc::new(Mutex::new(None)); + let externals = UserDefinedElements { + executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), + }; + + let program = program_with_externals(externals, "env").unwrap(); + + let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = program.add_module("test", module, None).expect("Failed to initialize module"); + + *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap()) }); + + let data = b"Hello world"; + let size = data.len() as u32; + let offset = fe_context.lock().unwrap().as_mut().unwrap().allocate(size); + module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); + + module.execute_export("test_data_in", vec![I32(offset as i32), I32(size as i32)].into()).unwrap(); + + panic!(); + } + #[test] fn should_provide_externalities() { use parity_wasm::ModuleInstanceInterface; use parity_wasm::RuntimeValue::{I64}; + let fe_context = Arc::new(Mutex::new(None)); let externals = UserDefinedElements { - executor: Some(FunctionExecutor::default()), + executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), globals: HashMap::new(), functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), }; @@ -235,11 +294,18 @@ mod tests { let program = program_with_externals(externals, "env").unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); - let module_code = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module("test", module_code, None).expect("Failed to initialize module"); + let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = program.add_module("test", module, None).expect("Failed to initialize module"); + + *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap()) }); + + let argument: u64 = 20; + assert_eq!(Some(I64(((argument + 1) * 2) as i64)), module.execute_export("test", vec![I64(argument as i64)].into()).unwrap()); - let argument: u64 = 42; - assert_eq!(Some(I64((argument * 2) as i64)), module.execute_export("test", vec![I64(argument as i64)].into()).unwrap()); + let mut x = [0u64; 2]; + module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap().get_into(1024, unsafe { transmute::<_, &mut [u8; 8]>(&mut x) }).unwrap(); + println!("heap: {:?}", x); + panic!(); } #[test] @@ -251,7 +317,7 @@ mod tests { let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); - let argument: i64 = 42; - assert_eq!(Some(I64(argument * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); + let argument: i64 = 20; + assert_eq!(Some(I64((argument + 1) * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 0a68a8ee96928..5db697b226563 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -9,3 +9,6 @@ crate-type = ["cdylib"] [dependencies] pwasm-libc = { path = "./pwasm-libc", version = "0.1" } pwasm-alloc = { path = "./pwasm-alloc", version = "0.1" } + +[profile.release] +panic = "abort" diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b64e46602a195..95b40537001a4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -3,6 +3,7 @@ #![cfg_attr(feature = "strict", deny(warnings))] #![feature(alloc)] + extern crate alloc; use alloc::boxed::Box; @@ -10,12 +11,13 @@ extern crate pwasm_libc; extern crate pwasm_alloc; #[lang = "panic_fmt"] -fn panic_fmt() -> ! { +#[no_mangle] +pub fn panic_fmt() -> ! { loop {} } extern "C" { - fn imported(n: u64, m: u64); + fn imported(n: u64) -> u64; } fn do_something(param: u64) -> u64 { @@ -24,9 +26,22 @@ fn do_something(param: u64) -> u64 { /// Test some execution. #[no_mangle] -pub fn test(data_length: u64) -> u64 { - unsafe { imported(1, 2); } - let b = Box::new(1); - let c = Box::new(2); - do_something(data_length) +pub fn test(value: u64) -> u64 { + let b = Box::new(unsafe { imported(value) }); + do_something(*b) +} + +/// Test passing of data. +#[no_mangle] +pub fn test_data_in(freeable_data: *mut u8, size: usize) { + // Interpret data + let slice = unsafe { core::slice::from_raw_parts(freeable_data, size) }; + let copy = slice.to_vec(); + + unsafe { pwasm_libc::free(freeable_data); } + + // Do some stuff. + for b in © { + unsafe { imported(*b as u64); } + } } diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index 643770138543d320c775aa64da4d6a7533e3d4fb..3c66312a49c153b7f30cfcaea26f6e76957b11e3 100644 GIT binary patch literal 377 zcmYL_F>k^!5QX2JO$ZoNvochMtz)OIN!6-?4#Z+6{@G3$B8h#NJ^Y+?jaHLXq(e!iI*v4!@`)c1W21L Rn?$abt`aANTpMl0?+;JHLD2vJ literal 72 zcmV~$u@QhE6hy&y@WBxhC`l{PfRND<5)*58mveLh3Odl|oiNi4!Hc?77klg1^Ezy{ RF<})c%Gi~25}Rc!6n;nW2si)$ diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm index 9babbcca2591996bbadf076d6874625fc758688e..ec6712b5076a819b8d8ba08f9fa825cb0a50f9f6 100644 GIT binary patch literal 464 zcmYL_v2KGf5QhJ=9RfJ0WX({KY#lpwg-TUlpgYjuRzxvGK+4!4@kwHly7JuMxN-hSnh)uqk`a|S>SJJjYv}~WNvo~6a2%!{9AdZNM*&6MvE2_IdHMVY`JI>2H zj0wXPH;Z1UYTKZ%-SKiMatzd|vKGTWF}Uh2#b?YUiDj3ngbVIRm^0;C@Mq~*rl|xm Qmn3{4$oLo!f!CXxAH=#P+W-In From 1ace6b2204b457818524c3a5b2ad8e059d89ceb8 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 14:31:16 +0100 Subject: [PATCH 11/49] Can now pass in bytes to WasmExecutor. --- executor/src/wasm_executor.rs | 134 +++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 35 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index d003feaa2023e..862b2ba193243 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -24,6 +24,21 @@ use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; +use std::sync::{Weak}; + +fn program_with_externals(externals: parity_wasm::interpreter::UserDefinedElements, module_name: &str) -> result::Result { + let program = parity_wasm::ProgramInstance::new(); + let instance = { + let module = parity_wasm::builder::module().build(); + let mut instance = parity_wasm::ModuleInstance::new(Weak::default(), module_name.into(), module)?; + instance.instantiate(None)?; + instance + }; + let other_instance = parity_wasm::interpreter::native_module(Arc::new(instance), externals)?; + program.insert_loaded_module(module_name, other_instance)?; + Ok(program) +} + pub trait ConvertibleToWasm { const VALUE_TYPE: parity_wasm::elements::ValueType; type NativeType; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue; } impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self) } } impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } @@ -44,8 +59,8 @@ macro_rules! convert_args { #[macro_export] macro_rules! convert_fn { - ( $name:ident ( $( $params:ty ),* ) ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) ); - ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some(<$returns>::VALUE_TYPE) ) ); + ( $name:ident ( $( $params:ty ),* ) ) => ( parity_wasm::interpreter::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) ); + ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( parity_wasm::interpreter::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some(<$returns>::VALUE_TYPE) ) ); } #[macro_export] @@ -83,7 +98,7 @@ macro_rules! marshall { #[macro_export] macro_rules! dispatch { ( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( - fn execute(&mut self, name: &str, context: CallerContext) + fn execute(&mut self, name: &str, context: parity_wasm::interpreter::CallerContext) -> result::Result, parity_wasm::interpreter::Error> { let $objectname = self; match name { @@ -99,7 +114,7 @@ macro_rules! dispatch { #[macro_export] macro_rules! signatures { ( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => ( - const SIGNATURES: &'static [UserFunctionDescriptor] = &[ + const SIGNATURES: &'static [parity_wasm::interpreter::UserFunctionDescriptor] = &[ $( convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ), )* @@ -110,7 +125,7 @@ macro_rules! signatures { #[macro_export] macro_rules! function_executor { ( $objectname:ident : $structname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( - impl UserFunctionExecutor for $structname { + impl parity_wasm::interpreter::UserFunctionExecutor for $structname { dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); } impl $structname { @@ -119,6 +134,57 @@ macro_rules! function_executor { ); } +use std::result; +use std::sync::{Arc, Mutex}; + +// user function executor +#[derive(Default)] +struct FunctionExecutor { + context: Arc>>, +} + +struct FEContext { + heap_end: u32, + memory: Arc, +} + +impl FEContext { + fn new(m: &Arc) -> Self { + use parity_wasm::ModuleInstanceInterface; + FEContext { heap_end: 1024, memory: Arc::clone(&m.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap()) } + } + fn allocate(&mut self, size: u32) -> u32 { + let r = self.heap_end; + self.heap_end += size; + r + } + fn deallocate(&mut self, _offset: u32) { + } +} + +function_executor!(this: FunctionExecutor, + imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, + ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { + let mut context = this.context.lock().unwrap(); + context.as_mut().unwrap().memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); + println!("memcpy {} from {}, {} bytes", dest, src, count); + dest + }, + ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, + ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, + ext_malloc(size: usize) -> *mut u8 => { + let mut context = this.context.lock().unwrap(); + let r = context.as_mut().unwrap().allocate(size); + println!("malloc {} bytes at {}", size, r); + r + }, + ext_free(addr: *mut u8) => { + let mut context = this.context.lock().unwrap(); + context.as_mut().unwrap().deallocate(addr); + println!("free {}", addr) + } +); + /// Dummy rust executor for contracts. /// @@ -137,7 +203,6 @@ impl CodeExecutor for WasmExecutor { method: &str, data: &CallData, ) -> Result { - // TODO: avoid copying code by requiring code to remain immutable through execution, // splitting it off from potentially mutable externalities. let code = match ext.code() { @@ -146,12 +211,31 @@ impl CodeExecutor for WasmExecutor { }; use parity_wasm::ModuleInstanceInterface; - use parity_wasm::RuntimeValue::{I64}; - let program = parity_wasm::ProgramInstance::new(); + use parity_wasm::interpreter::UserDefinedElements; + use parity_wasm::RuntimeValue::I32; + use std::collections::HashMap; + + let fe_context = Arc::new(Mutex::new(None)); + let externals = UserDefinedElements { + executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), + }; + + let program = program_with_externals(externals, "env").unwrap(); let module = parity_wasm::deserialize_buffer(code).expect("Failed to load module"); - let module = program.add_module("main", module, None).expect("Failed to initialize module"); - module.execute_export(method, vec![I64(data.0.len() as i64)].into()) - .map(|o| OutData(vec![1; if let Some(I64(l)) = o { l as usize } else { 0 }])) + let module = program.add_module("test", module, None).expect("Failed to initialize module"); + *fe_context.lock().unwrap() = Some(FEContext::new(&module)); + + let size = data.0.len() as u32; + let offset = fe_context.lock().unwrap().as_mut().unwrap().allocate(size); + module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); + + module.execute_export(method, vec![I32(offset as i32), I32(size as i32)].into()) + .map(|o| { + // TODO: populate vec properly + OutData(vec![1; if let Some(I32(l)) = o { l as usize } else { 0 }]) + }) .map_err(|_| ErrorKind::Runtime.into()) } } @@ -188,9 +272,11 @@ mod tests { } use std::result; - use std::sync::{Arc, Weak, Mutex}; + use std::sync::{Arc, Mutex}; use std::mem::transmute; - use parity_wasm::interpreter::{CallerContext, MemoryInstance, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; + use parity_wasm::interpreter::{MemoryInstance, UserDefinedElements}; + use parity_wasm::ModuleInstanceInterface; + use parity_wasm::RuntimeValue::{I32, I64}; // user function executor #[derive(Default)] @@ -236,24 +322,8 @@ mod tests { } ); - fn program_with_externals(externals: UserDefinedElements, module_name: &str) -> result::Result { - let program = parity_wasm::ProgramInstance::new(); - let instance = { - let module = parity_wasm::builder::module().build(); - let mut instance = parity_wasm::ModuleInstance::new(Weak::default(), module_name.into(), module)?; - instance.instantiate(None)?; - instance - }; - let other_instance = parity_wasm::interpreter::native_module(Arc::new(instance), externals)?; - program.insert_loaded_module(module_name, other_instance)?; - Ok(program) - } - #[test] fn should_pass_freeable_data() { - use parity_wasm::ModuleInstanceInterface; - use parity_wasm::RuntimeValue::{I32}; - let fe_context = Arc::new(Mutex::new(None)); let externals = UserDefinedElements { executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), @@ -281,9 +351,6 @@ mod tests { #[test] fn should_provide_externalities() { - use parity_wasm::ModuleInstanceInterface; - use parity_wasm::RuntimeValue::{I64}; - let fe_context = Arc::new(Mutex::new(None)); let externals = UserDefinedElements { executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), @@ -310,9 +377,6 @@ mod tests { #[test] fn should_run_wasm() { - use parity_wasm::ModuleInstanceInterface; - use parity_wasm::RuntimeValue::{I64}; - let program = parity_wasm::ProgramInstance::new(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); From 27e5fed3c3e091ddc09de9289ed4a5e080fd0087 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 14:32:18 +0100 Subject: [PATCH 12/49] Additional cleanup. --- executor/src/wasm_executor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 862b2ba193243..6dc2a551a4a10 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -274,8 +274,7 @@ mod tests { use std::result; use std::sync::{Arc, Mutex}; use std::mem::transmute; - use parity_wasm::interpreter::{MemoryInstance, UserDefinedElements}; - use parity_wasm::ModuleInstanceInterface; + use parity_wasm::{MemoryInstance, ModuleInstanceInterface, UserDefinedElements}; use parity_wasm::RuntimeValue::{I32, I64}; // user function executor From 13dcd891d2f411d8e9cd827257ef360170b83926 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 16:05:05 +0100 Subject: [PATCH 13/49] Switch usages of `OutData` to `u64` No need to be able to return bytes anymore. --- client/src/lib.rs | 4 ++-- executor/src/error.rs | 6 ++++++ executor/src/wasm_executor.rs | 14 ++++++-------- rpc/src/state/mod.rs | 6 +++--- state_machine/src/lib.rs | 6 +++--- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 4c47c7dc7f04b..39c9edb448d1c 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -27,7 +27,7 @@ extern crate error_chain; pub mod error; use primitives::{block, Address, H256}; -use primitives::contract::{CallData, OutData, StorageKey, StorageData}; +use primitives::contract::{CallData, StorageKey, StorageData}; use state_machine::backend::Backend; use self::error::ResultExt; @@ -80,7 +80,7 @@ impl Client where } /// Execute a call to a contract on top of state in a block of given hash. - pub fn call(&self, hash: &block::HeaderHash, method: &str, call_data: &CallData) -> error::Result { + pub fn call(&self, hash: &block::HeaderHash, method: &str, call_data: &CallData) -> error::Result { let state = self.state_at(hash)?; let mut changes = state_machine::OverlayedChanges::default(); diff --git a/executor/src/error.rs b/executor/src/error.rs index 13f302a2831ea..85d1b70d98160 100644 --- a/executor/src/error.rs +++ b/executor/src/error.rs @@ -43,6 +43,12 @@ error_chain! { display("Externalities error: {}", e), } + /// Invalid return type. + InvalidReturn { + description("u64 was not returned"), + display("Invalid type returned (should be u64)"), + } + /// Runtime failed. Runtime { description("runtime failure"), diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 6dc2a551a4a10..bf7bc218df03a 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -18,7 +18,7 @@ use parity_wasm; -use primitives::contract::{CallData, OutData}; +use primitives::contract::CallData; //use serializer::{from_slice as de, to_vec as ser}; use state_machine::{Externalities, CodeExecutor}; @@ -202,7 +202,7 @@ impl CodeExecutor for WasmExecutor { ext: &mut E, method: &str, data: &CallData, - ) -> Result { + ) -> Result { // TODO: avoid copying code by requiring code to remain immutable through execution, // splitting it off from potentially mutable externalities. let code = match ext.code() { @@ -212,7 +212,7 @@ impl CodeExecutor for WasmExecutor { use parity_wasm::ModuleInstanceInterface; use parity_wasm::interpreter::UserDefinedElements; - use parity_wasm::RuntimeValue::I32; + use parity_wasm::RuntimeValue::{I32, I64}; use std::collections::HashMap; let fe_context = Arc::new(Mutex::new(None)); @@ -232,11 +232,8 @@ impl CodeExecutor for WasmExecutor { module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); module.execute_export(method, vec![I32(offset as i32), I32(size as i32)].into()) - .map(|o| { - // TODO: populate vec properly - OutData(vec![1; if let Some(I32(l)) = o { l as usize } else { 0 }]) - }) .map_err(|_| ErrorKind::Runtime.into()) + .and_then(|i| if let Some(I64(r)) = i { Ok(r as u64) } else { Err(ErrorKind::InvalidReturn.into()) }) } } @@ -274,7 +271,8 @@ mod tests { use std::result; use std::sync::{Arc, Mutex}; use std::mem::transmute; - use parity_wasm::{MemoryInstance, ModuleInstanceInterface, UserDefinedElements}; + use parity_wasm::{ModuleInstanceInterface}; + use parity_wasm::interpreter::{MemoryInstance, UserDefinedElements}; use parity_wasm::RuntimeValue::{I32, I64}; // user function executor diff --git a/rpc/src/state/mod.rs b/rpc/src/state/mod.rs index b3ab79147eab6..d136364fe1309 100644 --- a/rpc/src/state/mod.rs +++ b/rpc/src/state/mod.rs @@ -23,7 +23,7 @@ mod tests; use client::{self, Client}; use primitives::{block}; -use primitives::contract::{CallData, OutData, StorageKey, StorageData}; +use primitives::contract::{CallData, StorageKey, StorageData}; use state_machine; use self::error::Result; @@ -37,7 +37,7 @@ build_rpc_trait! { /// Call a contract. #[rpc(name = "state_call")] - fn call(&self, String, CallData, block::HeaderHash) -> Result; + fn call(&self, String, CallData, block::HeaderHash) -> Result; } } @@ -49,7 +49,7 @@ impl StateApi for Client where Ok(self.storage(&block, object, &key)?) } - fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result { + fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result { Ok(self.call(&block, &method, &data)?) } } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 31682c6dc5b67..49f6d8d053a8f 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -30,7 +30,7 @@ extern crate triehash; use std::collections::HashMap; use std::fmt; -use primitives::contract::{CallData, OutData}; +use primitives::contract::{CallData}; pub mod backend; mod ext; @@ -199,7 +199,7 @@ pub trait CodeExecutor: Sized { ext: &mut E, method: &str, data: &CallData, - ) -> Result; + ) -> Result; } /// Execute a call using the given state backend, overlayed changes, and call executor. @@ -211,7 +211,7 @@ pub fn execute( exec: &Exec, method: &str, call_data: &CallData, -) -> Result> { +) -> Result> { let result = { let mut externalities = ext::Ext { From 84b9f84323552ab82ad63d765a8e145c5a565b81 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 16:34:33 +0100 Subject: [PATCH 14/49] convert to safe but extremely verbose type conversion. @rphmeier any more concise way of doing this? --- Cargo.lock | 1 + state_machine/Cargo.toml | 1 + state_machine/src/backend.rs | 8 ++++++-- state_machine/src/lib.rs | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f9b2b920e5fa..9bc2f80afb01a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -699,6 +699,7 @@ dependencies = [ name = "polkadot-state-machine" version = "0.1.0" dependencies = [ + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/state_machine/Cargo.toml b/state_machine/Cargo.toml index cdcec9d6a014e..a0d25c821cb4b 100644 --- a/state_machine/Cargo.toml +++ b/state_machine/Cargo.toml @@ -11,3 +11,4 @@ keccak-hash = "0.1.0" patricia-trie = "0.1.0" memorydb = "0.1.1" triehash = "0.1" +byteorder = "1.1" diff --git a/state_machine/src/backend.rs b/state_machine/src/backend.rs index 78778531b9319..060ff84071b95 100644 --- a/state_machine/src/backend.rs +++ b/state_machine/src/backend.rs @@ -82,10 +82,14 @@ impl Backend for InMemory { self.inner.update(changes); // fully recalculate trie roots. - use std::mem::transmute; let storage_roots = self.inner.storage.iter().map(|(object, storage)| { let flat_trie = storage.iter().map(|(k, v)| (k.to_vec(), v.clone())).collect(); - (unsafe { transmute::(object.to_be()) }.to_vec(), sec_trie_root(flat_trie).to_vec()) + ({ + use byteorder::{BigEndian, ByteOrder}; + let mut r = [0u8; 8]; + BigEndian::write_u64(&mut r, *object); + r.to_vec() + }, sec_trie_root(flat_trie).to_vec()) }).collect(); let storage_tree_root = H256(sec_trie_root(storage_roots).0); diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 49f6d8d053a8f..007352a69b4a4 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -27,6 +27,8 @@ extern crate keccak_hash; extern crate patricia_trie; extern crate triehash; +extern crate byteorder; + use std::collections::HashMap; use std::fmt; From 7f318996bf82cfd55b030e1c6f62bae6da1691ae Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 17:59:24 +0100 Subject: [PATCH 15/49] Remove StaticExternalities distinction. --- client/src/lib.rs | 2 +- executor/src/wasm_executor.rs | 15 ++++++--------- state_machine/src/ext.rs | 33 +++------------------------------ state_machine/src/lib.rs | 27 +++++++-------------------- 4 files changed, 17 insertions(+), 60 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 39c9edb448d1c..0faebda6974d4 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -26,7 +26,7 @@ extern crate error_chain; pub mod error; -use primitives::{block, Address, H256}; +use primitives::{block, H256}; use primitives::contract::{CallData, StorageKey, StorageData}; use state_machine::backend::Backend; diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index bf7bc218df03a..06640a7320913 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -242,28 +242,25 @@ mod tests { use super::*; use std::collections::HashMap; - use state_machine::StaticExternalities; #[derive(Debug, Default)] struct TestExternalities; impl Externalities for TestExternalities { - fn set_code(&mut self, _code: Vec) { + type Error = Error; + + fn code(&self) -> Result<&[u8]> { unimplemented!() } - fn set_storage(&mut self, _object: u64, _key: Vec, _value: Vec) { + fn storage(&self, _object: u64, _key: &[u8]) -> Result<&[u8]> { unimplemented!() } - } - impl StaticExternalities for TestExternalities { - type Error = Error; - - fn code(&self) -> Result<&[u8]> { + fn set_code(&mut self, _code: Vec) { unimplemented!() } - fn storage(&self, _object: u64, _key: &[u8]) -> Result<&[u8]> { + fn set_storage(&mut self, _object: u64, _key: Vec, _value: Vec) { unimplemented!() } } diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index 0207b7b3b1530..5e7d6fbde37c9 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -19,7 +19,7 @@ use std::{error, fmt}; use backend::Backend; -use {Externalities, StaticExternalities, OverlayedChanges}; +use {Externalities, OverlayedChanges}; /// Errors that can occur when interacting with the externalities. #[derive(Debug, Copy, Clone)] @@ -56,7 +56,7 @@ pub struct Ext<'a, B: 'a> { pub backend: &'a B, } -impl<'a, B: 'a> StaticExternalities for Ext<'a, B> +impl<'a, B: 'a> Externalities for Ext<'a, B> where B: Backend { type Error = B::Error; @@ -71,11 +71,7 @@ impl<'a, B: 'a> StaticExternalities for Ext<'a, B> None => self.backend.storage(object, key) } } -} - -impl<'a, B: 'a> Externalities for Ext<'a, B> - where B: Backend -{ + fn set_code(&mut self, code: Vec) { self.overlay.set_code(code); } @@ -84,26 +80,3 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> self.overlay.set_storage(object, key, value); } } - -// Static externalities -struct StaticExt<'a, B: 'a> { - overlay: &'a OverlayedChanges, - backend: &'a B, -} - -impl<'a, B: 'a> StaticExternalities for StaticExt<'a, B> - where B: Backend -{ - type Error = B::Error; - - fn code(&self) -> Result<&[u8], Self::Error> { - Ok(self.overlay.code()) - } - - fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error> { - match self.overlay.storage(object, key) { - Some(x) => Ok(x), - None => self.backend.storage(object, key) - } - } -} diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 007352a69b4a4..fd486a4639b07 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -160,26 +160,7 @@ pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} impl Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} /// Externalities: pinned to specific active address. -pub trait Externalities: StaticExternalities { -/* // Get the current runtime. - fn code(&self) -> Result<&[u8], Self::Error> { - StaticExternalities::code(self) - } -*/ - /// Set the new runtime. - fn set_code(&mut self, code: Vec); - -/* /// Read storage of current contract being called. - fn storage(&self, index: u64, key: &[u8]) -> Result<&[u8], Self::Error> { - StaticExternalities::storage(self, index, key) - } -*/ - /// Set storage of current contract being called. - fn set_storage(&mut self, index: u64, key: Vec, value: Vec); -} - -/// Static externalities: used only for read-only requests. -pub trait StaticExternalities { +pub trait Externalities { /// Externalities error type. type Error: Error; @@ -188,6 +169,12 @@ pub trait StaticExternalities { /// Read storage of current contract being called. fn storage(&self, index: u64, key: &[u8]) -> Result<&[u8], Self::Error>; + + /// Set the new runtime. + fn set_code(&mut self, code: Vec); + + /// Set storage of current contract being called. + fn set_storage(&mut self, index: u64, key: Vec, value: Vec); } /// Code execution engine. From edf061e029980d6d4c5a4a6afbed99d89ac387dc Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 18:00:55 +0100 Subject: [PATCH 16/49] Remove another unused use. --- client/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 0faebda6974d4..6ce26470c6202 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -26,7 +26,7 @@ extern crate error_chain; pub mod error; -use primitives::{block, H256}; +use primitives::{block}; use primitives::contract::{CallData, StorageKey, StorageData}; use state_machine::backend::Backend; From edb6beac986deedfff4f38bf6117fb1fda38f5b5 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 19:31:20 +0100 Subject: [PATCH 17/49] Refactor wasm utils out --- executor/src/lib.rs | 2 + executor/src/wasm_executor.rs | 158 +++++----------------------------- executor/src/wasm_utils.rs | 132 ++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 138 deletions(-) create mode 100644 executor/src/wasm_utils.rs diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 8a0cabb95e5ee..7c67827f55280 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -42,6 +42,8 @@ extern crate error_chain; //#[macro_use] extern crate assert_matches; +#[macro_use] +mod wasm_utils; mod wasm_executor; pub mod error; diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 06640a7320913..32a2f57eedf6c 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -16,126 +16,15 @@ //! Rust implementation of Polkadot contracts. -use parity_wasm; - +use parity_wasm::{deserialize_buffer, ModuleInstanceInterface}; +use parity_wasm::interpreter::{ItemIndex, ModuleInstance, MemoryInstance, UserDefinedElements}; +use parity_wasm::RuntimeValue::{I32, I64}; +use std::collections::HashMap; use primitives::contract::CallData; -//use serializer::{from_slice as de, to_vec as ser}; use state_machine::{Externalities, CodeExecutor}; - use error::{Error, ErrorKind, Result}; - -use std::sync::{Weak}; - -fn program_with_externals(externals: parity_wasm::interpreter::UserDefinedElements, module_name: &str) -> result::Result { - let program = parity_wasm::ProgramInstance::new(); - let instance = { - let module = parity_wasm::builder::module().build(); - let mut instance = parity_wasm::ModuleInstance::new(Weak::default(), module_name.into(), module)?; - instance.instantiate(None)?; - instance - }; - let other_instance = parity_wasm::interpreter::native_module(Arc::new(instance), externals)?; - program.insert_loaded_module(module_name, other_instance)?; - Ok(program) -} - -pub trait ConvertibleToWasm { const VALUE_TYPE: parity_wasm::elements::ValueType; type NativeType; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue; } -impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self) } } -impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } -impl ConvertibleToWasm for i64 { type NativeType = i64; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self) } } -impl ConvertibleToWasm for u64 { type NativeType = u64; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I64(self as i64) } } -impl ConvertibleToWasm for f32 { type NativeType = f32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F32(self) } } -impl ConvertibleToWasm for f64 { type NativeType = f64; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::F64; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::F64(self) } } -impl ConvertibleToWasm for isize { type NativeType = i32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as i32) } } -impl ConvertibleToWasm for usize { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as u32 as i32) } } -impl ConvertibleToWasm for *const T { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } -impl ConvertibleToWasm for *mut T { type NativeType = u32; const VALUE_TYPE: parity_wasm::elements::ValueType = parity_wasm::elements::ValueType::I32; fn to_runtime_value(self) -> parity_wasm::interpreter::RuntimeValue { parity_wasm::interpreter::RuntimeValue::I32(self as isize as i32) } } - -#[macro_export] -macro_rules! convert_args { - () => ([]); - ( $( $t:ty ),* ) => ( [ $( <$t> :: VALUE_TYPE, )* ] ); -} - -#[macro_export] -macro_rules! convert_fn { - ( $name:ident ( $( $params:ty ),* ) ) => ( parity_wasm::interpreter::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) ); - ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( parity_wasm::interpreter::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some(<$returns>::VALUE_TYPE) ) ); -} - -#[macro_export] -macro_rules! reverse_params { - // Entry point, use brackets to recursively reverse above. - ($body:tt, $self:ident, $context:ident, $( $names:ident : $params:ty ),*) => ( - reverse_params!($body $self $context [ $( $names : $params ),* ]); - ); - ($body:tt $self:ident $context:ident [] $( $names:ident : $params:ty ),*) => ({ - $( - let $names : <$params as ConvertibleToWasm>::NativeType = match $context.value_stack.pop_as() { - Ok(value) => value, - Err(error) => return Err(error.into()), - }; - )* - $body - }); - ($body:tt $self:ident $context:ident [ $name:ident : $param:ty $(, $names:ident : $params:ty )* ] $( $reversed_names:ident : $reversed_params:ty ),*) => ( - reverse_params!($body $self $context [ $( $names : $params ),* ] $name : $param $( , $reversed_names : $reversed_params )*); - ); -} - -#[macro_export] -macro_rules! marshall { - ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ - let r : <$returns as ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*); - Ok(Some(r.to_runtime_value())) - }); - ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ - reverse_params!($body, $self, $context, $( $names : $params ),*); - Ok(None) - }) -} - -#[macro_export] -macro_rules! dispatch { - ( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( - fn execute(&mut self, name: &str, context: parity_wasm::interpreter::CallerContext) - -> result::Result, parity_wasm::interpreter::Error> { - let $objectname = self; - match name { - $( - stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body), - )* - n => Err(parity_wasm::interpreter::Error::Trap(format!("not implemented: {}", n)).into()) - } - } - ); -} - -#[macro_export] -macro_rules! signatures { - ( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => ( - const SIGNATURES: &'static [parity_wasm::interpreter::UserFunctionDescriptor] = &[ - $( - convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ), - )* - ]; - ); -} - -#[macro_export] -macro_rules! function_executor { - ( $objectname:ident : $structname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( - impl parity_wasm::interpreter::UserFunctionExecutor for $structname { - dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); - } - impl $structname { - signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); - } - ); -} - -use std::result; use std::sync::{Arc, Mutex}; +use wasm_utils::program_with_externals; // user function executor #[derive(Default)] @@ -145,13 +34,12 @@ struct FunctionExecutor { struct FEContext { heap_end: u32, - memory: Arc, + memory: Arc, } impl FEContext { - fn new(m: &Arc) -> Self { - use parity_wasm::ModuleInstanceInterface; - FEContext { heap_end: 1024, memory: Arc::clone(&m.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap()) } + fn new(m: &Arc) -> Self { + FEContext { heap_end: 1024, memory: Arc::clone(&m.memory(ItemIndex::Internal(0)).unwrap()) } } fn allocate(&mut self, size: u32) -> u32 { let r = self.heap_end; @@ -210,11 +98,6 @@ impl CodeExecutor for WasmExecutor { Err(e) => Err(ErrorKind::Externalities(Box::new(e)))?, }; - use parity_wasm::ModuleInstanceInterface; - use parity_wasm::interpreter::UserDefinedElements; - use parity_wasm::RuntimeValue::{I32, I64}; - use std::collections::HashMap; - let fe_context = Arc::new(Mutex::new(None)); let externals = UserDefinedElements { executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), @@ -223,13 +106,13 @@ impl CodeExecutor for WasmExecutor { }; let program = program_with_externals(externals, "env").unwrap(); - let module = parity_wasm::deserialize_buffer(code).expect("Failed to load module"); + let module = deserialize_buffer(code).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); *fe_context.lock().unwrap() = Some(FEContext::new(&module)); let size = data.0.len() as u32; let offset = fe_context.lock().unwrap().as_mut().unwrap().allocate(size); - module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); + module.memory(ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); module.execute_export(method, vec![I32(offset as i32), I32(size as i32)].into()) .map_err(|_| ErrorKind::Runtime.into()) @@ -265,11 +148,10 @@ mod tests { } } - use std::result; use std::sync::{Arc, Mutex}; use std::mem::transmute; - use parity_wasm::{ModuleInstanceInterface}; - use parity_wasm::interpreter::{MemoryInstance, UserDefinedElements}; + use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; + use parity_wasm::interpreter::{ItemIndex, MemoryInstance, UserDefinedElements}; use parity_wasm::RuntimeValue::{I32, I64}; // user function executor @@ -328,15 +210,15 @@ mod tests { let program = program_with_externals(externals, "env").unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); - let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); - *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap()) }); + *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) }); let data = b"Hello world"; let size = data.len() as u32; let offset = fe_context.lock().unwrap().as_mut().unwrap().allocate(size); - module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); + module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); module.execute_export("test_data_in", vec![I32(offset as i32), I32(size as i32)].into()).unwrap(); @@ -355,25 +237,25 @@ mod tests { let program = program_with_externals(externals, "env").unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); - let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); - *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap()) }); + *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) }); let argument: u64 = 20; assert_eq!(Some(I64(((argument + 1) * 2) as i64)), module.execute_export("test", vec![I64(argument as i64)].into()).unwrap()); let mut x = [0u64; 2]; - module.memory(parity_wasm::interpreter::ItemIndex::Internal(0)).unwrap().get_into(1024, unsafe { transmute::<_, &mut [u8; 8]>(&mut x) }).unwrap(); + module.memory(ItemIndex::Internal(0)).unwrap().get_into(1024, unsafe { transmute::<_, &mut [u8; 8]>(&mut x) }).unwrap(); println!("heap: {:?}", x); panic!(); } #[test] fn should_run_wasm() { - let program = parity_wasm::ProgramInstance::new(); + let program = ProgramInstance::new(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); - let module = parity_wasm::deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); let argument: i64 = 20; assert_eq!(Some(I64((argument + 1) * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs new file mode 100644 index 0000000000000..31f3337a7a55d --- /dev/null +++ b/executor/src/wasm_utils.rs @@ -0,0 +1,132 @@ +// 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 . + +//! Rust implementation of Polkadot contracts. + +use std::sync::{Arc, Weak}; +pub use std::result; +pub use parity_wasm::elements::{ValueType}; +pub use parity_wasm::interpreter::{RuntimeValue, UserFunctionDescriptor, UserFunctionExecutor, + CallerContext, UserDefinedElements, Error, native_module}; +pub use parity_wasm::{builder, ProgramInstance, ModuleInstance}; + +pub fn program_with_externals(externals: UserDefinedElements, module_name: &str) -> result::Result { + let program = ProgramInstance::new(); + let instance = { + let module = builder::module().build(); + let mut instance = ModuleInstance::new(Weak::default(), module_name.into(), module)?; + instance.instantiate(None)?; + instance + }; + let other_instance = native_module(Arc::new(instance), externals)?; + program.insert_loaded_module(module_name, other_instance)?; + Ok(program) +} + +pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; } +impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } } +impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for i64 { type NativeType = i64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self) } } +impl ConvertibleToWasm for u64 { type NativeType = u64; const VALUE_TYPE: ValueType = ValueType::I64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I64(self as i64) } } +impl ConvertibleToWasm for f32 { type NativeType = f32; const VALUE_TYPE: ValueType = ValueType::F32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F32(self) } } +impl ConvertibleToWasm for f64 { type NativeType = f64; const VALUE_TYPE: ValueType = ValueType::F64; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::F64(self) } } +impl ConvertibleToWasm for isize { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } +impl ConvertibleToWasm for usize { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as u32 as i32) } } +impl ConvertibleToWasm for *const T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } } +impl ConvertibleToWasm for *mut T { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as isize as i32) } } + +#[macro_export] +macro_rules! convert_args { + () => ([]); + ( $( $t:ty ),* ) => ( [ $( { use $crate::wasm_utils::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] ); +} + +#[macro_export] +macro_rules! convert_fn { + ( $name:ident ( $( $params:ty ),* ) ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) ); + ( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some({ use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE }) ) ); +} + +#[macro_export] +macro_rules! reverse_params { + // Entry point, use brackets to recursively reverse above. + ($body:tt, $self:ident, $context:ident, $( $names:ident : $params:ty ),*) => ( + reverse_params!($body $self $context [ $( $names : $params ),* ]); + ); + ($body:tt $self:ident $context:ident [] $( $names:ident : $params:ty ),*) => ({ + $( + let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType = match $context.value_stack.pop_as() { + Ok(value) => value, + Err(error) => return Err(error.into()), + }; + )* + $body + }); + ($body:tt $self:ident $context:ident [ $name:ident : $param:ty $(, $names:ident : $params:ty )* ] $( $reversed_names:ident : $reversed_params:ty ),*) => ( + reverse_params!($body $self $context [ $( $names : $params ),* ] $name : $param $( , $reversed_names : $reversed_params )*); + ); +} + +#[macro_export] +macro_rules! marshall { + ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ + let r : <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*); + Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() })) + }); + ( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ + reverse_params!($body, $self, $context, $( $names : $params ),*); + Ok(None) + }) +} + +#[macro_export] +macro_rules! dispatch { + ( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( + fn execute(&mut self, name: &str, context: $crate::wasm_utils::CallerContext) + -> $crate::wasm_utils::result::Result, $crate::wasm_utils::Error> { + let $objectname = self; + match name { + $( + stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body), + )* + n => Err($crate::wasm_utils::Error::Trap(format!("not implemented: {}", n)).into()) + } + } + ); +} + +#[macro_export] +macro_rules! signatures { + ( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => ( + const SIGNATURES: &'static [$crate::wasm_utils::UserFunctionDescriptor] = &[ + $( + convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ), + )* + ]; + ); +} + +#[macro_export] +macro_rules! function_executor { + ( $objectname:ident : $structname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( + impl $crate::wasm_utils::UserFunctionExecutor for $structname { + dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); + } + impl $structname { + signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); + } + ); +} From 9f59f48cd6119aa902fd43cd966e922b8b483d66 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 11 Dec 2017 19:50:03 +0100 Subject: [PATCH 18/49] Remove extraneous copies that weren't really testing anything. --- executor/src/wasm_executor.rs | 66 ++++------------------------------- 1 file changed, 6 insertions(+), 60 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 32a2f57eedf6c..3953e0aea7ed5 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -126,6 +126,12 @@ mod tests { use super::*; use std::collections::HashMap; + use std::sync::{Arc, Mutex}; + use std::mem::transmute; + use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; + use parity_wasm::interpreter::{ItemIndex, UserDefinedElements}; + use parity_wasm::RuntimeValue::{I32, I64}; + #[derive(Debug, Default)] struct TestExternalities; impl Externalities for TestExternalities { @@ -148,56 +154,6 @@ mod tests { } } - use std::sync::{Arc, Mutex}; - use std::mem::transmute; - use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; - use parity_wasm::interpreter::{ItemIndex, MemoryInstance, UserDefinedElements}; - use parity_wasm::RuntimeValue::{I32, I64}; - - // user function executor - #[derive(Default)] - struct FunctionExecutor { - context: Arc>>, - } - - struct FEContext { - heap_end: u32, - memory: Arc, - } - - impl FEContext { - fn allocate(&mut self, size: u32) -> u32 { - let r = self.heap_end; - self.heap_end += size; - r - } - fn deallocate(&mut self, _offset: u32) { - } - } - - function_executor!(this: FunctionExecutor, - imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, - ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { - let mut context = this.context.lock().unwrap(); - context.as_mut().unwrap().memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); - println!("memcpy {} from {}, {} bytes", dest, src, count); - dest - }, - ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, - ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, - ext_malloc(size: usize) -> *mut u8 => { - let mut context = this.context.lock().unwrap(); - let r = context.as_mut().unwrap().allocate(size); - println!("malloc {} bytes at {}", size, r); - r - }, - ext_free(addr: *mut u8) => { - let mut context = this.context.lock().unwrap(); - context.as_mut().unwrap().deallocate(addr); - println!("free {}", addr) - } - ); - #[test] fn should_pass_freeable_data() { let fe_context = Arc::new(Mutex::new(None)); @@ -250,14 +206,4 @@ mod tests { println!("heap: {:?}", x); panic!(); } - - #[test] - fn should_run_wasm() { - let program = ProgramInstance::new(); - let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); - let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module("test", module, None).expect("Failed to initialize module"); - let argument: i64 = 20; - assert_eq!(Some(I64((argument + 1) * 2)), module.execute_export("test", vec![I64(argument)].into()).unwrap()); - } } From 287b29d6d14ba791e896e38efd047f0818960f0c Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 31 Dec 2017 12:58:12 +0000 Subject: [PATCH 19/49] Try to use wasm 0.15 --- Cargo.lock | 6 +-- executor/Cargo.toml | 2 +- executor/src/wasm_executor.rs | 91 +++++++++++++++++++++++++++++++---- executor/src/wasm_utils.rs | 24 ++++++--- 4 files changed, 102 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bc2f80afb01a..38ec2e36d4304 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -544,7 +544,7 @@ dependencies = [ [[package]] name = "parity-wasm" -version = "0.17.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -641,7 +641,7 @@ version = "0.1.0" dependencies = [ "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", "polkadot-serializer 0.1.0", "polkadot-state-machine 0.1.0", @@ -1175,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "514f0d73e64be53ff320680ca671b64fe3fb91da01e1ae2ddc99eb51d453b20d" "checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parity-wasm 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57f3b2611a0c890a290b408be079dcbb10291b3b101bcd058032c55fb2c6dadc" +"checksum parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)" = "235801e9531998c4bb307f4ea6833c9f40a4cf132895219ac8c2cd25a9b310f7" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595" "checksum patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1e2f638d79aba5c4a71a4f373df6e3cd702250a53b7f0ed4da1e2a7be9737ae" diff --git a/executor/Cargo.toml b/executor/Cargo.toml index 58a791ac457ab..0b3d154a98266 100644 --- a/executor/Cargo.toml +++ b/executor/Cargo.toml @@ -10,7 +10,7 @@ polkadot-serializer = { path = "../serializer", version = "0.1" } polkadot-state-machine = { path = "../state_machine" , version = "0.1" } serde = "1.0" serde_derive = "1.0" -parity-wasm = "0.17.0" +parity-wasm = "0.15.0" [dev-dependencies] assert_matches = "1.1" diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 3953e0aea7ed5..5fb1e03da79f1 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -17,14 +17,14 @@ //! Rust implementation of Polkadot contracts. use parity_wasm::{deserialize_buffer, ModuleInstanceInterface}; -use parity_wasm::interpreter::{ItemIndex, ModuleInstance, MemoryInstance, UserDefinedElements}; +use parity_wasm::interpreter::{ItemIndex}; use parity_wasm::RuntimeValue::{I32, I64}; use std::collections::HashMap; use primitives::contract::CallData; use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; use std::sync::{Arc, Mutex}; -use wasm_utils::program_with_externals; +use wasm_utils::{program_with_externals, ModuleInstance, MemoryInstance, UserDefinedElements}; // user function executor #[derive(Default)] @@ -74,6 +74,27 @@ function_executor!(this: FunctionExecutor, ); +function_executor!(this: FEContext, + imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, + ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { + this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); + println!("memcpy {} from {}, {} bytes", dest, src, count); + dest + }, + ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, + ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, + ext_malloc(size: usize) -> *mut u8 => { + let r = this.allocate(size); + println!("malloc {} bytes at {}", size, r); + r + }, + ext_free(addr: *mut u8) => { + this.deallocate(addr); + println!("free {}", addr) + } +); + + /// Dummy rust executor for contracts. /// /// Instead of actually executing the provided code it just @@ -97,15 +118,16 @@ impl CodeExecutor for WasmExecutor { Ok(e) => e.to_owned(), Err(e) => Err(ErrorKind::Externalities(Box::new(e)))?, }; - let fe_context = Arc::new(Mutex::new(None)); + let mut fe = FunctionExecutor { context: Arc::clone(&fe_context) }; + let externals = UserDefinedElements { - executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), + executor: Some(&mut fe), globals: HashMap::new(), functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), }; - let program = program_with_externals(externals, "env").unwrap(); + let program = program_with_externals::(externals, "env").unwrap(); let module = deserialize_buffer(code).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); *fe_context.lock().unwrap() = Some(FEContext::new(&module)); @@ -131,6 +153,7 @@ mod tests { use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; use parity_wasm::interpreter::{ItemIndex, UserDefinedElements}; use parity_wasm::RuntimeValue::{I32, I64}; + use parity_wasm::interpreter; #[derive(Debug, Default)] struct TestExternalities; @@ -154,16 +177,65 @@ mod tests { } } + #[test] + fn should_pass_externalities_at_call() { + let program = ProgramInstance::new().unwrap(); + + let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); + let module = program.add_module("test", module, None).expect("Failed to initialize module"); + + let mut fec = FEContext { + heap_end: 1024, + memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) + }; + + let data = b"Hello world"; + let size = data.len() as u32; + let offset = fec.allocate(size); + module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); + +/* use parity_wasm::builder; + use std::sync::{Arc, Weak}; + use parity_wasm::ModuleInstance; + let env_instance = { + let module = builder::module().build(); + let mut instance = ModuleInstance::new(Weak::default(), "env".into(), module).unwrap(); + instance.instantiate(None).unwrap(); + Arc::new(instance) + }; +*/ + + let execute = |fec| module.execute_export( + "test_data_in", + interpreter::ExecutionParams::with_external( + "env".into(), + Arc::new(interpreter::env_native_module(program.module("env").unwrap(), UserDefinedElements { + executor: Some(fec), + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(FEContext::SIGNATURES), + }).unwrap()) + ) + .add_argument(I32(offset as i32)) + .add_argument(I32(size as i32)) + ).unwrap(); + + execute(&mut fec); + + panic!(); + } + #[test] fn should_pass_freeable_data() { let fe_context = Arc::new(Mutex::new(None)); + let mut fex = FunctionExecutor { context: Arc::clone(&fe_context) }; let externals = UserDefinedElements { - executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), + executor: Some(&mut fex), globals: HashMap::new(), functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), }; - let program = program_with_externals(externals, "env").unwrap(); + let program = program_with_externals::(externals, "env").unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); @@ -184,13 +256,14 @@ mod tests { #[test] fn should_provide_externalities() { let fe_context = Arc::new(Mutex::new(None)); + let mut fex = FunctionExecutor { context: Arc::clone(&fe_context) }; let externals = UserDefinedElements { - executor: Some(FunctionExecutor { context: Arc::clone(&fe_context) }), + executor: Some(&mut fex), globals: HashMap::new(), functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), }; - let program = program_with_externals(externals, "env").unwrap(); + let program = program_with_externals::(externals, "env").unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs index 31f3337a7a55d..74007048bccac 100644 --- a/executor/src/wasm_utils.rs +++ b/executor/src/wasm_utils.rs @@ -20,19 +20,27 @@ use std::sync::{Arc, Weak}; pub use std::result; pub use parity_wasm::elements::{ValueType}; pub use parity_wasm::interpreter::{RuntimeValue, UserFunctionDescriptor, UserFunctionExecutor, - CallerContext, UserDefinedElements, Error, native_module}; -pub use parity_wasm::{builder, ProgramInstance, ModuleInstance}; + UserDefinedElements, env_native_module, DummyUserError}; +pub use parity_wasm::{builder}; +use parity_wasm::interpreter; -pub fn program_with_externals(externals: UserDefinedElements, module_name: &str) -> result::Result { - let program = ProgramInstance::new(); +pub type Error = interpreter::Error; +pub type MemoryInstance = interpreter::MemoryInstance; +pub type ProgramInstance = interpreter::ProgramInstance; +pub type ModuleInstance = interpreter::ModuleInstance; +pub type ModuleInstanceInterface = interpreter::ModuleInstanceInterface; +pub type CallerContext<'a> = interpreter::CallerContext<'a, DummyUserError>; + +pub fn program_with_externals + 'static>(externals: UserDefinedElements, module_name: &str) -> result::Result { + let program = ProgramInstance::new().unwrap(); let instance = { let module = builder::module().build(); let mut instance = ModuleInstance::new(Weak::default(), module_name.into(), module)?; instance.instantiate(None)?; instance }; - let other_instance = native_module(Arc::new(instance), externals)?; - program.insert_loaded_module(module_name, other_instance)?; + let other_instance = env_native_module(Arc::new(instance), externals)?; +// program.insert_loaded_module(module_name, Arc::new(other_instance)); Ok(program) } @@ -102,7 +110,7 @@ macro_rules! dispatch { $( stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body), )* - n => Err($crate::wasm_utils::Error::Trap(format!("not implemented: {}", n)).into()) + _ => panic!()//Err($crate::wasm_utils::Error::Trap(format!("not implemented: {}", n)).into()) } } ); @@ -122,7 +130,7 @@ macro_rules! signatures { #[macro_export] macro_rules! function_executor { ( $objectname:ident : $structname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( - impl $crate::wasm_utils::UserFunctionExecutor for $structname { + impl $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname { dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); } impl $structname { From 1bd55fe2c5569d0272e35806080bee6338ece10c Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 31 Dec 2017 19:32:33 +0000 Subject: [PATCH 20/49] Make it work! --- executor/src/wasm_executor.rs | 64 ++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 5fb1e03da79f1..e6db41abf98f2 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -154,6 +154,7 @@ mod tests { use parity_wasm::interpreter::{ItemIndex, UserDefinedElements}; use parity_wasm::RuntimeValue::{I32, I64}; use parity_wasm::interpreter; + use parity_wasm::elements; #[derive(Debug, Default)] struct TestExternalities; @@ -177,13 +178,63 @@ mod tests { } } + use std::result; + use parity_wasm::elements::Module; + use parity_wasm::interpreter::UserFunctionDescriptor; + + #[derive(Clone)] + struct DummyUserFunctionExecutor; + impl interpreter::UserFunctionExecutor for DummyUserFunctionExecutor { + fn execute(&mut self, name: &str, context: interpreter::CallerContext) -> + result::Result, interpreter::Error> + { + unimplemented!() + } + } + trait AddModuleWithoutFullDependentInstance { + fn add_module_by_sigs( + &self, + name: &str, + module: Module, + sigs: HashMap<&str, &'static [UserFunctionDescriptor]> + ) -> result::Result>, interpreter::Error>; + } + impl AddModuleWithoutFullDependentInstance for ProgramInstance { + fn add_module_by_sigs( + &self, + name: &str, + module: Module, + sigs: HashMap<&str, &'static [UserFunctionDescriptor]> + ) -> result::Result>, interpreter::Error> { + let mut dufe = vec![DummyUserFunctionExecutor; sigs.len()]; + let dufe_refs = dufe.iter_mut().collect::>(); + let fake_module_map = sigs.into_iter() + .zip(dufe_refs.into_iter()) + .map(|((dep_mod_name, sigs), dufe)| { + let fake_module = Arc::new( + interpreter::env_native_module( + self.module(dep_mod_name).unwrap(), UserDefinedElements { + executor: Some(dufe), + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(sigs), + } + ).unwrap() + ); + let fake_module: Arc> = fake_module; + (dep_mod_name.into(), fake_module) + }) + .collect::>(); + self.add_module(name, module, Some(&fake_module_map)) + } + } + #[test] fn should_pass_externalities_at_call() { let program = ProgramInstance::new().unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module("test", module, None).expect("Failed to initialize module"); + let module = program.add_module_by_sigs("test", module, vec![("env", FEContext::SIGNATURES)].into_iter().collect()).expect("Failed to initialize module"); let mut fec = FEContext { heap_end: 1024, @@ -195,17 +246,6 @@ mod tests { let offset = fec.allocate(size); module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); -/* use parity_wasm::builder; - use std::sync::{Arc, Weak}; - use parity_wasm::ModuleInstance; - let env_instance = { - let module = builder::module().build(); - let mut instance = ModuleInstance::new(Weak::default(), "env".into(), module).unwrap(); - instance.instantiate(None).unwrap(); - Arc::new(instance) - }; -*/ - let execute = |fec| module.execute_export( "test_data_in", interpreter::ExecutionParams::with_external( From 36e254a1c80e2cb4ed4154976fe15455d9f497bb Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 1 Jan 2018 16:25:56 +0000 Subject: [PATCH 21/49] Call-time externalities working. --- executor/src/wasm_executor.rs | 183 ++++++---------------------------- executor/src/wasm_utils.rs | 95 ++++++++++++++++-- 2 files changed, 117 insertions(+), 161 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index e6db41abf98f2..5adba4c111e91 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -16,30 +16,27 @@ //! Rust implementation of Polkadot contracts. -use parity_wasm::{deserialize_buffer, ModuleInstanceInterface}; +use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; use parity_wasm::interpreter::{ItemIndex}; use parity_wasm::RuntimeValue::{I32, I64}; use std::collections::HashMap; use primitives::contract::CallData; use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; -use std::sync::{Arc, Mutex}; -use wasm_utils::{program_with_externals, ModuleInstance, MemoryInstance, UserDefinedElements}; +use std::sync::{Arc}; +use wasm_utils::{ModuleInstance, MemoryInstance, UserDefinedElements, AddModuleWithoutFullDependentInstance}; -// user function executor -#[derive(Default)] struct FunctionExecutor { - context: Arc>>, -} - -struct FEContext { heap_end: u32, memory: Arc, } -impl FEContext { +impl FunctionExecutor { fn new(m: &Arc) -> Self { - FEContext { heap_end: 1024, memory: Arc::clone(&m.memory(ItemIndex::Internal(0)).unwrap()) } + FunctionExecutor { + heap_end: 1024, + memory: Arc::clone(&m.memory(ItemIndex::Internal(0)).unwrap()), + } } fn allocate(&mut self, size: u32) -> u32 { let r = self.heap_end; @@ -51,30 +48,6 @@ impl FEContext { } function_executor!(this: FunctionExecutor, - imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, - ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { - let mut context = this.context.lock().unwrap(); - context.as_mut().unwrap().memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); - println!("memcpy {} from {}, {} bytes", dest, src, count); - dest - }, - ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, - ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, - ext_malloc(size: usize) -> *mut u8 => { - let mut context = this.context.lock().unwrap(); - let r = context.as_mut().unwrap().allocate(size); - println!("malloc {} bytes at {}", size, r); - r - }, - ext_free(addr: *mut u8) => { - let mut context = this.context.lock().unwrap(); - context.as_mut().unwrap().deallocate(addr); - println!("free {}", addr) - } -); - - -function_executor!(this: FEContext, imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); @@ -94,7 +67,6 @@ function_executor!(this: FEContext, } ); - /// Dummy rust executor for contracts. /// /// Instead of actually executing the provided code it just @@ -118,27 +90,25 @@ impl CodeExecutor for WasmExecutor { Ok(e) => e.to_owned(), Err(e) => Err(ErrorKind::Externalities(Box::new(e)))?, }; - let fe_context = Arc::new(Mutex::new(None)); - let mut fe = FunctionExecutor { context: Arc::clone(&fe_context) }; - let externals = UserDefinedElements { - executor: Some(&mut fe), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), - }; + let program = ProgramInstance::new().unwrap(); - let program = program_with_externals::(externals, "env").unwrap(); let module = deserialize_buffer(code).expect("Failed to load module"); - let module = program.add_module("test", module, None).expect("Failed to initialize module"); - *fe_context.lock().unwrap() = Some(FEContext::new(&module)); + let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::SIGNATURES]).expect("Failed to initialize module"); + + let mut fec = FunctionExecutor::new(&module); let size = data.0.len() as u32; - let offset = fe_context.lock().unwrap().as_mut().unwrap().allocate(size); + let offset = fec.allocate(size); module.memory(ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); - module.execute_export(method, vec![I32(offset as i32), I32(size as i32)].into()) + let r = module.execute_export(method, + program.params_with_external("env", &mut fec) + .add_argument(I32(offset as i32)) + .add_argument(I32(size as i32))) .map_err(|_| ErrorKind::Runtime.into()) - .and_then(|i| if let Some(I64(r)) = i { Ok(r as u64) } else { Err(ErrorKind::InvalidReturn.into()) }) + .and_then(|i| if let Some(I64(r)) = i { Ok(r as u64) } else { Err(ErrorKind::InvalidReturn.into()) }); + r } } @@ -146,15 +116,6 @@ impl CodeExecutor for WasmExecutor { mod tests { use super::*; - use std::collections::HashMap; - - use std::sync::{Arc, Mutex}; - use std::mem::transmute; - use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; - use parity_wasm::interpreter::{ItemIndex, UserDefinedElements}; - use parity_wasm::RuntimeValue::{I32, I64}; - use parity_wasm::interpreter; - use parity_wasm::elements; #[derive(Debug, Default)] struct TestExternalities; @@ -178,65 +139,15 @@ mod tests { } } - use std::result; - use parity_wasm::elements::Module; - use parity_wasm::interpreter::UserFunctionDescriptor; - - #[derive(Clone)] - struct DummyUserFunctionExecutor; - impl interpreter::UserFunctionExecutor for DummyUserFunctionExecutor { - fn execute(&mut self, name: &str, context: interpreter::CallerContext) -> - result::Result, interpreter::Error> - { - unimplemented!() - } - } - trait AddModuleWithoutFullDependentInstance { - fn add_module_by_sigs( - &self, - name: &str, - module: Module, - sigs: HashMap<&str, &'static [UserFunctionDescriptor]> - ) -> result::Result>, interpreter::Error>; - } - impl AddModuleWithoutFullDependentInstance for ProgramInstance { - fn add_module_by_sigs( - &self, - name: &str, - module: Module, - sigs: HashMap<&str, &'static [UserFunctionDescriptor]> - ) -> result::Result>, interpreter::Error> { - let mut dufe = vec![DummyUserFunctionExecutor; sigs.len()]; - let dufe_refs = dufe.iter_mut().collect::>(); - let fake_module_map = sigs.into_iter() - .zip(dufe_refs.into_iter()) - .map(|((dep_mod_name, sigs), dufe)| { - let fake_module = Arc::new( - interpreter::env_native_module( - self.module(dep_mod_name).unwrap(), UserDefinedElements { - executor: Some(dufe), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(sigs), - } - ).unwrap() - ); - let fake_module: Arc> = fake_module; - (dep_mod_name.into(), fake_module) - }) - .collect::>(); - self.add_module(name, module, Some(&fake_module_map)) - } - } - #[test] fn should_pass_externalities_at_call() { let program = ProgramInstance::new().unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module_by_sigs("test", module, vec![("env", FEContext::SIGNATURES)].into_iter().collect()).expect("Failed to initialize module"); + let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::SIGNATURES]).expect("Failed to initialize module"); - let mut fec = FEContext { + let mut fec = FunctionExecutor { heap_end: 1024, memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) }; @@ -246,53 +157,17 @@ mod tests { let offset = fec.allocate(size); module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); - let execute = |fec| module.execute_export( - "test_data_in", - interpreter::ExecutionParams::with_external( - "env".into(), - Arc::new(interpreter::env_native_module(program.module("env").unwrap(), UserDefinedElements { - executor: Some(fec), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(FEContext::SIGNATURES), - }).unwrap()) - ) - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32)) + module.execute_export("test_data_in", + program.params_with_external("env", &mut fec) + .add_argument(I32(offset as i32)) + .add_argument(I32(size as i32)) ).unwrap(); - execute(&mut fec); - - panic!(); - } - - #[test] - fn should_pass_freeable_data() { - let fe_context = Arc::new(Mutex::new(None)); - let mut fex = FunctionExecutor { context: Arc::clone(&fe_context) }; - let externals = UserDefinedElements { - executor: Some(&mut fex), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), - }; - - let program = program_with_externals::(externals, "env").unwrap(); - - let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); - let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module("test", module, None).expect("Failed to initialize module"); - - *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) }); - - let data = b"Hello world"; - let size = data.len() as u32; - let offset = fe_context.lock().unwrap().as_mut().unwrap().allocate(size); - module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); - - module.execute_export("test_data_in", vec![I32(offset as i32), I32(size as i32)].into()).unwrap(); + // TODO: program.execute(module, "test_data_in", map!["env" => &mut fec], [I32(offset as i32), I32(size as i32)]); panic!(); } - +/* #[test] fn should_provide_externalities() { let fe_context = Arc::new(Mutex::new(None)); @@ -309,7 +184,7 @@ mod tests { let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module("test", module, None).expect("Failed to initialize module"); - *fe_context.lock().unwrap() = Some(FEContext { heap_end: 1024, memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) }); + *fe_context.lock().unwrap() = Some(FunctionExecutor { heap_end: 1024, memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) }); let argument: u64 = 20; assert_eq!(Some(I64(((argument + 1) * 2) as i64)), module.execute_export("test", vec![I64(argument as i64)].into()).unwrap()); @@ -318,5 +193,5 @@ mod tests { module.memory(ItemIndex::Internal(0)).unwrap().get_into(1024, unsafe { transmute::<_, &mut [u8; 8]>(&mut x) }).unwrap(); println!("heap: {:?}", x); panic!(); - } + }*/ } diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs index 74007048bccac..daf199e20d46c 100644 --- a/executor/src/wasm_utils.rs +++ b/executor/src/wasm_utils.rs @@ -16,22 +16,21 @@ //! Rust implementation of Polkadot contracts. -use std::sync::{Arc, Weak}; +use std::sync::{Arc/*, Weak*/}; +use std::collections::HashMap; pub use std::result; -pub use parity_wasm::elements::{ValueType}; +pub use parity_wasm::elements::{ValueType, Module}; pub use parity_wasm::interpreter::{RuntimeValue, UserFunctionDescriptor, UserFunctionExecutor, - UserDefinedElements, env_native_module, DummyUserError}; + UserDefinedElements, env_native_module, DummyUserError, ExecutionParams, UserError}; pub use parity_wasm::{builder}; use parity_wasm::interpreter; pub type Error = interpreter::Error; pub type MemoryInstance = interpreter::MemoryInstance; -pub type ProgramInstance = interpreter::ProgramInstance; pub type ModuleInstance = interpreter::ModuleInstance; -pub type ModuleInstanceInterface = interpreter::ModuleInstanceInterface; pub type CallerContext<'a> = interpreter::CallerContext<'a, DummyUserError>; -pub fn program_with_externals + 'static>(externals: UserDefinedElements, module_name: &str) -> result::Result { +/*pub fn program_with_externals + 'static>(externals: UserDefinedElements, module_name: &str) -> result::Result { let program = ProgramInstance::new().unwrap(); let instance = { let module = builder::module().build(); @@ -43,7 +42,7 @@ pub fn program_with_externals + 'static> // program.insert_loaded_module(module_name, Arc::new(other_instance)); Ok(program) } - +*/ pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; } impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } } impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } @@ -127,6 +126,10 @@ macro_rules! signatures { ); } +pub trait IntoUserDefinedElements { + fn into_user_defined_elements(&mut self) -> UserDefinedElements; +} + #[macro_export] macro_rules! function_executor { ( $objectname:ident : $structname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( @@ -136,5 +139,83 @@ macro_rules! function_executor { impl $structname { signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); } + impl $crate::wasm_utils::IntoUserDefinedElements for $structname { + fn into_user_defined_elements(&mut self) -> UserDefinedElements<$crate::wasm_utils::DummyUserError> { + $crate::wasm_utils::UserDefinedElements { + executor: Some(self), + globals: HashMap::new(), // TODO: provide + functions: ::std::borrow::Cow::from($structname::SIGNATURES), + } + } + } ); } + +#[derive(Clone)] +struct DummyUserFunctionExecutor; +impl interpreter::UserFunctionExecutor for DummyUserFunctionExecutor { + fn execute(&mut self, _name: &str, _context: interpreter::CallerContext) -> + result::Result, interpreter::Error> + { + unimplemented!() + } +} + +pub trait AddModuleWithoutFullDependentInstance { + fn add_module_by_sigs( + &self, + name: &str, + module: Module, + functions: HashMap<&str, &'static [UserFunctionDescriptor]>, + ) -> result::Result>, interpreter::Error>; + + fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> ExecutionParams<'a, DummyUserError>; +} + +impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance { + fn add_module_by_sigs( + &self, + name: &str, + module: Module, + functions: HashMap<&str, &'static [UserFunctionDescriptor]> + ) -> result::Result>, interpreter::Error> { + let mut dufe = vec![DummyUserFunctionExecutor; functions.len()]; + let dufe_refs = dufe.iter_mut().collect::>(); + let fake_module_map = functions.into_iter() + .zip(dufe_refs.into_iter()) + .map(|((dep_mod_name, functions), dufe)| { + let fake_module = Arc::new( + interpreter::env_native_module( + self.module(dep_mod_name).unwrap(), UserDefinedElements { + executor: Some(dufe), + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(functions), + } + ).unwrap() + ); + let fake_module: Arc> = fake_module; + (dep_mod_name.into(), fake_module) + }) + .collect::>(); + self.add_module(name, module, Some(&fake_module_map)) + } + + fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> ExecutionParams<'a, DummyUserError> { + interpreter::ExecutionParams::with_external( + externals_name.into(), + Arc::new( + interpreter::env_native_module( + self.module(externals_name).unwrap(), + externals.into_user_defined_elements() + ).unwrap() + ) + ) + } +} + +#[macro_export] +macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) +} From cd651a389844899cdb9c4d5c571dad36c63182a5 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 1 Jan 2018 22:02:46 +0000 Subject: [PATCH 22/49] Add basic externalities. --- Cargo.lock | 1 + executor/Cargo.toml | 1 + executor/src/lib.rs | 1 + executor/src/wasm_executor.rs | 80 +++++++++++++++++++++++++---------- executor/src/wasm_utils.rs | 10 ++--- 5 files changed, 66 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38ec2e36d4304..908f432d04a28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -640,6 +640,7 @@ name = "polkadot-executor" version = "0.1.0" dependencies = [ "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", diff --git a/executor/Cargo.toml b/executor/Cargo.toml index 0b3d154a98266..8aa72f4327b81 100644 --- a/executor/Cargo.toml +++ b/executor/Cargo.toml @@ -11,6 +11,7 @@ polkadot-state-machine = { path = "../state_machine" , version = "0.1" } serde = "1.0" serde_derive = "1.0" parity-wasm = "0.15.0" +byteorder = "1.1" [dev-dependencies] assert_matches = "1.1" diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 7c67827f55280..2b2013e647b01 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -32,6 +32,7 @@ extern crate polkadot_serializer as serializer; extern crate polkadot_state_machine as state_machine; extern crate serde; extern crate parity_wasm; +extern crate byteorder; #[macro_use] extern crate error_chain; diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 5adba4c111e91..5fbf727942466 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -26,28 +26,42 @@ use error::{Error, ErrorKind, Result}; use std::sync::{Arc}; use wasm_utils::{ModuleInstance, MemoryInstance, UserDefinedElements, AddModuleWithoutFullDependentInstance}; -struct FunctionExecutor { - heap_end: u32, - memory: Arc, +struct Heap { + end: u32, } -impl FunctionExecutor { - fn new(m: &Arc) -> Self { - FunctionExecutor { - heap_end: 1024, - memory: Arc::clone(&m.memory(ItemIndex::Internal(0)).unwrap()), +impl Heap { + fn new() -> Self { + Heap { + end: 1024, } } fn allocate(&mut self, size: u32) -> u32 { - let r = self.heap_end; - self.heap_end += size; + let r = self.end; + self.end += size; r } fn deallocate(&mut self, _offset: u32) { } } -function_executor!(this: FunctionExecutor, +struct FunctionExecutor<'e, E: Externalities + 'e> { + heap: Heap, + memory: Arc, + ext: &'e mut E, +} + +impl<'e, E: Externalities> FunctionExecutor<'e, E> { + fn new(m: &Arc, e: &'e mut E) -> Self { + FunctionExecutor { + heap: Heap::new(), + memory: Arc::clone(&m.memory(ItemIndex::Internal(0)).unwrap()), + ext: e, + } + } +} + +function_executor!(this: FunctionExecutor<'e, E>, imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); @@ -57,14 +71,37 @@ function_executor!(this: FunctionExecutor, ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, ext_malloc(size: usize) -> *mut u8 => { - let r = this.allocate(size); + let r = this.heap.allocate(size); println!("malloc {} bytes at {}", size, r); r }, ext_free(addr: *mut u8) => { - this.deallocate(addr); + this.heap.deallocate(addr); println!("free {}", addr) + }, + set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32) => { + if let (Ok(key), Ok(value)) = (this.memory.get(key_data, key_len as usize), this.memory.get(value_data, value_len as usize)) { + this.ext.set_storage(0, key, value); + } + }, + get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8 => { + let (offset, written) = if let Ok(key) = this.memory.get(key_data, key_len as usize) { + if let Ok(value) = this.ext.storage(0, &key) { + let offset = this.heap.allocate(value.len() as u32) as u32; + let _ = this.memory.set(offset, value); + (offset, value.len() as u32) + } else { (0, 0) } + } else { (0, 0) }; + + if written > 0 { + use byteorder::{LittleEndian, ByteOrder}; + let mut r = [0u8; 4]; + LittleEndian::write_u32(&mut r, written); + let _ = this.memory.set(written_out, &r); + } + offset as u32 } + => <'e, E: Externalities + 'e> ); /// Dummy rust executor for contracts. @@ -94,12 +131,12 @@ impl CodeExecutor for WasmExecutor { let program = ProgramInstance::new().unwrap(); let module = deserialize_buffer(code).expect("Failed to load module"); - let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::SIGNATURES]).expect("Failed to initialize module"); + let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); - let mut fec = FunctionExecutor::new(&module); + let mut fec = FunctionExecutor::new(&module, ext); let size = data.0.len() as u32; - let offset = fec.allocate(size); + let offset = fec.heap.allocate(size); module.memory(ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); let r = module.execute_export(method, @@ -141,20 +178,19 @@ mod tests { #[test] fn should_pass_externalities_at_call() { + let mut ext = TestExternalities; + let program = ProgramInstance::new().unwrap(); let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::SIGNATURES]).expect("Failed to initialize module"); + let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); - let mut fec = FunctionExecutor { - heap_end: 1024, - memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) - }; + let mut fec = FunctionExecutor::new(&module, &mut ext); let data = b"Hello world"; let size = data.len() as u32; - let offset = fec.allocate(size); + let offset = fec.heap.allocate(size); module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); module.execute_export("test_data_in", diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs index daf199e20d46c..b0cc314b9641d 100644 --- a/executor/src/wasm_utils.rs +++ b/executor/src/wasm_utils.rs @@ -132,19 +132,19 @@ pub trait IntoUserDefinedElements { #[macro_export] macro_rules! function_executor { - ( $objectname:ident : $structname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => ( - impl $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname { + ( $objectname:ident : $structname:ty, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* => $($pre:tt)+ ) => ( + impl $( $pre ) + $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname { dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); } - impl $structname { + impl $( $pre ) + $structname { signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); } - impl $crate::wasm_utils::IntoUserDefinedElements for $structname { + impl $( $pre ) + $crate::wasm_utils::IntoUserDefinedElements for $structname { fn into_user_defined_elements(&mut self) -> UserDefinedElements<$crate::wasm_utils::DummyUserError> { $crate::wasm_utils::UserDefinedElements { executor: Some(self), globals: HashMap::new(), // TODO: provide - functions: ::std::borrow::Cow::from($structname::SIGNATURES), + functions: ::std::borrow::Cow::from(Self::SIGNATURES), } } } From 4404846c4bbc599a63f4c68d716ad4f675dff70d Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 1 Jan 2018 22:23:23 +0000 Subject: [PATCH 23/49] Fix grumbles and note unwraps to be sorted. --- executor/src/lib.rs | 3 --- executor/src/wasm_executor.rs | 38 ++++------------------------------- executor/src/wasm_utils.rs | 8 ++++---- 3 files changed, 8 insertions(+), 41 deletions(-) diff --git a/executor/src/lib.rs b/executor/src/lib.rs index 2b2013e647b01..8bf7f42b623fe 100644 --- a/executor/src/lib.rs +++ b/executor/src/lib.rs @@ -36,11 +36,8 @@ extern crate byteorder; #[macro_use] extern crate error_chain; -//#[macro_use] -//extern crate serde_derive; #[cfg(test)] -//#[macro_use] extern crate assert_matches; #[macro_use] diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 5fbf727942466..e4d1fde88cbd6 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -61,7 +61,7 @@ impl<'e, E: Externalities> FunctionExecutor<'e, E> { } } -function_executor!(this: FunctionExecutor<'e, E>, +impl_function_executor!(this: FunctionExecutor<'e, E>, imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); @@ -109,8 +109,7 @@ function_executor!(this: FunctionExecutor<'e, E>, /// Instead of actually executing the provided code it just /// dispatches the calls to pre-defined hardcoded implementations in rust. #[derive(Debug, Default)] -pub struct WasmExecutor { -} +pub struct WasmExecutor; impl CodeExecutor for WasmExecutor { type Error = Error; @@ -128,7 +127,7 @@ impl CodeExecutor for WasmExecutor { Err(e) => Err(ErrorKind::Externalities(Box::new(e)))?, }; - let program = ProgramInstance::new().unwrap(); + let program = ProgramInstance::new().unwrap(); // TODO: handle let module = deserialize_buffer(code).expect("Failed to load module"); let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); @@ -137,7 +136,7 @@ impl CodeExecutor for WasmExecutor { let size = data.0.len() as u32; let offset = fec.heap.allocate(size); - module.memory(ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); + module.memory(ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); // TODO: handle x2 let r = module.execute_export(method, program.params_with_external("env", &mut fec) @@ -199,35 +198,6 @@ mod tests { .add_argument(I32(size as i32)) ).unwrap(); - // TODO: program.execute(module, "test_data_in", map!["env" => &mut fec], [I32(offset as i32), I32(size as i32)]); - panic!(); } -/* - #[test] - fn should_provide_externalities() { - let fe_context = Arc::new(Mutex::new(None)); - let mut fex = FunctionExecutor { context: Arc::clone(&fe_context) }; - let externals = UserDefinedElements { - executor: Some(&mut fex), - globals: HashMap::new(), - functions: ::std::borrow::Cow::from(FunctionExecutor::SIGNATURES), - }; - - let program = program_with_externals::(externals, "env").unwrap(); - - let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.wasm"); - let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); - let module = program.add_module("test", module, None).expect("Failed to initialize module"); - - *fe_context.lock().unwrap() = Some(FunctionExecutor { heap_end: 1024, memory: Arc::clone(&module.memory(ItemIndex::Internal(0)).unwrap()) }); - - let argument: u64 = 20; - assert_eq!(Some(I64(((argument + 1) * 2) as i64)), module.execute_export("test", vec![I64(argument as i64)].into()).unwrap()); - - let mut x = [0u64; 2]; - module.memory(ItemIndex::Internal(0)).unwrap().get_into(1024, unsafe { transmute::<_, &mut [u8; 8]>(&mut x) }).unwrap(); - println!("heap: {:?}", x); - panic!(); - }*/ } diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs index b0cc314b9641d..8c062396e7b2f 100644 --- a/executor/src/wasm_utils.rs +++ b/executor/src/wasm_utils.rs @@ -131,7 +131,7 @@ pub trait IntoUserDefinedElements { } #[macro_export] -macro_rules! function_executor { +macro_rules! impl_function_executor { ( $objectname:ident : $structname:ty, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* => $($pre:tt)+ ) => ( impl $( $pre ) + $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname { dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); @@ -191,7 +191,7 @@ impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance> = fake_module; (dep_mod_name.into(), fake_module) @@ -205,9 +205,9 @@ impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance Date: Wed, 3 Jan 2018 18:32:50 +0100 Subject: [PATCH 24/49] Test storage externality. Unforunately had to change signatures of externalities to avoid immutable function returning a reference. Not sure what to do about this... --- executor/src/wasm_executor.rs | 40 ++++++++++-------- runtime/src/lib.rs | 5 +++ .../release/runtime.compact.wasm | Bin 377 -> 417 bytes .../release/runtime.wasm | Bin 464 -> 504 bytes state_machine/src/ext.rs | 12 +++--- state_machine/src/lib.rs | 4 +- 6 files changed, 36 insertions(+), 25 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index e4d1fde88cbd6..a38ca5b6c07a4 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -88,7 +88,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, let (offset, written) = if let Ok(key) = this.memory.get(key_data, key_len as usize) { if let Ok(value) = this.ext.storage(0, &key) { let offset = this.heap.allocate(value.len() as u32) as u32; - let _ = this.memory.set(offset, value); + let _ = this.memory.set(offset, &value); (offset, value.len() as u32) } else { (0, 0) } } else { (0, 0) }; @@ -154,16 +154,18 @@ mod tests { use super::*; #[derive(Debug, Default)] - struct TestExternalities; + struct TestExternalities { + data: HashMap, Vec>, + } impl Externalities for TestExternalities { type Error = Error; - fn code(&self) -> Result<&[u8]> { + fn code(&self) -> Result> { unimplemented!() } - fn storage(&self, _object: u64, _key: &[u8]) -> Result<&[u8]> { - unimplemented!() + fn storage(&self, _object: u64, _key: &[u8]) -> Result> { + Ok(self.data.get(_key).map(|v| v.clone()).unwrap_or_else(|| vec![])) } fn set_code(&mut self, _code: Vec) { @@ -171,13 +173,13 @@ mod tests { } fn set_storage(&mut self, _object: u64, _key: Vec, _value: Vec) { - unimplemented!() + self.data.insert(_key, _value); } } #[test] fn should_pass_externalities_at_call() { - let mut ext = TestExternalities; + let mut ext = TestExternalities::default(); let program = ProgramInstance::new().unwrap(); @@ -185,18 +187,22 @@ mod tests { let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); - let mut fec = FunctionExecutor::new(&module, &mut ext); + { + let mut fec = FunctionExecutor::new(&module, &mut ext); - let data = b"Hello world"; - let size = data.len() as u32; - let offset = fec.heap.allocate(size); - module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); + let data = b"Hello world"; + let size = data.len() as u32; + let offset = fec.heap.allocate(size); + module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); - module.execute_export("test_data_in", - program.params_with_external("env", &mut fec) - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32)) - ).unwrap(); + module.execute_export("test_data_in", + program.params_with_external("env", &mut fec) + .add_argument(I32(offset as i32)) + .add_argument(I32(size as i32)) + ).unwrap(); + } + + println!("storage: {:?}", ext.data); panic!(); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 95b40537001a4..83d475b57b433 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -18,6 +18,8 @@ pub fn panic_fmt() -> ! { extern "C" { fn imported(n: u64) -> u64; + fn set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); + fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; } fn do_something(param: u64) -> u64 { @@ -40,6 +42,9 @@ pub fn test_data_in(freeable_data: *mut u8, size: usize) { unsafe { pwasm_libc::free(freeable_data); } + let key = b"thekey"; + unsafe { set_storage(key as *const u8, key.len() as i32, ©[0] as *const u8, copy.len() as i32); } + // Do some stuff. for b in © { unsafe { imported(*b as u64); } diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index 3c66312a49c153b7f30cfcaea26f6e76957b11e3..b9509edf0f3d11aad055869fd4c35efc1f425321 100644 GIT binary patch delta 242 zcmXAg&q@O^7{urMHk-AZ7IP87Q!MmU1u6929Qp)ayzPcIYUw}h2Ej}742w_T#fR~Y zyqVx(_{|J6zx-9q;JQ5lph?wIL=wfeh%tlCbZ$lRsRvq40^x8O_=S*K7``JM34y{ZDY4Q0$ delta 180 zcmXYqu?oU407UQ7v{j?fSwW(!Iw-iC!Nu9V30gsDRkX9&-HKnJzvfrC*@DCI;NHQ# zg0Gms-3kKWFtYThLvI7`8}QD%1+4HM!pLf2!Z?yaSUqM_Da-6uBJ4Iu_pWs5@q{QU zT%MnIXvxDh%g}BUop<$F&bOo&E3wjC_n?_@EPC~?G8_q|g>JgIuaV%td2TqSdR{nC L9l}T@lUn`&Hu@qt diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm index ec6712b5076a819b8d8ba08f9fa825cb0a50f9f6..c3b7ea4b63800cdc433e84c5e752e6833d23591d 100644 GIT binary patch delta 249 zcmWlRy-ve06ot>Vjnl*lWTBOqOeH1;L?!lSsKg_%C01*GMAJZHMe0&NLzO-b55eOw zVh>l}IiHU1FaP5Un7$H#55%RBv6~yKZmZ zECf#{7hIms56_8kws)B15%tD6Q-AD`%GS2)uLH6qgti+jR8-*!a@m^gwp*hRuX}58 znG&*}QPI=lg~(TQ%h-}FHLVJ0DrwGE1u4yRY}t)Qi7KyT5LC+Xo95z50ppn-r`HUX PP$EvkMoIT>KkeZkp*A-9 delta 209 zcmXYrv1-FG07UO(MX{|IJa;Hc=$0Y4(A^md`GD@Ri4_P~Nr_FMi}eAWjK8Bpw|+)H zr(#SmUgRP- od`)1OawU@Sl-_bFv=L@v(St#X|IP5p74`7VOvl7lYMGAZ9f{K`Gynhq diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index 5e7d6fbde37c9..fafff181882c6 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -61,17 +61,17 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> { type Error = B::Error; - fn code(&self) -> Result<&[u8], Self::Error> { - Ok(self.overlay.code()) + fn code(&self) -> Result, Self::Error> { + Ok(self.overlay.code().into()) } - fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error> { + fn storage(&self, object: u64, key: &[u8]) -> Result, Self::Error> { match self.overlay.storage(object, key) { - Some(x) => Ok(x), - None => self.backend.storage(object, key) + Some(x) => Ok(x.into()), + None => self.backend.storage(object, key).map(Into::into) } } - + fn set_code(&mut self, code: Vec) { self.overlay.set_code(code); } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index fd486a4639b07..eefd375ce758c 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -165,10 +165,10 @@ pub trait Externalities { type Error: Error; /// Get the current runtime. - fn code(&self) -> Result<&[u8], Self::Error>; + fn code(&self) -> Result, Self::Error>; /// Read storage of current contract being called. - fn storage(&self, index: u64, key: &[u8]) -> Result<&[u8], Self::Error>; + fn storage(&self, index: u64, key: &[u8]) -> Result, Self::Error>; /// Set the new runtime. fn set_code(&mut self, code: Vec); From 319d9c0a8acbdcd9f36bd3c358b519e14bd01975 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 3 Jan 2018 19:07:23 +0100 Subject: [PATCH 25/49] Fix nits. --- executor/src/wasm_executor.rs | 29 +++++++++++++++++------------ executor/src/wasm_utils.rs | 18 ++---------------- state_machine/src/backend.rs | 4 +--- state_machine/src/lib.rs | 4 +--- 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index a38ca5b6c07a4..abf7ae8fe72af 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -16,15 +16,16 @@ //! Rust implementation of Polkadot contracts. +use std::sync::Arc; +use std::collections::HashMap; use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance}; use parity_wasm::interpreter::{ItemIndex}; use parity_wasm::RuntimeValue::{I32, I64}; -use std::collections::HashMap; use primitives::contract::CallData; use state_machine::{Externalities, CodeExecutor}; use error::{Error, ErrorKind, Result}; -use std::sync::{Arc}; -use wasm_utils::{ModuleInstance, MemoryInstance, UserDefinedElements, AddModuleWithoutFullDependentInstance}; +use wasm_utils::{MemoryInstance, UserDefinedElements, + AddModuleWithoutFullDependentInstance}; struct Heap { end: u32, @@ -52,10 +53,10 @@ struct FunctionExecutor<'e, E: Externalities + 'e> { } impl<'e, E: Externalities> FunctionExecutor<'e, E> { - fn new(m: &Arc, e: &'e mut E) -> Self { + fn new(m: &Arc, e: &'e mut E) -> Self { FunctionExecutor { heap: Heap::new(), - memory: Arc::clone(&m.memory(ItemIndex::Internal(0)).unwrap()), + memory: Arc::clone(m), ext: e, } } @@ -127,16 +128,19 @@ impl CodeExecutor for WasmExecutor { Err(e) => Err(ErrorKind::Externalities(Box::new(e)))?, }; - let program = ProgramInstance::new().unwrap(); // TODO: handle + // TODO: handle all expects as errors to be returned. + + let program = ProgramInstance::new().expect("this really shouldn't be able to fail; qed"); - let module = deserialize_buffer(code).expect("Failed to load module"); - let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); + let module = deserialize_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed"); + let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("runtime signatures always provided; qed"); - let mut fec = FunctionExecutor::new(&module, ext); + let memory = module.memory(ItemIndex::Internal(0)).expect("all modules compiled with rustc include memory segments; qed"); + let mut fec = FunctionExecutor::new(&memory, ext); let size = data.0.len() as u32; let offset = fec.heap.allocate(size); - module.memory(ItemIndex::Internal(0)).unwrap().set(offset, &data.0).unwrap(); // TODO: handle x2 + memory.set(offset, &data.0).expect("heap always gives a sensible offset to write"); let r = module.execute_export(method, program.params_with_external("env", &mut fec) @@ -188,12 +192,13 @@ mod tests { let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); { - let mut fec = FunctionExecutor::new(&module, &mut ext); + let memory = module.memory(ItemIndex::Internal(0)).unwrap(); + let mut fec = FunctionExecutor::new(&memory, &mut ext); let data = b"Hello world"; let size = data.len() as u32; let offset = fec.heap.allocate(size); - module.memory(ItemIndex::Internal(0)).unwrap().set(offset, data).unwrap(); + memory.set(offset, data).unwrap(); module.execute_export("test_data_in", program.params_with_external("env", &mut fec) diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs index 8c062396e7b2f..6b20885256019 100644 --- a/executor/src/wasm_utils.rs +++ b/executor/src/wasm_utils.rs @@ -16,33 +16,19 @@ //! Rust implementation of Polkadot contracts. -use std::sync::{Arc/*, Weak*/}; +use std::sync::{Arc}; use std::collections::HashMap; pub use std::result; +pub use parity_wasm::builder; pub use parity_wasm::elements::{ValueType, Module}; pub use parity_wasm::interpreter::{RuntimeValue, UserFunctionDescriptor, UserFunctionExecutor, UserDefinedElements, env_native_module, DummyUserError, ExecutionParams, UserError}; -pub use parity_wasm::{builder}; use parity_wasm::interpreter; pub type Error = interpreter::Error; pub type MemoryInstance = interpreter::MemoryInstance; -pub type ModuleInstance = interpreter::ModuleInstance; pub type CallerContext<'a> = interpreter::CallerContext<'a, DummyUserError>; -/*pub fn program_with_externals + 'static>(externals: UserDefinedElements, module_name: &str) -> result::Result { - let program = ProgramInstance::new().unwrap(); - let instance = { - let module = builder::module().build(); - let mut instance = ModuleInstance::new(Weak::default(), module_name.into(), module)?; - instance.instantiate(None)?; - instance - }; - let other_instance = env_native_module(Arc::new(instance), externals)?; -// program.insert_loaded_module(module_name, Arc::new(other_instance)); - Ok(program) -} -*/ pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; } impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } } impl ConvertibleToWasm for u32 { type NativeType = u32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self as i32) } } diff --git a/state_machine/src/backend.rs b/state_machine/src/backend.rs index 060ff84071b95..4d3b0d0606d4e 100644 --- a/state_machine/src/backend.rs +++ b/state_machine/src/backend.rs @@ -67,8 +67,6 @@ pub struct InMemory { inner: MemoryState, // keeps all the state in memory. } -const EMPTY_BYTES: [u8; 0] = []; - impl Backend for InMemory { type Error = Void; @@ -94,7 +92,7 @@ impl Backend for InMemory { let storage_tree_root = H256(sec_trie_root(storage_roots).0); - let code_hash = hash(&self.inner.code().unwrap_or_else(|| &EMPTY_BYTES)); + let code_hash = hash(&self.inner.code().unwrap_or_else(|| &[])); Committed { code_hash, diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index eefd375ce758c..5ce9884d68fea 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -110,13 +110,11 @@ pub struct OverlayedChanges { committed: MemoryState, } -const EMPTY_BYTES: [u8; 0] = []; - impl OverlayedChanges { fn code(&self) -> &[u8] { self.prospective.code() .or_else(|| self.committed.code()) - .unwrap_or_else(|| &EMPTY_BYTES) + .unwrap_or_else(|| &[]) } fn storage(&self, object: u64, key: &[u8]) -> Option<&[u8]> { From 2934d945d674d5c2717787727c33cc8d99572ac7 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 3 Jan 2018 19:18:05 +0100 Subject: [PATCH 26/49] Compile collation logic. --- Cargo.lock | 4 ++-- Cargo.toml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5545f27942e5b..15f526edd955d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -644,7 +644,7 @@ dependencies = [ ] [[package]] -name = "polkadot-executor" +name = "polkadot-collator" version = "0.1.0" dependencies = [ "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", @@ -652,7 +652,7 @@ dependencies = [ ] [[package]] -name = "polkadot-contracts" +name = "polkadot-executor" version = "0.1.0" dependencies = [ "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index c2741c5b7ce15..a0f6303a6782f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ polkadot-cli = { path = "cli", version = "0.1" } members = [ "candidate-agreement", "client", + "collator", "executor", "primitives", "rpc", From 5998aa130692d91113f369ff723b90c4fccddd38 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 3 Jan 2018 19:43:26 +0100 Subject: [PATCH 27/49] Move back to refs. Yey. --- executor/src/wasm_executor.rs | 6 +++--- state_machine/src/ext.rs | 10 +++++----- state_machine/src/lib.rs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index abf7ae8fe72af..aeb0aaa39298d 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -164,12 +164,12 @@ mod tests { impl Externalities for TestExternalities { type Error = Error; - fn code(&self) -> Result> { + fn code(&self) -> Result<&[u8]> { unimplemented!() } - fn storage(&self, _object: u64, _key: &[u8]) -> Result> { - Ok(self.data.get(_key).map(|v| v.clone()).unwrap_or_else(|| vec![])) + fn storage(&self, _object: u64, key: &[u8]) -> Result<&[u8]> { + Ok(self.data.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) } fn set_code(&mut self, _code: Vec) { diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index fafff181882c6..fa72d07101a9f 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -61,14 +61,14 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> { type Error = B::Error; - fn code(&self) -> Result, Self::Error> { - Ok(self.overlay.code().into()) + fn code(&self) -> Result<&[u8], Self::Error> { + Ok(self.overlay.code()) } - fn storage(&self, object: u64, key: &[u8]) -> Result, Self::Error> { + fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error> { match self.overlay.storage(object, key) { - Some(x) => Ok(x.into()), - None => self.backend.storage(object, key).map(Into::into) + Some(x) => Ok(x), + None => self.backend.storage(object, key) } } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 5ce9884d68fea..196ec6f9dc954 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -163,10 +163,10 @@ pub trait Externalities { type Error: Error; /// Get the current runtime. - fn code(&self) -> Result, Self::Error>; + fn code(&self) -> Result<&[u8], Self::Error>; /// Read storage of current contract being called. - fn storage(&self, index: u64, key: &[u8]) -> Result, Self::Error>; + fn storage(&self, index: u64, key: &[u8]) -> Result<&[u8], Self::Error>; /// Set the new runtime. fn set_code(&mut self, code: Vec); From 3f4085ae20ae9b60fd5fb9591a0c9921ba246e2a Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 4 Jan 2018 11:36:35 +0100 Subject: [PATCH 28/49] Remove "object" id for storage access. --- executor/src/wasm_executor.rs | 13 +++--- state_machine/src/backend.rs | 24 +++++------- state_machine/src/ext.rs | 10 ++--- state_machine/src/lib.rs | 74 ++++++++++++++--------------------- 4 files changed, 50 insertions(+), 71 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index aeb0aaa39298d..b1581b9f7615f 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -82,12 +82,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, }, set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32) => { if let (Ok(key), Ok(value)) = (this.memory.get(key_data, key_len as usize), this.memory.get(value_data, value_len as usize)) { - this.ext.set_storage(0, key, value); + this.ext.set_storage(key, value); } }, get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8 => { let (offset, written) = if let Ok(key) = this.memory.get(key_data, key_len as usize) { - if let Ok(value) = this.ext.storage(0, &key) { + if let Ok(value) = this.ext.storage(&key) { let offset = this.heap.allocate(value.len() as u32) as u32; let _ = this.memory.set(offset, &value); (offset, value.len() as u32) @@ -160,23 +160,24 @@ mod tests { #[derive(Debug, Default)] struct TestExternalities { data: HashMap, Vec>, + code: Vec, } impl Externalities for TestExternalities { type Error = Error; fn code(&self) -> Result<&[u8]> { - unimplemented!() + Ok(self.code.as_slice()) } - fn storage(&self, _object: u64, key: &[u8]) -> Result<&[u8]> { + fn storage(&self, key: &[u8]) -> Result<&[u8]> { Ok(self.data.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) } fn set_code(&mut self, _code: Vec) { - unimplemented!() + self.code = _code; } - fn set_storage(&mut self, _object: u64, _key: Vec, _value: Vec) { + fn set_storage(&mut self, _key: Vec, _value: Vec) { self.data.insert(_key, _value); } } diff --git a/state_machine/src/backend.rs b/state_machine/src/backend.rs index 4d3b0d0606d4e..d48ecdc793ff9 100644 --- a/state_machine/src/backend.rs +++ b/state_machine/src/backend.rs @@ -38,7 +38,7 @@ pub trait Backend { type Error: super::Error; /// Get keyed storage associated with specific address. - fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error>; + fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; /// Commit updates to the backend and get new state. fn commit(&mut self, changes: I) -> Committed @@ -70,8 +70,8 @@ pub struct InMemory { impl Backend for InMemory { type Error = Void; - fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Void> { - Ok(self.inner.storage(object, key).unwrap_or(&[])) + fn storage(&self, key: &[u8]) -> Result<&[u8], Void> { + Ok(self.inner.storage(key).unwrap_or(&[])) } fn commit(&mut self, changes: I) -> Committed @@ -80,19 +80,13 @@ impl Backend for InMemory { self.inner.update(changes); // fully recalculate trie roots. - let storage_roots = self.inner.storage.iter().map(|(object, storage)| { - let flat_trie = storage.iter().map(|(k, v)| (k.to_vec(), v.clone())).collect(); - ({ - use byteorder::{BigEndian, ByteOrder}; - let mut r = [0u8; 8]; - BigEndian::write_u64(&mut r, *object); - r.to_vec() - }, sec_trie_root(flat_trie).to_vec()) - }).collect(); - - let storage_tree_root = H256(sec_trie_root(storage_roots).0); - + let storage_tree_root = H256(sec_trie_root( + self.inner.storage.iter() + .map(|(k, v)| (k.to_vec(), v.clone())) + .collect() + ).0); let code_hash = hash(&self.inner.code().unwrap_or_else(|| &[])); + // TODO: include validators list. Committed { code_hash, diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index fa72d07101a9f..d6ec99748af59 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -65,10 +65,10 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> Ok(self.overlay.code()) } - fn storage(&self, object: u64, key: &[u8]) -> Result<&[u8], Self::Error> { - match self.overlay.storage(object, key) { + fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { + match self.overlay.storage(key) { Some(x) => Ok(x), - None => self.backend.storage(object, key) + None => self.backend.storage(key) } } @@ -76,7 +76,7 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> self.overlay.set_code(code); } - fn set_storage(&mut self, object: u64, key: Vec, value: Vec) { - self.overlay.set_storage(object, key, value); + fn set_storage(&mut self, key: Vec, value: Vec) { + self.overlay.set_storage(key, value); } } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 196ec6f9dc954..b639327b1f79e 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -40,7 +40,7 @@ mod ext; /// Updates to be committed to the state. pub enum Update { /// Set storage of object at given key -- empty is deletion. - Storage(u64, Vec, Vec), + Storage(Vec, Vec), /// Set code -- empty is deletion. Code(Vec), } @@ -49,7 +49,7 @@ pub enum Update { #[derive(Default)] struct MemoryState { code: Option>, // None is unchanged. - storage: HashMap, Vec>>, + storage: HashMap, Vec>, } impl MemoryState { @@ -57,10 +57,8 @@ impl MemoryState { self.code.as_ref().map(|x| &**x) } - fn storage(&self, object: u64, key: &[u8]) -> Option<&[u8]> { - self.storage.get(&object) - .and_then(|m| m.get(key)) - .map(|v| &v[..]) + fn storage(&self, key: &[u8]) -> Option<&[u8]> { + self.storage.get(key).map(|v| &v[..]) } #[allow(unused)] @@ -68,28 +66,18 @@ impl MemoryState { self.code = Some(code); } - fn set_storage(&mut self, object: u64, key: Vec, val: Vec) { - self.storage.entry(object) - .or_insert_with(HashMap::new) - .insert(key, val); + fn set_storage(&mut self, key: Vec, val: Vec) { + self.storage.insert(key, val); } fn update(&mut self, changes: I) where I: IntoIterator { for update in changes { match update { - Update::Storage(object, key, val) => { + Update::Storage(key, val) => { if val.is_empty() { - let mut empty = false; - if let Some(s) = self.storage.get_mut(&object) { - s.remove(&key); - empty = s.is_empty(); - }; - - if empty { self.storage.remove(&object); } + self.storage.remove(&key); } else { - self.storage.entry(object) - .or_insert_with(HashMap::new) - .insert(key, val); + self.storage.insert(key, val); } } Update::Code(code) => { @@ -117,9 +105,9 @@ impl OverlayedChanges { .unwrap_or_else(|| &[]) } - fn storage(&self, object: u64, key: &[u8]) -> Option<&[u8]> { - self.prospective.storage(object, key) - .or_else(|| self.committed.storage(object, key)) + 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) }) } @@ -128,8 +116,8 @@ impl OverlayedChanges { self.prospective.set_code(code); } - fn set_storage(&mut self, object: u64, key: Vec, val: Vec) { - self.prospective.set_storage(object, key, val); + fn set_storage(&mut self, key: Vec, val: Vec) { + self.prospective.set_storage(key, val); } /// Discard prospective changes to state. @@ -144,8 +132,7 @@ impl OverlayedChanges { .map(|code| Update::Code(code)); let storage_updates = self.prospective.storage.drain() - .flat_map(|(object, storages)| storages.into_iter().map(move |(k, v)| (object, k, v))) - .map(|(object, key, value)| Update::Storage(object, key, value)); + .map(|(key, value)| Update::Storage(key, value)); self.committed.update(code_updates.chain(storage_updates)); } @@ -166,13 +153,13 @@ pub trait Externalities { fn code(&self) -> Result<&[u8], Self::Error>; /// Read storage of current contract being called. - fn storage(&self, index: u64, key: &[u8]) -> Result<&[u8], Self::Error>; + fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; /// Set the new runtime. fn set_code(&mut self, code: Vec); /// Set storage of current contract being called. - fn set_storage(&mut self, index: u64, key: Vec, value: Vec); + fn set_storage(&mut self, key: Vec, value: Vec); } /// Code execution engine. @@ -234,48 +221,45 @@ mod tests { let mut overlayed = OverlayedChanges::default(); let key = vec![42, 69, 169, 142]; - let object = 69; - assert!(overlayed.storage(object, &key).is_none()); + assert!(overlayed.storage(&key).is_none()); - overlayed.set_storage(object, key, vec![1, 2, 3]); - assert_eq!(overlayed.storage(object, &key).unwrap(), &[1, 2, 3]); + overlayed.set_storage(key, vec![1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); overlayed.commit_prospective(); - assert_eq!(overlayed.storage(object, &key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); - overlayed.set_storage(object, key, vec![]); - assert!(overlayed.storage(object, &key).is_none()); + overlayed.set_storage(key, vec![]); + assert!(overlayed.storage(&key).is_none()); overlayed.discard_prospective(); - assert_eq!(overlayed.storage(object, &key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); - overlayed.set_storage(object, key, vec![]); + overlayed.set_storage(key, vec![]); overlayed.commit_prospective(); - assert!(overlayed.storage(object, &key).is_none()); + assert!(overlayed.storage(&key).is_none()); } #[test] fn overlayed_code_works() { let mut overlayed = OverlayedChanges::default(); - let object = 69; - assert!(overlayed.code(&object).is_none()); - overlayed.set_code(object, vec![1, 2, 3]); + overlayed.set_code(vec![1, 2, 3]); assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); overlayed.commit_prospective(); assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); - overlayed.set_code(object, vec![]); + overlayed.set_code(vec![]); assert!(overlayed.code(&object).is_none()); overlayed.discard_prospective(); assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); - overlayed.set_code(object, vec![]); + overlayed.set_code(vec![]); overlayed.commit_prospective(); assert!(overlayed.code(&object).is_none()); } From 4be0537fc11e96174bfcb8e2ba0cccf221ac8d25 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 4 Jan 2018 12:16:37 +0100 Subject: [PATCH 29/49] Fix test. --- state_machine/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index b639327b1f79e..2435a33de91c6 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -245,22 +245,22 @@ mod tests { fn overlayed_code_works() { let mut overlayed = OverlayedChanges::default(); - assert!(overlayed.code(&object).is_none()); + assert!(overlayed.code().is_none()); overlayed.set_code(vec![1, 2, 3]); - assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code().unwrap(), &[1, 2, 3]); overlayed.commit_prospective(); - assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code().unwrap(), &[1, 2, 3]); overlayed.set_code(vec![]); - assert!(overlayed.code(&object).is_none()); + assert!(overlayed.code().is_none()); overlayed.discard_prospective(); - assert_eq!(overlayed.code(&object).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code().unwrap(), &[1, 2, 3]); overlayed.set_code(vec![]); overlayed.commit_prospective(); - assert!(overlayed.code(&object).is_none()); + assert!(overlayed.code().is_none()); } } From 01d70192920522711722806e28939ae638178423 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 4 Jan 2018 12:36:09 +0100 Subject: [PATCH 30/49] Fix up rest of tests. --- client/src/lib.rs | 4 ++-- executor/src/wasm_executor.rs | 5 ++--- rpc/src/state/mod.rs | 6 +++--- rpc/src/state/tests.rs | 7 ++++--- state_machine/src/lib.rs | 18 +++++++++--------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 6ce26470c6202..d40cd5aa61dc6 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -72,9 +72,9 @@ impl Client where } /// Return single storage entry of contract under given address in state in a block of given hash. - pub fn storage(&self, hash: &block::HeaderHash, object: u64, key: &StorageKey) -> error::Result { + pub fn storage(&self, hash: &block::HeaderHash, key: &StorageKey) -> error::Result { self.state_at(hash)? - .storage(object, &key.0) + .storage(&key.0) .map(|x| StorageData(x.to_vec())) .chain_err(|| error::ErrorKind::Backend) } diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index b1581b9f7615f..37da9144cf585 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -208,8 +208,7 @@ mod tests { ).unwrap(); } - println!("storage: {:?}", ext.data); - - panic!(); + let expected: HashMap<_, _> = map![vec![116, 104, 101, 107, 101, 121] => vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]]; + assert_eq!(expected, ext.data); } } diff --git a/rpc/src/state/mod.rs b/rpc/src/state/mod.rs index d136364fe1309..1db83df1ee9e5 100644 --- a/rpc/src/state/mod.rs +++ b/rpc/src/state/mod.rs @@ -33,7 +33,7 @@ build_rpc_trait! { pub trait StateApi { /// Returns a storage entry. #[rpc(name = "state_getStorage")] - fn storage(&self, u64, StorageKey, block::HeaderHash) -> Result; + fn storage(&self, StorageKey, block::HeaderHash) -> Result; /// Call a contract. #[rpc(name = "state_call")] @@ -45,8 +45,8 @@ impl StateApi for Client where B: client::Blockchain + Send + Sync + 'static, E: state_machine::CodeExecutor + Send + Sync + 'static, { - fn storage(&self, object: u64, key: StorageKey, block: block::HeaderHash) -> Result { - Ok(self.storage(&block, object, &key)?) + fn storage(&self, key: StorageKey, block: block::HeaderHash) -> Result { + Ok(self.storage(&block, &key)?) } fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result { diff --git a/rpc/src/state/tests.rs b/rpc/src/state/tests.rs index 8c1a2adb0f3aa..20051d4144a19 100644 --- a/rpc/src/state/tests.rs +++ b/rpc/src/state/tests.rs @@ -25,18 +25,19 @@ fn should_return_storage() { let client = Client::new(Blockchain::default(), executor::executor()); assert_matches!( - StateApi::storage(&client, 5.into(), 10.into(), 0.into()), + StateApi::storage(&client, StorageKey(vec![10]), 0.into()), Ok(ref x) if x.0.is_empty() ) } #[test] +#[ignore] // TODO: [ToDr] reenable once we can properly mock the wasm executor env fn should_call_contract() { // TODO [ToDr] Fix test after we are able to mock state. - let client = Client::new(Blockchain::default(), contracts::executor()); + let client = Client::new(Blockchain::default(), executor::executor()); assert_matches!( - StateApi::call(&client, 1.into(), "balanceOf".into(), CallData(vec![1,2,3]), 0.into()), + StateApi::call(&client, "balanceOf".into(), CallData(vec![1,2,3]), 0.into()), Err(Error(ErrorKind::Client(client::error::ErrorKind::Execution(_)), _)) ) } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 2435a33de91c6..7ff6860e0aadf 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -224,19 +224,19 @@ mod tests { assert!(overlayed.storage(&key).is_none()); - overlayed.set_storage(key, vec![1, 2, 3]); + 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, vec![]); + 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, vec![]); + overlayed.set_storage(key.clone(), vec![]); overlayed.commit_prospective(); assert!(overlayed.storage(&key).is_none()); } @@ -245,22 +245,22 @@ mod tests { fn overlayed_code_works() { let mut overlayed = OverlayedChanges::default(); - assert!(overlayed.code().is_none()); + assert!(overlayed.code().len() == 0); overlayed.set_code(vec![1, 2, 3]); - assert_eq!(overlayed.code().unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code(), &[1, 2, 3]); overlayed.commit_prospective(); - assert_eq!(overlayed.code().unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code(), &[1, 2, 3]); overlayed.set_code(vec![]); - assert!(overlayed.code().is_none()); + assert!(overlayed.code().len() == 0); overlayed.discard_prospective(); - assert_eq!(overlayed.code().unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.code(), &[1, 2, 3]); overlayed.set_code(vec![]); overlayed.commit_prospective(); - assert!(overlayed.code().is_none()); + assert!(overlayed.code().len() == 0); } } From db1adee5a2b4fad80db25123fda00d99aa9daa62 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 4 Jan 2018 12:37:52 +0100 Subject: [PATCH 31/49] remove unwrap. --- executor/src/wasm_executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 37da9144cf585..0a0392c715af6 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -65,7 +65,7 @@ impl<'e, E: Externalities> FunctionExecutor<'e, E> { impl_function_executor!(this: FunctionExecutor<'e, E>, imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { - this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize).unwrap(); + let _ = this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize); println!("memcpy {} from {}, {} bytes", dest, src, count); dest }, From 87c54f7521788172f4ad4c37479fd2b745bacfd5 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 5 Jan 2018 01:16:23 +0100 Subject: [PATCH 32/49] Expose set/get code in externalities Also improve tests and add nice wrappers in rust-wasm. --- executor/src/wasm_executor.rs | 38 ++++++++++++--- runtime/src/lib.rs | 46 +++++++++++++++--- .../release/runtime.compact.wasm | Bin 417 -> 525 bytes .../release/runtime.wasm | Bin 504 -> 641 bytes 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 0a0392c715af6..353ca9b3e7dae 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -62,6 +62,19 @@ impl<'e, E: Externalities> FunctionExecutor<'e, E> { } } +trait WritePrimitive { + fn write_primitive(&self, offset: u32, t: T); +} + +impl WritePrimitive for MemoryInstance { + fn write_primitive(&self, offset: u32, t: u32) { + use byteorder::{LittleEndian, ByteOrder}; + let mut r = [0u8; 4]; + LittleEndian::write_u32(&mut r, t); + let _ = self.set(offset, &r); + } +} + impl_function_executor!(this: FunctionExecutor<'e, E>, imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { @@ -94,12 +107,21 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, } else { (0, 0) } } else { (0, 0) }; - if written > 0 { - use byteorder::{LittleEndian, ByteOrder}; - let mut r = [0u8; 4]; - LittleEndian::write_u32(&mut r, written); - let _ = this.memory.set(written_out, &r); + this.memory.write_primitive(written_out, written); + offset as u32 + }, + set_code(code_data: *const u8, code_len: i32) => { + if let Ok(code) = this.memory.get(code_data, code_len as usize) { + this.ext.set_code(code); } + }, + get_allocated_code(written_out: *mut i32) -> *mut u8 => { + let (offset, written) = if let Ok(code) = this.ext.code() { + let offset = this.heap.allocate(code.len() as u32) as u32; + let _ = this.memory.set(offset, &code); + (offset, code.len() as u32) + } else { (0, 0) }; + this.memory.write_primitive(written_out, written); offset as u32 } => <'e, E: Externalities + 'e> @@ -185,6 +207,7 @@ mod tests { #[test] fn should_pass_externalities_at_call() { let mut ext = TestExternalities::default(); + ext.code = b"The code".to_vec(); let program = ProgramInstance::new().unwrap(); @@ -208,7 +231,10 @@ mod tests { ).unwrap(); } - let expected: HashMap<_, _> = map![vec![116, 104, 101, 107, 101, 121] => vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]]; + let expected: HashMap<_, _> = map![ + vec![105, 110, 112, 117, 116] => vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100], + vec![99, 111, 100, 101] => vec![84, 104, 101, 32, 99, 111, 100, 101] + ]; assert_eq!(expected, ext.data); } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 83d475b57b433..00b78b9fbdee8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -6,6 +6,7 @@ extern crate alloc; use alloc::boxed::Box; +use alloc::vec::Vec; extern crate pwasm_libc; extern crate pwasm_alloc; @@ -20,6 +21,38 @@ extern "C" { fn imported(n: u64) -> u64; fn set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; + fn set_code(code_data: *const u8, code_len: i32); + fn get_allocated_code(written_out: *mut i32) -> *mut u8; +} + +mod state { + use alloc::vec::Vec; + use super::{get_allocated_storage, set_storage as super_set_storage, get_allocated_code, + set_code as super_set_code}; + + pub fn storage(key: &[u8]) -> Vec { + let mut length: i32 = 0; + unsafe { + let ptr = get_allocated_storage(&key[0], key.len() as i32, &mut length); + Vec::from_raw_parts(ptr, length as usize, length as usize) + } + } + + pub fn set_storage(key: &[u8], value: &[u8]) { + unsafe { super_set_storage(&key[0] as *const u8, key.len() as i32, &value[0] as *const u8, value.len() as i32); } + } + + pub fn code() -> Vec { + let mut length: i32 = 0; + unsafe { + let ptr = get_allocated_code(&mut length); + Vec::from_raw_parts(ptr, length as usize, length as usize) + } + } + + pub fn set_code(new: &[u8]) { + unsafe { super_set_code(&new[0] as *const u8, new.len() as i32); } + } } fn do_something(param: u64) -> u64 { @@ -35,15 +68,14 @@ pub fn test(value: u64) -> u64 { /// Test passing of data. #[no_mangle] -pub fn test_data_in(freeable_data: *mut u8, size: usize) { - // Interpret data - let slice = unsafe { core::slice::from_raw_parts(freeable_data, size) }; - let copy = slice.to_vec(); +pub fn test_data_in(input_data: *mut u8, input_len: usize) { + let input = unsafe { Vec::from_raw_parts(input_data, input_len, input_len) }; - unsafe { pwasm_libc::free(freeable_data); } + state::set_storage(b"input", &input); + state::set_storage(b"code", &state::code()); - let key = b"thekey"; - unsafe { set_storage(key as *const u8, key.len() as i32, ©[0] as *const u8, copy.len() as i32); } + state::set_code(&input); + let copy = state::storage(b"input"); // Do some stuff. for b in © { diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index b9509edf0f3d11aad055869fd4c35efc1f425321..c1e643308b2a73f09ec9f5879f273ea3f210c6ae 100644 GIT binary patch literal 525 zcmY*VL2lbH5S%3`SuquaDbh=U0yM3(|2M|a57o{?%%*j_?+4aH&U9ptc~V8HBmJjO zWSXQ>5tO1mkVwM$;x0{F-}b{Ff%D-ohVr}L)m?e$p6YIkg$jNQ$npr~whC2QcQ{Gk zV=vYG9DPDqzEP4vu5)PUYof`*s1?N8Wrhm6KZPZWTghNx@s(cAG@xIJadblIoycMT{y+ptTCbxR$YnAq*g>L7A8kgK2P#|W8{j&I?D1w@NT*0 z?Xvj-4)w_jD;+h3{B{Z*<7m#x5!l2uykDp6YUl{i`m=;nT163Du9Ldk9|GKkR`H|% E0uH@sZ~y=R delta 291 zcmXAhu};G<6h-f|9XE{=5eAk{PQ+4)1tDhQq2dEr*wUyq(9$$jVj()^3y`Sz0v0}l zZ^IUEx$oTf?)?zo{IfV0qaQ4RCG*N2qSMXgZDQYD-P(3_ID&Dze~UNmq3fMpg9lWD zb@kx7zInC~d?F(*+3EM{E)vcT4l)f=uN&9Yn>{A7ZFakEg{gSztwk~=BtIl$@G6CZ zniGv0IsckdIX-$YR4`VtO4A~PrkqN)$Vh3X1I07{43iq^4a$W<;hVy7dktSR)1+Z& oTjD;059K&hfJzY39+{GU&FNpD)CGePN`zJCtx52*Sb46J-v zN9-U5Jow)0^Sbhpy$2tIF0E@!c}uEl z4{WP)lg%xt$Hbyomm9|iYsRhfXq4QK(vN@qfJGu?eE)g<)aI4lR*+BCcSG!m*MC65=?rSC%3nMcjZW*U71< zC|OVQ=DnHsefxj>C%^HIZm<9r%qbg!({p(ruA6<^S+fG?W;e***s8Z}SHBy0JPLg- zPQQ<@foDx`5hkNltJ>DpdWT3frdjR}hy%AY?O-6s$U%^ZrrvJbB~tILGX_@?A-+%; zg3l62$OodMLgt^ps3=QkH06xV*i6wpg`$iKHcv?^rbZV}+%t5dg$pPX8o48l$ITQD kGf||VX;a`Og)3#amcaRmP48|Qv?sl>?*LNR5A$gb|8<-;`2YX_ From 471ea1e9d0f501fa905eeaf8bca3f19073a203cc Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 5 Jan 2018 17:40:10 +0100 Subject: [PATCH 33/49] Add validator set. --- executor/src/error.rs | 6 ++++ executor/src/wasm_executor.rs | 56 ++++++++++++++++++++++++++++++----- runtime/src/lib.rs | 19 ++++++++++-- state_machine/src/lib.rs | 16 ++++++++-- 4 files changed, 85 insertions(+), 12 deletions(-) diff --git a/executor/src/error.rs b/executor/src/error.rs index 85d1b70d98160..4fd0ec0604e45 100644 --- a/executor/src/error.rs +++ b/executor/src/error.rs @@ -43,6 +43,12 @@ error_chain! { display("Externalities error: {}", e), } + /// Invalid index. + InvalidIndex { + description("index given was not in range"), + display("Invalid index provided"), + } + /// Invalid return type. InvalidReturn { description("u64 was not returned"), diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 353ca9b3e7dae..3ec1de26a2834 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -123,14 +123,33 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, } else { (0, 0) }; this.memory.write_primitive(written_out, written); offset as u32 + }, + get_validator_count() -> i32 => { + this.ext.validator_count() as i32 + }, + get_allocated_validator(index: i32, written_out: *mut i32) -> *mut u8 => { + let (offset, written) = if let Ok(v) = this.ext.validator(index as usize) { + let offset = this.heap.allocate(v.len() as u32) as u32; + let _ = this.memory.set(offset, &v); + (offset, v.len() as u32) + } else { (0, 0) }; + this.memory.write_primitive(written_out, written); + offset as u32 + }, + set_validator_count(validator_count: i32) => { + this.ext.set_validator_count(validator_count as usize); + }, + set_validator(index: i32, validator_data: *const u8, validator_len: i32) => { + if let Ok(validator) = this.memory.get(validator_data, validator_len as usize) { + this.ext.set_validator(index as usize, validator); + } } => <'e, E: Externalities + 'e> ); -/// Dummy rust executor for contracts. +/// Wasm rust executor for contracts. /// -/// Instead of actually executing the provided code it just -/// dispatches the calls to pre-defined hardcoded implementations in rust. +/// Executes the provided code in a sandboxed wasm runtime. #[derive(Debug, Default)] pub struct WasmExecutor; @@ -183,6 +202,7 @@ mod tests { struct TestExternalities { data: HashMap, Vec>, code: Vec, + validators: Vec>, } impl Externalities for TestExternalities { type Error = Error; @@ -195,12 +215,34 @@ mod tests { Ok(self.data.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) } - fn set_code(&mut self, _code: Vec) { - self.code = _code; + fn validator(&self, index: usize) -> Result<&[u8]> { + if index < self.validators.len() { + Ok(self.validators[index].as_slice()) + } else { + Err(ErrorKind::InvalidIndex.into()) + } + } + + fn validator_count(&self) -> usize { + self.validators.len() + } + + fn set_code(&mut self, code: Vec) { + self.code = code; + } + + fn set_storage(&mut self, key: Vec, value: Vec) { + self.data.insert(key, value); + } + + fn set_validator(&mut self, index: usize, value: Vec) { + if index < self.validators.len() { + self.validators[index] = value; + } } - fn set_storage(&mut self, _key: Vec, _value: Vec) { - self.data.insert(_key, _value); + fn set_validator_count(&mut self, count: usize) { + self.validators.resize(count, vec![]); } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 00b78b9fbdee8..0206a27e48ccf 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -23,6 +23,10 @@ extern "C" { fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; fn set_code(code_data: *const u8, code_len: i32); fn get_allocated_code(written_out: *mut i32) -> *mut u8; + fn get_validator_count() -> i32; + fn get_allocated_validator(index: i32, written_out: *mut i32) -> *mut u8; + fn set_validator_count(validator_count: i32); + fn set_validator(index: i32, validator_data: *const u8, validator_len: i32); } mod state { @@ -39,7 +43,12 @@ mod state { } pub fn set_storage(key: &[u8], value: &[u8]) { - unsafe { super_set_storage(&key[0] as *const u8, key.len() as i32, &value[0] as *const u8, value.len() as i32); } + unsafe { + super_set_storage( + &key[0] as *const u8, key.len() as i32, + &value[0] as *const u8, value.len() as i32 + ); + } } pub fn code() -> Vec { @@ -51,7 +60,9 @@ mod state { } pub fn set_code(new: &[u8]) { - unsafe { super_set_code(&new[0] as *const u8, new.len() as i32); } + unsafe { + super_set_code(&new[0] as *const u8, new.len() as i32); + } } } @@ -69,7 +80,9 @@ pub fn test(value: u64) -> u64 { /// Test passing of data. #[no_mangle] pub fn test_data_in(input_data: *mut u8, input_len: usize) { - let input = unsafe { Vec::from_raw_parts(input_data, input_len, input_len) }; + let input = unsafe { + Vec::from_raw_parts(input_data, input_len, input_len) + }; state::set_storage(b"input", &input); state::set_storage(b"code", &state::code()); diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 7ff6860e0aadf..357ff7d7053ed 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -155,11 +155,23 @@ pub trait Externalities { /// Read storage of current contract being called. fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; - /// Set the new runtime. + /// Read the current validator set. + fn validator(&self, _index: usize) -> Result<&[u8], Self::Error> { Ok(&[]) } + + /// How many validators are there? + fn validator_count(&self) -> usize { 0 } + + /// Set the new runtime (effective from the next block). fn set_code(&mut self, code: Vec); - /// Set storage of current contract being called. + /// Set storage of current contract being called (effective immediately). fn set_storage(&mut self, key: Vec, value: Vec); + + /// Set a new validator set (effective from the next block). + fn set_validator(&mut self, _index: usize, _value: Vec) {} + + /// Resize the validators array - extra validators will be empty vectors. + fn set_validator_count(&mut self, _count: usize) {} } /// Code execution engine. From fa35993c952b1fc1505dc3676e85ee1c58e9fce8 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 5 Jan 2018 18:13:38 +0100 Subject: [PATCH 34/49] Introduce validator set into externalities and test. --- executor/src/wasm_executor.rs | 3 ++ runtime/src/lib.rs | 44 +++++++++++++++++- .../release/runtime.compact.wasm | Bin 525 -> 1347 bytes .../release/runtime.wasm | Bin 641 -> 1434 bytes 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 3ec1de26a2834..daeaaaf6a4863 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -278,5 +278,8 @@ mod tests { vec![99, 111, 100, 101] => vec![84, 104, 101, 32, 99, 111, 100, 101] ]; assert_eq!(expected, ext.data); + + let expected = vec![ b"Hello world".to_vec() ]; + assert_eq!(expected, ext.validators); } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0206a27e48ccf..6fe97fc2bddc2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -29,10 +29,11 @@ extern "C" { fn set_validator(index: i32, validator_data: *const u8, validator_len: i32); } -mod state { +pub mod state { use alloc::vec::Vec; use super::{get_allocated_storage, set_storage as super_set_storage, get_allocated_code, - set_code as super_set_code}; + set_code as super_set_code, set_validator as super_set_validator, get_allocated_validator, + get_validator_count, set_validator_count as super_set_validator_count}; pub fn storage(key: &[u8]) -> Vec { let mut length: i32 = 0; @@ -64,6 +65,41 @@ mod state { super_set_code(&new[0] as *const u8, new.len() as i32); } } + + pub fn set_validator(index: usize, validator: &[u8]) { + unsafe { + super_set_validator(index as i32, &validator[0] as *const u8, validator.len() as i32); + } + } + + pub fn validator(index: usize) -> Vec { + let mut length: i32 = 0; + unsafe { + let ptr = get_allocated_validator(index as i32, &mut length); + Vec::from_raw_parts(ptr, length as usize, length as usize) + } + } + + pub fn set_validator_count(count: usize) { + unsafe { + super_set_validator_count(count as i32); + } + } + + pub fn validator_count() -> usize { + unsafe { + get_validator_count() as usize + } + } + + pub fn validators() -> Vec> { + (0..validator_count()).into_iter().map(validator).collect() + } + + pub fn set_validators(validators: &[&[u8]]) { + set_validator_count(validators.len()); + validators.iter().enumerate().for_each(|(v, i)| set_validator(v, i)); + } } fn do_something(param: u64) -> u64 { @@ -94,4 +130,8 @@ pub fn test_data_in(input_data: *mut u8, input_len: usize) { for b in © { unsafe { imported(*b as u64); } } + + let mut v = state::validators(); + v.push(copy); + state::set_validators(&v.iter().map(Vec::as_slice).collect::>()); } diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index c1e643308b2a73f09ec9f5879f273ea3f210c6ae..f2a69cd5a77d1293b48a5b09ee873e2fa6b0b479 100644 GIT binary patch literal 1347 zcmY*ZO>f*b5S^hYi4vunPExo?QDCG~dutDIuFIls&_jR2dSk1`Zui63mDA*sy#)C; z`H2PkhT6C=781$f%zN`5sinnX!$icd>T5m{9zI@Y$D6=D}IRq@`%PpN1C%?{Z^6^>4RDUYV&&>HT+AcS{82;|- zON>p-LBOGk+I1ZX1dvz67tu-`vffDzMWGU>Sja?R%F@z0*v^<)%IP!JpLI@5$JtEy z7eZezRmZAhaq+n7T}$4paiQo-O`l#c3!N8N@Tr7kO_+9yj?tI{+C(Z~4|!Js6mDo| zeKLooQja*g)#MiZSJD!+4B9s>!F}=oE4|cGXm%~-r2(M2YL!uPmylYVr|dsFP9HReTs%#rLbenodj7!4?5`Hc6@gc{1p9+hB%vpuj3(`X2Onl|$=J z$Km4mcn1{AYEGpI$cnH&2KZ~!b*U4fdLTT)Hyz19KRm!^lI}_ z?rOh;lz#iED__qR<G+-ZM33OkHGQ$;1vrX_doR zXm{omkGe1-6IP9cHn9~3Cu8()N>7`-Ul=(gF==gSCSLj+ZOO{w7My!-g_VwqOiud) TB%H$yhJWBIrr-yfL3Hy6mU}*o diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm index 92bf4fc2148a8afbe4c3360585b2c85033753535..21802f87e09b4c7976566381cd00465f2119e3da 100644 GIT binary patch literal 1434 zcmY*ZO>^5e5Zwhq5+K9`{z%8(A)xeA$aGClM+NUF_xq>7S9qTS>Y-SQ)H z>L2KT%v(@)#-kwtEOy`9_W+6(`<{u2-&Aux5FS3vv%^6KmEMFIf9B7!q`Afx2Fai~ zk@`=)$hysg={^p#zjs|nB+E*c{bIe|UK4*0bp7?+CsGn{DWmKA?L#+4#+t6*cf%Ms zz3qls!dML5&Fp%6(~V%So^R}j?QU^9 z6rJqu_rvV3?smDE-EaO~Zf@wM9=iQNej>vR5*D-NhEAkJJUlHiN#31F2*h+=91VY^ z(?R@TnX0d){lc6dqHc0Eef$NAdi7dS%U3l8!mP;y$H}_QF#Nb~)rXox-sp*P@i)({ zt`(6BN~R$XSy1x)`LjQ5RLDIAbyX{eS{|C*1q%970T?VH$Lys;wV`Y}AxG|4=0hNF z8&cC~0GYbrKyNb&3^01fUsB2cIom^|hz@o1|FqR5#!1XUz}`jax{3q>$SdM`)S>cu zYeEG@-UTCA$gCc-q_pxyqtrCzw3+D78WU8**);I)guZ?f9jk_eiHB8dYI07D^QJ8& zeSEtOV`~BJ__DD8g&WdYo6O;0#UqYpHM#}=g|q}MgZ4vB zaGykF=|U9>$*w|qsQ@UhYGDjH@<8wcY(|c>DHBIiphloefJtS68P38s`L=Mbgd!q)`gZ zA`werGow_42u!zPvlugxu1Y9zE?8d!)qylpT;IuKwHF@6tnyuu-M$Kx<@c)A0Er6tEo0%IXN`tw8p delta 364 zcmX9&!AiqG5Ph?|NjFWZE9k}3HXziKsL*p45f2{x01s_wS7~gT(u9Ht20y@q!_1p`yf<&YnS00T4gb|GN3S(#v8~ zZfn1QlzzR_m9MAsa^a!6HSX4`F1OjUM<62|O4Tmu;<2x4U)6|{4yKE&&Zf&XlCbc_ zeDjJykB6e%c^q^UU%_EmWUEy=hq2J^*eM=$Zh}-;H4@rHRv7Gs`d^gno$Sv|I3zLYWJxM+ l2OMq5%KQ?XduoN1j`CFA4Neeq4mTM7fv=dHA87A`hd&!zM=<~Z From a0f64df3716b635d535bb0930f5e38221344b210 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 6 Jan 2018 16:49:26 +0100 Subject: [PATCH 35/49] Add another external function. --- executor/src/wasm_executor.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index daeaaaf6a4863..e24aa42965751 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -82,8 +82,16 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, println!("memcpy {} from {}, {} bytes", dest, src, count); dest }, - ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { println!("memmove {} from {}, {} bytes", dest, src, count); dest }, - ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { println!("memset {} with {}, {} bytes", dest, val, count); dest }, + ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { + let _ = this.memory.copy(src as usize, dest as usize, count as usize); + println!("memmove {} from {}, {} bytes", dest, src, count); + dest + }, + ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { +// let _ = this.memory.set(dest as usize, val as u8, count as usize); + println!("memset {} with {}, {} bytes", dest, val, count); + dest + }, ext_malloc(size: usize) -> *mut u8 => { let r = this.heap.allocate(size); println!("malloc {} bytes at {}", size, r); @@ -274,8 +282,8 @@ mod tests { } let expected: HashMap<_, _> = map![ - vec![105, 110, 112, 117, 116] => vec![72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100], - vec![99, 111, 100, 101] => vec![84, 104, 101, 32, 99, 111, 100, 101] + b"input".to_vec() => b"Hello world".to_vec(), + b"code".to_vec() => b"The code".to_vec() ]; assert_eq!(expected, ext.data); From e736d46a54200c89356f3117dea78a6c024e47fb Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 6 Jan 2018 18:37:55 +0100 Subject: [PATCH 36/49] Remove code and validators; use storage for everything. --- executor/src/wasm_executor.rs | 108 ++++-------------- runtime/src/lib.rs | 78 ++++++------- .../release/runtime.compact.wasm | Bin 1347 -> 3260 bytes .../release/runtime.wasm | Bin 1434 -> 3348 bytes state_machine/src/backend.rs | 6 - state_machine/src/ext.rs | 10 +- state_machine/src/lib.rs | 80 ++----------- 7 files changed, 63 insertions(+), 219 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index e24aa42965751..3a424a719ebbc 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -76,7 +76,13 @@ impl WritePrimitive for MemoryInstance { } impl_function_executor!(this: FunctionExecutor<'e, E>, - imported(n: u64) -> u64 => { println!("imported {:?}", n); n + 1 }, + ext_print(utf8_data: *const u8, utf8_len: i32) => { + if let Ok(utf8) = this.memory.get(utf8_data, utf8_len as usize) { + if let Ok(message) = String::from_utf8(utf8) { + println!("Runtime: {}", message); + } + } + }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { let _ = this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize); println!("memcpy {} from {}, {} bytes", dest, src, count); @@ -88,7 +94,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, dest }, ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { -// let _ = this.memory.set(dest as usize, val as u8, count as usize); + let _ = this.memory.clear(dest as usize, val as u8, count as usize); println!("memset {} with {}, {} bytes", dest, val, count); dest }, @@ -117,40 +123,6 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.memory.write_primitive(written_out, written); offset as u32 - }, - set_code(code_data: *const u8, code_len: i32) => { - if let Ok(code) = this.memory.get(code_data, code_len as usize) { - this.ext.set_code(code); - } - }, - get_allocated_code(written_out: *mut i32) -> *mut u8 => { - let (offset, written) = if let Ok(code) = this.ext.code() { - let offset = this.heap.allocate(code.len() as u32) as u32; - let _ = this.memory.set(offset, &code); - (offset, code.len() as u32) - } else { (0, 0) }; - this.memory.write_primitive(written_out, written); - offset as u32 - }, - get_validator_count() -> i32 => { - this.ext.validator_count() as i32 - }, - get_allocated_validator(index: i32, written_out: *mut i32) -> *mut u8 => { - let (offset, written) = if let Ok(v) = this.ext.validator(index as usize) { - let offset = this.heap.allocate(v.len() as u32) as u32; - let _ = this.memory.set(offset, &v); - (offset, v.len() as u32) - } else { (0, 0) }; - this.memory.write_primitive(written_out, written); - offset as u32 - }, - set_validator_count(validator_count: i32) => { - this.ext.set_validator_count(validator_count as usize); - }, - set_validator(index: i32, validator_data: *const u8, validator_len: i32) => { - if let Ok(validator) = this.memory.get(validator_data, validator_len as usize) { - this.ext.set_validator(index as usize, validator); - } } => <'e, E: Externalities + 'e> ); @@ -167,21 +139,15 @@ impl CodeExecutor for WasmExecutor { fn call( &self, ext: &mut E, + code: &[u8], method: &str, data: &CallData, ) -> Result { - // TODO: avoid copying code by requiring code to remain immutable through execution, - // splitting it off from potentially mutable externalities. - let code = match ext.code() { - Ok(e) => e.to_owned(), - Err(e) => Err(ErrorKind::Externalities(Box::new(e)))?, - }; - // TODO: handle all expects as errors to be returned. let program = ProgramInstance::new().expect("this really shouldn't be able to fail; qed"); - let module = deserialize_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed"); + let module = deserialize_buffer(code.to_vec()).expect("all modules compiled with rustc are valid wasm code; qed"); let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("runtime signatures always provided; qed"); let memory = module.memory(ItemIndex::Internal(0)).expect("all modules compiled with rustc include memory segments; qed"); @@ -208,56 +174,24 @@ mod tests { #[derive(Debug, Default)] struct TestExternalities { - data: HashMap, Vec>, - code: Vec, - validators: Vec>, + storage: HashMap, Vec>, } impl Externalities for TestExternalities { type Error = Error; - fn code(&self) -> Result<&[u8]> { - Ok(self.code.as_slice()) - } - fn storage(&self, key: &[u8]) -> Result<&[u8]> { - Ok(self.data.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) - } - - fn validator(&self, index: usize) -> Result<&[u8]> { - if index < self.validators.len() { - Ok(self.validators[index].as_slice()) - } else { - Err(ErrorKind::InvalidIndex.into()) - } - } - - fn validator_count(&self) -> usize { - self.validators.len() - } - - fn set_code(&mut self, code: Vec) { - self.code = code; + Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) } fn set_storage(&mut self, key: Vec, value: Vec) { - self.data.insert(key, value); - } - - fn set_validator(&mut self, index: usize, value: Vec) { - if index < self.validators.len() { - self.validators[index] = value; - } - } - - fn set_validator_count(&mut self, count: usize) { - self.validators.resize(count, vec![]); + self.storage.insert(key, value); } } #[test] fn should_pass_externalities_at_call() { let mut ext = TestExternalities::default(); - ext.code = b"The code".to_vec(); + ext.set_storage(b"\0code".to_vec(), b"The code".to_vec()); let program = ProgramInstance::new().unwrap(); @@ -274,20 +208,20 @@ mod tests { let offset = fec.heap.allocate(size); memory.set(offset, data).unwrap(); - module.execute_export("test_data_in", + assert!(module.execute_export("test_data_in", program.params_with_external("env", &mut fec) .add_argument(I32(offset as i32)) .add_argument(I32(size as i32)) - ).unwrap(); + ).is_ok()); } let expected: HashMap<_, _> = map![ + b"\0code".to_vec() => b"Hello world".to_vec(), b"input".to_vec() => b"Hello world".to_vec(), - b"code".to_vec() => b"The code".to_vec() + b"code".to_vec() => b"The code".to_vec(), + b"\0validator_count".to_vec() => vec![1], + b"\0validator".to_vec() => b"Hello world".to_vec() ]; - assert_eq!(expected, ext.data); - - let expected = vec![ b"Hello world".to_vec() ]; - assert_eq!(expected, ext.validators); + assert_eq!(expected, ext.storage); } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 6fe97fc2bddc2..e51431bc98342 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -5,7 +5,6 @@ #![feature(alloc)] extern crate alloc; -use alloc::boxed::Box; use alloc::vec::Vec; extern crate pwasm_libc; @@ -18,22 +17,14 @@ pub fn panic_fmt() -> ! { } extern "C" { - fn imported(n: u64) -> u64; + fn ext_print(utf8_data: *const u8, utf8_len: i32); fn set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; - fn set_code(code_data: *const u8, code_len: i32); - fn get_allocated_code(written_out: *mut i32) -> *mut u8; - fn get_validator_count() -> i32; - fn get_allocated_validator(index: i32, written_out: *mut i32) -> *mut u8; - fn set_validator_count(validator_count: i32); - fn set_validator(index: i32, validator_data: *const u8, validator_len: i32); } pub mod state { use alloc::vec::Vec; - use super::{get_allocated_storage, set_storage as super_set_storage, get_allocated_code, - set_code as super_set_code, set_validator as super_set_validator, get_allocated_validator, - get_validator_count, set_validator_count as super_set_validator_count}; + use super::{get_allocated_storage, set_storage as super_set_storage}; pub fn storage(key: &[u8]) -> Vec { let mut length: i32 = 0; @@ -53,43 +44,37 @@ pub mod state { } pub fn code() -> Vec { - let mut length: i32 = 0; - unsafe { - let ptr = get_allocated_code(&mut length); - Vec::from_raw_parts(ptr, length as usize, length as usize) - } + storage(b"\0code") } pub fn set_code(new: &[u8]) { - unsafe { - super_set_code(&new[0] as *const u8, new.len() as i32); + set_storage(b"\0code", new) + } + + pub fn value_vec(mut value: usize, initial: Vec) -> Vec { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; } + acc } pub fn set_validator(index: usize, validator: &[u8]) { - unsafe { - super_set_validator(index as i32, &validator[0] as *const u8, validator.len() as i32); - } + set_storage(&value_vec(index, b"\0validator".to_vec()), validator); } pub fn validator(index: usize) -> Vec { - let mut length: i32 = 0; - unsafe { - let ptr = get_allocated_validator(index as i32, &mut length); - Vec::from_raw_parts(ptr, length as usize, length as usize) - } + storage(&value_vec(index, b"\0validator".to_vec())) } pub fn set_validator_count(count: usize) { - unsafe { - super_set_validator_count(count as i32); - } + (count..validator_count()).for_each(|i| set_validator(i, &[])); + set_storage(b"\0validator_count", &value_vec(count, Vec::new())); } pub fn validator_count() -> usize { - unsafe { - get_validator_count() as usize - } + storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| acc << 8 + (i as usize)) } pub fn validators() -> Vec> { @@ -102,15 +87,10 @@ pub mod state { } } -fn do_something(param: u64) -> u64 { - param * 2 -} - -/// Test some execution. -#[no_mangle] -pub fn test(value: u64) -> u64 { - let b = Box::new(unsafe { imported(value) }); - do_something(*b) +pub fn print(utf8: &[u8]) { + unsafe { + ext_print(&utf8[0] as *const u8, utf8.len() as i32); + } } /// Test passing of data. @@ -120,18 +100,24 @@ pub fn test_data_in(input_data: *mut u8, input_len: usize) { Vec::from_raw_parts(input_data, input_len, input_len) }; + print(b"set_storage"); state::set_storage(b"input", &input); + + print(b"code"); state::set_storage(b"code", &state::code()); + print(b"set_code"); state::set_code(&input); - let copy = state::storage(b"input"); - // Do some stuff. - for b in © { - unsafe { imported(*b as u64); } - } + print(b"storage"); + let copy = state::storage(b"input"); + print(b"validators"); let mut v = state::validators(); v.push(copy); + + print(b"set_validators"); state::set_validators(&v.iter().map(Vec::as_slice).collect::>()); + + print(b"finished!"); } diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index f2a69cd5a77d1293b48a5b09ee873e2fa6b0b479..651176dfaadf7150841971f16ff5100ccd6dbb7f 100644 GIT binary patch literal 3260 zcmbtW%W@nw6qVHKp6-^^ZpU`~w$;s}5+KA?xa<^!Dqjl6VUfxwIL4lY>G*{`He>%r@yn^I}~|x8JyK2XqQQm|8g_^S;}}o zx{B4t@Xrg<%`GKrx#@%#m{qZtWcTX`!_0oIww&~FrL8iwQp#F8C4}*cr#|+PSNwZ4 zUz$q!xD;M(I%Tj|_N&-<;q{t=VR-Q|W@84se~@}G3_xvM6N2(GSg197S5?H`HC#q8 zjs&!o#)ogXmtONPgaBoPfLsuQ@X^RM2N7Ju`$Z&^0SQ8~n#$*OvZnMf8ZQ|O*u>gn z0HR1Xb?nt+2Zz`KGwd3OtO?O>qMV@sdL$}@L1{zcu3Y#`AaxSA)#N&63FYwOlNOEe zl_GOs#cOF(MU+>Pa>}<{NH(uxqg%xQi<-;ank7+ zAJ^z~Wc0x0!G<%a87hR-7$Lydc=aj5RFt0A%5`jFd{TCN#QhNV3SbUPu!31IwpmG= zLJ~lMjTk%7TCEBYbScG%H{(JHN-%{sTrdtI^a21);TR!sN-$3hM%h4P%uobTY)E3z zZR*ho2u21#uu)-zNky~~Ccl=-4djo*;$Dd7wSzTUAyYA(x&9DLvX(B*VM(A*o&yW5dpk5;e(P=5wJ8SAo%rQ)2z)I=!&?Bt@ePv^LW#c3tOWug#-FBn* zng3^467Ji8G7bpA@gOf=Y7K5{0N@ow0v^DVLBn}KraoO$_b>p$ zRb-NSvjvnHq%y&9j|r382DviFu_Tyh&oO3TPu<|mfq$5qxWh5vE2TdL73c`fQRJ-! ziFS-o_6$D-cbcXfn*ItNbFv4uqNv|8pEOtnG5Wlz(+7cYG9OhM$uVugo@Sb5L@ukR zJ!pq{2n?fr#t38q0R)#KC&LFzBZ1KWSNrp_Pc}kN!hr?~fA2@+#{CH5@;UcoihhKb zM~I9$G2aAt=WspZL$J>!A3EkkE-wadKMW6j2HM8FZL=!9!iXTi0%Xtz`p~;CBv%yr zT*#5WtT-JUjtay}0!#{qD40U=hn0dkLs!Lrkmq%T`hR4;D7=y)N@Cqfv)ou$juqn* zlT$aRXJ+T-7Z#UpEH86zNCcdlaw(A)Ge~Z6=rxLkuP-0CDkQNAq=7uxJ6x?fvvjyr!A3|-i}u!ul#QH54P zgm?qw;744zOO!(6ZMd7f#UTrK>=Kob-nt&4+%8;2eUS?2 zzCS|fdej$b99r)U5eoGKRchbCy$FQdl3nC1@>W1Mp#2U6xLGDJt&O*QJS~_%-f|N( z2_e$P3t^t7Fo!&X?oCXW4ZRkW@x{Un#OF6q5<@*l)6iJrtHSQ%uxk`+1`_xN&oD2r z&MYLzky!&a=kPTjb}%z$)W=2OrfDAHxscE5rkkM!e9pGzUBq+-mpS$_;uoR`S2n*? zVk}MZtzFp3_RLN2tzFm~;po|MzO@Tm8sZ+oHP3k#CbWVxw2UunXbA(jiE+={Av2+3 zsi83l@l;^Y5))v=D}iCbD}h7CD}fWnbBXi9iRWmU0dnD)hE=Idf@vw+0VMdFw0H~{ z{Sz`0WKAq~kM_EPCb8#yXTOJc_0fqSZ^}2H+q*|+c-_;AdF%a7_f6b8JU+w9OJQ_t t=M|nP{K#*=E`;d}pZje$(Bn{sORJvb_KR literal 1347 zcmY*ZO>f*b5S^hYi4vunPExo?QDCG~dutDIuFIls&_jR2dSk1`Zui63mDA*sy#)C; z`H2PkhT6C=781$f%zN`5sinnX!$icd>T5m{9zI@Y$D6=D}IRq@`%PpN1C%?{Z^6^>4RDUYV&&>HT+AcS{82;|- zON>p-LBOGk+I1ZX1dvz67tu-`vffDzMWGU>Sja?R%F@z0*v^<)%IP!JpLI@5$JtEy z7eZezRmZAhaq+n7T}$4paiQo-O`l#c3!N8N@Tr7kO_+9yj?tI{+C(Z~4|!Js6mDo| zeKLooQja*g)#MiZSJD!+4B9s>!F}=oE4|cGXm%~-r2(M2YL!uPmylYVr|dsFP9HReTs%#rLbenodj7!4?5`Hc6@gc{1p9+hB%vpuj3(`X2Onl|$=J z$Km4mcn1{AYEGpI$cnH&2KZ~!b*qwUz5T6|{cn5wyTaH(7qZ>$!S+^f zUsztt4!aII!O?<6?BG~(9$l$J)Z>{H`ES?rpQMb3q^nqMT>N=Ky1Ai5EjOL;0=+8s zlI&g`p_$pM)dMGeTxqKet(3CXP6=VW;;xT<s?9)>Rd;bq$jdlp_FbrSV}5^U`bnf(uv-7Z3|v z5I!22W+#GaSU-zoG9*C=R#W-BPS%vZ@WxBV0y;7G7=S2}O&xpn*ufySKnQ+x6sbg>>Nx3ijE`&73L?7i@}R>RoEc6Cp)o>$ zukq?rxTz>Tt(EK8#Q3Dle8l+>_6lGQP0)f~P_|h~n?e#mfsPnE5+qMp0t8)3G5pQ= zpado8Vl_-K4kF|N08L>SE^tUtPYg!cV8xiB2%^}KM5Ei#qY)5{41i#z!U&U!Xd`rf zDU}<_A1)5}Ogyh0w9yKgGN!u}!?G+pTmtkGISHYtQtvp1jUXY5MUH+KO_|i0cQ@2V z+3^`7cm;2Tp$6d)4%Y)U zoC*z_TY^!d(L5k018j#KI0;>VH(H9vc$JT*1f`0 z(8>MPy0fP4p^s(~4Q5K8ha71Y=t~>ROB*M-EO{fEciWBLXa1jVNtkaP%Ge<|$BksS zSL(6#5W?6xVoZ_mIBRfQ0{|}}67T??3>xYIk@|E^-9ZBgR}o3-%>$s!AeD^e9VSdp z8^p>S$C99)Eyw7AJ#~Yc1OG5JafYM8D5XCI703w9UgWI?iFAx`>=`}HR=BDA7tIIHoP=(@e9B$oc7M587cK0>fyZF#=gY0M4a| z$#7w51Q1gAa(*&9A-;`}lQ5ux!r$`|v2i|vxO~p}m?9rx)K z&WDcqkdGG)4Dslp&p_Mgw`o?TmlzQQSbz*#M<06kx#SbYdOpa}dRcKeD2@umO9D&^ zhA5aq@rRj$IYU>)e~{-@g!+GEz9_tsB1&T2NweHoSB@3q6O&Wdr)Os8<`)*1t}QQf zYzPFLoAOa2E@qJ2qUbda3!{7YT@`{@1=2ts>>aMwoY^~L_Rg5SGq(ilEbJ$U`AVI} z!dO1ixy*>zm66VnFkqU-Yu1U>ImMkn3x+Q2ZD@oexTr#^aC#uaz>m0amng-Gk6X6S z(7k2gP&D8KTEtNn_M>p?G!-5V;h-!TNIx{*;A*N8Zg9a}q|PE)%zl7bPF{HYZ;5oy zN=O$@^|uqtvTX-FHw|Z#w5Z!q%I(5s)EBA1+V@8YU5)x8jbqh&7YMb* zgNpPW+>1cSE!jnmB5wtB4XfXQ05{77y0!6^%hQ7S;|FenCLu)Fcp=Qw6#9@ySbH7a zWkat8Wqh$P19ANZN@A$zXc{Y)_^PmbD0Yp*nt=qi!86PYyk-^>#K^1xopTt?haJp} z84Yj|xM`Y)crL`Vy6I+U0mIq$co)%~!DWuUjQE*o!j;W0l^9b~d}|kWvaPuZzO@T` zEtH-e=Ucn5r6JA{T=N`fVL~c6L(3RhLrZADO^kcqHkk=0mKqv^5KjdLEinN`yb>4| zyb{=Dyb?HIJeN2wR6Kjj43G=YG^|SXrps^fbE!57x}|IzP~)%C;)!7NkBC!{HL=(^ z*zF3M#Gcdby&hiT2Sf|p*Fy%r{W+S~6Pf8E`!{sJJP$W{OV literal 1434 zcmY*ZO>^5e5Zwhq5+K9`{z%8(A)xeA$aGClM+NUF_xq>7S9qTS>Y-SQ)H z>L2KT%v(@)#-kwtEOy`9_W+6(`<{u2-&Aux5FS3vv%^6KmEMFIf9B7!q`Afx2Fai~ zk@`=)$hysg={^p#zjs|nB+E*c{bIe|UK4*0bp7?+CsGn{DWmKA?L#+4#+t6*cf%Ms zz3qls!dML5&Fp%6(~V%So^R}j?QU^9 z6rJqu_rvV3?smDE-EaO~Zf@wM9=iQNej>vR5*D-NhEAkJJUlHiN#31F2*h+=91VY^ z(?R@TnX0d){lc6dqHc0Eef$NAdi7dS%U3l8!mP;y$H}_QF#Nb~)rXox-sp*P@i)({ zt`(6BN~R$XSy1x)`LjQ5RLDIAbyX{eS{|C*1q%970T?VH$Lys;wV`Y}AxG|4=0hNF z8&cC~0GYbrKyNb&3^01fUsB2cIom^|hz@o1|FqR5#!1XUz}`jax{3q>$SdM`)S>cu zYeEG@-UTCA$gCc-q_pxyqtrCzw3+D78WU8**);I)guZ?f9jk_eiHB8dYI07D^QJ8& zeSEtOV`~BJ__DD8g&WdYo6O;0#UqYpHM#}=g|q}MgZ4vB zaGykF=|U9>$*w|qsQ@UhYGDjH@<8wcY(|c>DHBIiphloefJtS68P38s`L=Mbgd!q)`gZ zA`werGow_42u!zPvlugxu1Y9zE?8d!)qylpT;IuKwHF@6tnyuu-M$Kx<@c)A0Er6tEo0%IXN`tw8p diff --git a/state_machine/src/backend.rs b/state_machine/src/backend.rs index d48ecdc793ff9..ee6b0bbe8027b 100644 --- a/state_machine/src/backend.rs +++ b/state_machine/src/backend.rs @@ -17,7 +17,6 @@ //! State machine backends. These manage the code and storage of contracts. use std::{error, fmt}; -use primitives::hash; use primitives::hash::H256; use triehash::sec_trie_root; @@ -25,8 +24,6 @@ use super::{Update, MemoryState}; /// Output of a commit. pub struct Committed { - /// Root of the code tree after changes committed. - pub code_hash: H256, /// Root of the storage tree after changes committed. pub storage_tree_root: H256, } @@ -85,11 +82,8 @@ impl Backend for InMemory { .map(|(k, v)| (k.to_vec(), v.clone())) .collect() ).0); - let code_hash = hash(&self.inner.code().unwrap_or_else(|| &[])); - // TODO: include validators list. Committed { - code_hash, storage_tree_root, } } diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index d6ec99748af59..ce7b9a02680dd 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -25,8 +25,10 @@ use {Externalities, OverlayedChanges}; #[derive(Debug, Copy, Clone)] pub enum Error { /// Failure to load state data from the backend. + #[allow(unused)] Backend(B), /// Failure to execute a function. + #[allow(unused)] Executor(E), } @@ -61,10 +63,6 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> { type Error = B::Error; - fn code(&self) -> Result<&[u8], Self::Error> { - Ok(self.overlay.code()) - } - fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { match self.overlay.storage(key) { Some(x) => Ok(x), @@ -72,10 +70,6 @@ impl<'a, B: 'a> Externalities for Ext<'a, B> } } - fn set_code(&mut self, code: Vec) { - self.overlay.set_code(code); - } - fn set_storage(&mut self, key: Vec, value: Vec) { self.overlay.set_storage(key, value); } diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 357ff7d7053ed..a0d2e8a925046 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -41,31 +41,19 @@ mod ext; pub enum Update { /// Set storage of object at given key -- empty is deletion. Storage(Vec, Vec), - /// Set code -- empty is deletion. - Code(Vec), } // in-memory section of the state. #[derive(Default)] struct MemoryState { - code: Option>, // None is unchanged. storage: HashMap, Vec>, } impl MemoryState { - fn code(&self) -> Option<&[u8]> { - self.code.as_ref().map(|x| &**x) - } - fn storage(&self, key: &[u8]) -> Option<&[u8]> { self.storage.get(key).map(|v| &v[..]) } - #[allow(unused)] - fn set_code(&mut self, code: Vec) { - self.code = Some(code); - } - fn set_storage(&mut self, key: Vec, val: Vec) { self.storage.insert(key, val); } @@ -80,9 +68,6 @@ impl MemoryState { self.storage.insert(key, val); } } - Update::Code(code) => { - self.code = Some(code); - } } } } @@ -99,42 +84,27 @@ pub struct OverlayedChanges { } impl OverlayedChanges { - fn code(&self) -> &[u8] { - self.prospective.code() - .or_else(|| self.committed.code()) - .unwrap_or_else(|| &[]) - } - 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) }) } - #[allow(unused)] - fn set_code(&mut self, code: Vec) { - self.prospective.set_code(code); - } - fn set_storage(&mut self, key: Vec, val: Vec) { self.prospective.set_storage(key, val); } /// Discard prospective changes to state. pub fn discard_prospective(&mut self) { - self.prospective.code = None; self.prospective.storage.clear(); } /// Commit prospective changes to state. pub fn commit_prospective(&mut self) { - let code_updates = self.prospective.code.take().into_iter() - .map(|code| Update::Code(code)); - let storage_updates = self.prospective.storage.drain() .map(|(key, value)| Update::Storage(key, value)); - self.committed.update(code_updates.chain(storage_updates)); + self.committed.update(storage_updates); } } @@ -149,29 +119,11 @@ pub trait Externalities { /// Externalities error type. type Error: Error; - /// Get the current runtime. - fn code(&self) -> Result<&[u8], Self::Error>; - /// Read storage of current contract being called. fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; - /// Read the current validator set. - fn validator(&self, _index: usize) -> Result<&[u8], Self::Error> { Ok(&[]) } - - /// How many validators are there? - fn validator_count(&self) -> usize { 0 } - - /// Set the new runtime (effective from the next block). - fn set_code(&mut self, code: Vec); - /// Set storage of current contract being called (effective immediately). fn set_storage(&mut self, key: Vec, value: Vec); - - /// Set a new validator set (effective from the next block). - fn set_validator(&mut self, _index: usize, _value: Vec) {} - - /// Resize the validators array - extra validators will be empty vectors. - fn set_validator_count(&mut self, _count: usize) {} } /// Code execution engine. @@ -183,6 +135,7 @@ pub trait CodeExecutor: Sized { fn call( &self, ext: &mut E, + code: &[u8], method: &str, data: &CallData, ) -> Result; @@ -191,6 +144,9 @@ pub trait CodeExecutor: Sized { /// 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( backend: &B, overlay: &mut OverlayedChanges, @@ -204,9 +160,12 @@ pub fn execute( 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, ) @@ -252,27 +211,4 @@ mod tests { overlayed.commit_prospective(); assert!(overlayed.storage(&key).is_none()); } - - #[test] - fn overlayed_code_works() { - let mut overlayed = OverlayedChanges::default(); - - assert!(overlayed.code().len() == 0); - - overlayed.set_code(vec![1, 2, 3]); - assert_eq!(overlayed.code(), &[1, 2, 3]); - - overlayed.commit_prospective(); - assert_eq!(overlayed.code(), &[1, 2, 3]); - - overlayed.set_code(vec![]); - assert!(overlayed.code().len() == 0); - - overlayed.discard_prospective(); - assert_eq!(overlayed.code(), &[1, 2, 3]); - - overlayed.set_code(vec![]); - overlayed.commit_prospective(); - assert!(overlayed.code().len() == 0); - } } From 234297c87b5a222f5304eee0ea478ecba6e38625 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 6 Jan 2018 19:31:27 +0100 Subject: [PATCH 37/49] Introduce validators function. --- runtime/src/lib.rs | 2 +- state_machine/src/lib.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e51431bc98342..55daf9c0db73b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -51,7 +51,7 @@ pub mod state { set_storage(b"\0code", new) } - pub fn value_vec(mut value: usize, initial: Vec) -> Vec { + fn value_vec(mut value: usize, initial: Vec) -> Vec { let mut acc = initial; while value > 0 { acc.push(value as u8); diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index a0d2e8a925046..7311a8bbc2855 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -114,6 +114,15 @@ impl OverlayedChanges { pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} impl Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} +fn value_vec(mut value: usize, initial: Vec) -> Vec { + 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. @@ -124,6 +133,15 @@ pub trait Externalities { /// Set storage of current contract being called (effective immediately). fn set_storage(&mut self, key: Vec, value: Vec); + + /// Get the current set of validators. + fn validators(&self) -> Result, 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. From 3f8a96d1eaa3fa3b1175807ab0ddd65046a2615b Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 6 Jan 2018 20:37:03 +0100 Subject: [PATCH 38/49] Tests (and a fix) for the validators getter. --- runtime/src/lib.rs | 2 +- .../release/runtime.compact.wasm | Bin 3260 -> 3246 bytes .../release/runtime.wasm | Bin 3348 -> 3334 bytes state_machine/src/lib.rs | 41 +++++++++++++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 55daf9c0db73b..d089c7e90c35b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -74,7 +74,7 @@ pub mod state { } pub fn validator_count() -> usize { - storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| acc << 8 + (i as usize)) + storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) } pub fn validators() -> Vec> { diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index 651176dfaadf7150841971f16ff5100ccd6dbb7f..ef0257292d1979e821309c4f56fc3fbc25cf1792 100644 GIT binary patch delta 138 zcmdlZxlVGzSH`Z5-ySnD_D;UVywZ-7$pINE@MSupsH-+8n{2!U_P# C6dXkW delta 152 zcmZ1{xkqxsSH>9|zddGRoIUv#^Ga71CI@7wz^lRJpv36P%izeM$j$7az^%Zdz?kJ& zpQX#d;K-5XC|_8j$gRK#6j0(+WK`hfWdN$!e2^uY$&`c10ihkJl|_LALo3iQkXA0B ORxXfMj?G@|DXai$As&AK diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm index df60e8b0cf8bec6aaebddb0eecee5f1078605fac..d48cb7c9b0921c48921fbcc8d47065ab97ec45c9 100644 GIT binary patch delta 114 zcmbOt)h4yUoRM+CW(&s0OpLvgWmr}+a!x+RA}`0Oz?kJ&pQX#dpup|OQBtJHt-uK6 xD>3pia4T?b=3Ia!_J)M{fB*mh diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 7311a8bbc2855..a604deab3fa55 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -138,7 +138,7 @@ pub trait Externalities { fn validators(&self) -> Result, Self::Error> { (0..self.storage(b"\0validator_count")?.into_iter() .rev() - .fold(0, |acc, &i| acc << 8 + (i as usize))) + .fold(0, |acc, &i| (acc << 8) + (i as usize))) .map(|i| self.storage(&value_vec(i, b"\0validator".to_vec()))) .collect() } @@ -203,7 +203,8 @@ pub fn execute( #[cfg(test)] mod tests { - use super::OverlayedChanges; + use std::collections::HashMap; + use super::{OverlayedChanges, Externalities}; #[test] fn overlayed_storage_works() { @@ -229,4 +230,40 @@ mod tests { overlayed.commit_prospective(); assert!(overlayed.storage(&key).is_none()); } + + #[derive(Debug, Default)] + struct TestExternalities { + storage: HashMap, Vec>, + } + 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, value: Vec) { + 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"[..]])); + } } From 66365202020a143508627e7effd9733d99581725 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 7 Jan 2018 17:34:30 +0100 Subject: [PATCH 39/49] Allow calls into runtime to return data. --- client/src/lib.rs | 17 +++++-- executor/src/wasm_executor.rs | 48 ++++++++++++++---- rpc/src/state/mod.rs | 8 +-- runtime/src/lib.rs | 13 ++++- .../release/runtime.compact.wasm | Bin 3246 -> 3386 bytes .../release/runtime.wasm | Bin 3334 -> 3473 bytes state_machine/src/lib.rs | 4 +- 7 files changed, 71 insertions(+), 19 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index d40cd5aa61dc6..242e12765e6bd 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -44,6 +44,14 @@ pub trait Blockchain { fn header(&self, hash: &block::HeaderHash) -> Result, Self::Error>; } +/// Information regarding the result of a call. +pub struct CallResult { + /// The data that was returned from the call. + pub return_data: Vec, + /// The changes made to the state by the call. + pub changes: state_machine::OverlayedChanges, +} + /// Polkadot Client #[derive(Debug)] pub struct Client { @@ -80,16 +88,19 @@ impl Client where } /// Execute a call to a contract on top of state in a block of given hash. - pub fn call(&self, hash: &block::HeaderHash, method: &str, call_data: &CallData) -> error::Result { + /// + /// No changes are made. + pub fn call(&self, hash: &block::HeaderHash, method: &str, call_data: &CallData) -> error::Result { let state = self.state_at(hash)?; let mut changes = state_machine::OverlayedChanges::default(); - Ok(state_machine::execute( + let _ = state_machine::execute( &state, &mut changes, &self.executor, method, call_data, - )?) + )?; + Ok(CallResult { return_data: vec![], changes }) } } diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 3a424a719ebbc..b45a38e4d4c07 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -83,6 +83,9 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, } } }, + ext_print_num(number: u64) => { + println!("Runtime: {}", number); + }, ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => { let _ = this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize); println!("memcpy {} from {}, {} bytes", dest, src, count); @@ -142,7 +145,7 @@ impl CodeExecutor for WasmExecutor { code: &[u8], method: &str, data: &CallData, - ) -> Result { + ) -> Result> { // TODO: handle all expects as errors to be returned. let program = ProgramInstance::new().expect("this really shouldn't be able to fail; qed"); @@ -157,13 +160,30 @@ impl CodeExecutor for WasmExecutor { let offset = fec.heap.allocate(size); memory.set(offset, &data.0).expect("heap always gives a sensible offset to write"); - let r = module.execute_export(method, + let returned: Result<_> = module.execute_export(method, program.params_with_external("env", &mut fec) .add_argument(I32(offset as i32)) .add_argument(I32(size as i32))) - .map_err(|_| ErrorKind::Runtime.into()) - .and_then(|i| if let Some(I64(r)) = i { Ok(r as u64) } else { Err(ErrorKind::InvalidReturn.into()) }); - r + .map_err(|_| ErrorKind::Runtime.into()); + + if let Some(I64(r)) = returned? { + memory.get(r as u32, (r >> 32) as u32 as usize) + .map_err(|_| ErrorKind::Runtime.into()) + } else { + Err(ErrorKind::InvalidReturn.into()) + } + +/* let returned: Result = module.execute_export(method, + program.params_with_external("env", &mut fec) + .add_argument(I32(offset as i32)) + .add_argument(I32(size as i32))) + .map_err(|_| ErrorKind::Runtime.into())?; + if let Some(I64(r)) = returned { + memory.get(r as u32, (r << 32) as usize) + .map_err(|_| ErrorKind::Runtime.into()) + } else { + Err(ErrorKind::InvalidReturn.into()) + }*/ } } @@ -199,7 +219,7 @@ mod tests { let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); - { + let output = { let memory = module.memory(ItemIndex::Internal(0)).unwrap(); let mut fec = FunctionExecutor::new(&memory, &mut ext); @@ -208,12 +228,22 @@ mod tests { let offset = fec.heap.allocate(size); memory.set(offset, data).unwrap(); - assert!(module.execute_export("test_data_in", + let returned = module.execute_export("exec_test_data_in", program.params_with_external("env", &mut fec) .add_argument(I32(offset as i32)) .add_argument(I32(size as i32)) - ).is_ok()); - } + ); + assert!(returned.is_ok()); + + if let Some(I64(r)) = returned.unwrap() { + println!("returned {:?} ({:?}, {:?})", r, r as u32, (r >> 32) as u32 as usize); + memory.get(r as u32, (r >> 32) as u32 as usize).expect("memory address should be reasonable.") + } else { + panic!("bad return value, not u64"); + } + }; + + assert_eq!(output, b"all ok!".to_vec()); let expected: HashMap<_, _> = map![ b"\0code".to_vec() => b"Hello world".to_vec(), diff --git a/rpc/src/state/mod.rs b/rpc/src/state/mod.rs index 1db83df1ee9e5..7a7ee4f3333f6 100644 --- a/rpc/src/state/mod.rs +++ b/rpc/src/state/mod.rs @@ -21,7 +21,7 @@ mod error; #[cfg(test)] mod tests; -use client::{self, Client}; +use client::{self, Client, CallResult}; use primitives::{block}; use primitives::contract::{CallData, StorageKey, StorageData}; use state_machine; @@ -37,7 +37,7 @@ build_rpc_trait! { /// Call a contract. #[rpc(name = "state_call")] - fn call(&self, String, CallData, block::HeaderHash) -> Result; + fn call(&self, String, CallData, block::HeaderHash) -> Result>; } } @@ -49,7 +49,7 @@ impl StateApi for Client where Ok(self.storage(&block, &key)?) } - fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result { - Ok(self.call(&block, &method, &data)?) + fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result> { + Ok(self.call(&block, &method, &data)?.return_data) } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index d089c7e90c35b..1dd44bc67003a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -18,6 +18,7 @@ pub fn panic_fmt() -> ! { extern "C" { fn ext_print(utf8_data: *const u8, utf8_len: i32); + fn ext_print_num(value: u64); fn set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; } @@ -95,11 +96,20 @@ pub fn print(utf8: &[u8]) { /// Test passing of data. #[no_mangle] -pub fn test_data_in(input_data: *mut u8, input_len: usize) { +pub fn exec_test_data_in(input_data: *mut u8, input_len: usize) -> u64 { let input = unsafe { Vec::from_raw_parts(input_data, input_len, input_len) }; + let output = test_data_in(input); + unsafe { + ext_print_num(&output[0] as *const u8 as u64); + ext_print_num(output.len() as u64); + } + &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) +} + +fn test_data_in(input: Vec) -> Vec { print(b"set_storage"); state::set_storage(b"input", &input); @@ -120,4 +130,5 @@ pub fn test_data_in(input_data: *mut u8, input_len: usize) { state::set_validators(&v.iter().map(Vec::as_slice).collect::>()); print(b"finished!"); + b"all ok!".to_vec() } diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index ef0257292d1979e821309c4f56fc3fbc25cf1792..d3ccf0951d3d58515bf7eb7aed51718071a5ab42 100644 GIT binary patch delta 1198 zcmZuxzi%T|5T4n6`*U{fb$-WA?6-!4=pbAUk>~^zHxc1PLqS0Sg&pn;iiw?5&JjnG zjZP6Yxvi1;4Pn~=GhZVx>-3W;1l zaF~qY`$V40)C%@Je=?e!oPO?5>-6lwZ1BA zwBeRRIoF-!ancuyyDIOkx6{5--bnV!@50`0PhH?$p=Vc0V#UV)Ra!O3ezo+X-iK`s zfN!D>hvuwSv--Bav2o^GK1h2SB4Y#Ih#Gd1NsWT?d1n8K)qt%4vgveuj%)I`B5?iZSL_L^>PyqjXI-z<26F zympYT>n7axTxl0@T`Y>N`KI5@%zluWdXMM{FM7P^7~9;x!0&guYzmR6hnf{phFPwb z*+`Z|9hgO_pUr}(53f~7{TMQ-N7s~k=MB*wf~ly5Nu*wv%uOuyq9f5JVxiP~2&CSO zFZHCodAX5VlPmQ|9I2JFXeEb4VzOj;(>j?WK5)i!gV6V!k{U5zmXdr8c)=3jBVbQH G&Hn~26164( delta 1085 zcmZuwJ#Q3O6uo!m&F6iyp0)Y#uG!r;n}C)OgCh_EDJWbi4v7>+X`>Xz2^-{N!(9ls)Qc~s*Ams<7O_VdUNtA+ObniR&>z#Aoyz0N`t<(0MJtiV{ z@pY~VAJ^Bzx^7HR12FJmh=SYk;ib*P!_(iJFiVNTC=8g4;oC$}$ka`)MDgBq@7BTn zfVu~F@65M;nBLsk-@3bhZ)g7oWp+LVc4K?Ky|uGXdAzs1yL;6D6$DSzVo zyv5=l@MD&jfATlXiT^tMgpGd~*8HpgD?G`O|InPeY{r=@Cc)J*a#>0GhBPfhwHi5c zTt@nqG$}(@C|wM$>Zia9eND5{_@B)f{hpZvPG9z-E_3S7#OoaU@8Wj|e3ty?tMsPd zNMqwV9p`^Zhv$|@Bl)L6;?hY#6}Qu?+*abyusMDHMNuhL9ecrln^)&Uc;|)QUndp~ z^Z)8bYLLC*&t{{#rwROXs6)$&)oIzz8XK2`EpHY>O_6aHgTXw5yDl!%a6U$CpgDk# z#f6S%Rpi1-a3`w*^o%%}1g44`Lsg=A$>Y4Kv50Ia*}njVD@JVfp>xFgX&AWicCCc#E!u)9n345S;0 z`g$BGLvgHjx5}J>$g4mwoYgd0O->I8s4$Lb+Q(_Ft(ZZIOy{$4Rvgzg!2Ooch0S7B zPvF>FtYrXqRa{d`nEsY#^~Jy}36W@o*ili2Inr#WV^>52m}P0-L02@!sg!oPOVTp! zNV~&AbcC1{9m0sT`sNFhNxPX;bP2IUTG?W0g^c9ORv9Dhr9){c2huWQ(dNE{*P2uF Z1G%6KB!*k^!jQ;wwxmYPBmcxc{TpNzoLT?? diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm index d48cb7c9b0921c48921fbcc8d47065ab97ec45c9..802e75a908e0bee21b594191dfaf1460226d081d 100644 GIT binary patch delta 1158 zcmZuwJ!~9h5S`im_IGyo_AZVS`|P`K4?;}9iH(RcKZVDNW2Hg#D4dPC2p>M%vJVK2 z&q7gJFxybjAS9@$QqY1#K}m^dsE{Zr=xFkG9U+RGbo$=BZ)SGhyxoVhXYEzmJ{~a< z@wIS^Cxkyww%p02I-E&GoY0m*3`kT_eByk^(b@I)^K@{3+&?_p-5>Y&KOB)c`(m-} zxe^n(e&9F`lQDcq^xI_utzdZ})fiDfLH#(azx~lm!5z zgO3K(sHo$?m~wx#y|;I;Lrrt`P3E{9y2~7tU%3*R9dd9|(%o}M}Lu4%B(U2bj-Vht9JMQ7GqcMoIp8LLfro9UEJfqX`+vysl}MPTgKMeZ~pMEnD( zusO)NXExHdUIL9uqyA87ksk7EM2HNc8~k9xuh$_hIH$YZ zU9qgmUJ+TWKxe>UW=0!iRXMX@Aea$imNuffx?+fhMLHh#qV%%Pfo|7^y}Xgm=zNh) zlG$^m9bCPQ&EdEk=h?QKVqq!jYJ!`3#IlUklGnv>P>s+ s^+sw@uGA}Wq*l(Nl^l}t`D8izfm2c==JRqV`3CTmCBS!W4?ZX!11j9Mp8x;= delta 1125 zcmbVKzi%8>5T4n6yLKI>nh!;=LQ z5&QHm*MyJjyK!ANCaM7#_%K9K;LhsSgVi@X0~3oSijz2sA|_+_5m6E|^|JR@e-2gL za(1#jxPQ1jJb3sqxpXmG+&oR8|Y-Vq_uQ$cyx4pk4p2=k}gm3v%E}p*?y~mt~XYnql;k&rzXW?ygfwM3(7vD%Hxi6;CUYYs4B>joBQzq&( z@#MM8^aW{FCcaR*8SUwtzzhAJ=4BdwG!xAG-8^Rd{DY*&Y{OA{nLFY0^fx4)X1|2p zcr)z9>HqwU@!hTuKX*n~*T-Y|dvWeN(}*f=UtL*OiN`~l=$&_qN~!9)8)0Ru>oL6R zLND(Vhk^FrzOfoqUkk_ixZc(b{#Ep0WX0;WY%+w?eOUqer0^1mlLz@+Bsbtr#5)Bi=fx+tiw!*k5b8(!#XvXV1CLSr3f!^iACRW`ai-C=%HVdl z>N!X^67}^gN=D*Y-BFc$1Ch6YU^%O4s+yW@AfUnsF|?0p(00rrMXt;FBrneD0C2x0 zbYs8R(sKy6PtP)hyCuHqC2W5|^ZI6F)`UnjL2gr&Va_zy>ADTk2xeW{|DY$DA}Xal z?wYhryVBmU5FI1uL`N_pt-kiudsFF#YOF8}}l diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index a604deab3fa55..58ba558322ac8 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -156,7 +156,7 @@ pub trait CodeExecutor: Sized { code: &[u8], method: &str, data: &CallData, - ) -> Result; + ) -> Result, Self::Error>; } /// Execute a call using the given state backend, overlayed changes, and call executor. @@ -171,7 +171,7 @@ pub fn execute( exec: &Exec, method: &str, call_data: &CallData, -) -> Result> { +) -> Result, Box> { let result = { let mut externalities = ext::Ext { From 964659ed97c3322f3c544358aa4241d3af636792 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 7 Jan 2018 17:35:02 +0100 Subject: [PATCH 40/49] Remove unneeded trace. --- runtime/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1dd44bc67003a..bbfe65400c1cd 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -102,10 +102,6 @@ pub fn exec_test_data_in(input_data: *mut u8, input_len: usize) -> u64 { }; let output = test_data_in(input); - unsafe { - ext_print_num(&output[0] as *const u8 as u64); - ext_print_num(output.len() as u64); - } &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) } From 8ca1b7bbee0a138e5ab9d7662e97e7f6b5fb7b10 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 7 Jan 2018 17:49:36 +0100 Subject: [PATCH 41/49] Make runtime printing a bit nicer. --- runtime/src/lib.rs | 36 +++++++++++++----- .../release/runtime.compact.wasm | Bin 3386 -> 3351 bytes .../release/runtime.wasm | Bin 3473 -> 3439 bytes 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index bbfe65400c1cd..d14b4373379ba 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -88,12 +88,28 @@ pub mod state { } } -pub fn print(utf8: &[u8]) { - unsafe { - ext_print(&utf8[0] as *const u8, utf8.len() as i32); +trait Printable { + fn print(self); +} + +impl<'a> Printable for &'a [u8] { + fn print(self) { + unsafe { + ext_print(&self[0] as *const u8, self.len() as i32); + } } } +impl Printable for u64 { + fn print(self) { + unsafe { ext_print_num(self); } + } +} + +fn print(value: T) { + value.print(); +} + /// Test passing of data. #[no_mangle] pub fn exec_test_data_in(input_data: *mut u8, input_len: usize) -> u64 { @@ -106,25 +122,25 @@ pub fn exec_test_data_in(input_data: *mut u8, input_len: usize) -> u64 { } fn test_data_in(input: Vec) -> Vec { - print(b"set_storage"); + print(b"set_storage" as &[u8]); state::set_storage(b"input", &input); - print(b"code"); + print(b"code" as &[u8]); state::set_storage(b"code", &state::code()); - print(b"set_code"); + print(b"set_code" as &[u8]); state::set_code(&input); - print(b"storage"); + print(b"storage" as &[u8]); let copy = state::storage(b"input"); - print(b"validators"); + print(b"validators" as &[u8]); let mut v = state::validators(); v.push(copy); - print(b"set_validators"); + print(b"set_validators" as &[u8]); state::set_validators(&v.iter().map(Vec::as_slice).collect::>()); - print(b"finished!"); + print(b"finished!" as &[u8]); b"all ok!".to_vec() } diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm index d3ccf0951d3d58515bf7eb7aed51718071a5ab42..a45007fa55756d70901f21ae7be6bbc30dacb50a 100644 GIT binary patch delta 307 zcmWkoyGjE=6uoEXu{*n27!?Fl=nyn0vV{miK`>L;iH((25+oKeL5YQ6k%i<7mMQFP z1UsuO6f4EXGQD8u2lxSslj1z?DbBes)!XVeb*=^=BGkh+a`HglW_do7Aol_H-V4#} z#3;Cw#m+%dY$(oDM8t$F-ZQA;XJIjxgHFGHaY_|_HtcpORiOCORnbD?ZZJkt{^AG- z-67iocO#bL%l)x62)D}XyS%1Mx*jyOFiMmCARB3BD^?gX(85m1hMJkwQX^>EJ)fuc zm3o@H39tFS2R=kpE{QoHa+@-PbSLr~yxb43XRA-qvFAj!{NA~G^FE7P^THrqsVpZZ cwy_C~)Z+dwhV~DT@1Ihy5pcsF%jaZ_fB)V>CIA2c delta 333 zcmWknJxc>Y6nyV?Z}0X#%qG~F!Yl!U1X6^6Smp#<5&Z$1Bd(B;L`hCem4l$AMOehn z#!j#?ot>SfKf-{uzrelX&6^pRnWySU^$^n87y$sAeh+i7hk1|WdGVaQFc#3GR$B^C zSh#TR&|7tmMXcwUx}FXunVQ^;fv&O^cM10a2=i92t!7ZxbKO^&o@MGH%~Ca(K*+}F zXmr(wm|jk`hAoZ|Yz(5p$fUT$X#ImHh`#BNHo8WW9DmJ*>>)EDZPeU4m+`*S6-;sg z$rmKPAW4+C5+n*{PU((K4Qbut1Dzs&cgu*}R9Fi=(`6fU*A4Hs7VDG38W{PU@!LpR^RF+L61B^CAG64=IzL<9*6qS?kuZ0xkS9C1ZME|KK$qw&Pv;@C!P zZM8Z(3l%IZ^A~8PmSU%!n_}ku<`wTvKJh)i2ys6_0Kh71UC~2 zc;cC*(ao3k^ZQwk*ntmVJ2d~CUk}RaL^Y#S_0woKPUGmH10|ZMqAv2J4j}(jy zt-eZOlqPYz-E9IVdp)I~>>>C+dIuv6^#fjDSgcx`h@QT*7m(^lJHt1Y5Qb;&_HOSZmuxI76m<>wfgmDCRIqUE6-2SUgt%g&7bQ70HXfv~Ki0xN zg^gDC2ei`4kIL3Yu=5AlxL3@~Gfy$^{EnofD^Tx72mmv$6_{ZY$1B;%v?mO?#u*#)C`$IM{SbnRa$X*Fl($chr0~K3kZiFBo<~>p?Qf*nA-%{`znmT2m)ub2 z-MCMh_q=e(;TTL5#(nMyk5g_lXYlOd$U~*X> Date: Mon, 8 Jan 2018 12:16:10 +0100 Subject: [PATCH 42/49] Create separate runtimes for testing and polkadot. --- executor/src/wasm_executor.rs | 2 +- runtime/Cargo.lock | 10 +- runtime/Cargo.toml | 16 +- runtime/build.sh | 9 +- runtime/polkadot/Cargo.toml | 11 ++ runtime/{ => polkadot}/src/lib.rs | 24 +-- .../release/runtime.compact.wasm | Bin 3351 -> 0 bytes .../release/runtime.wasm | Bin 3439 -> 0 bytes runtime/test/Cargo.toml | 11 ++ runtime/test/src/lib.rs | 152 ++++++++++++++++++ 10 files changed, 212 insertions(+), 23 deletions(-) create mode 100644 runtime/polkadot/Cargo.toml rename runtime/{ => polkadot}/src/lib.rs (88%) delete mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm delete mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime.wasm create mode 100644 runtime/test/Cargo.toml create mode 100644 runtime/test/src/lib.rs diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index b45a38e4d4c07..67cca263b291b 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -228,7 +228,7 @@ mod tests { let offset = fec.heap.allocate(size); memory.set(offset, data).unwrap(); - let returned = module.execute_export("exec_test_data_in", + let returned = module.execute_export("test_data_in", program.params_with_external("env", &mut fec) .add_argument(I32(offset as i32)) .add_argument(I32(size as i32)) diff --git a/runtime/Cargo.lock b/runtime/Cargo.lock index 28a7ce9a3236d..62de3b1de536b 100644 --- a/runtime/Cargo.lock +++ b/runtime/Cargo.lock @@ -10,7 +10,15 @@ name = "pwasm-libc" version = "0.1.0" [[package]] -name = "runtime" +name = "runtime-polkadot" +version = "0.1.0" +dependencies = [ + "pwasm-alloc 0.1.0", + "pwasm-libc 0.1.0", +] + +[[package]] +name = "runtime-test" version = "0.1.0" dependencies = [ "pwasm-alloc 0.1.0", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5db697b226563..0dc0216fe52ea 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,14 +1,8 @@ -[package] -name = "runtime" -version = "0.1.0" -authors = ["Parity Technologies "] - -[lib] -crate-type = ["cdylib"] - -[dependencies] -pwasm-libc = { path = "./pwasm-libc", version = "0.1" } -pwasm-alloc = { path = "./pwasm-alloc", version = "0.1" } +[workspace] +members = [ + "test", + "polkadot", +] [profile.release] panic = "abort" diff --git a/runtime/build.sh b/runtime/build.sh index 66b2d2f5494ee..b600e8d2ce308 100755 --- a/runtime/build.sh +++ b/runtime/build.sh @@ -1,4 +1,11 @@ #!/bin/sh cargo +nightly build --target=wasm32-unknown-unknown --release -wasm-gc target/wasm32-unknown-unknown/release/runtime.wasm target/wasm32-unknown-unknown/release/runtime.compact.wasm +dirs=`find * -maxdepth 0 -type d | grep -v pwasm-` +for i in $dirs +do + if [[ -e $i/Cargo.toml ]] + then + wasm-gc target/wasm32-unknown-unknown/release/runtime_$i.wasm target/wasm32-unknown-unknown/release/runtime_$i.compact.wasm + fi +done diff --git a/runtime/polkadot/Cargo.toml b/runtime/polkadot/Cargo.toml new file mode 100644 index 0000000000000..215a390b599cf --- /dev/null +++ b/runtime/polkadot/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "runtime-polkadot" +version = "0.1.0" +authors = ["Parity Technologies "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pwasm-libc = { path = "../pwasm-libc", version = "0.1" } +pwasm-alloc = { path = "../pwasm-alloc", version = "0.1" } diff --git a/runtime/src/lib.rs b/runtime/polkadot/src/lib.rs similarity index 88% rename from runtime/src/lib.rs rename to runtime/polkadot/src/lib.rs index d14b4373379ba..8aef65a1122a8 100644 --- a/runtime/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -110,17 +110,23 @@ fn print(value: T) { value.print(); } -/// Test passing of data. -#[no_mangle] -pub fn exec_test_data_in(input_data: *mut u8, input_len: usize) -> u64 { - let input = unsafe { - Vec::from_raw_parts(input_data, input_len, input_len) - }; - - let output = test_data_in(input); - &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) +macro_rules! impl_stub { + ($name:ident) => { + pub mod _internal { + #[no_mangle] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input = unsafe { + super::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) + }; + + let output = super::$name(input); + &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) + } + } + } } +impl_stub!(test_data_in); fn test_data_in(input: Vec) -> Vec { print(b"set_storage" as &[u8]); state::set_storage(b"input", &input); diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm deleted file mode 100644 index a45007fa55756d70901f21ae7be6bbc30dacb50a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3351 zcmcIn&2rtg6+Qq#UXmav%9ej6ONVTdI&Ivj9aqk@S?G8oJ6UA%2#PA*xT5tp(zTN; zn(IX->%K_W-FA^hmRfeEGhOuoy2u0cJD~2hJXMyl<{H2OaDG300BPs!&=8S%XY{$5 z5`Jd-c`}`9W2X=}^wALM(0pT0vSfVpG9Q06+5X~mJSNg%sl&sagM;H;GOq>W!`+jw z$*Q2x6(^_rM-xhPeB;@8vK=?&7@5|6T1xy#jwIDdJ7)A|6<&9=78P2!UfAF zYUrF=w{4=<9ZV)rjsRR`JjXH28+Q7OE?_ZSKrC>9IGLMPC!%RsKTk|H6%qvNqpPCLUHB689XnjxgGXZr`ZX(fNz*)Xay&sj6#MDY;! z5^@er(1Ko6w$+*{ha@HiI#TQaw1y?fV8|tfzl9cxBtsXgVM6mj5erCY4#RLkLxOtI zWHdciEO<#EO1&5~ZU{Y@CqgrWM5t2o^E#Rc%_kXkN@(%QMkb?9)Oy2+8US%vfg7>BgIPXIaF9d5jeb?@S|iM zGsuQcm0alQsNyv0q_T{Mq3T}ODd^Px!)~-;KSCeP5*o}jTt;vV3f!fQ&83Y?dLDHn znzx3HjBNbBTL$I_hYEEF&S_)R?e%WqQ4C?>5n{{%bQBq+)&t9zG7|HFd}`8g5D+Qn z8}sCR*%g*hj;db#xpES6Foc2<(fKzYVv7L3Sc-{nst(-fSv4#6p3L7!88 z)r?$J&oTH$2?*5UxX=s$QLqi?a>V5IL^%Wyfqxm8rB)E=jbMa<@-+~l4M0R-b0C%g zBGi8hmy`v|EwFV7n{x&P-&`hY2ptvwX%e3YoC1c=6U=1M_kvfHI!tj8|6jmtI#ae zi`SQK+`M)B&fVped#h`F?V1LNXh5~O781cJag%_QvGhOGr|QN)3Z~ZDD_KR^>;zcb@f|lgd@10 zB2W|@z!zu{_w1VFSg}5ke1Y!!peF!V;j@Z**41y~Wz)BKE5vG5;vs$D+2clPGi`9M z&G54-6=vVVtU?za{}h@oI0=|hpudT(N^J+|`L$O_afKrmuhunbApO)tHhKF0>j@-lW?hxlHx_5B(D_3B@7VDgPWk~n8$FK13zw~W{HO<5Lp$&FkUz?(qwJCCIve<2yh;^y&W5$qUSU zPu>lBe!we7KNg13Ir-7-vjL9`U(QXO5pEh^>(a}zAITSA>>ur){dv6ClDsm0gijJa He%}5M;;+U4 diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime.wasm deleted file mode 100644 index edda5cfc3f69200c50032a20a9d0542e2af8954e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3439 zcmcInOLH7W60WMQepXkjWy?>=Qg-8A%)>CdVPs}Xz!SC4fUO8~!s3Rv%`vZ0(_yK%bJsQh__PADzD=Vw=o%L0<+CCm@rIh}2`bbX| zetP;SnoeafQ!p%O3`)sVe`9{oS=1ezWZjpOt~2rCxAqQHDFWiPPeBB4D`AP33&||R+J?{zjy+ zMsvZ%Lmz@Iniz4WrImcK0fh`G4BBzpq%PVxbNQl?(kjzvRvRa2>L_kCC5Y&OFM zR5|4`6>S9K*V=d-{`w{FV8D~XJH{Fn80 zVK&4F4UL*n+-R8bJnFG3kz%J%78RDJ@Z?{M=zcW93bG+f1?6%&%CL+&2``~xD!NCq z1f4?PYtwb}A!rOs7_d@P5t?IAV9yEG=LDDJJc=NO+k+q>8-H|5!~Eb-E)Kyt8ML@P zY8UQ@5Ekwu#te;)A_LTV^zylkgnS^Mk~ACyL`vDZc^3mxcn^_e`3fgR|LK1Sfa}eOB;QGBT^4CE$$!5TM0UE*U^a!8V-B z5R;P=Wf4H=-Sdq(>ywSpj4)8VXhdj3BO(~qLsQ-WYFPn0feDp> zCjPKe2};Fb4R@$%A@tJkjIxVgM?Yjuq-U6KG1 z4WKrYLLxW;j-qUVnkW@!A3pGHh-BTN9|LHI8&&E;xqx;qpq=|Qju7uV2>e2wt|Ujr zTqj9d%D>KahIRqQ^khXRN_~Q>{w9FAD*pnFa0K^LXcTyetG@s&;+|dO3_C^x$mby6 z20Wp0rF~XW&#HVYTsD1!w?e2^$qwARjvQ{JCXoU6+FAdsa*5S9u_~<#kN;OU%`gc) zBaQxYca>t>L(dydT8c^>v2eAnaSiV47iXv_Z=KKjDp%NhXP%*pv%boU*!AjJhC=-S zl$KIlhscm$D_13oVqowk?EVuNxKJ*GyzQuz!zsmyANnO;0`nC-uQvS(UjdCqh_Lr6 zNM!Ff#X0Vvba-(r^EI#`X*t1>yQ{v_L+xxhfZyub8|038uJK$E-$sHx1CB|Bmiam~ ztjXO{?gJs@4RQkNtX#Y;oP z@zRh$@Y0Yp;K?P?0z)JQfM%gc0NvE)1u1X(8P~c5WUYq*`~QXOnys_nBY4H7i`CBI zZl`z&GAG;ndw9$r9w{bQznE_A96rOB1&`cI-wyn~i}wzmKEujS%Gp8B&v^UD2gWcv vRW3dI_`uV{6>}5E3ipqzWb1j}Z_4GL?H%kLf8Omjil1wj;xmR0pESP%9ar0# diff --git a/runtime/test/Cargo.toml b/runtime/test/Cargo.toml new file mode 100644 index 0000000000000..80d022f35a94d --- /dev/null +++ b/runtime/test/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "runtime-test" +version = "0.1.0" +authors = ["Parity Technologies "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +pwasm-libc = { path = "../pwasm-libc", version = "0.1" } +pwasm-alloc = { path = "../pwasm-alloc", version = "0.1" } diff --git a/runtime/test/src/lib.rs b/runtime/test/src/lib.rs new file mode 100644 index 0000000000000..8aef65a1122a8 --- /dev/null +++ b/runtime/test/src/lib.rs @@ -0,0 +1,152 @@ +#![no_std] +#![feature(lang_items)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] + +extern crate alloc; +use alloc::vec::Vec; + +extern crate pwasm_libc; +extern crate pwasm_alloc; + +#[lang = "panic_fmt"] +#[no_mangle] +pub fn panic_fmt() -> ! { + loop {} +} + +extern "C" { + fn ext_print(utf8_data: *const u8, utf8_len: i32); + fn ext_print_num(value: u64); + fn set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); + fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; +} + +pub mod state { + use alloc::vec::Vec; + use super::{get_allocated_storage, set_storage as super_set_storage}; + + pub fn storage(key: &[u8]) -> Vec { + let mut length: i32 = 0; + unsafe { + let ptr = get_allocated_storage(&key[0], key.len() as i32, &mut length); + Vec::from_raw_parts(ptr, length as usize, length as usize) + } + } + + pub fn set_storage(key: &[u8], value: &[u8]) { + unsafe { + super_set_storage( + &key[0] as *const u8, key.len() as i32, + &value[0] as *const u8, value.len() as i32 + ); + } + } + + pub fn code() -> Vec { + storage(b"\0code") + } + + pub fn set_code(new: &[u8]) { + set_storage(b"\0code", new) + } + + fn value_vec(mut value: usize, initial: Vec) -> Vec { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; + } + acc + } + + pub fn set_validator(index: usize, validator: &[u8]) { + set_storage(&value_vec(index, b"\0validator".to_vec()), validator); + } + + pub fn validator(index: usize) -> Vec { + storage(&value_vec(index, b"\0validator".to_vec())) + } + + pub fn set_validator_count(count: usize) { + (count..validator_count()).for_each(|i| set_validator(i, &[])); + set_storage(b"\0validator_count", &value_vec(count, Vec::new())); + } + + pub fn validator_count() -> usize { + storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) + } + + pub fn validators() -> Vec> { + (0..validator_count()).into_iter().map(validator).collect() + } + + pub fn set_validators(validators: &[&[u8]]) { + set_validator_count(validators.len()); + validators.iter().enumerate().for_each(|(v, i)| set_validator(v, i)); + } +} + +trait Printable { + fn print(self); +} + +impl<'a> Printable for &'a [u8] { + fn print(self) { + unsafe { + ext_print(&self[0] as *const u8, self.len() as i32); + } + } +} + +impl Printable for u64 { + fn print(self) { + unsafe { ext_print_num(self); } + } +} + +fn print(value: T) { + value.print(); +} + +macro_rules! impl_stub { + ($name:ident) => { + pub mod _internal { + #[no_mangle] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input = unsafe { + super::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) + }; + + let output = super::$name(input); + &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) + } + } + } +} + +impl_stub!(test_data_in); +fn test_data_in(input: Vec) -> Vec { + print(b"set_storage" as &[u8]); + state::set_storage(b"input", &input); + + print(b"code" as &[u8]); + state::set_storage(b"code", &state::code()); + + print(b"set_code" as &[u8]); + state::set_code(&input); + + print(b"storage" as &[u8]); + let copy = state::storage(b"input"); + + print(b"validators" as &[u8]); + let mut v = state::validators(); + v.push(copy); + + print(b"set_validators" as &[u8]); + state::set_validators(&v.iter().map(Vec::as_slice).collect::>()); + + print(b"finished!" as &[u8]); + b"all ok!".to_vec() +} From 611a7ac401528755d6db6e824626cfa142efac9a Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 8 Jan 2018 12:16:42 +0100 Subject: [PATCH 43/49] Remove commented code. --- executor/src/wasm_executor.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 67cca263b291b..6d557dc896158 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -172,18 +172,6 @@ impl CodeExecutor for WasmExecutor { } else { Err(ErrorKind::InvalidReturn.into()) } - -/* let returned: Result = module.execute_export(method, - program.params_with_external("env", &mut fec) - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32))) - .map_err(|_| ErrorKind::Runtime.into())?; - if let Some(I64(r)) = returned { - memory.get(r as u32, (r << 32) as usize) - .map_err(|_| ErrorKind::Runtime.into()) - } else { - Err(ErrorKind::InvalidReturn.into()) - }*/ } } From c3afecc07f9f42302bf26d7af070b0887d7ff8c5 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 8 Jan 2018 12:17:05 +0100 Subject: [PATCH 44/49] Use new path. --- executor/src/wasm_executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 6d557dc896158..1a5c1627e135e 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -203,7 +203,7 @@ mod tests { let program = ProgramInstance::new().unwrap(); - let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime.compact.wasm"); + let test_module = include_bytes!("../../runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); let module = deserialize_buffer(test_module.to_vec()).expect("Failed to load module"); let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::::SIGNATURES]).expect("Failed to initialize module"); From ea4d6c5d330425a52db20957c1ab0e16d44eaa8e Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 8 Jan 2018 12:31:10 +0100 Subject: [PATCH 45/49] Refactor into shared support module. --- executor/src/wasm_executor.rs | 4 +- runtime/Cargo.lock | 10 ++- runtime/build.sh | 2 +- runtime/polkadot/Cargo.toml | 3 +- runtime/polkadot/src/lib.rs | 134 +++------------------------------- runtime/support/Cargo.toml | 11 +++ runtime/support/src/lib.rs | 123 +++++++++++++++++++++++++++++++ runtime/test/Cargo.toml | 3 +- runtime/test/src/lib.rs | 134 +++------------------------------- 9 files changed, 165 insertions(+), 259 deletions(-) create mode 100644 runtime/support/Cargo.toml create mode 100644 runtime/support/src/lib.rs diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 1a5c1627e135e..5ecc49c1f7008 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -110,12 +110,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.heap.deallocate(addr); println!("free {}", addr) }, - set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32) => { + ext_set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32) => { if let (Ok(key), Ok(value)) = (this.memory.get(key_data, key_len as usize), this.memory.get(value_data, value_len as usize)) { this.ext.set_storage(key, value); } }, - get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8 => { + ext_get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8 => { let (offset, written) = if let Ok(key) = this.memory.get(key_data, key_len as usize) { if let Ok(value) = this.ext.storage(&key) { let offset = this.heap.allocate(value.len() as u32) as u32; diff --git a/runtime/Cargo.lock b/runtime/Cargo.lock index 62de3b1de536b..d134139a3c9b7 100644 --- a/runtime/Cargo.lock +++ b/runtime/Cargo.lock @@ -12,6 +12,13 @@ version = "0.1.0" [[package]] name = "runtime-polkadot" version = "0.1.0" +dependencies = [ + "runtime-support 0.1.0", +] + +[[package]] +name = "runtime-support" +version = "0.1.0" dependencies = [ "pwasm-alloc 0.1.0", "pwasm-libc 0.1.0", @@ -21,7 +28,6 @@ dependencies = [ name = "runtime-test" version = "0.1.0" dependencies = [ - "pwasm-alloc 0.1.0", - "pwasm-libc 0.1.0", + "runtime-support 0.1.0", ] diff --git a/runtime/build.sh b/runtime/build.sh index b600e8d2ce308..c3030f99d3b17 100755 --- a/runtime/build.sh +++ b/runtime/build.sh @@ -1,7 +1,7 @@ #!/bin/sh cargo +nightly build --target=wasm32-unknown-unknown --release -dirs=`find * -maxdepth 0 -type d | grep -v pwasm-` +dirs=`find * -maxdepth 0 -type d | grep -v pwasm- | grep -v support` for i in $dirs do if [[ -e $i/Cargo.toml ]] diff --git a/runtime/polkadot/Cargo.toml b/runtime/polkadot/Cargo.toml index 215a390b599cf..a313da2a1ef88 100644 --- a/runtime/polkadot/Cargo.toml +++ b/runtime/polkadot/Cargo.toml @@ -7,5 +7,4 @@ authors = ["Parity Technologies "] crate-type = ["cdylib"] [dependencies] -pwasm-libc = { path = "../pwasm-libc", version = "0.1" } -pwasm-alloc = { path = "../pwasm-alloc", version = "0.1" } +runtime-support = { path = "../support", version = "0.1" } diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 8aef65a1122a8..ec3fabb89cd15 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -3,149 +3,33 @@ #![cfg_attr(feature = "strict", deny(warnings))] #![feature(alloc)] - extern crate alloc; use alloc::vec::Vec; -extern crate pwasm_libc; -extern crate pwasm_alloc; - -#[lang = "panic_fmt"] -#[no_mangle] -pub fn panic_fmt() -> ! { - loop {} -} - -extern "C" { - fn ext_print(utf8_data: *const u8, utf8_len: i32); - fn ext_print_num(value: u64); - fn set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); - fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; -} - -pub mod state { - use alloc::vec::Vec; - use super::{get_allocated_storage, set_storage as super_set_storage}; - - pub fn storage(key: &[u8]) -> Vec { - let mut length: i32 = 0; - unsafe { - let ptr = get_allocated_storage(&key[0], key.len() as i32, &mut length); - Vec::from_raw_parts(ptr, length as usize, length as usize) - } - } - - pub fn set_storage(key: &[u8], value: &[u8]) { - unsafe { - super_set_storage( - &key[0] as *const u8, key.len() as i32, - &value[0] as *const u8, value.len() as i32 - ); - } - } - - pub fn code() -> Vec { - storage(b"\0code") - } - - pub fn set_code(new: &[u8]) { - set_storage(b"\0code", new) - } - - fn value_vec(mut value: usize, initial: Vec) -> Vec { - let mut acc = initial; - while value > 0 { - acc.push(value as u8); - value /= 256; - } - acc - } - - pub fn set_validator(index: usize, validator: &[u8]) { - set_storage(&value_vec(index, b"\0validator".to_vec()), validator); - } - - pub fn validator(index: usize) -> Vec { - storage(&value_vec(index, b"\0validator".to_vec())) - } - - pub fn set_validator_count(count: usize) { - (count..validator_count()).for_each(|i| set_validator(i, &[])); - set_storage(b"\0validator_count", &value_vec(count, Vec::new())); - } - - pub fn validator_count() -> usize { - storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) - } - - pub fn validators() -> Vec> { - (0..validator_count()).into_iter().map(validator).collect() - } - - pub fn set_validators(validators: &[&[u8]]) { - set_validator_count(validators.len()); - validators.iter().enumerate().for_each(|(v, i)| set_validator(v, i)); - } -} - -trait Printable { - fn print(self); -} - -impl<'a> Printable for &'a [u8] { - fn print(self) { - unsafe { - ext_print(&self[0] as *const u8, self.len() as i32); - } - } -} - -impl Printable for u64 { - fn print(self) { - unsafe { ext_print_num(self); } - } -} - -fn print(value: T) { - value.print(); -} - -macro_rules! impl_stub { - ($name:ident) => { - pub mod _internal { - #[no_mangle] - pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { - let input = unsafe { - super::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) - }; - - let output = super::$name(input); - &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) - } - } - } -} +#[macro_use] +extern crate runtime_support; +use runtime_support::{set_storage, code, set_code, storage, validators, set_validators, print}; impl_stub!(test_data_in); fn test_data_in(input: Vec) -> Vec { print(b"set_storage" as &[u8]); - state::set_storage(b"input", &input); + set_storage(b"input", &input); print(b"code" as &[u8]); - state::set_storage(b"code", &state::code()); + set_storage(b"code", &code()); print(b"set_code" as &[u8]); - state::set_code(&input); + set_code(&input); print(b"storage" as &[u8]); - let copy = state::storage(b"input"); + let copy = storage(b"input"); print(b"validators" as &[u8]); - let mut v = state::validators(); + let mut v = validators(); v.push(copy); print(b"set_validators" as &[u8]); - state::set_validators(&v.iter().map(Vec::as_slice).collect::>()); + set_validators(&v.iter().map(Vec::as_slice).collect::>()); print(b"finished!" as &[u8]); b"all ok!".to_vec() diff --git a/runtime/support/Cargo.toml b/runtime/support/Cargo.toml new file mode 100644 index 0000000000000..828fdab534e00 --- /dev/null +++ b/runtime/support/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "runtime-support" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +pwasm-libc = { path = "../pwasm-libc", version = "0.1" } +pwasm-alloc = { path = "../pwasm-alloc", version = "0.1" } + +[features] +strict = [] diff --git a/runtime/support/src/lib.rs b/runtime/support/src/lib.rs new file mode 100644 index 0000000000000..b4e1d22bdcbd4 --- /dev/null +++ b/runtime/support/src/lib.rs @@ -0,0 +1,123 @@ +#![no_std] +#![feature(lang_items)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] + +extern crate alloc; +use alloc::vec::Vec; + +extern crate pwasm_libc; +extern crate pwasm_alloc; + +#[lang = "panic_fmt"] +#[no_mangle] +pub fn panic_fmt() -> ! { + loop {} +} + +extern "C" { + fn ext_print(utf8_data: *const u8, utf8_len: i32); + fn ext_print_num(value: u64); + fn ext_set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); + fn ext_get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; +} + +pub fn storage(key: &[u8]) -> Vec { + let mut length: i32 = 0; + unsafe { + let ptr = ext_get_allocated_storage(&key[0], key.len() as i32, &mut length); + Vec::from_raw_parts(ptr, length as usize, length as usize) + } +} + +pub fn set_storage(key: &[u8], value: &[u8]) { + unsafe { + ext_set_storage( + &key[0] as *const u8, key.len() as i32, + &value[0] as *const u8, value.len() as i32 + ); + } +} + +pub fn code() -> Vec { + storage(b"\0code") +} + +pub fn set_code(new: &[u8]) { + set_storage(b"\0code", new) +} + +fn value_vec(mut value: usize, initial: Vec) -> Vec { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; + } + acc +} + +pub fn set_validator(index: usize, validator: &[u8]) { + set_storage(&value_vec(index, b"\0validator".to_vec()), validator); +} + +pub fn validator(index: usize) -> Vec { + storage(&value_vec(index, b"\0validator".to_vec())) +} + +pub fn set_validator_count(count: usize) { + (count..validator_count()).for_each(|i| set_validator(i, &[])); + set_storage(b"\0validator_count", &value_vec(count, Vec::new())); +} + +pub fn validator_count() -> usize { + storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) +} + +pub fn validators() -> Vec> { + (0..validator_count()).into_iter().map(validator).collect() +} + +pub fn set_validators(validators: &[&[u8]]) { + set_validator_count(validators.len()); + validators.iter().enumerate().for_each(|(v, i)| set_validator(v, i)); +} + +pub trait Printable { + fn print(self); +} + +impl<'a> Printable for &'a [u8] { + fn print(self) { + unsafe { + ext_print(&self[0] as *const u8, self.len() as i32); + } + } +} + +impl Printable for u64 { + fn print(self) { + unsafe { ext_print_num(self); } + } +} + +pub fn print(value: T) { + value.print(); +} + +#[macro_export] +macro_rules! impl_stub { + ($name:ident) => { + pub mod _internal { + #[no_mangle] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input = unsafe { + super::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) + }; + + let output = super::$name(input); + &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) + } + } + } +} diff --git a/runtime/test/Cargo.toml b/runtime/test/Cargo.toml index 80d022f35a94d..57015d26836b3 100644 --- a/runtime/test/Cargo.toml +++ b/runtime/test/Cargo.toml @@ -7,5 +7,4 @@ authors = ["Parity Technologies "] crate-type = ["cdylib"] [dependencies] -pwasm-libc = { path = "../pwasm-libc", version = "0.1" } -pwasm-alloc = { path = "../pwasm-alloc", version = "0.1" } +runtime-support = { path = "../support", version = "0.1" } diff --git a/runtime/test/src/lib.rs b/runtime/test/src/lib.rs index 8aef65a1122a8..ec3fabb89cd15 100644 --- a/runtime/test/src/lib.rs +++ b/runtime/test/src/lib.rs @@ -3,149 +3,33 @@ #![cfg_attr(feature = "strict", deny(warnings))] #![feature(alloc)] - extern crate alloc; use alloc::vec::Vec; -extern crate pwasm_libc; -extern crate pwasm_alloc; - -#[lang = "panic_fmt"] -#[no_mangle] -pub fn panic_fmt() -> ! { - loop {} -} - -extern "C" { - fn ext_print(utf8_data: *const u8, utf8_len: i32); - fn ext_print_num(value: u64); - fn set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); - fn get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; -} - -pub mod state { - use alloc::vec::Vec; - use super::{get_allocated_storage, set_storage as super_set_storage}; - - pub fn storage(key: &[u8]) -> Vec { - let mut length: i32 = 0; - unsafe { - let ptr = get_allocated_storage(&key[0], key.len() as i32, &mut length); - Vec::from_raw_parts(ptr, length as usize, length as usize) - } - } - - pub fn set_storage(key: &[u8], value: &[u8]) { - unsafe { - super_set_storage( - &key[0] as *const u8, key.len() as i32, - &value[0] as *const u8, value.len() as i32 - ); - } - } - - pub fn code() -> Vec { - storage(b"\0code") - } - - pub fn set_code(new: &[u8]) { - set_storage(b"\0code", new) - } - - fn value_vec(mut value: usize, initial: Vec) -> Vec { - let mut acc = initial; - while value > 0 { - acc.push(value as u8); - value /= 256; - } - acc - } - - pub fn set_validator(index: usize, validator: &[u8]) { - set_storage(&value_vec(index, b"\0validator".to_vec()), validator); - } - - pub fn validator(index: usize) -> Vec { - storage(&value_vec(index, b"\0validator".to_vec())) - } - - pub fn set_validator_count(count: usize) { - (count..validator_count()).for_each(|i| set_validator(i, &[])); - set_storage(b"\0validator_count", &value_vec(count, Vec::new())); - } - - pub fn validator_count() -> usize { - storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) - } - - pub fn validators() -> Vec> { - (0..validator_count()).into_iter().map(validator).collect() - } - - pub fn set_validators(validators: &[&[u8]]) { - set_validator_count(validators.len()); - validators.iter().enumerate().for_each(|(v, i)| set_validator(v, i)); - } -} - -trait Printable { - fn print(self); -} - -impl<'a> Printable for &'a [u8] { - fn print(self) { - unsafe { - ext_print(&self[0] as *const u8, self.len() as i32); - } - } -} - -impl Printable for u64 { - fn print(self) { - unsafe { ext_print_num(self); } - } -} - -fn print(value: T) { - value.print(); -} - -macro_rules! impl_stub { - ($name:ident) => { - pub mod _internal { - #[no_mangle] - pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { - let input = unsafe { - super::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) - }; - - let output = super::$name(input); - &output[0] as *const u8 as u64 + ((output.len() as u64) << 32) - } - } - } -} +#[macro_use] +extern crate runtime_support; +use runtime_support::{set_storage, code, set_code, storage, validators, set_validators, print}; impl_stub!(test_data_in); fn test_data_in(input: Vec) -> Vec { print(b"set_storage" as &[u8]); - state::set_storage(b"input", &input); + set_storage(b"input", &input); print(b"code" as &[u8]); - state::set_storage(b"code", &state::code()); + set_storage(b"code", &code()); print(b"set_code" as &[u8]); - state::set_code(&input); + set_code(&input); print(b"storage" as &[u8]); - let copy = state::storage(b"input"); + let copy = storage(b"input"); print(b"validators" as &[u8]); - let mut v = state::validators(); + let mut v = validators(); v.push(copy); print(b"set_validators" as &[u8]); - state::set_validators(&v.iter().map(Vec::as_slice).collect::>()); + set_validators(&v.iter().map(Vec::as_slice).collect::>()); print(b"finished!" as &[u8]); b"all ok!".to_vec() From 709693dd362865b9482cd8f5200c2afca81ed2d2 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 8 Jan 2018 15:54:56 +0100 Subject: [PATCH 46/49] Fix warning. --- rpc/src/state/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/src/state/mod.rs b/rpc/src/state/mod.rs index 7a7ee4f3333f6..113fe4849b2a8 100644 --- a/rpc/src/state/mod.rs +++ b/rpc/src/state/mod.rs @@ -21,7 +21,7 @@ mod error; #[cfg(test)] mod tests; -use client::{self, Client, CallResult}; +use client::{self, Client}; use primitives::{block}; use primitives::contract::{CallData, StorageKey, StorageData}; use state_machine; From ec1e6b60164a108199081a84a7f7d23a63b41b85 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 8 Jan 2018 16:17:44 +0100 Subject: [PATCH 47/49] Remove unwraps. --- executor/src/wasm_executor.rs | 29 ++++++++++++++++------------- executor/src/wasm_utils.rs | 24 ++++++++++++------------ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/executor/src/wasm_executor.rs b/executor/src/wasm_executor.rs index 5ecc49c1f7008..f52b2cbf715e1 100644 --- a/executor/src/wasm_executor.rs +++ b/executor/src/wasm_executor.rs @@ -160,13 +160,15 @@ impl CodeExecutor for WasmExecutor { let offset = fec.heap.allocate(size); memory.set(offset, &data.0).expect("heap always gives a sensible offset to write"); - let returned: Result<_> = module.execute_export(method, - program.params_with_external("env", &mut fec) - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32))) - .map_err(|_| ErrorKind::Runtime.into()); + let returned = program + .params_with_external("env", &mut fec) + .map(|p| p + .add_argument(I32(offset as i32)) + .add_argument(I32(size as i32))) + .and_then(|p| module.execute_export(method, p)) + .map_err(|_| -> Error { ErrorKind::Runtime.into() })?; - if let Some(I64(r)) = returned? { + if let Some(I64(r)) = returned { memory.get(r as u32, (r >> 32) as u32 as usize) .map_err(|_| ErrorKind::Runtime.into()) } else { @@ -216,14 +218,15 @@ mod tests { let offset = fec.heap.allocate(size); memory.set(offset, data).unwrap(); - let returned = module.execute_export("test_data_in", - program.params_with_external("env", &mut fec) - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32)) - ); - assert!(returned.is_ok()); + let returned = program + .params_with_external("env", &mut fec) + .map(|p| p + .add_argument(I32(offset as i32)) + .add_argument(I32(size as i32))) + .and_then(|p| module.execute_export("test_data_in", p)) + .map_err(|_| -> Error { ErrorKind::Runtime.into() }).expect("function should be callable"); - if let Some(I64(r)) = returned.unwrap() { + if let Some(I64(r)) = returned { println!("returned {:?} ({:?}, {:?})", r, r as u32, (r >> 32) as u32 as usize); memory.get(r as u32, (r >> 32) as u32 as usize).expect("memory address should be reasonable.") } else { diff --git a/executor/src/wasm_utils.rs b/executor/src/wasm_utils.rs index 6b20885256019..577e6ab0b23d6 100644 --- a/executor/src/wasm_utils.rs +++ b/executor/src/wasm_utils.rs @@ -95,7 +95,7 @@ macro_rules! dispatch { $( stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body), )* - _ => panic!()//Err($crate::wasm_utils::Error::Trap(format!("not implemented: {}", n)).into()) + _ => panic!() } } ); @@ -155,7 +155,7 @@ pub trait AddModuleWithoutFullDependentInstance { functions: HashMap<&str, &'static [UserFunctionDescriptor]>, ) -> result::Result>, interpreter::Error>; - fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> ExecutionParams<'a, DummyUserError>; + fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result, Error>; } impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance { @@ -169,33 +169,33 @@ impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance>(); let fake_module_map = functions.into_iter() .zip(dufe_refs.into_iter()) - .map(|((dep_mod_name, functions), dufe)| { + .map(|((dep_mod_name, functions), dufe)| -> result::Result<_, interpreter::Error> { let fake_module = Arc::new( interpreter::env_native_module( - self.module(dep_mod_name).unwrap(), UserDefinedElements { + self.module(dep_mod_name).ok_or(DummyUserError)?, UserDefinedElements { executor: Some(dufe), globals: HashMap::new(), functions: ::std::borrow::Cow::from(functions), } - ).unwrap() // TODO: handle + )? ); let fake_module: Arc> = fake_module; - (dep_mod_name.into(), fake_module) + Ok((dep_mod_name.into(), fake_module)) }) - .collect::>(); + .collect::, interpreter::Error>>()?; self.add_module(name, module, Some(&fake_module_map)) } - fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> ExecutionParams<'a, DummyUserError> { - interpreter::ExecutionParams::with_external( + fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result, Error> { + Ok(interpreter::ExecutionParams::with_external( externals_name.into(), Arc::new( interpreter::env_native_module( - self.module(externals_name).unwrap(), // TODO: handle + self.module(externals_name).ok_or(DummyUserError)?, externals.into_user_defined_elements() - ).unwrap() // TODO: handle + )? ) - ) + )) } } From 5c0ec3d38801f352d5dd18fd9c0e353cd41d473c Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 8 Jan 2018 16:22:55 +0100 Subject: [PATCH 48/49] Make macro a little less unhygenic. --- runtime/support/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/support/src/lib.rs b/runtime/support/src/lib.rs index b4e1d22bdcbd4..472497d284548 100644 --- a/runtime/support/src/lib.rs +++ b/runtime/support/src/lib.rs @@ -109,10 +109,12 @@ pub fn print(value: T) { macro_rules! impl_stub { ($name:ident) => { pub mod _internal { + extern crate alloc; + #[no_mangle] pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { let input = unsafe { - super::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) + ::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) }; let output = super::$name(input); From 79ab46f750a702b81799ca8cd45897682c1dc823 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 8 Jan 2018 16:24:30 +0100 Subject: [PATCH 49/49] Add wasm files. --- .../release/runtime_polkadot.compact.wasm | Bin 0 -> 3042 bytes .../release/runtime_polkadot.wasm | Bin 0 -> 3130 bytes .../release/runtime_test.compact.wasm | Bin 0 -> 3042 bytes .../release/runtime_test.wasm | Bin 0 -> 3130 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm create mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm create mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm create mode 100644 runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6ce8afb77ce2136b71e41d74bf115b4c8f918264 GIT binary patch literal 3042 zcmcgu&2ke*5T2Qx{n5_MY6S+%fX(b;h)F?05@J%JQst(~xH#|zRM`d#B4NueS$5zM zltU`#yhM(<=A1|10dnM+LyoDO^7SmTb#N+`OQe!|x@Wql=j)#tv3yc=P$F*As%-1!iCL#+%!_yZg_!$Gx4+ z!}0!T`$bPkp3ixHFH8z`D(LM!J9yo!Xz_}JQGYlV5ie*ne+Y|HJ37iz5hp?_p|v~^ zB95f!q+i9!UT<%I^jZl!?j4RdcecmdoBg3kJEJ48PkS%=!_A}NH~ry`$ddOy7#)1K zEh+M!=F%Uej3=b4SglRJUXUKFC{fFGC%nL@ioGPeTSp+>-CAup>ElXUW#~?+YVDK| z#w#BB*hgOR@A2g_uau8V;nliR2D)-w#l{P-R}~Cn7awEPHqid7Bg+X1!giYc%euV^ zYRQzEx)ei4mN9wK;6;`DHi=bLvVh(R%(tl=l(Kfdy`su8_O2APOc_>(=|=q)rKO&# zveiZlr(>-uqq~%!F8EVWIbGqV*v!(P6+nvCyz_EElfi8vd`{VT1Ro{%S~?&DZyH6P zGo;NQ00m$SjnAvZO2cx6(HQvHdWwD+k4r6JfDk}^)Y|^`Rmr{(4Gvf;cKEV)*$T%bsNbn+XnKxty z6^#1)k%IxR0%G2-stjDh3T?cVp>UYDgIf7w1G&vWa702m&X!DptvEi@0^uxpUm$5H5Z-KBAcz{` z>K=27HpwpkIoW5j8ykb`at0`jLm5;kgGx3jt`VZfxh}#W??ojqMY^3Vm7&UTG@uAP zHe8J0asW{ZB?nl5AixO-0<5djL~l+D;r|`^vZzpYS9KI8Y1Yb(EnK@icYf}|#rf{S zrOQ_qmwcx)Taed-|M&l;AZv?~II|#YRMbgzFZ1#37bnKhUj$d#z^7c@2?fGz+{H0K{$uato>`HDnLFGlXIkeq3t&#$-Zd()=%W*0 z&Tf0l_S%`i6m|*n08CRc+~aU5FfJ}A6_hsuI){BeY`WZe3~!#aN8_^%H&1gAt{5M) z;}ALeY5Ak&zgc3k=TRd%|r~^)0e8-?BR@9SKj6#Dd zd<2X+BA&X?UFJ*O?xH0iFKGc1oRC`xR`HrkkN_wN7EW9Shd_ju#l2*V*5(%J3dG1F z9dwpx5$x6Ggi9C#8t8_k;1=o!+zQj5AAE<;AL4tw9r*P?DVAO`xCBbOz@56ZUGCJS zoo_1mVpGo-LIvBork>B6kZ9*jfURRfK9z>9La%MegA^u&))^|n*@i>YV=gz;0cja( zgJgy{BB=>6N=(2U*99BHb-|}ZH(`v>O&AJv6FwkXXeOeyaBX}m_2jgpgcs6#yhZa4 zQ)tNijH)$1!BiEoF^zCW{B+QGl1U)RJn}_)+z(!dcNi7}MFb9ajPMZ`Zg_w3ODw#< z0Adaq6jT|R1jAA_1P=a!R}5*Re?%w*;Vztgol@7t{o%pUSdcgR+5S!sPcM`4l7_iu z(&i+0Z;1)9lFPxRV6{hvPKkOfV-P@@I{U%I`*R$V$3Fe(Jv1qLR T&JLgG3A7Ybp7PlGXgK~GM3S9O literal 0 HcmV?d00001 diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm new file mode 100644 index 0000000000000000000000000000000000000000..9fe583e7e40eeab56bf09d407ddd10c4e9070248 GIT binary patch literal 3130 zcmcguPj6dA5Z~SR*Kc>9pWC!?+9uuinwAu`P@trUUNV)^sE2+4B%~y5++sI&BIhO0 z12qT4nLD3>D;F+&gnj^yJ#yfPIPsf(wH>4XS?99&W&i>}luGl>qN+E>2k?hEc z*a;?+iJV}FKQ)0Mga8C%`D#Z9^@B`<-smLleN}Hi8~1ubFuTO;^IpBZyT5<%bhqy9 zZ6DPKyA$dOI`JtZ_YA)yvpB}z!G_=>~4TlFOf3xWgk9u{Z(qfgvaeq{cfY)d< zkJ;H)VUV>%6bmV(5@9G0g@^(vI>~2gJnRh*#xIpHb?>O&-rKErxBDZ}>Wq)UKIuL0 zkG79TU-n0PB4b_l%On_he=LjGk34>Rh<4}?U$msieqLyOCuKCDu#D8k^y>tLgEb{8 zxoL$H7?qKeWcI57q`P0KEh}AAhDK?+Q>ZGmN(k)~4_)K}r}+2ya$!p4qCz;eX_bbp ztjkC{;lg!=g|Ul^FluX9|Jjkngal!`klc%^y$)(f7cq4yf{iQ^auk9WW#-x>)>Xj* zdLuC3reaXY%DVQNDvHF}LeMIu(Du`f`7KI{cxuY4HdZ(tTa_u@rEIS7hoEArVW-%f zrryef6l^%_{05@k5&#ttr5=ktPWedWcNW00!I`KH$bX3+=xMyLTDOJ_zpx9F|kdVzR&tM0|dn zjtPPu_~Wuq3L}&pK(sDpx2!J~5O68|A}B|$qAUl9V2GpEO=WWi7yb|Y+4|Q8scgnbBZ>}F8?{%Ph~eY2G`{bP#F6% zC{YHbY*IW#h#Kd*2!p&A6s(GLo2g1)WjGp81Rh&9LU1{ND20LpEI<(87z6uY3RR|4Ttu1|@N3 zK~|`!S#_`S@y#c*#n4{_SJ}WNT-^xkNtM`TbLGs%wBLlQTT+uKoE-@&7MZYVD@w8W z$8a7M+`{lK`X(}$^QS~a3-gh7NccgSB4s%)xE&z<5QUCA1N{~RH8f42k_=`)z%1u3 zjsfx?dnfnIk}Q_F!;SJ(>%3+F%)ISgqZ~@_&44+(?X8+?X9AO(705j>O~r7J!==Eu zSZ|b5)(Gev_IbDIa_2F;IctyFrCWB979d>HE@Hu3?;>o^F-fabP%(nK-zIPLHqgPK@VpRZ%&8&vKBV9XJ5)P?OTU+QKbEdhB+OOW7% z+(MwmI+q{;P!cShxC{<~@GXmbNsZQKm+1<`$fD5Otk5#ptIY{lFa$KP^+~}k)D5^5 zCOw|G17>wyxeUedS(3bVwWx-ebt)P-4WD)?ek&lh|Jn}w#H&ugD(=d_2d zqkTRVny$jGt;vDp+K1L^D!`eBL(^j})6@ZJX=;O{nm8hf_A!dJ#~jxMFNW)aPl;~A z7@?am6zC>=K(x?Q1RMU^_*#aOc}MXtq_=pB<{hTckog%^h5Q6lWx$Jt2xq`ghY(LP z2_%_EzDO7H&%eW<3=|PK+%duhT)6)I#V;}c{sM>@WH3-gFdv5VVWeXy6sjR``zO3@ zDAeKi2#_G$kf&d?)YVacba-3~ayoo^u-C(bOsA~UFt>CveQ48Not^CN_wlwp7#|7T zZ8~Sg)3Vn>x1RM!{i82>d!?XXg--A&cLy)Pyb(GQ&8olgF=loGjYb!99$O!e>c0R6 CpR&{d literal 0 HcmV?d00001 diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6ce8afb77ce2136b71e41d74bf115b4c8f918264 GIT binary patch literal 3042 zcmcgu&2ke*5T2Qx{n5_MY6S+%fX(b;h)F?05@J%JQst(~xH#|zRM`d#B4NueS$5zM zltU`#yhM(<=A1|10dnM+LyoDO^7SmTb#N+`OQe!|x@Wql=j)#tv3yc=P$F*As%-1!iCL#+%!_yZg_!$Gx4+ z!}0!T`$bPkp3ixHFH8z`D(LM!J9yo!Xz_}JQGYlV5ie*ne+Y|HJ37iz5hp?_p|v~^ zB95f!q+i9!UT<%I^jZl!?j4RdcecmdoBg3kJEJ48PkS%=!_A}NH~ry`$ddOy7#)1K zEh+M!=F%Uej3=b4SglRJUXUKFC{fFGC%nL@ioGPeTSp+>-CAup>ElXUW#~?+YVDK| z#w#BB*hgOR@A2g_uau8V;nliR2D)-w#l{P-R}~Cn7awEPHqid7Bg+X1!giYc%euV^ zYRQzEx)ei4mN9wK;6;`DHi=bLvVh(R%(tl=l(Kfdy`su8_O2APOc_>(=|=q)rKO&# zveiZlr(>-uqq~%!F8EVWIbGqV*v!(P6+nvCyz_EElfi8vd`{VT1Ro{%S~?&DZyH6P zGo;NQ00m$SjnAvZO2cx6(HQvHdWwD+k4r6JfDk}^)Y|^`Rmr{(4Gvf;cKEV)*$T%bsNbn+XnKxty z6^#1)k%IxR0%G2-stjDh3T?cVp>UYDgIf7w1G&vWa702m&X!DptvEi@0^uxpUm$5H5Z-KBAcz{` z>K=27HpwpkIoW5j8ykb`at0`jLm5;kgGx3jt`VZfxh}#W??ojqMY^3Vm7&UTG@uAP zHe8J0asW{ZB?nl5AixO-0<5djL~l+D;r|`^vZzpYS9KI8Y1Yb(EnK@icYf}|#rf{S zrOQ_qmwcx)Taed-|M&l;AZv?~II|#YRMbgzFZ1#37bnKhUj$d#z^7c@2?fGz+{H0K{$uato>`HDnLFGlXIkeq3t&#$-Zd()=%W*0 z&Tf0l_S%`i6m|*n08CRc+~aU5FfJ}A6_hsuI){BeY`WZe3~!#aN8_^%H&1gAt{5M) z;}ALeY5Ak&zgc3k=TRd%|r~^)0e8-?BR@9SKj6#Dd zd<2X+BA&X?UFJ*O?xH0iFKGc1oRC`xR`HrkkN_wN7EW9Shd_ju#l2*V*5(%J3dG1F z9dwpx5$x6Ggi9C#8t8_k;1=o!+zQj5AAE<;AL4tw9r*P?DVAO`xCBbOz@56ZUGCJS zoo_1mVpGo-LIvBork>B6kZ9*jfURRfK9z>9La%MegA^u&))^|n*@i>YV=gz;0cja( zgJgy{BB=>6N=(2U*99BHb-|}ZH(`v>O&AJv6FwkXXeOeyaBX}m_2jgpgcs6#yhZa4 zQ)tNijH)$1!BiEoF^zCW{B+QGl1U)RJn}_)+z(!dcNi7}MFb9ajPMZ`Zg_w3ODw#< z0Adaq6jT|R1jAA_1P=a!R}5*Re?%w*;Vztgol@7t{o%pUSdcgR+5S!sPcM`4l7_iu z(&i+0Z;1)9lFPxRV6{hvPKkOfV-P@@I{U%I`*R$V$3Fe(Jv1qLR T&JLgG3A7Ybp7PlGXgK~GM3S9O literal 0 HcmV?d00001 diff --git a/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm b/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm new file mode 100644 index 0000000000000000000000000000000000000000..9fe583e7e40eeab56bf09d407ddd10c4e9070248 GIT binary patch literal 3130 zcmcguPj6dA5Z~SR*Kc>9pWC!?+9uuinwAu`P@trUUNV)^sE2+4B%~y5++sI&BIhO0 z12qT4nLD3>D;F+&gnj^yJ#yfPIPsf(wH>4XS?99&W&i>}luGl>qN+E>2k?hEc z*a;?+iJV}FKQ)0Mga8C%`D#Z9^@B`<-smLleN}Hi8~1ubFuTO;^IpBZyT5<%bhqy9 zZ6DPKyA$dOI`JtZ_YA)yvpB}z!G_=>~4TlFOf3xWgk9u{Z(qfgvaeq{cfY)d< zkJ;H)VUV>%6bmV(5@9G0g@^(vI>~2gJnRh*#xIpHb?>O&-rKErxBDZ}>Wq)UKIuL0 zkG79TU-n0PB4b_l%On_he=LjGk34>Rh<4}?U$msieqLyOCuKCDu#D8k^y>tLgEb{8 zxoL$H7?qKeWcI57q`P0KEh}AAhDK?+Q>ZGmN(k)~4_)K}r}+2ya$!p4qCz;eX_bbp ztjkC{;lg!=g|Ul^FluX9|Jjkngal!`klc%^y$)(f7cq4yf{iQ^auk9WW#-x>)>Xj* zdLuC3reaXY%DVQNDvHF}LeMIu(Du`f`7KI{cxuY4HdZ(tTa_u@rEIS7hoEArVW-%f zrryef6l^%_{05@k5&#ttr5=ktPWedWcNW00!I`KH$bX3+=xMyLTDOJ_zpx9F|kdVzR&tM0|dn zjtPPu_~Wuq3L}&pK(sDpx2!J~5O68|A}B|$qAUl9V2GpEO=WWi7yb|Y+4|Q8scgnbBZ>}F8?{%Ph~eY2G`{bP#F6% zC{YHbY*IW#h#Kd*2!p&A6s(GLo2g1)WjGp81Rh&9LU1{ND20LpEI<(87z6uY3RR|4Ttu1|@N3 zK~|`!S#_`S@y#c*#n4{_SJ}WNT-^xkNtM`TbLGs%wBLlQTT+uKoE-@&7MZYVD@w8W z$8a7M+`{lK`X(}$^QS~a3-gh7NccgSB4s%)xE&z<5QUCA1N{~RH8f42k_=`)z%1u3 zjsfx?dnfnIk}Q_F!;SJ(>%3+F%)ISgqZ~@_&44+(?X8+?X9AO(705j>O~r7J!==Eu zSZ|b5)(Gev_IbDIa_2F;IctyFrCWB979d>HE@Hu3?;>o^F-fabP%(nK-zIPLHqgPK@VpRZ%&8&vKBV9XJ5)P?OTU+QKbEdhB+OOW7% z+(MwmI+q{;P!cShxC{<~@GXmbNsZQKm+1<`$fD5Otk5#ptIY{lFa$KP^+~}k)D5^5 zCOw|G17>wyxeUedS(3bVwWx-ebt)P-4WD)?ek&lh|Jn}w#H&ugD(=d_2d zqkTRVny$jGt;vDp+K1L^D!`eBL(^j})6@ZJX=;O{nm8hf_A!dJ#~jxMFNW)aPl;~A z7@?am6zC>=K(x?Q1RMU^_*#aOc}MXtq_=pB<{hTckog%^h5Q6lWx$Jt2xq`ghY(LP z2_%_EzDO7H&%eW<3=|PK+%duhT)6)I#V;}c{sM>@WH3-gFdv5VVWeXy6sjR``zO3@ zDAeKi2#_G$kf&d?)YVacba-3~ayoo^u-C(bOsA~UFt>CveQ48Not^CN_wlwp7#|7T zZ8~Sg)3Vn>x1RM!{i82>d!?XXg--A&cLy)Pyb(GQ&8olgF=loGjYb!99$O!e>c0R6 CpR&{d literal 0 HcmV?d00001