Merge branch 'master' into rh-candidate-agreement-glue

This commit is contained in:
Robert Habermeier
2018-01-10 22:02:57 +01:00
44 changed files with 1087 additions and 611 deletions
+2
View File
@@ -1,3 +1,5 @@
/target/
**/*.rs.bk
*.swp
runtime/**/target/
**/._*
+17 -3
View File
@@ -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"
+4 -1
View File
@@ -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"
]
+1 -1
View File
@@ -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" }
+2 -2
View File
@@ -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();
+18 -8
View File
@@ -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 })
}
}
-38
View File
@@ -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!()
}
}
-64
View File
@@ -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!()
}
}
-156
View File
@@ -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"
);
}
}
-31
View File
@@ -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!()
}
}
@@ -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"
@@ -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()
}
+248
View File
@@ -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);
}
}
+207
View File
@@ -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()
)
}
+4
View File
@@ -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>);
+1 -1
View File
@@ -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" }
+1 -1
View File
@@ -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;
+8 -8
View File
@@ -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)
}
}
+6 -5
View File
@@ -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(_)), _))
)
}
+33
View File
@@ -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",
]
+8
View File
@@ -0,0 +1,8 @@
[workspace]
members = [
"test",
"polkadot",
]
[profile.release]
panic = "abort"
+11
View File
@@ -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
+6
View File
@@ -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
+10
View File
@@ -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" }
+36
View File
@@ -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()
}
+18
View File
@@ -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 = []
+12
View File
@@ -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.
+30
View File
@@ -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;
+15
View File
@@ -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 = []
+12
View File
@@ -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.
+46
View File
@@ -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);
}
+11
View File
@@ -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 = []
+125
View File
@@ -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)
}
}
}
}
+10
View File
@@ -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" }
+36
View File
@@ -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()
}
+1
View File
@@ -11,3 +11,4 @@ keccak-hash = "0.1.0"
patricia-trie = "0.1.0"
memorydb = "0.1.1"
triehash = "0.1"
byteorder = "1.1"
+8 -28
View File
@@ -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,
}
}
+12 -109
View File
@@ -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);
}
}
+93 -145
View File
@@ -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, key: &[u8]) -> Option<&[u8]> {
self.storage.get(key).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[..])
}
#[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))
fn storage(&self, key: &[u8]) -> Option<&[u8]> {
self.prospective.storage(key)
.or_else(|| self.committed.storage(key))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
}
fn storage(&self, address: &Address, key: &H256) -> Option<&[u8]> {
self.prospective.storage(address, key)
.or_else(|| self.committed.storage(address, 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>;
fn storage(&self, key: &[u8]) -> 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>;
/// 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());
}
#[derive(Debug, Default)]
struct TestExternalities {
storage: HashMap<Vec<u8>, Vec<u8>>,
}
impl Externalities for TestExternalities {
type Error = u8;
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> {
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
}
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
self.storage.insert(key, value);
}
}
#[test]
fn overlayed_code_works() {
let mut overlayed = OverlayedChanges::default();
fn validators_call_works() {
let mut ext = TestExternalities::default();
let addr = Address::random();
assert_eq!(ext.validators(), Ok(vec![]));
assert!(overlayed.code(&addr).is_none());
ext.set_storage(b"\0validator_count".to_vec(), vec![]);
assert_eq!(ext.validators(), Ok(vec![]));
overlayed.set_code(addr, vec![1, 2, 3]);
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.commit_prospective();
assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]);
ext.set_storage(b"\0validator".to_vec(), b"first".to_vec());
assert_eq!(ext.validators(), Ok(vec![&b"first"[..]]));
overlayed.set_code(addr, vec![]);
assert!(overlayed.code(&addr).is_none());
overlayed.discard_prospective();
assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]);
overlayed.set_code(addr, vec![]);
overlayed.commit_prospective();
assert!(overlayed.code(&addr).is_none());
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"[..]]));
}
}