mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-20 21:01:03 +00:00
Merge branch 'master' into rh-candidate-agreement-glue
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
/target/
|
/target/
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
*.swp
|
*.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)",
|
"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]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@@ -635,7 +645,7 @@ dependencies = [
|
|||||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"polkadot-client 0.1.0",
|
"polkadot-client 0.1.0",
|
||||||
"polkadot-contracts 0.1.0",
|
"polkadot-executor 0.1.0",
|
||||||
"polkadot-primitives 0.1.0",
|
"polkadot-primitives 0.1.0",
|
||||||
"polkadot-rpc-servers 0.1.0",
|
"polkadot-rpc-servers 0.1.0",
|
||||||
]
|
]
|
||||||
@@ -658,11 +668,13 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polkadot-contracts"
|
name = "polkadot-executor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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-primitives 0.1.0",
|
||||||
"polkadot-serializer 0.1.0",
|
"polkadot-serializer 0.1.0",
|
||||||
"polkadot-state-machine 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-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git)",
|
||||||
"jsonrpc-macros 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-client 0.1.0",
|
||||||
"polkadot-contracts 0.1.0",
|
"polkadot-executor 0.1.0",
|
||||||
"polkadot-primitives 0.1.0",
|
"polkadot-primitives 0.1.0",
|
||||||
"polkadot-state-machine 0.1.0",
|
"polkadot-state-machine 0.1.0",
|
||||||
]
|
]
|
||||||
@@ -720,6 +732,7 @@ dependencies = [
|
|||||||
name = "polkadot-state-machine"
|
name = "polkadot-state-machine"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
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)",
|
"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)",
|
"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)",
|
"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 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.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 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.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 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
|
||||||
"checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595"
|
"checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ members = [
|
|||||||
"candidate-agreement",
|
"candidate-agreement",
|
||||||
"client",
|
"client",
|
||||||
"collator",
|
"collator",
|
||||||
"contracts",
|
"executor",
|
||||||
"primitives",
|
"primitives",
|
||||||
"rpc",
|
"rpc",
|
||||||
"rpc_servers",
|
"rpc_servers",
|
||||||
@@ -20,3 +20,6 @@ members = [
|
|||||||
"state_machine",
|
"state_machine",
|
||||||
"validator",
|
"validator",
|
||||||
]
|
]
|
||||||
|
exclude = [
|
||||||
|
"runtime"
|
||||||
|
]
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ env_logger = "0.4"
|
|||||||
error-chain = "0.11"
|
error-chain = "0.11"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
polkadot-client = { path = "../client", version = "0.1" }
|
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-primitives = { path = "../primitives", version = "0.1" }
|
||||||
polkadot-rpc-servers = { path = "../rpc_servers", version = "0.1" }
|
polkadot-rpc-servers = { path = "../rpc_servers", version = "0.1" }
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate polkadot_client as client;
|
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_primitives as primitives;
|
||||||
extern crate polkadot_rpc_servers as rpc;
|
extern crate polkadot_rpc_servers as rpc;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
|
|||||||
|
|
||||||
// Create client
|
// Create client
|
||||||
let blockchain = DummyBlockchain;
|
let blockchain = DummyBlockchain;
|
||||||
let executor = contracts::executor();
|
let executor = executor::executor();
|
||||||
let client = client::Client::new(blockchain, executor);
|
let client = client::Client::new(blockchain, executor);
|
||||||
|
|
||||||
let address = "127.0.0.1:9933".parse().unwrap();
|
let address = "127.0.0.1:9933".parse().unwrap();
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ extern crate error_chain;
|
|||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
use primitives::{block, Address, H256};
|
use primitives::{block};
|
||||||
use primitives::contract::{CallData, OutData, StorageData};
|
use primitives::contract::{CallData, StorageKey, StorageData};
|
||||||
use state_machine::backend::Backend;
|
use state_machine::backend::Backend;
|
||||||
|
|
||||||
use self::error::ResultExt;
|
use self::error::ResultExt;
|
||||||
@@ -44,6 +44,14 @@ pub trait Blockchain {
|
|||||||
fn header(&self, hash: &block::HeaderHash) -> Result<Option<block::Header>, Self::Error>;
|
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
|
/// Polkadot Client
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Client<B, E> {
|
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.
|
/// 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)?
|
self.state_at(hash)?
|
||||||
.storage(address, key)
|
.storage(&key.0)
|
||||||
.map(|x| StorageData(x.to_vec()))
|
.map(|x| StorageData(x.to_vec()))
|
||||||
.chain_err(|| error::ErrorKind::Backend)
|
.chain_err(|| error::ErrorKind::Backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a call to a contract on top of state in a block of given hash.
|
/// 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 state = self.state_at(hash)?;
|
||||||
let mut changes = state_machine::OverlayedChanges::default();
|
let mut changes = state_machine::OverlayedChanges::default();
|
||||||
|
|
||||||
Ok(state_machine::execute(
|
let _ = state_machine::execute(
|
||||||
&state,
|
&state,
|
||||||
&mut changes,
|
&mut changes,
|
||||||
&self.executor,
|
&self.executor,
|
||||||
address,
|
|
||||||
method,
|
method,
|
||||||
call_data,
|
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]
|
[package]
|
||||||
name = "polkadot-contracts"
|
name = "polkadot-executor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
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" }
|
polkadot-state-machine = { path = "../state_machine" , version = "0.1" }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
parity-wasm = "0.15.0"
|
||||||
|
byteorder = "1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_matches = "1.1"
|
assert_matches = "1.1"
|
||||||
@@ -42,5 +42,23 @@ error_chain! {
|
|||||||
description("externalities failure"),
|
description("externalities failure"),
|
||||||
display("Externalities error: {}", e),
|
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.
|
//! Temporary crate for contracts implementations.
|
||||||
//!
|
//!
|
||||||
//! This will be replaced with WASM contracts stored on-chain.
|
//! 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)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
@@ -24,24 +31,22 @@ extern crate polkadot_primitives as primitives;
|
|||||||
extern crate polkadot_serializer as serializer;
|
extern crate polkadot_serializer as serializer;
|
||||||
extern crate polkadot_state_machine as state_machine;
|
extern crate polkadot_state_machine as state_machine;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
extern crate parity_wasm;
|
||||||
|
extern crate byteorder;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
|
||||||
extern crate assert_matches;
|
extern crate assert_matches;
|
||||||
|
|
||||||
mod auth;
|
#[macro_use]
|
||||||
mod balances;
|
mod wasm_utils;
|
||||||
mod validator_set;
|
mod wasm_executor;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod executor;
|
|
||||||
|
|
||||||
/// Creates new RustExecutor for contracts.
|
/// Creates new RustExecutor for contracts.
|
||||||
pub fn executor() -> executor::RustExecutor {
|
pub fn executor() -> wasm_executor::WasmExecutor {
|
||||||
executor::RustExecutor::default()
|
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)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct OutData(#[serde(with="bytes")] pub Vec<u8>);
|
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.
|
/// Contract storage entry data.
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct StorageData(#[serde(with="bytes")] pub Vec<u8>);
|
pub struct StorageData(#[serde(with="bytes")] pub Vec<u8>);
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ polkadot-state-machine = { path = "../state_machine", version = "0.1" }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_matches = "1.1"
|
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;
|
extern crate jsonrpc_macros;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate polkadot_contracts;
|
extern crate polkadot_executor;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate assert_matches;
|
extern crate assert_matches;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ mod error;
|
|||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use primitives::{block, Address, H256};
|
use primitives::{block};
|
||||||
use primitives::contract::{CallData, OutData, StorageData};
|
use primitives::contract::{CallData, StorageKey, StorageData};
|
||||||
use state_machine;
|
use state_machine;
|
||||||
|
|
||||||
use self::error::Result;
|
use self::error::Result;
|
||||||
@@ -33,11 +33,11 @@ build_rpc_trait! {
|
|||||||
pub trait StateApi {
|
pub trait StateApi {
|
||||||
/// Returns a storage entry.
|
/// Returns a storage entry.
|
||||||
#[rpc(name = "state_getStorage")]
|
#[rpc(name = "state_getStorage")]
|
||||||
fn storage(&self, Address, H256, block::HeaderHash) -> Result<StorageData>;
|
fn storage(&self, StorageKey, block::HeaderHash) -> Result<StorageData>;
|
||||||
|
|
||||||
/// Call a contract.
|
/// Call a contract.
|
||||||
#[rpc(name = "state_call")]
|
#[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,
|
B: client::Blockchain + Send + Sync + 'static,
|
||||||
E: state_machine::CodeExecutor + Send + Sync + 'static,
|
E: state_machine::CodeExecutor + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
fn storage(&self, address: Address, key: H256, block: block::HeaderHash) -> Result<StorageData> {
|
fn storage(&self, key: StorageKey, block: block::HeaderHash) -> Result<StorageData> {
|
||||||
Ok(self.storage(&block, &address, &key)?)
|
Ok(self.storage(&block, &key)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, address: Address, method: String, data: CallData, block: block::HeaderHash) -> Result<OutData> {
|
fn call(&self, method: String, data: CallData, block: block::HeaderHash) -> Result<Vec<u8>> {
|
||||||
Ok(self.call(&block, &address, &method, &data)?)
|
Ok(self.call(&block, &method, &data)?.return_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,28 +15,29 @@
|
|||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use polkadot_contracts as contracts;
|
use polkadot_executor as executor;
|
||||||
|
|
||||||
use self::error::{Error, ErrorKind};
|
use self::error::{Error, ErrorKind};
|
||||||
use test_helpers::Blockchain;
|
use test_helpers::Blockchain;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_storage() {
|
fn should_return_storage() {
|
||||||
let client = Client::new(Blockchain::default(), contracts::executor());
|
let client = Client::new(Blockchain::default(), executor::executor());
|
||||||
|
|
||||||
assert_matches!(
|
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()
|
Ok(ref x) if x.0.is_empty()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore] // TODO: [ToDr] reenable once we can properly mock the wasm executor env
|
||||||
fn should_call_contract() {
|
fn should_call_contract() {
|
||||||
// TODO [ToDr] Fix test after we are able to mock state.
|
// 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!(
|
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(_)), _))
|
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"
|
patricia-trie = "0.1.0"
|
||||||
memorydb = "0.1.1"
|
memorydb = "0.1.1"
|
||||||
triehash = "0.1"
|
triehash = "0.1"
|
||||||
|
byteorder = "1.1"
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
//! State machine backends. These manage the code and storage of contracts.
|
//! State machine backends. These manage the code and storage of contracts.
|
||||||
|
|
||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
|
||||||
use primitives::Address;
|
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use triehash::sec_trie_root;
|
use triehash::sec_trie_root;
|
||||||
|
|
||||||
@@ -26,8 +24,6 @@ use super::{Update, MemoryState};
|
|||||||
|
|
||||||
/// Output of a commit.
|
/// Output of a commit.
|
||||||
pub struct Committed {
|
pub struct Committed {
|
||||||
/// Root of the code tree after changes committed.
|
|
||||||
pub code_tree_root: H256,
|
|
||||||
/// Root of the storage tree after changes committed.
|
/// Root of the storage tree after changes committed.
|
||||||
pub storage_tree_root: H256,
|
pub storage_tree_root: H256,
|
||||||
}
|
}
|
||||||
@@ -38,11 +34,8 @@ pub trait Backend {
|
|||||||
/// An error type when fetching data is not possible.
|
/// An error type when fetching data is not possible.
|
||||||
type Error: super::Error;
|
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.
|
/// 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.
|
/// Commit updates to the backend and get new state.
|
||||||
fn commit<I>(&mut self, changes: I) -> Committed
|
fn commit<I>(&mut self, changes: I) -> Committed
|
||||||
@@ -74,12 +67,8 @@ pub struct InMemory {
|
|||||||
impl Backend for InMemory {
|
impl Backend for InMemory {
|
||||||
type Error = Void;
|
type Error = Void;
|
||||||
|
|
||||||
fn code(&self, address: &Address) -> Result<&[u8], Void> {
|
fn storage(&self, key: &[u8]) -> Result<&[u8], Void> {
|
||||||
Ok(self.inner.code(address).unwrap_or(&[]))
|
Ok(self.inner.storage(key).unwrap_or(&[]))
|
||||||
}
|
|
||||||
|
|
||||||
fn storage(&self, address: &Address, key: &H256) -> Result<&[u8], Void> {
|
|
||||||
Ok(self.inner.storage(address, key).unwrap_or(&[]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit<I>(&mut self, changes: I) -> Committed
|
fn commit<I>(&mut self, changes: I) -> Committed
|
||||||
@@ -88,22 +77,13 @@ impl Backend for InMemory {
|
|||||||
self.inner.update(changes);
|
self.inner.update(changes);
|
||||||
|
|
||||||
// fully recalculate trie roots.
|
// fully recalculate trie roots.
|
||||||
|
let storage_tree_root = H256(sec_trie_root(
|
||||||
let storage_roots = self.inner.storage.iter().map(|(addr, storage)| {
|
self.inner.storage.iter()
|
||||||
let flat_trie = storage.iter().map(|(k, v)| (k.to_vec(), v.clone())).collect();
|
.map(|(k, v)| (k.to_vec(), v.clone()))
|
||||||
(addr.to_vec(), sec_trie_root(flat_trie).to_vec())
|
.collect()
|
||||||
}).collect();
|
).0);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Committed {
|
Committed {
|
||||||
code_tree_root,
|
|
||||||
storage_tree_root,
|
storage_tree_root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,17 +19,16 @@
|
|||||||
use std::{error, fmt};
|
use std::{error, fmt};
|
||||||
|
|
||||||
use backend::Backend;
|
use backend::Backend;
|
||||||
use primitives::Address;
|
use {Externalities, OverlayedChanges};
|
||||||
use primitives::contract::{CallData, OutData};
|
|
||||||
use primitives::hash::H256;
|
|
||||||
use {Externalities, CodeExecutor, StaticExternalities, OverlayedChanges};
|
|
||||||
|
|
||||||
/// Errors that can occur when interacting with the externalities.
|
/// Errors that can occur when interacting with the externalities.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Error<B, E> {
|
pub enum Error<B, E> {
|
||||||
/// Failure to load state data from the backend.
|
/// Failure to load state data from the backend.
|
||||||
|
#[allow(unused)]
|
||||||
Backend(B),
|
Backend(B),
|
||||||
/// Failure to execute a function.
|
/// Failure to execute a function.
|
||||||
|
#[allow(unused)]
|
||||||
Executor(E),
|
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.
|
/// 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.
|
/// The overlayed changes to write to.
|
||||||
pub overlay: &'a mut OverlayedChanges,
|
pub overlay: &'a mut OverlayedChanges,
|
||||||
/// The storage backend to read from.
|
/// The storage backend to read from.
|
||||||
pub backend: &'a B,
|
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>
|
impl<'a, B: 'a> Externalities for Ext<'a, B>
|
||||||
where B: Backend, E: CodeExecutor
|
where B: Backend
|
||||||
{
|
{
|
||||||
type Error = Error<B::Error, E::Error>;
|
type Error = B::Error;
|
||||||
|
|
||||||
fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> {
|
fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error> {
|
||||||
match self.overlay.storage(&self.local, key) {
|
match self.overlay.storage(key) {
|
||||||
Some(x) => Ok(x),
|
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> {
|
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
|
||||||
let inner_ext = StaticExt {
|
self.overlay.set_storage(key, value);
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,76 +27,45 @@ extern crate keccak_hash;
|
|||||||
extern crate patricia_trie;
|
extern crate patricia_trie;
|
||||||
extern crate triehash;
|
extern crate triehash;
|
||||||
|
|
||||||
|
extern crate byteorder;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use primitives::Address;
|
use primitives::contract::{CallData};
|
||||||
use primitives::contract::{CallData, OutData};
|
|
||||||
use primitives::hash::H256;
|
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
mod ext;
|
mod ext;
|
||||||
|
|
||||||
/// Updates to be committed to the state.
|
/// Updates to be committed to the state.
|
||||||
pub enum Update {
|
pub enum Update {
|
||||||
/// Set storage of address at given key -- empty is deletion.
|
/// Set storage of object at given key -- empty is deletion.
|
||||||
Storage(Address, H256, Vec<u8>),
|
Storage(Vec<u8>, Vec<u8>),
|
||||||
/// Set code of address -- empty is deletion.
|
|
||||||
Code(Address, Vec<u8>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// in-memory section of the state.
|
// in-memory section of the state.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct MemoryState {
|
struct MemoryState {
|
||||||
code: HashMap<Address, Vec<u8>>,
|
storage: HashMap<Vec<u8>, Vec<u8>>,
|
||||||
storage: HashMap<Address, HashMap<H256, Vec<u8>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemoryState {
|
impl MemoryState {
|
||||||
fn code(&self, address: &Address) -> Option<&[u8]> {
|
fn storage(&self, key: &[u8]) -> Option<&[u8]> {
|
||||||
self.code.get(address).map(|v| &v[..])
|
self.storage.get(key).map(|v| &v[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn storage(&self, address: &Address, key: &H256) -> Option<&[u8]> {
|
fn set_storage(&mut self, key: Vec<u8>, val: Vec<u8>) {
|
||||||
self.storage.get(address)
|
self.storage.insert(key, val);
|
||||||
.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 update<I>(&mut self, changes: I) where I: IntoIterator<Item=Update> {
|
fn update<I>(&mut self, changes: I) where I: IntoIterator<Item=Update> {
|
||||||
for update in changes {
|
for update in changes {
|
||||||
match update {
|
match update {
|
||||||
Update::Storage(addr, key, val) => {
|
Update::Storage(key, val) => {
|
||||||
if val.is_empty() {
|
if val.is_empty() {
|
||||||
let mut empty = false;
|
self.storage.remove(&key);
|
||||||
if let Some(s) = self.storage.get_mut(&addr) {
|
|
||||||
s.remove(&key);
|
|
||||||
empty = s.is_empty();
|
|
||||||
};
|
|
||||||
|
|
||||||
if empty { self.storage.remove(&addr); }
|
|
||||||
} else {
|
} else {
|
||||||
self.storage.entry(addr)
|
self.storage.insert(key, val);
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,43 +84,27 @@ pub struct OverlayedChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OverlayedChanges {
|
impl OverlayedChanges {
|
||||||
fn code(&self, address: &Address) -> Option<&[u8]> {
|
fn storage(&self, key: &[u8]) -> Option<&[u8]> {
|
||||||
self.prospective.code(address)
|
self.prospective.storage(key)
|
||||||
.or_else(|| self.committed.code(address))
|
.or_else(|| self.committed.storage(key))
|
||||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn storage(&self, address: &Address, key: &H256) -> Option<&[u8]> {
|
fn set_storage(&mut self, key: Vec<u8>, val: Vec<u8>) {
|
||||||
self.prospective.storage(address, key)
|
self.prospective.set_storage(key, val);
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Discard prospective changes to state.
|
/// Discard prospective changes to state.
|
||||||
pub fn discard_prospective(&mut self) {
|
pub fn discard_prospective(&mut self) {
|
||||||
self.prospective.code.clear();
|
|
||||||
self.prospective.storage.clear();
|
self.prospective.storage.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit prospective changes to state.
|
/// Commit prospective changes to state.
|
||||||
pub fn commit_prospective(&mut self) {
|
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()
|
let storage_updates = self.prospective.storage.drain()
|
||||||
.flat_map(|(addr, storages)| storages.into_iter().map(move |(k, v)| (addr, k, v)))
|
.map(|(key, value)| Update::Storage(key, value));
|
||||||
.map(|(addr, key, value)| Update::Storage(addr, 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 {}
|
pub trait Error: 'static + fmt::Debug + fmt::Display + Send {}
|
||||||
impl<E> Error for E where E: '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.
|
fn value_vec(mut value: usize, initial: Vec<u8>) -> Vec<u8> {
|
||||||
pub trait Externalities<CodeExecutor>: StaticExternalities<CodeExecutor> {
|
let mut acc = initial;
|
||||||
/// Read storage of current contract being called.
|
while value > 0 {
|
||||||
fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> {
|
acc.push(value as u8);
|
||||||
StaticExternalities::storage(self, key)
|
value /= 256;
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
}
|
||||||
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Static externalities: used only for read-only requests.
|
/// Externalities: pinned to specific active address.
|
||||||
pub trait StaticExternalities<CodeExecutor> {
|
pub trait Externalities {
|
||||||
/// Externalities error type.
|
/// Externalities error type.
|
||||||
type Error: Error;
|
type Error: Error;
|
||||||
|
|
||||||
/// Read storage of current contract being called.
|
/// 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.
|
/// Set storage of current contract being called (effective immediately).
|
||||||
fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result<OutData, Self::Error>;
|
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 {
|
pub trait CodeExecutor: Sized {
|
||||||
/// Error type for contract execution.
|
/// Externalities error type.
|
||||||
type Error: Error;
|
type Error: Error;
|
||||||
|
|
||||||
/// Execute a contract in read-only mode.
|
/// Call a given method in the runtime.
|
||||||
/// The execution is not allowed to modify the state.
|
fn call<E: Externalities>(
|
||||||
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>>(
|
|
||||||
&self,
|
&self,
|
||||||
ext: &mut E,
|
ext: &mut E,
|
||||||
code: &[u8],
|
code: &[u8],
|
||||||
method: &str,
|
method: &str,
|
||||||
data: &CallData,
|
data: &CallData,
|
||||||
) -> Result<OutData, Self::Error>;
|
) -> Result<Vec<u8>, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a call using the given state backend, overlayed changes, and call executor.
|
/// Execute a call using the given state backend, overlayed changes, and call executor.
|
||||||
///
|
///
|
||||||
/// On an error, no prospective changes are written to the overlay.
|
/// 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>(
|
pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
|
||||||
backend: &B,
|
backend: &B,
|
||||||
overlay: &mut OverlayedChanges,
|
overlay: &mut OverlayedChanges,
|
||||||
exec: &Exec,
|
exec: &Exec,
|
||||||
address: &Address,
|
|
||||||
method: &str,
|
method: &str,
|
||||||
call_data: &CallData,
|
call_data: &CallData,
|
||||||
) -> Result<OutData, Box<Error>> {
|
) -> Result<Vec<u8>, 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(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
let mut externalities = ext::Ext {
|
let mut externalities = ext::Ext {
|
||||||
backend,
|
backend,
|
||||||
exec,
|
overlay: &mut *overlay
|
||||||
overlay: &mut *overlay,
|
|
||||||
local: *address,
|
|
||||||
};
|
};
|
||||||
|
// make a copy.
|
||||||
|
let code = externalities.storage(b"\0code").unwrap_or(&[]).to_vec();
|
||||||
|
|
||||||
exec.call(
|
exec.call(
|
||||||
&mut externalities,
|
&mut externalities,
|
||||||
&code[..],
|
&code,
|
||||||
method,
|
method,
|
||||||
call_data,
|
call_data,
|
||||||
)
|
)
|
||||||
@@ -263,59 +203,67 @@ pub fn execute<B: backend::Backend, Exec: CodeExecutor>(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::OverlayedChanges;
|
use std::collections::HashMap;
|
||||||
|
use super::{OverlayedChanges, Externalities};
|
||||||
use primitives::hash::H256;
|
|
||||||
use primitives::Address;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn overlayed_storage_works() {
|
fn overlayed_storage_works() {
|
||||||
let mut overlayed = OverlayedChanges::default();
|
let mut overlayed = OverlayedChanges::default();
|
||||||
|
|
||||||
let key = H256::random();
|
let key = vec![42, 69, 169, 142];
|
||||||
let addr = Address::random();
|
|
||||||
|
|
||||||
assert!(overlayed.storage(&addr, &key).is_none());
|
assert!(overlayed.storage(&key).is_none());
|
||||||
|
|
||||||
overlayed.set_storage(addr, key, vec![1, 2, 3]);
|
overlayed.set_storage(key.clone(), vec![1, 2, 3]);
|
||||||
assert_eq!(overlayed.storage(&addr, &key).unwrap(), &[1, 2, 3]);
|
assert_eq!(overlayed.storage(&key).unwrap(), &[1, 2, 3]);
|
||||||
|
|
||||||
overlayed.commit_prospective();
|
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![]);
|
overlayed.set_storage(key.clone(), vec![]);
|
||||||
assert!(overlayed.storage(&addr, &key).is_none());
|
assert!(overlayed.storage(&key).is_none());
|
||||||
|
|
||||||
overlayed.discard_prospective();
|
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();
|
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]
|
#[test]
|
||||||
fn overlayed_code_works() {
|
fn validators_call_works() {
|
||||||
let mut overlayed = OverlayedChanges::default();
|
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]);
|
ext.set_storage(b"\0validator_count".to_vec(), vec![1]);
|
||||||
assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]);
|
assert_eq!(ext.validators(), Ok(vec![&[][..]]));
|
||||||
|
|
||||||
overlayed.commit_prospective();
|
ext.set_storage(b"\0validator".to_vec(), b"first".to_vec());
|
||||||
assert_eq!(overlayed.code(&addr).unwrap(), &[1, 2, 3]);
|
assert_eq!(ext.validators(), Ok(vec![&b"first"[..]]));
|
||||||
|
|
||||||
overlayed.set_code(addr, vec![]);
|
ext.set_storage(b"\0validator_count".to_vec(), vec![2]);
|
||||||
assert!(overlayed.code(&addr).is_none());
|
ext.set_storage(b"\0validator\x01".to_vec(), b"second".to_vec());
|
||||||
|
assert_eq!(ext.validators(), Ok(vec![&b"first"[..], &b"second"[..]]));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user