diff --git a/substrate/.gitignore b/substrate/.gitignore index a2f4b3c27a017a84489de16af4a74c586e0dcc64..cd789637f93b09e130d8a682acac3a2f517f943d 100644 --- a/substrate/.gitignore +++ b/substrate/.gitignore @@ -1,3 +1,5 @@ /target/ **/*.rs.bk *.swp +runtime/**/target/ +**/._* diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 00e753f7f2694799af1060198a98bb635be48e94..356dbf4265370bfd0c837312ebc5fbcbc5498106 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -547,6 +547,16 @@ dependencies = [ "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-wasm" +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)", + "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.3.8" @@ -635,7 +645,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", ] @@ -658,11 +668,13 @@ 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)", + "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", "polkadot-serializer 0.1.0", "polkadot-state-machine 0.1.0", @@ -694,7 +706,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", ] @@ -720,6 +732,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)", @@ -1215,6 +1228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba" "checksum owning_ref 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9d52571ddcb42e9c900c901a18d8d67e393df723fcd51dd59c5b1a85d0acb6cc" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)" = "235801e9531998c4bb307f4ea6833c9f40a4cf132895219ac8c2cd25a9b310f7" "checksum parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fa12d706797d42551663426a45e2db2e0364bd1dbf6aeada87e89c5f981f43e9" "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" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 1b8fb4c30950ef7d32a5d937e662a6dfe7cfbd39..a0f6303a6782fe2e1d10aaf5606070646af4d5b0 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -12,7 +12,7 @@ members = [ "candidate-agreement", "client", "collator", - "contracts", + "executor", "primitives", "rpc", "rpc_servers", @@ -20,3 +20,6 @@ members = [ "state_machine", "validator", ] +exclude = [ + "runtime" +] diff --git a/substrate/cli/Cargo.toml b/substrate/cli/Cargo.toml index f19977785ffb15cf7c969e03b4cea070510deb52..64305642bfaa966b98bdc3ffe1f267224655e69c 100644 --- a/substrate/cli/Cargo.toml +++ b/substrate/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/substrate/cli/src/lib.rs b/substrate/cli/src/lib.rs index dfd708045120829f409ae899bfca323a59ec0d46..73168b73b41381fc36af27fa32b62c4834118e7b 100644 --- a/substrate/cli/src/lib.rs +++ b/substrate/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<I, T>(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/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index 642dab79e2d0e0a804b77239aeec0eb4dc97519b..242e12765e6bd2e6c5462243979e34c3bf0f00d4 100644 --- a/substrate/client/src/lib.rs +++ b/substrate/client/src/lib.rs @@ -26,8 +26,8 @@ extern crate error_chain; pub mod error; -use primitives::{block, Address, H256}; -use primitives::contract::{CallData, OutData, StorageData}; +use primitives::{block}; +use primitives::contract::{CallData, StorageKey, StorageData}; use state_machine::backend::Backend; use self::error::ResultExt; @@ -44,6 +44,14 @@ pub trait Blockchain { fn header(&self, hash: &block::HeaderHash) -> Result<Option<block::Header>, Self::Error>; } +/// Information regarding the result of a call. +pub struct CallResult { + /// The data that was returned from the call. + pub return_data: Vec<u8>, + /// The changes made to the state by the call. + pub changes: state_machine::OverlayedChanges, +} + /// Polkadot Client #[derive(Debug)] pub struct Client<B, E> { @@ -72,25 +80,27 @@ impl<B, E> Client<B, E> 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<StorageData> { + pub fn storage(&self, hash: &block::HeaderHash, key: &StorageKey) -> error::Result<StorageData> { self.state_at(hash)? - .storage(address, key) + .storage(&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<OutData> { + /// + /// No changes are made. + pub fn call(&self, hash: &block::HeaderHash, method: &str, call_data: &CallData) -> error::Result<CallResult> { 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, - address, method, call_data, - )?) + )?; + Ok(CallResult { return_data: vec![], changes }) } } diff --git a/substrate/contracts/src/auth.rs b/substrate/contracts/src/auth.rs deleted file mode 100644 index a244526ddd83c2a7f7e744f8a13817bc8cac5cfd..0000000000000000000000000000000000000000 --- a/substrate/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 <http://www.gnu.org/licenses/>. - -use primitives::Address; -use state_machine::StaticExternalities; - -use error::Result; -use executor::RustExecutor; - -/// Data and some sort of Authentication Data -type DataAndAuth = (Vec<u8>, Vec<u8>); - -/// 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<E: StaticExternalities<RustExecutor>>(&self, _ext: &E, _data: DataAndAuth) -> Result<Option<Address>> { - unimplemented!() - } -} diff --git a/substrate/contracts/src/balances.rs b/substrate/contracts/src/balances.rs deleted file mode 100644 index d5e3fd6b662fbb1c7f847bee2c7bd8b98defdbb0..0000000000000000000000000000000000000000 --- a/substrate/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 <http://www.gnu.org/licenses/>. - -use primitives::Address; -use primitives::uint::U256; -use state_machine::{Externalities, StaticExternalities}; - -use error::Result; -use 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<u8>, -} - -/// Balances contract rust implementation. -#[derive(Debug, Default)] -pub struct Contract; -impl Contract { - /// Returns a balance of given address. - pub fn balance_of<E: StaticExternalities<RustExecutor>>(&self, _ext: &E, _data: Address) -> Result<U256> { - unimplemented!() - } - - /// Returns the next nonce to authorize the transfer from given address. - pub fn next_nonce<E: StaticExternalities<RustExecutor>>(&self, _ext: &E, _data: Address) -> Result<U256> { - unimplemented!() - } - - /// Checks preconditions for transfer. - /// Should verify: - /// - signature - /// - replay protection - /// - enough balance - pub fn transfer_preconditions<E: StaticExternalities<RustExecutor>>(&self, _db: &E, _data: Transfer) -> Result<bool> { - unimplemented!() - } - - /// Perform a transfer. - /// This should first make sure that precondtions are satisfied and later perform the transfer. - pub fn transfer<E: Externalities<RustExecutor>>(&self, _ext: &mut E, _data: Transfer) -> Result<bool> { - unimplemented!() - } -} diff --git a/substrate/contracts/src/executor.rs b/substrate/contracts/src/executor.rs deleted file mode 100644 index dde20b42576fe0e6c8f387a272729a97c676cc9e..0000000000000000000000000000000000000000 --- a/substrate/contracts/src/executor.rs +++ /dev/null @@ -1,156 +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 <http://www.gnu.org/licenses/>. - -//! 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 RustExecutor { - const AUTH: u8 = 1; - const BALANCES: u8 = 2; - const VALIDATOR_SET: u8 = 3; -} - -impl CodeExecutor for RustExecutor { - type Error = Error; - - fn call_static<E: StaticExternalities<Self>>( - &self, - ext: &E, - code: &[u8], - method: &str, - data: &CallData, - ) -> Result<OutData> { - 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<E: Externalities<Self>>( - &self, - ext: &mut E, - code: &[u8], - method: &str, - data: &CallData, - ) -> Result<OutData> { - 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])), - })) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use primitives::Address; - use primitives::hash::H256; - - #[derive(Debug, Default)] - struct TestExternalities; - impl Externalities<RustExecutor> for TestExternalities { - fn set_storage(&mut self, _key: H256, _value: Vec<u8>) { - unimplemented!() - } - - fn call(&mut self, _address: &Address, _method: &str, _data: &CallData) -> Result<OutData> { - unimplemented!() - } - } - - impl StaticExternalities<RustExecutor> for TestExternalities { - type Error = Error; - - fn storage(&self, _key: &H256) -> Result<&[u8]> { - unimplemented!() - } - - fn call_static(&self, _address: &Address, _method: &str, _data: &CallData) -> Result<OutData> { - unimplemented!() - } - } - - #[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 - let mut ext = TestExternalities::default(); - let executor = RustExecutor::default(); - - assert_matches!( - *executor.call(&mut ext, &[2], "any", &CallData(vec![])).unwrap_err().kind(), - ErrorKind::MethodNotFound(ref method) if &*method == "any" - ); - } -} diff --git a/substrate/contracts/src/validator_set.rs b/substrate/contracts/src/validator_set.rs deleted file mode 100644 index 7462781c6b73a43b8102cdd41d5cf63475470d05..0000000000000000000000000000000000000000 --- a/substrate/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 <http://www.gnu.org/licenses/>. - -use primitives::Address; -use state_machine::StaticExternalities; - -use error::Result; -use executor::RustExecutor; - -/// Harcoded validator set contract. -#[derive(Debug, Default)] -pub struct Contract; -impl Contract { - /// Returns current validator set. - pub fn validator_set<E: StaticExternalities<RustExecutor>>(&self, _db: &E, _data: ()) -> Result<Vec<Address>> { - unimplemented!() - } -} diff --git a/substrate/contracts/Cargo.toml b/substrate/executor/Cargo.toml similarity index 85% rename from substrate/contracts/Cargo.toml rename to substrate/executor/Cargo.toml index 84d52c87f83b775f272e91600fcff84242aa41ce..8aa72f4327b81386aaaea7af82081ca4140288f9 100644 --- a/substrate/contracts/Cargo.toml +++ b/substrate/executor/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "polkadot-contracts" +name = "polkadot-executor" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] @@ -10,6 +10,8 @@ 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.15.0" +byteorder = "1.1" [dev-dependencies] assert_matches = "1.1" diff --git a/substrate/contracts/src/error.rs b/substrate/executor/src/error.rs similarity index 78% rename from substrate/contracts/src/error.rs rename to substrate/executor/src/error.rs index 04e3144ed66ba4382f8b67d3b344c0c14d9d70b1..4fd0ec0604e450d3ce0b03ac5cfa3a3fba076781 100644 --- a/substrate/contracts/src/error.rs +++ b/substrate/executor/src/error.rs @@ -42,5 +42,23 @@ error_chain! { description("externalities failure"), 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"), + display("Invalid type returned (should be u64)"), + } + + /// Runtime failed. + Runtime { + description("runtime failure"), + display("Runtime error"), + } } } diff --git a/substrate/contracts/src/lib.rs b/substrate/executor/src/lib.rs similarity index 67% rename from substrate/contracts/src/lib.rs rename to substrate/executor/src/lib.rs index 6226edf4fcc8a6fff5bdd7df927b19e3f08bbc84..8bf7f42b623fe6fca49ada796a8a40409c9a9d9e 100644 --- a/substrate/contracts/src/lib.rs +++ b/substrate/executor/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,22 @@ 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; +extern crate byteorder; #[macro_use] extern crate error_chain; -#[macro_use] -extern crate serde_derive; #[cfg(test)] -#[macro_use] extern crate assert_matches; -mod auth; -mod balances; -mod validator_set; +#[macro_use] +mod wasm_utils; +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/substrate/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs new file mode 100644 index 0000000000000000000000000000000000000000..f52b2cbf715e15a7126b7320a5a6103c614a00f2 --- /dev/null +++ b/substrate/executor/src/wasm_executor.rs @@ -0,0 +1,248 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. + +//! 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 primitives::contract::CallData; +use state_machine::{Externalities, CodeExecutor}; +use error::{Error, ErrorKind, Result}; +use wasm_utils::{MemoryInstance, UserDefinedElements, + AddModuleWithoutFullDependentInstance}; + +struct Heap { + end: u32, +} + +impl Heap { + fn new() -> Self { + Heap { + end: 1024, + } + } + fn allocate(&mut self, size: u32) -> u32 { + let r = self.end; + self.end += size; + r + } + fn deallocate(&mut self, _offset: u32) { + } +} + +struct FunctionExecutor<'e, E: Externalities + 'e> { + heap: Heap, + memory: Arc<MemoryInstance>, + ext: &'e mut E, +} + +impl<'e, E: Externalities> FunctionExecutor<'e, E> { + fn new(m: &Arc<MemoryInstance>, e: &'e mut E) -> Self { + FunctionExecutor { + heap: Heap::new(), + memory: Arc::clone(m), + ext: e, + } + } +} + +trait WritePrimitive<T: Sized> { + fn write_primitive(&self, offset: u32, t: T); +} + +impl WritePrimitive<u32> 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>, + 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_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); + 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.clear(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); + r + }, + ext_free(addr: *mut u8) => { + this.heap.deallocate(addr); + println!("free {}", addr) + }, + 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); + } + }, + 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; + let _ = this.memory.set(offset, &value); + (offset, value.len() as u32) + } else { (0, 0) } + } else { (0, 0) }; + + this.memory.write_primitive(written_out, written); + offset as u32 + } + => <'e, E: Externalities + 'e> +); + +/// Wasm rust executor for contracts. +/// +/// Executes the provided code in a sandboxed wasm runtime. +#[derive(Debug, Default)] +pub struct WasmExecutor; + +impl CodeExecutor for WasmExecutor { + type Error = Error; + + fn call<E: Externalities>( + &self, + ext: &mut E, + code: &[u8], + method: &str, + data: &CallData, + ) -> Result<Vec<u8>> { + // 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.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::<E>::SIGNATURES]).expect("runtime signatures always provided; qed"); + + 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); + memory.set(offset, &data.0).expect("heap always gives a sensible offset to write"); + + 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 { + memory.get(r as u32, (r >> 32) as u32 as usize) + .map_err(|_| ErrorKind::Runtime.into()) + } else { + Err(ErrorKind::InvalidReturn.into()) + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[derive(Debug, Default)] + struct TestExternalities { + storage: HashMap<Vec<u8>, Vec<u8>>, + } + impl Externalities for TestExternalities { + type Error = Error; + + fn storage(&self, key: &[u8]) -> Result<&[u8]> { + Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) + } + + fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) { + self.storage.insert(key, value); + } + } + + #[test] + fn should_pass_externalities_at_call() { + let mut ext = TestExternalities::default(); + ext.set_storage(b"\0code".to_vec(), b"The code".to_vec()); + + let program = ProgramInstance::new().unwrap(); + + 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::<TestExternalities>::SIGNATURES]).expect("Failed to initialize module"); + + let output = { + 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); + memory.set(offset, data).unwrap(); + + 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 { + 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(), + b"input".to_vec() => b"Hello world".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.storage); + } +} diff --git a/substrate/executor/src/wasm_utils.rs b/substrate/executor/src/wasm_utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..577e6ab0b23d64ea1be309c3602f470fa4a2c176 --- /dev/null +++ b/substrate/executor/src/wasm_utils.rs @@ -0,0 +1,207 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. + +//! Rust implementation of Polkadot contracts. + +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}; +use parity_wasm::interpreter; + +pub type Error = interpreter::Error<DummyUserError>; +pub type MemoryInstance = interpreter::MemoryInstance<DummyUserError>; +pub type CallerContext<'a> = interpreter::CallerContext<'a, DummyUserError>; + +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<T> 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<T> 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<Option<$crate::wasm_utils::RuntimeValue>, $crate::wasm_utils::Error> { + let $objectname = self; + match name { + $( + stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body), + )* + _ => panic!() + } + } + ); +} + +#[macro_export] +macro_rules! signatures { + ( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => ( + const SIGNATURES: &'static [$crate::wasm_utils::UserFunctionDescriptor] = &[ + $( + convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ), + )* + ]; + ); +} + +pub trait IntoUserDefinedElements { + fn into_user_defined_elements(&mut self) -> UserDefinedElements<DummyUserError>; +} + +#[macro_export] +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 ),*); + } + impl $( $pre ) + $structname { + signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*); + } + 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(Self::SIGNATURES), + } + } + } + ); +} + +#[derive(Clone)] +struct DummyUserFunctionExecutor; +impl<E: UserError> interpreter::UserFunctionExecutor<E> for DummyUserFunctionExecutor { + fn execute(&mut self, _name: &str, _context: interpreter::CallerContext<E>) -> + result::Result<Option<interpreter::RuntimeValue>, interpreter::Error<E>> + { + unimplemented!() + } +} + +pub trait AddModuleWithoutFullDependentInstance { + fn add_module_by_sigs( + &self, + name: &str, + module: Module, + functions: HashMap<&str, &'static [UserFunctionDescriptor]>, + ) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>>; + + fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result<ExecutionParams<'a, DummyUserError>, Error>; +} + +impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance<DummyUserError> { + fn add_module_by_sigs( + &self, + name: &str, + module: Module, + functions: HashMap<&str, &'static [UserFunctionDescriptor]> + ) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>> { + let mut dufe = vec![DummyUserFunctionExecutor; functions.len()]; + let dufe_refs = dufe.iter_mut().collect::<Vec<_>>(); + let fake_module_map = functions.into_iter() + .zip(dufe_refs.into_iter()) + .map(|((dep_mod_name, functions), dufe)| -> result::Result<_, interpreter::Error<DummyUserError>> { + let fake_module = Arc::new( + interpreter::env_native_module( + self.module(dep_mod_name).ok_or(DummyUserError)?, UserDefinedElements { + executor: Some(dufe), + globals: HashMap::new(), + functions: ::std::borrow::Cow::from(functions), + } + )? + ); + let fake_module: Arc<interpreter::ModuleInstanceInterface<_>> = fake_module; + Ok((dep_mod_name.into(), fake_module)) + }) + .collect::<result::Result<HashMap<_, _>, interpreter::Error<DummyUserError>>>()?; + 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) -> result::Result<ExecutionParams<'a, DummyUserError>, Error> { + Ok(interpreter::ExecutionParams::with_external( + externals_name.into(), + Arc::new( + interpreter::env_native_module( + self.module(externals_name).ok_or(DummyUserError)?, + externals.into_user_defined_elements() + )? + ) + )) + } +} + +#[macro_export] +macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) +} diff --git a/substrate/primitives/src/contract.rs b/substrate/primitives/src/contract.rs index 33504d1b03f6ea90368d827ed932d0673b9b54e8..02411b73d790f8263af26da4e9dc9f21b43ac273 100644 --- a/substrate/primitives/src/contract.rs +++ b/substrate/primitives/src/contract.rs @@ -26,6 +26,10 @@ pub struct CallData(#[serde(with="bytes")] pub Vec<u8>); #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct OutData(#[serde(with="bytes")] pub Vec<u8>); +/// Contract storage key. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StorageKey(#[serde(with="bytes")] pub Vec<u8>); + /// Contract storage entry data. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct StorageData(#[serde(with="bytes")] pub Vec<u8>); diff --git a/substrate/rpc/Cargo.toml b/substrate/rpc/Cargo.toml index fb9a83583459fd9e79cf3b5635c224ce5f9bfd54..66d11d94dd4a44b691de54bee5597b377b0d920c 100644 --- a/substrate/rpc/Cargo.toml +++ b/substrate/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" } diff --git a/substrate/rpc/src/lib.rs b/substrate/rpc/src/lib.rs index 762514f7b092a39cab6a3b92c2632adbbe61b937..5061ffb56d8ba0f9361f90b4f30247d837f3a443 100644 --- a/substrate/rpc/src/lib.rs +++ b/substrate/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/substrate/rpc/src/state/mod.rs b/substrate/rpc/src/state/mod.rs index 3ba8758bd4ad72fafe892c871d994409d63a078e..113fe4849b2a8c0e9e64bef9f91c9bce5397be15 100644 --- a/substrate/rpc/src/state/mod.rs +++ b/substrate/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, 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<StorageData>; + fn storage(&self, StorageKey, block::HeaderHash) -> Result<StorageData>; /// Call a contract. #[rpc(name = "state_call")] - fn call(&self, Address, String, CallData, block::HeaderHash) -> Result<OutData>; + fn call(&self, String, CallData, block::HeaderHash) -> Result<Vec<u8>>; } } @@ -45,11 +45,11 @@ impl<B, E> StateApi for Client<B, E> 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<StorageData> { - Ok(self.storage(&block, &address, &key)?) + fn storage(&self, key: StorageKey, block: block::HeaderHash) -> Result<StorageData> { + Ok(self.storage(&block, &key)?) } - fn call(&self, address: Address, method: String, data: CallData, block: block::HeaderHash) -> Result<OutData> { - Ok(self.call(&block, &address, &method, &data)?) + fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result<Vec<u8>> { + Ok(self.call(&block, &method, &data)?.return_data) } } diff --git a/substrate/rpc/src/state/tests.rs b/substrate/rpc/src/state/tests.rs index d3e7493786746ad93b8bfb6a757dee770c238ccf..20051d4144a19c9ce558437bb792a9ae5c32b20f 100644 --- a/substrate/rpc/src/state/tests.rs +++ b/substrate/rpc/src/state/tests.rs @@ -15,28 +15,29 @@ // along with Polkadot. If not, see <http://www.gnu.org/licenses/>. 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()), + 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/substrate/runtime/Cargo.lock b/substrate/runtime/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..d134139a3c9b7771359f583b9194d6a308b32045 --- /dev/null +++ b/substrate/runtime/Cargo.lock @@ -0,0 +1,33 @@ +[[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-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", +] + +[[package]] +name = "runtime-test" +version = "0.1.0" +dependencies = [ + "runtime-support 0.1.0", +] + diff --git a/substrate/runtime/Cargo.toml b/substrate/runtime/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..0dc0216fe52ea3cddd9beaf61779d045f320a778 --- /dev/null +++ b/substrate/runtime/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +members = [ + "test", + "polkadot", +] + +[profile.release] +panic = "abort" diff --git a/substrate/runtime/build.sh b/substrate/runtime/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..c3030f99d3b171d67f9a7b6ab6353f175cfade05 --- /dev/null +++ b/substrate/runtime/build.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +cargo +nightly build --target=wasm32-unknown-unknown --release +dirs=`find * -maxdepth 0 -type d | grep -v pwasm- | grep -v support` +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/substrate/runtime/init.sh b/substrate/runtime/init.sh new file mode 100755 index 0000000000000000000000000000000000000000..02a0059a875844f4b317ad0dd09be51f837e84c0 --- /dev/null +++ b/substrate/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/substrate/runtime/polkadot/Cargo.toml b/substrate/runtime/polkadot/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a313da2a1ef882f205cf9a55d65f96085957d5c3 --- /dev/null +++ b/substrate/runtime/polkadot/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "runtime-polkadot" +version = "0.1.0" +authors = ["Parity Technologies <admin@parity.io>"] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +runtime-support = { path = "../support", version = "0.1" } diff --git a/substrate/runtime/polkadot/src/lib.rs b/substrate/runtime/polkadot/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..ec3fabb89cd15789b688715eed49a8d42231a6ec --- /dev/null +++ b/substrate/runtime/polkadot/src/lib.rs @@ -0,0 +1,36 @@ +#![no_std] +#![feature(lang_items)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] +extern crate alloc; +use alloc::vec::Vec; + +#[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<u8>) -> Vec<u8> { + print(b"set_storage" as &[u8]); + set_storage(b"input", &input); + + print(b"code" as &[u8]); + set_storage(b"code", &code()); + + print(b"set_code" as &[u8]); + set_code(&input); + + print(b"storage" as &[u8]); + let copy = storage(b"input"); + + print(b"validators" as &[u8]); + let mut v = validators(); + v.push(copy); + + print(b"set_validators" as &[u8]); + set_validators(&v.iter().map(Vec::as_slice).collect::<Vec<_>>()); + + print(b"finished!" as &[u8]); + b"all ok!".to_vec() +} diff --git a/substrate/runtime/pwasm-alloc/Cargo.toml b/substrate/runtime/pwasm-alloc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..e0a28f2c0ddb16389cf9f1497d8c267969160463 --- /dev/null +++ b/substrate/runtime/pwasm-alloc/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "pwasm-alloc" +version = "0.1.0" +authors = ["Nikolay Volf <nikvolf@gmail.com>"] +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/substrate/runtime/pwasm-alloc/README.md b/substrate/runtime/pwasm-alloc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..489d629bc10b53adfe6ef0bf229ad9aaa4e573ec --- /dev/null +++ b/substrate/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/substrate/runtime/pwasm-alloc/src/lib.rs b/substrate/runtime/pwasm-alloc/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..8bd73682057af6ebd300ca2fbf7485803ff6e4af --- /dev/null +++ b/substrate/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/substrate/runtime/pwasm-libc/Cargo.toml b/substrate/runtime/pwasm-libc/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..d3ff1f1f320e6b07f0e00b0805e24b25c3e79d2f --- /dev/null +++ b/substrate/runtime/pwasm-libc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "pwasm-libc" +version = "0.1.0" +authors = ["Sergey Pepyakin <s.pepyakin@gmail.com>"] +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/substrate/runtime/pwasm-libc/README.md b/substrate/runtime/pwasm-libc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b01c4a76707eb3bd05d4ac91e2ab851e175e08be --- /dev/null +++ b/substrate/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/substrate/runtime/pwasm-libc/src/lib.rs b/substrate/runtime/pwasm-libc/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..eed597e73b7b015b3b2cd3cd3f30f273854e3f02 --- /dev/null +++ b/substrate/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/substrate/runtime/support/Cargo.toml b/substrate/runtime/support/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..828fdab534e00f808387055f5636eb85f249c879 --- /dev/null +++ b/substrate/runtime/support/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "runtime-support" +version = "0.1.0" +authors = ["Parity Technologies <admin@parity.io>"] + +[dependencies] +pwasm-libc = { path = "../pwasm-libc", version = "0.1" } +pwasm-alloc = { path = "../pwasm-alloc", version = "0.1" } + +[features] +strict = [] diff --git a/substrate/runtime/support/src/lib.rs b/substrate/runtime/support/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..472497d2845480fc451f6acd9a0380f133ac72d5 --- /dev/null +++ b/substrate/runtime/support/src/lib.rs @@ -0,0 +1,125 @@ +#![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<u8> { + 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<u8> { + storage(b"\0code") +} + +pub fn set_code(new: &[u8]) { + set_storage(b"\0code", new) +} + +fn value_vec(mut value: usize, initial: Vec<u8>) -> Vec<u8> { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; + } + acc +} + +pub fn set_validator(index: usize, validator: &[u8]) { + set_storage(&value_vec(index, b"\0validator".to_vec()), validator); +} + +pub fn validator(index: usize) -> Vec<u8> { + 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<Vec<u8>> { + (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<T: Printable + Sized>(value: T) { + value.print(); +} + +#[macro_export] +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 { + ::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/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6ce8afb77ce2136b71e41d74bf115b4c8f918264 Binary files /dev/null and b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm differ diff --git a/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm new file mode 100644 index 0000000000000000000000000000000000000000..9fe583e7e40eeab56bf09d407ddd10c4e9070248 Binary files /dev/null and b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.wasm differ diff --git a/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm new file mode 100644 index 0000000000000000000000000000000000000000..6ce8afb77ce2136b71e41d74bf115b4c8f918264 Binary files /dev/null and b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm new file mode 100644 index 0000000000000000000000000000000000000000..9fe583e7e40eeab56bf09d407ddd10c4e9070248 Binary files /dev/null and b/substrate/runtime/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/runtime/test/Cargo.toml b/substrate/runtime/test/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..57015d26836b30a40852269797640b09636c733f --- /dev/null +++ b/substrate/runtime/test/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "runtime-test" +version = "0.1.0" +authors = ["Parity Technologies <admin@parity.io>"] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +runtime-support = { path = "../support", version = "0.1" } diff --git a/substrate/runtime/test/src/lib.rs b/substrate/runtime/test/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..ec3fabb89cd15789b688715eed49a8d42231a6ec --- /dev/null +++ b/substrate/runtime/test/src/lib.rs @@ -0,0 +1,36 @@ +#![no_std] +#![feature(lang_items)] +#![cfg_attr(feature = "strict", deny(warnings))] + +#![feature(alloc)] +extern crate alloc; +use alloc::vec::Vec; + +#[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<u8>) -> Vec<u8> { + print(b"set_storage" as &[u8]); + set_storage(b"input", &input); + + print(b"code" as &[u8]); + set_storage(b"code", &code()); + + print(b"set_code" as &[u8]); + set_code(&input); + + print(b"storage" as &[u8]); + let copy = storage(b"input"); + + print(b"validators" as &[u8]); + let mut v = validators(); + v.push(copy); + + print(b"set_validators" as &[u8]); + set_validators(&v.iter().map(Vec::as_slice).collect::<Vec<_>>()); + + print(b"finished!" as &[u8]); + b"all ok!".to_vec() +} diff --git a/substrate/state_machine/Cargo.toml b/substrate/state_machine/Cargo.toml index cdcec9d6a014ec217ca287371090a50b1e744a40..a0d25c821cb4b5b5dc6aba7a3673b435df40b777 100644 --- a/substrate/state_machine/Cargo.toml +++ b/substrate/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/substrate/state_machine/src/backend.rs b/substrate/state_machine/src/backend.rs index c7f694712e24e4ab8a6d70b4aa6e28b3dd6498f7..ee6b0bbe8027b27081daaf75a150604702caa7c4 100644 --- a/substrate/state_machine/src/backend.rs +++ b/substrate/state_machine/src/backend.rs @@ -17,8 +17,6 @@ //! State machine backends. These manage the code and storage of contracts. use std::{error, fmt}; - -use primitives::Address; use primitives::hash::H256; use triehash::sec_trie_root; @@ -26,8 +24,6 @@ use super::{Update, MemoryState}; /// Output of a commit. pub struct Committed { - /// Root of the code tree after changes committed. - pub code_tree_root: H256, /// Root of the storage tree after changes committed. pub storage_tree_root: H256, } @@ -38,11 +34,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, key: &[u8]) -> Result<&[u8], Self::Error>; /// Commit updates to the backend and get new state. fn commit<I>(&mut self, changes: I) -> Committed @@ -74,12 +67,8 @@ pub struct InMemory { 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, key: &[u8]) -> Result<&[u8], Void> { + Ok(self.inner.storage(key).unwrap_or(&[])) } fn commit<I>(&mut self, changes: I) -> Committed @@ -88,22 +77,13 @@ impl Backend for InMemory { self.inner.update(changes); // fully recalculate trie roots. - - let storage_roots = self.inner.storage.iter().map(|(addr, 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()) - }).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 storage_tree_root = H256(sec_trie_root( + self.inner.storage.iter() + .map(|(k, v)| (k.to_vec(), v.clone())) + .collect() + ).0); Committed { - code_tree_root, storage_tree_root, } } diff --git a/substrate/state_machine/src/ext.rs b/substrate/state_machine/src/ext.rs index 455fc4d22ef292ee199f11446171cfc25cd54e1f..ce7b9a02680ddab3812e59da4b4204041dfdc96c 100644 --- a/substrate/state_machine/src/ext.rs +++ b/substrate/state_machine/src/ext.rs @@ -19,17 +19,16 @@ 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, OverlayedChanges}; /// Errors that can occur when interacting with the externalities. #[derive(Debug, Copy, Clone)] pub enum Error<B, E> { /// Failure to load state data from the backend. + #[allow(unused)] Backend(B), /// Failure to execute a function. + #[allow(unused)] Executor(E), } @@ -52,122 +51,26 @@ impl<B: error::Error, E: error::Error> error::Error for Error<B, E> { } /// 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<E> for Ext<'a, B, E> - where B: Backend, E: CodeExecutor +impl<'a, B: 'a> Externalities for Ext<'a, B> + where B: Backend { - type Error = Error<B::Error, E::Error>; + type Error = B::Error; - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { - match self.overlay.storage(&self.local, key) { + fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { + match self.overlay.storage(key) { Some(x) => Ok(x), - None => self.backend.storage(&self.local, key).map_err(Error::Backend) + None => self.backend.storage(key) } } - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result<OutData, Self::Error> { - 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) - } -} - -impl<'a, B: 'a, E: 'a> Externalities<E> for Ext<'a, B, E> - where B: Backend, E: CodeExecutor -{ - fn set_storage(&mut self, key: H256, value: Vec<u8>) { - self.overlay.set_storage(self.local, key, value); - } - - fn call(&mut self, address: &Address, method: &str, data: &CallData) -> Result<OutData, Self::Error> { - 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) - } -} - -// Static externalities -struct StaticExt<'a, B: 'a, E: 'a> { - overlay: &'a OverlayedChanges, - backend: &'a B, - exec: &'a E, - local: Address, -} - -impl<'a, B: 'a, E: 'a> StaticExternalities<E> for StaticExt<'a, B, E> - where B: Backend, E: CodeExecutor -{ - type Error = Error<B::Error, E::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 call_static(&self, address: &Address, method: &str, data: &CallData) -> Result<OutData, Self::Error> { - 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 set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) { + self.overlay.set_storage(key, value); } } diff --git a/substrate/state_machine/src/lib.rs b/substrate/state_machine/src/lib.rs index 9eb6039fa32104caff4dde315c9bdaeca8e5328e..58ba558322ac8b805afd15beba4fcae63e5f796e 100644 --- a/substrate/state_machine/src/lib.rs +++ b/substrate/state_machine/src/lib.rs @@ -27,76 +27,45 @@ extern crate keccak_hash; extern crate patricia_trie; extern crate triehash; +extern crate byteorder; + use std::collections::HashMap; use std::fmt; -use primitives::Address; -use primitives::contract::{CallData, OutData}; -use primitives::hash::H256; +use primitives::contract::{CallData}; 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<u8>), - /// Set code of address -- empty is deletion. - Code(Address, Vec<u8>), + /// Set storage of object at given key -- empty is deletion. + Storage(Vec<u8>, Vec<u8>), } // in-memory section of the state. #[derive(Default)] struct MemoryState { - code: HashMap<Address, Vec<u8>>, - storage: HashMap<Address, HashMap<H256, Vec<u8>>>, + storage: HashMap<Vec<u8>, Vec<u8>>, } impl MemoryState { - fn code(&self, address: &Address) -> Option<&[u8]> { - self.code.get(address).map(|v| &v[..]) - } - - fn storage(&self, address: &Address, key: &H256) -> Option<&[u8]> { - self.storage.get(address) - .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)] - fn set_code(&mut self, address: Address, code: Vec<u8>) { - self.code.insert(address, code); - } - - fn set_storage(&mut self, address: Address, key: H256, val: Vec<u8>) { - self.storage.entry(address) - .or_insert_with(HashMap::new) - .insert(key, val); + fn set_storage(&mut self, key: Vec<u8>, val: Vec<u8>) { + self.storage.insert(key, val); } fn update<I>(&mut self, changes: I) where I: IntoIterator<Item=Update> { for update in changes { match update { - Update::Storage(addr, key, val) => { + Update::Storage(key, val) => { if val.is_empty() { - let mut empty = false; - if let Some(s) = self.storage.get_mut(&addr) { - s.remove(&key); - empty = s.is_empty(); - }; - - if empty { self.storage.remove(&addr); } + self.storage.remove(&key); } else { - self.storage.entry(addr) - .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); + self.storage.insert(key, val); } } } @@ -115,43 +84,27 @@ pub struct OverlayedChanges { } 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 storage(&self, address: &Address, key: &H256) -> Option<&[u8]> { - self.prospective.storage(address, key) - .or_else(|| self.committed.storage(address, 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) }) } - #[allow(unused)] - fn set_code(&mut self, address: Address, code: Vec<u8>) { - self.prospective.set_code(address, code); - } - - fn set_storage(&mut self, address: Address, key: H256, val: Vec<u8>) { - self.prospective.set_storage(address, key, val); + fn set_storage(&mut self, key: Vec<u8>, val: Vec<u8>) { + self.prospective.set_storage(key, val); } /// Discard prospective changes to state. pub fn discard_prospective(&mut self) { - self.prospective.code.clear(); 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 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)); + .map(|(key, value)| Update::Storage(key, value)); - self.committed.update(code_updates.chain(storage_updates)); + self.committed.update(storage_updates); } } @@ -161,89 +114,76 @@ impl OverlayedChanges { pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} impl<E> Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} -/// Externalities: pinned to specific active address. -pub trait Externalities<CodeExecutor>: StaticExternalities<CodeExecutor> { - /// Read storage of current contract being called. - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { - StaticExternalities::storage(self, key) - } - - /// Set storage of current contract being called. - fn set_storage(&mut self, key: H256, value: Vec<u8>); - - /// Make a sub-call to another contract. - fn call(&mut self, address: &Address, method: &str, data: &CallData) -> Result<OutData, Self::Error>; - - /// Make a static (read-only) call to another contract. - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result<OutData, Self::Error> { - StaticExternalities::call_static(self, address, method, data) +fn value_vec(mut value: usize, initial: Vec<u8>) -> Vec<u8> { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; } + acc } -/// Static externalities: used only for read-only requests. -pub trait StaticExternalities<CodeExecutor> { +/// Externalities: pinned to specific active address. +pub trait Externalities { /// Externalities error type. type Error: Error; /// Read storage of current contract being called. - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error>; - - /// Make a static (read-only) call to another contract. - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result<OutData, Self::Error>; + fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; + + /// Set storage of current contract being called (effective immediately). + fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>); + + /// Get the current set of validators. + fn validators(&self) -> Result<Vec<&[u8]>, Self::Error> { + (0..self.storage(b"\0validator_count")?.into_iter() + .rev() + .fold(0, |acc, &i| (acc << 8) + (i as usize))) + .map(|i| self.storage(&value_vec(i, b"\0validator".to_vec()))) + .collect() + } } -/// 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<E: StaticExternalities<Self>>( - &self, - ext: &E, - code: &[u8], - method: &str, - data: &CallData, - ) -> Result<OutData, Self::Error>; - - /// Execute a contract. - fn call<E: Externalities<Self>>( + /// Call a given method in the runtime. + fn call<E: Externalities>( &self, ext: &mut E, code: &[u8], method: &str, data: &CallData, - ) -> Result<OutData, Self::Error>; + ) -> Result<Vec<u8>, Self::Error>; } /// Execute a call using the given state backend, overlayed changes, and call executor. /// /// On an error, no prospective changes are written to the overlay. +/// +/// Note: changes to code will be in place if this call is made again. For running partial +/// blocks (e.g. a transaction at a time), ensure a differrent method is used. pub fn execute<B: backend::Backend, Exec: CodeExecutor>( backend: &B, overlay: &mut OverlayedChanges, exec: &Exec, - address: &Address, method: &str, call_data: &CallData, -) -> Result<OutData, Box<Error>> { - let code = match overlay.code(address) { - Some(x) => x.to_owned(), - None => backend.code(address).map_err(|e| Box::new(e) as _)?.to_owned(), - }; +) -> Result<Vec<u8>, Box<Error>> { let result = { let mut externalities = ext::Ext { backend, - exec, - overlay: &mut *overlay, - local: *address, + overlay: &mut *overlay }; + // make a copy. + let code = externalities.storage(b"\0code").unwrap_or(&[]).to_vec(); exec.call( &mut externalities, - &code[..], + &code, method, call_data, ) @@ -263,59 +203,67 @@ pub fn execute<B: backend::Backend, Exec: CodeExecutor>( #[cfg(test)] mod tests { - use super::OverlayedChanges; - - use primitives::hash::H256; - use primitives::Address; + use std::collections::HashMap; + use super::{OverlayedChanges, Externalities}; #[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]; - assert!(overlayed.storage(&addr, &key).is_none()); + assert!(overlayed.storage(&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(key.clone(), vec![1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); overlayed.commit_prospective(); - assert_eq!(overlayed.storage(&addr, &key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); - overlayed.set_storage(addr, key, vec![]); - assert!(overlayed.storage(&addr, &key).is_none()); + overlayed.set_storage(key.clone(), vec![]); + assert!(overlayed.storage(&key).is_none()); overlayed.discard_prospective(); - assert_eq!(overlayed.storage(&addr, &key).unwrap(), &[1, 2, 3]); + assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]); - overlayed.set_storage(addr, key, vec![]); + overlayed.set_storage(key.clone(), vec![]); overlayed.commit_prospective(); - assert!(overlayed.storage(&addr, &key).is_none()); + assert!(overlayed.storage(&key).is_none()); } - #[test] - fn overlayed_code_works() { - let mut overlayed = OverlayedChanges::default(); + #[derive(Debug, Default)] + struct TestExternalities { + storage: HashMap<Vec<u8>, Vec<u8>>, + } + impl Externalities for TestExternalities { + type Error = u8; - let addr = Address::random(); + fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> { + Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) + } - assert!(overlayed.code(&addr).is_none()); + fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) { + self.storage.insert(key, value); + } + } - overlayed.set_code(addr, vec![1, 2, 3]); - assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]); + #[test] + fn validators_call_works() { + let mut ext = TestExternalities::default(); - overlayed.commit_prospective(); - assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]); + assert_eq!(ext.validators(), Ok(vec![])); - overlayed.set_code(addr, vec![]); - assert!(overlayed.code(&addr).is_none()); + ext.set_storage(b"\0validator_count".to_vec(), vec![]); + assert_eq!(ext.validators(), Ok(vec![])); - overlayed.discard_prospective(); - assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]); + ext.set_storage(b"\0validator_count".to_vec(), vec![1]); + assert_eq!(ext.validators(), Ok(vec![&[][..]])); - overlayed.set_code(addr, vec![]); - overlayed.commit_prospective(); - assert!(overlayed.code(&addr).is_none()); + 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"[..]])); } }