mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-08 00:48:01 +00:00
Merge branch 'master' into rh-candidate-agreement-glue
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
*.swp
|
||||
runtime/**/target/
|
||||
**/._*
|
||||
|
||||
Generated
+17
-3
@@ -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"
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -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(_)), _))
|
||||
)
|
||||
}
|
||||
|
||||
Generated
+33
@@ -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",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"test",
|
||||
"polkadot",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
Executable
+11
@@ -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
|
||||
Executable
+6
@@ -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
|
||||
@@ -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" }
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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 = []
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
@@ -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 = []
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 = []
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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" }
|
||||
@@ -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()
|
||||
}
|
||||
@@ -11,3 +11,4 @@ keccak-hash = "0.1.0"
|
||||
patricia-trie = "0.1.0"
|
||||
memorydb = "0.1.1"
|
||||
triehash = "0.1"
|
||||
byteorder = "1.1"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"[..]]));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user