Skip to content
Snippets Groups Projects
Commit a670208a authored by Gav Wood's avatar Gav Wood Committed by asynchronous rob
Browse files

Introduce first groundwork for Wasm executor (#27)

* Introduce first groundwork for Wasm executor.

* Remove old Rust-runtime code.

* Avoid commiting compled files.

* Add runtime precompile.

* Rename so module makes more sense.

* Further renaming.

* Ensure tests work.

* Allow bringing in of externalities.

- Add util functions/macros.
- Add uncompacted runtime.
- Add some external crates from pwasm-std for managing allocs/memory
stuff.

* Nice macros for imports.

* Allow passing in of data through allocators.

Make memcpy and malloc work.
Basic allocator.

* Can now pass in bytes to WasmExecutor.

* Additional cleanup.

* Switch usages of `OutData` to `u64`

No need to be able to return bytes anymore.

* convert to safe but extremely verbose type conversion.

@rphmeier any more concise way of doing this?

* Remove StaticExternalities distinction.

* Remove another unused use.

* Refactor wasm utils out

* Remove extraneous copies that weren't really testing anything.

* Try to use wasm 0.15

* Make it work!

* Call-time externalities working.

* Add basic externalities.

* Fix grumbles and note unwraps to be sorted.

* Test storage externality.

Unforunately had to change signatures of externalities to avoid
immutable function returning a reference. Not sure what to do about
this...

* Fix nits.

* Compile collation logic.

* Move back to refs. Yey.

* Remove "object" id for storage access.

* Fix test.

* Fix up rest of tests.

* remove unwrap.

* Expose set/get code in externalities

Also improve tests and add nice wrappers in rust-wasm.

* Add validator set.

* Introduce validator set into externalities and test.

* Add another external function.

* Remove code and validators; use storage for everything.

* Introduce validators function.

* Tests (and a fix) for the validators getter.

* Allow calls into runtime to return data.

* Remove unneeded trace.

* Make runtime printing a bit nicer.

* Create separate runtimes for testing and polkadot.

* Remove commented code.

* Use new path.

* Refactor into shared support module.

* Fix warning.

* Remove unwraps.

* Make macro a little less unhygenic.

* Add wasm files.
parent 45c3e40a
No related merge requests found
Showing
with 554 additions and 329 deletions
/target/
**/*.rs.bk
*.swp
runtime/**/target/
**/._*
......@@ -542,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.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.4.8"
......@@ -619,7 +629,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",
]
......@@ -642,11 +652,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",
......@@ -678,7 +690,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",
]
......@@ -704,6 +716,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)",
......@@ -1179,6 +1192,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.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"
......
......@@ -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"
]
......@@ -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" }
......@@ -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();
......
......@@ -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 })
}
}
// 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!()
}
}
// 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!()
}
}
// 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"
);
}
}
// 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!()
}
}
[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"
......@@ -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"),
}
}
}
......@@ -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()
}
// 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);
}
}
// 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()
)
}
......@@ -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>);
......@@ -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" }
......@@ -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;
......
......@@ -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)
}
}
......@@ -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(_)), _))
)
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment