From d91138ab4e057eca72e774561a2537235b0653ca Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 00:37:15 +0100 Subject: [PATCH 01/10] client-api type and move duty roster types to primitives --- Cargo.lock | 40 +++++---- Cargo.toml | 1 + cli/Cargo.toml | 2 +- cli/src/lib.rs | 2 +- client-api/Cargo.toml | 8 ++ client-api/src/lib.rs | 47 ++++++++++ client/Cargo.toml | 2 +- client/src/lib.rs | 10 +-- consensus/Cargo.toml | 10 +++ consensus/src/lib.rs | 24 ++++++ network/Cargo.toml | 2 +- network/src/lib.rs | 2 +- polkadot-primitives/src/parachain.rs | 85 +++++++++++++++++-- primitives/src/lib.rs | 1 - rpc/Cargo.toml | 2 +- rpc/src/lib.rs | 2 +- .../polkadot/src/runtime/parachains.rs | 31 ++----- 17 files changed, 212 insertions(+), 59 deletions(-) create mode 100644 client-api/Cargo.toml create mode 100644 client-api/src/lib.rs create mode 100644 consensus/Cargo.toml create mode 100644 consensus/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 8f333b159e504..606badbadad8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -989,29 +989,18 @@ dependencies = [ "env_logger 0.4.3 (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.9 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-client 0.1.0", "polkadot-rpc-servers 0.1.0", + "substrate-client 0.1.0", "substrate-executor 0.1.0", "substrate-primitives 0.1.0", ] [[package]] -name = "polkadot-client" +name = "polkadot-client-api" version = "0.1.0" dependencies = [ - "ed25519 0.1.0", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "native-runtime 0.1.0", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-primitives 0.1.0", - "substrate-codec 0.1.0", - "substrate-executor 0.1.0", - "substrate-primitives 0.1.0", - "substrate-serializer 0.1.0", + "substrate-client 0.1.0", "substrate-state-machine 0.1.0", - "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1036,7 +1025,6 @@ dependencies = [ "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "polkadot-client 0.1.0", "polkadot-primitives 0.1.0", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1044,6 +1032,7 @@ dependencies = [ "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-client 0.1.0", "substrate-primitives 0.1.0", "substrate-serializer 0.1.0", "substrate-state-machine 0.1.0", @@ -1070,7 +1059,7 @@ dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git)", "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git)", - "polkadot-client 0.1.0", + "substrate-client 0.1.0", "substrate-executor 0.1.0", "substrate-primitives 0.1.0", "substrate-state-machine 0.1.0", @@ -1413,6 +1402,25 @@ name = "strsim" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "substrate-client" +version = "0.1.0" +dependencies = [ + "ed25519 0.1.0", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "native-runtime 0.1.0", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-primitives 0.1.0", + "substrate-codec 0.1.0", + "substrate-executor 0.1.0", + "substrate-primitives 0.1.0", + "substrate-serializer 0.1.0", + "substrate-state-machine 0.1.0", + "triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-codec" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 4780426456d93..779a514074ffe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ polkadot-network = { path = "network" } members = [ "candidate-agreement", "client", + "client-api", "collator", "environmental", "executor", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 74299ec2eb252..6bc198edbfe6e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -9,7 +9,7 @@ clap = { version = "2.27", features = ["yaml"] } env_logger = "0.4" error-chain = "0.11" log = "0.3" -polkadot-client = { path = "../client" } +substrate-client = { path = "../client" } substrate-executor = { path = "../executor" } substrate-primitives = { path = "../primitives" } polkadot-rpc-servers = { path = "../rpc-servers" } diff --git a/cli/src/lib.rs b/cli/src/lib.rs index faad72526e6e1..265b8aabeb267 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -19,7 +19,7 @@ #![warn(missing_docs)] extern crate env_logger; -extern crate polkadot_client as client; +extern crate substrate_client as client; extern crate substrate_executor as executor; extern crate substrate_primitives as primitives; extern crate polkadot_rpc_servers as rpc; diff --git a/client-api/Cargo.toml b/client-api/Cargo.toml new file mode 100644 index 0000000000000..bdf73b52009d4 --- /dev/null +++ b/client-api/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "polkadot-client-api" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +substrate-client = { path = "../client" } +substrate-state-machine = { path = "../state-machine" } diff --git a/client-api/src/lib.rs b/client-api/src/lib.rs new file mode 100644 index 0000000000000..34d314fc7f3e5 --- /dev/null +++ b/client-api/src/lib.rs @@ -0,0 +1,47 @@ +// 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 . + +//! Polkadot client API. This builds upon a substrate client to add strongly-typed +//! wrappers for state-fetching functions. + +extern crate substrate_client as s_client; +extern crate substrate_state_machine as s_state_machine; + +use s_client::Client; +use s_client::backend::Backend as ClientBackend; +use s_state_machine::backend::Backend as StateBackend; + +/// An implementation of the polkadot client API. +pub trait PolkadotClient { + type Error; +} + +impl PolkadotClient for Client where + B: ClientBackend, + E: s_state_machine::CodeExecutor, + s_client::error::Error: From<<::State as StateBackend>::Error> +{ + type Error = s_client::error::Error; + +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/client/Cargo.toml b/client/Cargo.toml index b5ee065dbb9b4..51494c37d6055 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "polkadot-client" +name = "substrate-client" version = "0.1.0" authors = ["Parity Technologies "] diff --git a/client/src/lib.rs b/client/src/lib.rs index f0d1fd1a6359a..81af8f7a08b74 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,20 +1,20 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate 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, +// Substrate 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 . +// along with Substrate. If not, see . -//! Polkadot Client +//! Substrate Client #![warn(missing_docs)] diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml new file mode 100644 index 0000000000000..0e75c56896580 --- /dev/null +++ b/consensus/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "polkadot-consensus" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +polkadot-candidate-agreement = { version = "0.1", path = "../candidate-agreement" } +substrate-client = { version = "0.1", path = "../client" } +polkadot-network = { version = "0.1", path = "../network" } +substrate-state-machine = { version = "0.1", path = "../state-machine" } diff --git a/consensus/src/lib.rs b/consensus/src/lib.rs new file mode 100644 index 0000000000000..315dd270991ca --- /dev/null +++ b/consensus/src/lib.rs @@ -0,0 +1,24 @@ +// 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 . + +//! Consensus and block generation. + +extern crate polkadot_candidate_agreement as agreement; +extern crate substrate_client as client; +extern crate polkadot_network as network; +extern crate substrate_state_machine as state_machine; + +use state_machine::{Backend, Error}; diff --git a/network/Cargo.toml b/network/Cargo.toml index e1e8310d33aad..8c0871ae14d06 100644 --- a/network/Cargo.toml +++ b/network/Cargo.toml @@ -12,7 +12,7 @@ ethcore-network = { git = "https://github.com/paritytech/parity.git" } ethcore-io = { git = "https://github.com/paritytech/parity.git" } substrate-primitives = { path = "../primitives" } polkadot-primitives = { path = "../polkadot-primitives" } -polkadot-client = { path = "../client" } +substrate-client = { path = "../client" } substrate-state-machine = { path = "../state-machine" } substrate-serializer = { path = "../serializer" } log = "0.3" diff --git a/network/src/lib.rs b/network/src/lib.rs index 6feabb0b463ff..bdd7b41ca62f0 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -35,7 +35,7 @@ extern crate serde_json; // TODO: remove these two; split off dependent logic into polkadot-network and rename this crate // to substrate-network. extern crate polkadot_primitives as polkadot_primitives; -extern crate polkadot_client as client; +extern crate substrate_client as client; #[macro_use] extern crate serde_derive; #[macro_use] extern crate log; #[macro_use] extern crate bitflags; diff --git a/polkadot-primitives/src/parachain.rs b/polkadot-primitives/src/parachain.rs index 75d5b6a326acd..30ec8f274b5a1 100644 --- a/polkadot-primitives/src/parachain.rs +++ b/polkadot-primitives/src/parachain.rs @@ -19,24 +19,25 @@ #[cfg(feature = "std")] use primitives::bytes; use primitives; +use codec::{Slicable, NonTrivialSlicable}; use rstd::vec::Vec; /// Unique identifier of a parachain. #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -pub struct Id(u64); +pub struct Id(u32); -impl From for u64 { +impl From for u32 { fn from(x: Id) -> Self { x.0 } } -impl From for Id { - fn from(x: u64) -> Self { Id(x) } +impl From for Id { + fn from(x: u32) -> Self { Id(x) } } impl ::codec::Slicable for Id { fn from_slice(value: &mut &[u8]) -> Option { - u64::from_slice(value).map(Id) + u32::from_slice(value).map(Id) } fn as_slice_then R>(&self, f: F) -> R { @@ -44,6 +45,80 @@ impl ::codec::Slicable for Id { } } +/// Identifier for a chain, either one of a number of parachains or the relay chain. +#[derive(Copy, Clone, PartialEq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum Chain { + /// The relay chain. + Relay, + /// A parachain of the given index. + Parachain(Id), +} + +impl Slicable for Chain { + fn from_slice(value: &mut &[u8]) -> Option { + let disc = try_opt!(u8::from_slice(value)); + + match disc { + 0 => Some(Chain::Relay), + 1 => Some(Chain::Parachain(try_opt!(Id::from_slice(value)))), + _ => None, + } + } + + fn to_vec(&self) -> Vec { + let mut v = Vec::new(); + match *self { + Chain::Relay => { 0u8.as_slice_then(|s| v.extend(s)); } + Chain::Parachain(id) => { + 1u8.as_slice_then(|s| v.extend(s)); + id.as_slice_then(|s| v.extend(s)); + } + } + + v + } + + fn as_slice_then R>(&self, f: F) -> R { + f(&self.to_vec().as_slice()) + } +} + +impl NonTrivialSlicable for Chain { } + +/// The duty roster specifying what jobs each validator must do. +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "std", derive(Default, Debug))] +pub struct DutyRoster { + /// Lookup from validator index to chain on which that validator has a duty to validate. + pub validator_duty: Vec, + /// Lookup from validator index to chain on which that validator has a duty to guarantee + /// availability. + pub guarantor_duty: Vec, +} + +impl Slicable for DutyRoster { + fn from_slice(value: &mut &[u8]) -> Option { + Some(DutyRoster { + validator_duty: try_opt!(Slicable::from_slice(value)), + guarantor_duty: try_opt!(Slicable::from_slice(value)), + }) + } + + fn to_vec(&self) -> Vec { + let mut v = Vec::new(); + + v.extend(self.validator_duty.to_vec()); + v.extend(self.guarantor_duty.to_vec()); + + v + } + + fn as_slice_then R>(&self, f: F) -> R { + f(&self.to_vec().as_slice()) + } +} + /// Candidate parachain block. /// /// https://github.com/w3f/polkadot-spec/blob/master/spec.md#candidate-para-chain-block diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 452889d74bf68..3822a77d8c649 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -43,7 +43,6 @@ extern crate serde_derive; #[cfg(feature = "std")] extern crate core; -#[macro_use] extern crate substrate_runtime_std as rstd; #[cfg(test)] diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index fdf9f7595b759..70a5f53b7d6bf 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Parity Technologies "] error-chain = "0.11" jsonrpc-core = { git="https://github.com/paritytech/jsonrpc.git" } jsonrpc-macros = { git="https://github.com/paritytech/jsonrpc.git" } -polkadot-client = { path = "../client" } +substrate-client = { path = "../client" } substrate-primitives = { path = "../primitives" } substrate-state-machine = { path = "../state-machine" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 68935fb00b9de..cb7cae10cdc91 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -19,7 +19,7 @@ #![warn(missing_docs)] extern crate jsonrpc_core as rpc; -extern crate polkadot_client as client; +extern crate substrate_client as client; extern crate substrate_primitives as primitives; extern crate substrate_state_machine as state_machine; diff --git a/wasm-runtime/polkadot/src/runtime/parachains.rs b/wasm-runtime/polkadot/src/runtime/parachains.rs index 222448467aa47..d75b702925766 100644 --- a/wasm-runtime/polkadot/src/runtime/parachains.rs +++ b/wasm-runtime/polkadot/src/runtime/parachains.rs @@ -17,34 +17,14 @@ //! Main parachains logic. For now this is just the determination of which validators do what. use rstd::prelude::*; -use runtime_io::mem; use codec::{Slicable, Joiner}; -use support::{Hashable, with_env, storage}; +use runtime_io::mem; use runtime::session; +use support::{Hashable, with_env, storage}; +use polkadot_primitives::parachain::{Id, Chain, DutyRoster}; const PARACHAIN_COUNT: &[u8] = b"par:cou"; -/// Identifier for a chain, either one of a number of parachains or the relay chain. -#[derive(Copy, Clone, PartialEq)] -#[cfg_attr(test, derive(Debug))] -pub enum Chain { - /// The relay chain. - Relay, - /// A parachain of the given index. - Parachain(u32), -} - -/// The duty roster specifying what jobs each validator must do. -#[derive(Clone, PartialEq)] -#[cfg_attr(test, derive(Default, Debug))] -pub struct DutyRoster { - /// Lookup from validator index to chain on which that validator has a duty to validate. - pub validator_duty: Vec, - /// Lookup from validator index to chain on which that validator has a duty to guarantee - /// availability. - pub guarantor_duty: Vec, -} - /// Get the number of parachains registered at present. pub fn parachain_count() -> u32 { storage::get_or(PARACHAIN_COUNT, 0) @@ -57,7 +37,8 @@ pub fn calculate_duty_roster() -> DutyRoster { let validators_per_parachain = (validator_count - 1) / parachain_count; let mut roles_val = (0..validator_count).map(|i| match i { - i if i < parachain_count * validators_per_parachain => Chain::Parachain(i / validators_per_parachain as u32), + i if i < parachain_count * validators_per_parachain => + Chain::Parachain(Id::from(i / validators_per_parachain as u32)), _ => Chain::Relay, }).collect::>(); let mut roles_gua = roles_val.clone(); @@ -115,7 +96,7 @@ mod tests { let check_roster = |duty_roster: &DutyRoster| { assert_eq!(duty_roster.validator_duty.len(), 8); assert_eq!(duty_roster.guarantor_duty.len(), 8); - for i in 0..2 { + for i in (0..2).map(Id::from) { assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3); } From 07cd25fe9009766214bbb262aa2891997f840676 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 01:07:44 +0100 Subject: [PATCH 02/10] tuple implementation for slicable --- codec/src/slicable.rs | 83 ++++++++++++++++++++++++++++++++ wasm-runtime/polkadot/src/api.rs | 15 ++++++ wasm-runtime/polkadot/src/lib.rs | 8 +-- 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 wasm-runtime/polkadot/src/api.rs diff --git a/codec/src/slicable.rs b/codec/src/slicable.rs index 653bbb7b168e5..45cc1613505ab 100644 --- a/codec/src/slicable.rs +++ b/codec/src/slicable.rs @@ -128,6 +128,73 @@ impl Slicable for Vec { } } +macro_rules! try_decode { + ($t:ty, $initial: expr, $current: expr) => { + match Slicable::from_slice($current) { + Some(x) => x, + None => { + *$current = $initial; + return None; + } + } + } +} + +macro_rules! tuple_impl { + ($one:ident,) => { + impl<$one: Slicable> Slicable for ($one,) { + fn from_slice(value: &mut &[u8]) -> Option { + match $one::from_slice(value) { + None => None, + Some($one) => Some(($one,)), + } + } + + fn as_slice_then R>(&self, f: F) -> R { + self.0.as_slice_then(f) + } + } + }; + ($first:ident, $($rest:ident,)+) => { + impl<$first: Slicable, $($rest: Slicable),+> + Slicable for + ($first, $($rest),+) { + fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; + Some(( + try_decode!($first, initial, value), + $(try_decode!($rest, initial, value),)+ + )) + } + + fn as_slice_then(&self, f: PROCESS) -> RETURN + where PROCESS: FnOnce(&[u8]) -> RETURN + { + let mut v = Vec::new(); + + let ( + ref $first, + $(ref $rest),+ + ) = *self; + + $first.as_slice_then(|s| v.extend(s)); + $($rest.as_slice_then(|s| v.extend(s));)+ + + f(v.as_slice()) + } + } + + tuple_impl!($($rest,)+); + } +} + +#[allow(non_snake_case)] +mod inner_tuple_impl { + use super::Slicable; + tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,); +} + + #[cfg(test)] mod tests { use super::*; @@ -139,4 +206,20 @@ mod tests { assert_eq!(slice, &b"\x0b\0\0\0Hello world") ); } + + #[test] + fn failed_tuple_decode_does_not_munch() { + let encoded = (vec![1u8, 3, 5, 9], 6u64).to_vec(); + let mut data = &encoded[..]; + assert!(<(Vec, u64, Vec)>::from_slice(&mut data).is_none()); + + // failure to decode shouldn't munch anything. + assert_eq!(data.len(), encoded.len()); + + // full decoding should have munched everything. + let decoded = <(Vec, u64)>::from_slice(&mut data).unwrap(); + assert!(data.is_empty()); + + assert_eq!(decoded, (vec![1, 3, 5,9], 6)); + } } diff --git a/wasm-runtime/polkadot/src/api.rs b/wasm-runtime/polkadot/src/api.rs new file mode 100644 index 0000000000000..bf5c45b538407 --- /dev/null +++ b/wasm-runtime/polkadot/src/api.rs @@ -0,0 +1,15 @@ +// 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 . diff --git a/wasm-runtime/polkadot/src/lib.rs b/wasm-runtime/polkadot/src/lib.rs index 72e2d23c50e3e..c670a2a241c9f 100644 --- a/wasm-runtime/polkadot/src/lib.rs +++ b/wasm-runtime/polkadot/src/lib.rs @@ -37,6 +37,7 @@ extern crate hex_literal; #[macro_use] pub mod support; pub mod runtime; +pub mod api; use rstd::prelude::*; use codec::Slicable; @@ -88,7 +89,8 @@ pub fn execute_block(mut input: &[u8]) -> Vec { Vec::new() } -/// Execute a given, serialised, transaction. Returns the empty vector. +/// Execute a transaction. Input data is the concatenation of a serialized header and +/// transaction. Returns the new header. pub fn execute_transaction(mut input: &[u8]) -> Vec { let header = Header::from_slice(&mut input).unwrap(); let utx = UncheckedTransaction::from_slice(&mut input).unwrap(); @@ -96,14 +98,14 @@ pub fn execute_transaction(mut input: &[u8]) -> Vec { header.to_vec() } -/// Execute a given, serialised, transaction. Returns the empty vector. +/// Finalize a block, given its header. pub fn finalise_block(mut input: &[u8]) -> Vec { let header = Header::from_slice(&mut input).unwrap(); let header = runtime::system::internal::finalise_block(header); header.to_vec() } -/// Run whatever tests we have. +/// Run whatever tests we have on a full block. pub fn run_tests(mut input: &[u8]) -> Vec { use runtime_io::print; From a078510914edcbd7d57e0283a134ebdb81f82925 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 01:16:51 +0100 Subject: [PATCH 03/10] mild cleanup of deserialization code --- codec/src/slicable.rs | 40 +++++++++++++----------- polkadot-primitives/src/block.rs | 17 +++++------ polkadot-primitives/src/lib.rs | 4 ++- polkadot-primitives/src/parachain.rs | 9 ++++-- polkadot-primitives/src/transaction.rs | 42 +++++++++++++++----------- 5 files changed, 63 insertions(+), 49 deletions(-) diff --git a/codec/src/slicable.rs b/codec/src/slicable.rs index 45cc1613505ab..fff7e98b94e2a 100644 --- a/codec/src/slicable.rs +++ b/codec/src/slicable.rs @@ -39,6 +39,21 @@ pub trait Slicable: Sized { /// Trait to mark that a type is not trivially (essentially "in place") serialisable. pub trait NonTrivialSlicable: Slicable {} +/// Attempt to decode a value from the slice, providing an initial value +/// to replace it with should it fail. +#[macro_export] +macro_rules! try_decode { + ($initial: expr, $current: expr) => { + match $crate::Slicable::from_slice($current) { + Some(x) => x, + None => { + *$current = $initial; + return None; + } + } + } +} + impl Slicable for T { fn from_slice(value: &mut &[u8]) -> Option { let size = mem::size_of::(); @@ -96,14 +111,12 @@ impl NonTrivialSlicable for Vec where Vec: Slicable {} impl Slicable for Vec { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; u32::from_slice(value).and_then(move |len| { let len = len as usize; let mut r = Vec::with_capacity(len); for _ in 0..len { - match T::from_slice(value) { - None => return None, - Some(v) => r.push(v), - } + r.push(try_decode!(initial, value)) } Some(r) @@ -128,18 +141,6 @@ impl Slicable for Vec { } } -macro_rules! try_decode { - ($t:ty, $initial: expr, $current: expr) => { - match Slicable::from_slice($current) { - Some(x) => x, - None => { - *$current = $initial; - return None; - } - } - } -} - macro_rules! tuple_impl { ($one:ident,) => { impl<$one: Slicable> Slicable for ($one,) { @@ -162,8 +163,11 @@ macro_rules! tuple_impl { fn from_slice(value: &mut &[u8]) -> Option { let initial = *value; Some(( - try_decode!($first, initial, value), - $(try_decode!($rest, initial, value),)+ + try_decode!(initial, value), + $({ + let x: $rest = try_decode!(initial, value); + x + },)+ )) } diff --git a/polkadot-primitives/src/block.rs b/polkadot-primitives/src/block.rs index 5ebe53007ff06..5fe01e1dbc579 100644 --- a/polkadot-primitives/src/block.rs +++ b/polkadot-primitives/src/block.rs @@ -82,10 +82,8 @@ pub struct Block { impl Slicable for Block { fn from_slice(value: &mut &[u8]) -> Option { - Some(Block { - header: try_opt!(Slicable::from_slice(value)), - transactions: try_opt!(Slicable::from_slice(value)), - }) + let (header, transactions) = try_opt!(Slicable::from_slice(value)); + Some(Block { header, transactions }) } fn to_vec(&self) -> Vec { @@ -137,12 +135,13 @@ impl Header { impl Slicable for Header { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; Some(Header { - parent_hash: try_opt!(Slicable::from_slice(value)), - number: try_opt!(Slicable::from_slice(value)), - state_root: try_opt!(Slicable::from_slice(value)), - transaction_root: try_opt!(Slicable::from_slice(value)), - digest: try_opt!(Slicable::from_slice(value)), + parent_hash: try_decode!(initial, value), + number: try_decode!(initial, value), + state_root: try_decode!(initial, value), + transaction_root: try_decode!(initial, value), + digest: try_decode!(initial, value), }) } diff --git a/polkadot-primitives/src/lib.rs b/polkadot-primitives/src/lib.rs index 83cb3be1cd93c..56ae47e2b1738 100644 --- a/polkadot-primitives/src/lib.rs +++ b/polkadot-primitives/src/lib.rs @@ -29,11 +29,13 @@ extern crate serde_derive; extern crate serde; extern crate substrate_runtime_std as rstd; -extern crate substrate_codec as codec; extern crate substrate_primitives as primitives; #[cfg(test)] extern crate substrate_serializer; +#[macro_use] +extern crate substrate_codec as codec; + macro_rules! try_opt { ($e: expr) => { match $e { diff --git a/polkadot-primitives/src/parachain.rs b/polkadot-primitives/src/parachain.rs index 30ec8f274b5a1..bd4310551ac86 100644 --- a/polkadot-primitives/src/parachain.rs +++ b/polkadot-primitives/src/parachain.rs @@ -57,11 +57,12 @@ pub enum Chain { impl Slicable for Chain { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; let disc = try_opt!(u8::from_slice(value)); match disc { 0 => Some(Chain::Relay), - 1 => Some(Chain::Parachain(try_opt!(Id::from_slice(value)))), + 1 => Some(Chain::Parachain(try_decode!(initial, value))), _ => None, } } @@ -99,9 +100,11 @@ pub struct DutyRoster { impl Slicable for DutyRoster { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; + Some(DutyRoster { - validator_duty: try_opt!(Slicable::from_slice(value)), - guarantor_duty: try_opt!(Slicable::from_slice(value)), + validator_duty: try_decode!(initial, value), + guarantor_duty: try_decode!(initial, value), }) } diff --git a/polkadot-primitives/src/transaction.rs b/polkadot-primitives/src/transaction.rs index 062d8c563dc29..b7bbc83bdd69b 100644 --- a/polkadot-primitives/src/transaction.rs +++ b/polkadot-primitives/src/transaction.rs @@ -92,22 +92,24 @@ pub enum Proposal { impl Slicable for Proposal { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; + let id = try_opt!(u8::from_slice(value).and_then(InternalFunctionId::from_u8)); let function = match id { InternalFunctionId::SystemSetCode => - Proposal::SystemSetCode(try_opt!(Slicable::from_slice(value))), + Proposal::SystemSetCode(try_decode!(initial, value)), InternalFunctionId::SessionSetLength => - Proposal::SessionSetLength(try_opt!(Slicable::from_slice(value))), + Proposal::SessionSetLength(try_decode!(initial, value)), InternalFunctionId::SessionForceNewSession => Proposal::SessionForceNewSession, InternalFunctionId::StakingSetSessionsPerEra => - Proposal::StakingSetSessionsPerEra(try_opt!(Slicable::from_slice(value))), + Proposal::StakingSetSessionsPerEra(try_decode!(initial, value)), InternalFunctionId::StakingSetBondingDuration => - Proposal::StakingSetBondingDuration(try_opt!(Slicable::from_slice(value))), + Proposal::StakingSetBondingDuration(try_decode!(initial, value)), InternalFunctionId::StakingSetValidatorCount => - Proposal::StakingSetValidatorCount(try_opt!(Slicable::from_slice(value))), + Proposal::StakingSetValidatorCount(try_decode!(initial, value)), InternalFunctionId::StakingForceNewEra => Proposal::StakingForceNewEra, InternalFunctionId::GovernanceSetApprovalPpmRequired => - Proposal::GovernanceSetApprovalPpmRequired(try_opt!(Slicable::from_slice(value))), + Proposal::GovernanceSetApprovalPpmRequired(try_decode!(initial, value)), }; Some(function) @@ -211,24 +213,25 @@ pub enum Function { impl Slicable for Function { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; let id = try_opt!(u8::from_slice(value).and_then(FunctionId::from_u8)); Some(match id { FunctionId::TimestampSet => - Function::TimestampSet(try_opt!(Slicable::from_slice(value))), + Function::TimestampSet(try_decode!(initial, value)), FunctionId::SessionSetKey => - Function::SessionSetKey(try_opt!(Slicable::from_slice(value))), + Function::SessionSetKey(try_decode!(initial, value)), FunctionId::StakingStake => Function::StakingStake, FunctionId::StakingUnstake => Function::StakingUnstake, FunctionId::StakingTransfer => { - let to = try_opt!(Slicable::from_slice(value)); - let amount = try_opt!(Slicable::from_slice(value)); + let to = try_decode!(initial, value); + let amount = try_decode!(initial, value); Function::StakingTransfer(to, amount) } FunctionId::GovernancePropose => - Function::GovernancePropose(try_opt!(Slicable::from_slice(value))), + Function::GovernancePropose(try_decode!(initial, value)), FunctionId::GovernanceApprove => - Function::GovernanceApprove(try_opt!(Slicable::from_slice(value))), + Function::GovernanceApprove(try_decode!(initial, value)), }) } @@ -286,10 +289,11 @@ pub struct Transaction { impl Slicable for Transaction { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; Some(Transaction { - signed: try_opt!(Slicable::from_slice(value)), - nonce: try_opt!(Slicable::from_slice(value)), - function: try_opt!(Slicable::from_slice(value)), + signed: try_decode!(initial, value), + nonce: try_decode!(initial, value), + function: try_decode!(initial, value), }) } @@ -322,15 +326,17 @@ pub struct UncheckedTransaction { impl Slicable for UncheckedTransaction { fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; + // This is a little more complicated than usua since the binary format must be compatible // with substrate's generic `Vec` type. Basically this just means accepting that there // will be a prefix of u32, which has the total number of bytes following (we don't need // to use this). - let _length_do_not_remove_me_see_above: u32 = try_opt!(Slicable::from_slice(value)); + let _length_do_not_remove_me_see_above: u32 = try_decode!(initial, value); Some(UncheckedTransaction { - transaction: try_opt!(Slicable::from_slice(value)), - signature: try_opt!(Slicable::from_slice(value)), + transaction: try_decode!(initial, value), + signature: try_decode!(initial, value), }) } From 68c114a84108bc76a2467fa3c9af8a0f1afbdbb6 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 01:58:59 +0100 Subject: [PATCH 04/10] stubs which handle encoding and decoding themselves --- codec/src/slicable.rs | 14 +++++++++++ executor/src/native_executor.rs | 8 +++---- runtime-io/with_std.rs | 18 +++++++++++++- runtime-io/without_std.rs | 22 +++++++++++++---- wasm-runtime/polkadot/src/api.rs | 34 ++++++++++++++++++++++++++ wasm-runtime/polkadot/src/lib.rs | 41 -------------------------------- 6 files changed, 87 insertions(+), 50 deletions(-) diff --git a/codec/src/slicable.rs b/codec/src/slicable.rs index fff7e98b94e2a..686f0d1cb9331 100644 --- a/codec/src/slicable.rs +++ b/codec/src/slicable.rs @@ -141,6 +141,20 @@ impl Slicable for Vec { } } +impl Slicable for () { + fn from_slice(_value: &mut &[u8]) -> Option<()> { + Some(()) + } + + fn as_slice_then R>(&self, f: F) -> R { + f(&[]) + } + + fn to_vec(&self) -> Vec { + Vec::new() + } +} + macro_rules! tuple_impl { ($one:ident,) => { impl<$one: Slicable> Slicable for ($one,) { diff --git a/executor/src/native_executor.rs b/executor/src/native_executor.rs index e4812be894a7b..98d1957ae850f 100644 --- a/executor/src/native_executor.rs +++ b/executor/src/native_executor.rs @@ -15,7 +15,7 @@ // along with Substrate. If not, see . use error::{Error, ErrorKind, Result}; -use native_runtime as runtime; +use native_runtime::api; use runtime_io; use state_machine::{Externalities, CodeExecutor}; use wasm_executor::WasmExecutor; @@ -43,9 +43,9 @@ impl CodeExecutor for NativeExecutor { let native_equivalent = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm"); if code == &native_equivalent[..] { runtime_io::with_externalities(ext, || match method { - "execute_block" => safe_call(|| runtime::execute_block(data)), - "execute_transaction" => safe_call(|| runtime::execute_transaction(data)), - "finalise_block" => safe_call(|| runtime::finalise_block(data)), + "execute_block" => safe_call(|| api::execute_encoded_block(data)), + "execute_transaction" => safe_call(|| api::execute_encoded_transaction(data)), + "finalise_block" => safe_call(|| api::finalise_encoded_block(data)), _ => Err(ErrorKind::MethodNotFound(method.to_owned()).into()), }) } else { diff --git a/runtime-io/with_std.rs b/runtime-io/with_std.rs index 63db356cbd95d..00f540938f3ec 100644 --- a/runtime-io/with_std.rs +++ b/runtime-io/with_std.rs @@ -22,6 +22,9 @@ extern crate substrate_primitives as primitives; extern crate triehash; extern crate ed25519; +#[doc(hidden)] +pub extern crate substrate_codec as codec; + pub use std::vec; pub use std::rc; pub use std::cell; @@ -131,7 +134,20 @@ pub fn print(value: T) { #[macro_export] macro_rules! impl_stubs { - ($( $name:ident ),*) => {} + ( $( $name:ident => $new_name:ident ),* ) => { + $( + /// Stub version of $name + pub fn $new_name(mut input: &[u8]) -> Vec { + let input = match $crate::codec::Slicable::from_slice(&mut input) { + Some(input) => input, + None => panic!("Bad input data provided to {}", stringify!($name)), + }; + + let output = $name(input); + $crate::codec::Slicable::to_vec(&output) + } + )* + } } #[cfg(test)] diff --git a/runtime-io/without_std.rs b/runtime-io/without_std.rs index 472b135beb8dc..3c6d6ef6806a0 100644 --- a/runtime-io/without_std.rs +++ b/runtime-io/without_std.rs @@ -22,6 +22,8 @@ extern crate pwasm_libc; #[cfg(feature = "nightly")] extern crate pwasm_alloc; +#[doc(hidden)] +pub extern crate substrate_codec as codec; extern crate substrate_primitives as primitives; pub use alloc::vec; @@ -186,12 +188,12 @@ pub fn print(value: T) { #[macro_export] macro_rules! impl_stubs { - ( $( $name:ident ),* ) => { + ( $( $name:ident => $new_name:ident ),* ) => { pub mod _internal { $( #[no_mangle] - pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { - let input = if input_len == 0 { + pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { + let mut input = if input_len == 0 { &[0u8; 0] } else { unsafe { @@ -199,8 +201,20 @@ macro_rules! impl_stubs { } }; + let input = match $crate::codec::Slicable::from_slice(&mut input) { + Some(input) => input, + None => panic!("Bad input data provided to {}", stringify!($name)), + }; + let output = super::$name(input); - output.as_ptr() as u64 + ((output.len() as u64) << 32) + let output = $crate::codec::Slicable::to_vec(&output); + let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); + + // Leak the output vector to avoid it being freed. + // This is fine in a WASM context since the heap + // will be discarded after the call. + ::core::mem::forget(output); + res } )* } diff --git a/wasm-runtime/polkadot/src/api.rs b/wasm-runtime/polkadot/src/api.rs index bf5c45b538407..c7b81751a9960 100644 --- a/wasm-runtime/polkadot/src/api.rs +++ b/wasm-runtime/polkadot/src/api.rs @@ -13,3 +13,37 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . + +use rstd::prelude::*; +use codec::Slicable; +use polkadot_primitives::{Header, Block, UncheckedTransaction}; + +/// Execute a block. +pub fn execute_block(block: Block) { + ::runtime::system::internal::execute_block(block); +} + +/// Execute a transaction. Input data is the concatenation of a serialized header and +/// transaction. Returns the new header. +pub fn execute_transaction((header, utx): (Header, UncheckedTransaction)) -> Header { + ::runtime::system::internal::execute_transaction(utx, header) +} + +/// Finalize a block, given its header. +pub fn finalise_block(header: Header) -> Vec { + let header = ::runtime::system::internal::finalise_block(header); + header.to_vec() +} + +/// Run whatever tests we have on a full block. +pub fn run_tests(block: Block) -> u32 { + let stxs = block.transactions.iter().map(Slicable::to_vec).collect::>(); + stxs.len() as u32 +} + +impl_stubs!( + execute_block => execute_encoded_block, + execute_transaction => execute_encoded_transaction, + finalise_block => finalise_encoded_block, + run_tests => run_encoded_block_tests +); diff --git a/wasm-runtime/polkadot/src/lib.rs b/wasm-runtime/polkadot/src/lib.rs index c670a2a241c9f..e50e3a73b8f9f 100644 --- a/wasm-runtime/polkadot/src/lib.rs +++ b/wasm-runtime/polkadot/src/lib.rs @@ -39,10 +39,6 @@ pub mod support; pub mod runtime; pub mod api; -use rstd::prelude::*; -use codec::Slicable; -use polkadot_primitives::{Header, Block, UncheckedTransaction}; - /// Type definitions and helpers for transactions. pub mod transaction { use rstd::ops; @@ -81,40 +77,3 @@ pub mod transaction { } } } - -/// Execute a block, with `input` being the canonical serialisation of the block. Returns the -/// empty vector. -pub fn execute_block(mut input: &[u8]) -> Vec { - runtime::system::internal::execute_block(Block::from_slice(&mut input).unwrap()); - Vec::new() -} - -/// Execute a transaction. Input data is the concatenation of a serialized header and -/// transaction. Returns the new header. -pub fn execute_transaction(mut input: &[u8]) -> Vec { - let header = Header::from_slice(&mut input).unwrap(); - let utx = UncheckedTransaction::from_slice(&mut input).unwrap(); - let header = runtime::system::internal::execute_transaction(utx, header); - header.to_vec() -} - -/// Finalize a block, given its header. -pub fn finalise_block(mut input: &[u8]) -> Vec { - let header = Header::from_slice(&mut input).unwrap(); - let header = runtime::system::internal::finalise_block(header); - header.to_vec() -} - -/// Run whatever tests we have on a full block. -pub fn run_tests(mut input: &[u8]) -> Vec { - use runtime_io::print; - - print("run_tests..."); - let block = Block::from_slice(&mut input).unwrap(); - print("deserialised block."); - let stxs = block.transactions.iter().map(Slicable::to_vec).collect::>(); - print("reserialised transactions."); - [stxs.len() as u8].to_vec() -} - -impl_stubs!(execute_block, execute_transaction, finalise_block, run_tests); From b25dee4d8a6e304a538170cae1d7551b9c080d33 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 02:31:32 +0100 Subject: [PATCH 05/10] fancier impl_stubs macro --- codec/src/slicable.rs | 53 +++++++++++++++++++- executor/src/native_executor.rs | 14 ++---- runtime-io/with_std.rs | 4 +- runtime-io/without_std.rs | 4 +- wasm-runtime/polkadot/src/api.rs | 36 +++---------- wasm-runtime/polkadot/src/runtime/session.rs | 2 +- 6 files changed, 66 insertions(+), 47 deletions(-) diff --git a/codec/src/slicable.rs b/codec/src/slicable.rs index 686f0d1cb9331..780e8b98d0a7b 100644 --- a/codec/src/slicable.rs +++ b/codec/src/slicable.rs @@ -86,11 +86,18 @@ impl Slicable for T { impl Slicable for Vec { fn from_slice(value: &mut &[u8]) -> Option { - u32::from_slice(value).map(move |len| { + let initial = *value; + u32::from_slice(value).and_then(move |len| { let len = len as usize; + + if value.len() < len { + *value = initial; + return None; + } + let res = value[..len].to_vec(); *value = &value[len..]; - res + Some(res) }) } fn as_slice_then R>(&self, f: F) -> R { @@ -107,6 +114,48 @@ impl Slicable for Vec { } } +macro_rules! impl_vec_simple_array { + ($($size:expr)*) => { + $( + impl Slicable for Vec<[T; $size]> + where [T; $size]: EndianSensitive + { + fn from_slice(value: &mut &[u8]) -> Option { + let initial = *value; + u32::from_slice(value).and_then(move |len| { + let len = len as usize; + let mut r = Vec::with_capacity(len); + for _ in 0..len { + r.push(try_decode!(initial, value)); + } + + Some(r) + }) + } + + fn as_slice_then R>(&self, f: F) -> R { + f(&self.to_vec()) + } + + fn to_vec(&self) -> Vec { + use rstd::iter::Extend; + + let len = self.len(); + assert!(len <= u32::max_value() as usize, "Attempted to serialize vec with too many elements."); + + let mut r: Vec = Vec::new().join(&(len as u32)); + for item in self { + r.extend(item.to_vec()); + } + r + } + } + )* + } +} + +impl_vec_simple_array!(1 2 4 8 16 32 64); + impl NonTrivialSlicable for Vec where Vec: Slicable {} impl Slicable for Vec { diff --git a/executor/src/native_executor.rs b/executor/src/native_executor.rs index 98d1957ae850f..302bedf9c91ae 100644 --- a/executor/src/native_executor.rs +++ b/executor/src/native_executor.rs @@ -43,9 +43,9 @@ impl CodeExecutor for NativeExecutor { let native_equivalent = include_bytes!("../../wasm-runtime/target/wasm32-unknown-unknown/release/runtime_polkadot.compact.wasm"); if code == &native_equivalent[..] { runtime_io::with_externalities(ext, || match method { - "execute_block" => safe_call(|| api::execute_encoded_block(data)), - "execute_transaction" => safe_call(|| api::execute_encoded_transaction(data)), - "finalise_block" => safe_call(|| api::finalise_encoded_block(data)), + "execute_block" => safe_call(|| api::execute_block(data)), + "execute_transaction" => safe_call(|| api::execute_transaction(data)), + "finalise_block" => safe_call(|| api::finalise_block(data)), _ => Err(ErrorKind::MethodNotFound(method.to_owned()).into()), }) } else { @@ -229,14 +229,6 @@ mod tests { ) } - #[test] - fn test_execution_works() { - let mut t = new_test_ext(); - println!("Testing Wasm..."); - let r = WasmExecutor.call(&mut t, COMPACT_CODE, "run_tests", &block2().0); - assert!(r.is_ok()); - } - #[test] fn full_native_block_import_works() { let mut t = new_test_ext(); diff --git a/runtime-io/with_std.rs b/runtime-io/with_std.rs index 00f540938f3ec..1af52f70eacde 100644 --- a/runtime-io/with_std.rs +++ b/runtime-io/with_std.rs @@ -134,7 +134,7 @@ pub fn print(value: T) { #[macro_export] macro_rules! impl_stubs { - ( $( $name:ident => $new_name:ident ),* ) => { + ( $( $new_name:ident => $invoke:expr ),* ) => { $( /// Stub version of $name pub fn $new_name(mut input: &[u8]) -> Vec { @@ -143,7 +143,7 @@ macro_rules! impl_stubs { None => panic!("Bad input data provided to {}", stringify!($name)), }; - let output = $name(input); + let output = $invoke(input); $crate::codec::Slicable::to_vec(&output) } )* diff --git a/runtime-io/without_std.rs b/runtime-io/without_std.rs index 3c6d6ef6806a0..bae16b48c40c6 100644 --- a/runtime-io/without_std.rs +++ b/runtime-io/without_std.rs @@ -188,7 +188,7 @@ pub fn print(value: T) { #[macro_export] macro_rules! impl_stubs { - ( $( $name:ident => $new_name:ident ),* ) => { + ( $( $new_name:ident => $invoke:expr ),* ) => { pub mod _internal { $( #[no_mangle] @@ -206,7 +206,7 @@ macro_rules! impl_stubs { None => panic!("Bad input data provided to {}", stringify!($name)), }; - let output = super::$name(input); + let output = ($invoke)(input); let output = $crate::codec::Slicable::to_vec(&output); let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); diff --git a/wasm-runtime/polkadot/src/api.rs b/wasm-runtime/polkadot/src/api.rs index c7b81751a9960..98a7564b5d780 100644 --- a/wasm-runtime/polkadot/src/api.rs +++ b/wasm-runtime/polkadot/src/api.rs @@ -15,35 +15,13 @@ // along with Polkadot. If not, see . use rstd::prelude::*; -use codec::Slicable; -use polkadot_primitives::{Header, Block, UncheckedTransaction}; - -/// Execute a block. -pub fn execute_block(block: Block) { - ::runtime::system::internal::execute_block(block); -} - -/// Execute a transaction. Input data is the concatenation of a serialized header and -/// transaction. Returns the new header. -pub fn execute_transaction((header, utx): (Header, UncheckedTransaction)) -> Header { - ::runtime::system::internal::execute_transaction(utx, header) -} - -/// Finalize a block, given its header. -pub fn finalise_block(header: Header) -> Vec { - let header = ::runtime::system::internal::finalise_block(header); - header.to_vec() -} - -/// Run whatever tests we have on a full block. -pub fn run_tests(block: Block) -> u32 { - let stxs = block.transactions.iter().map(Slicable::to_vec).collect::>(); - stxs.len() as u32 -} +use runtime::{system, parachains, consensus, session}; impl_stubs!( - execute_block => execute_encoded_block, - execute_transaction => execute_encoded_transaction, - finalise_block => finalise_encoded_block, - run_tests => run_encoded_block_tests + execute_block => |block| system::internal::execute_block(block), + execute_transaction => |(header, utx)| system::internal::execute_transaction(utx, header), + finalise_block => |header| system::internal::finalise_block(header), + validator_count => |()| session::validator_count(), + authorities => |()| consensus::authorities(), + duty_roster => |()| parachains::calculate_duty_roster() ); diff --git a/wasm-runtime/polkadot/src/runtime/session.rs b/wasm-runtime/polkadot/src/runtime/session.rs index 2e3f81b6450ee..56a12f05c085c 100644 --- a/wasm-runtime/polkadot/src/runtime/session.rs +++ b/wasm-runtime/polkadot/src/runtime/session.rs @@ -35,7 +35,7 @@ impl StorageVec for ValidatorStorageVec { const PREFIX: &'static[u8] = b"ses:val:"; } -/// Get the current set of authorities. These are the session keys. +/// Get the current set of validators. pub fn validators() -> Vec { ValidatorStorageVec::items() } From bd4ef7a3cd51b7bff7cd79db170c740d78a8b7ca Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 15:00:37 +0100 Subject: [PATCH 06/10] zero-copy slicable API --- Cargo.lock | 2 + client-api/Cargo.toml | 2 + client-api/src/lib.rs | 26 +++- client/src/lib.rs | 4 +- codec/src/lib.rs | 2 +- codec/src/slicable.rs | 136 ++++++++---------- executor/src/native_executor.rs | 4 + polkadot-primitives/src/block.rs | 29 ++-- polkadot-primitives/src/lib.rs | 1 - polkadot-primitives/src/parachain.rs | 29 ++-- polkadot-primitives/src/transaction.rs | 58 ++++---- primitives/src/block.rs | 34 ++--- primitives/src/hash.rs | 4 +- runtime-io/with_std.rs | 4 +- runtime-io/without_std.rs | 2 +- runtime-std/with_std.rs | 11 +- runtime-std/without_std.rs | 9 +- wasm-runtime/polkadot/src/api.rs | 1 + .../polkadot/src/runtime/parachains.rs | 4 +- wasm-runtime/polkadot/src/support/storage.rs | 35 ++++- 20 files changed, 213 insertions(+), 184 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 606badbadad8e..9bae7252437f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,7 +999,9 @@ dependencies = [ name = "polkadot-client-api" version = "0.1.0" dependencies = [ + "polkadot-primitives 0.1.0", "substrate-client 0.1.0", + "substrate-codec 0.1.0", "substrate-state-machine 0.1.0", ] diff --git a/client-api/Cargo.toml b/client-api/Cargo.toml index bdf73b52009d4..c25369dfe2d36 100644 --- a/client-api/Cargo.toml +++ b/client-api/Cargo.toml @@ -5,4 +5,6 @@ authors = ["Parity Technologies "] [dependencies] substrate-client = { path = "../client" } +substrate-codec = { path = "../codec" } substrate-state-machine = { path = "../state-machine" } +polkadot-primitives = { path = "../polkadot-primitives" } diff --git a/client-api/src/lib.rs b/client-api/src/lib.rs index 34d314fc7f3e5..2fbc482a05c0b 100644 --- a/client-api/src/lib.rs +++ b/client-api/src/lib.rs @@ -18,26 +18,48 @@ //! wrappers for state-fetching functions. extern crate substrate_client as s_client; +extern crate substrate_codec as codec; extern crate substrate_state_machine as s_state_machine; +extern crate polkadot_primitives; use s_client::Client; use s_client::backend::Backend as ClientBackend; use s_state_machine::backend::Backend as StateBackend; +use polkadot_primitives::block::HeaderHash; +use polkadot_primitives::parachain::DutyRoster; +use polkadot_primitives::{AccountId, SessionKey}; /// An implementation of the polkadot client API. pub trait PolkadotClient { type Error; + + /// Get the validators at the given block. + fn validators(&self, block: &HeaderHash) -> Result, Self::Error>; + + /// Get the authorities at the given block. These have a 1-to-1 correspondence + /// with the validators, but using temporary session keys. + fn authorities(&self, block: &HeaderHash) -> Result, Self::Error>; + /// Get the duty roster at a given block. + fn duty_roster(&self, block: &HeaderHash) -> Result; } -impl PolkadotClient for Client where +impl PolkadotClient for Client where B: ClientBackend, - E: s_state_machine::CodeExecutor, s_client::error::Error: From<<::State as StateBackend>::Error> { type Error = s_client::error::Error; + fn validators(&self, block: &HeaderHash) -> Result, Self::Error> { + self.call( + block, + "validators", + &[] + ) + } } +// TODO: specialize implementation for native executor to avoid the serialization round-trip. + #[cfg(test)] mod tests { #[test] diff --git a/client/src/lib.rs b/client/src/lib.rs index 81af8f7a08b74..0aeb103f98cdb 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -161,14 +161,14 @@ impl Client where let state = self.state_at(hash)?; let mut changes = state_machine::OverlayedChanges::default(); - let _ = state_machine::execute( + let return_data = state_machine::execute( &state, &mut changes, &self.executor, method, call_data, )?; - Ok(CallResult { return_data: vec![], changes }) + Ok(CallResult { return_data, changes }) } /// Queue a block for import. diff --git a/codec/src/lib.rs b/codec/src/lib.rs index 0a672161e9f9a..c354f432b67ba 100644 --- a/codec/src/lib.rs +++ b/codec/src/lib.rs @@ -28,6 +28,6 @@ mod joiner; mod keyedvec; pub use self::endiansensitive::EndianSensitive; -pub use self::slicable::{Slicable, NonTrivialSlicable}; +pub use self::slicable::{Input, Slicable, NonTrivialSlicable}; pub use self::joiner::Joiner; pub use self::keyedvec::KeyedVec; diff --git a/codec/src/slicable.rs b/codec/src/slicable.rs index 780e8b98d0a7b..6c15223332e18 100644 --- a/codec/src/slicable.rs +++ b/codec/src/slicable.rs @@ -21,13 +21,25 @@ use rstd::vec::Vec; use super::joiner::Joiner; use super::endiansensitive::EndianSensitive; +/// Trait that allows reading of data into a slice. +pub trait Input { + /// Read into the provided input slice. Returns the number of bytes read. + fn read(&mut self, into: &mut [u8]) -> usize; +} + +impl<'a> Input for &'a [u8] { + fn read(&mut self, into: &mut [u8]) -> usize { + let len = ::rstd::cmp::min(into.len(), self.len()); + into[..len].copy_from_slice(&self[..len]); + *self = &self[len..]; + len + } +} + /// Trait that allows zero-copy read/write of value-references to/from slices in LE format. pub trait Slicable: Sized { - /// Attempt to deserialise the value from a slice. Ignore trailing bytes and - /// set the slice's start to just after the last byte consumed. - /// - /// If `None` is returned, then the slice should be unmodified. - fn from_slice(value: &mut &[u8]) -> Option; + /// Attempt to deserialise the value from input. + fn decode(value: &mut I) -> Option; /// Convert self to an owned vector. fn to_vec(&self) -> Vec { self.as_slice_then(|s| s.to_vec()) @@ -39,32 +51,20 @@ pub trait Slicable: Sized { /// Trait to mark that a type is not trivially (essentially "in place") serialisable. pub trait NonTrivialSlicable: Slicable {} -/// Attempt to decode a value from the slice, providing an initial value -/// to replace it with should it fail. -#[macro_export] -macro_rules! try_decode { - ($initial: expr, $current: expr) => { - match $crate::Slicable::from_slice($current) { - Some(x) => x, - None => { - *$current = $initial; - return None; - } - } - } -} - impl Slicable for T { - fn from_slice(value: &mut &[u8]) -> Option { + fn decode(input: &mut I) -> Option { let size = mem::size_of::(); assert!(size > 0, "EndianSensitive can never be implemented for a zero-sized type."); - if value.len() >= size { - let x: T = unsafe { ::rstd::ptr::read(value.as_ptr() as *const T) }; - *value = &value[size..]; - Some(x.from_le()) - } else { - None + let mut val: T = unsafe { mem::zeroed() }; + + unsafe { + let raw: &mut [u8] = slice::from_raw_parts_mut( + &mut val as *mut T as *mut u8, + size + ); + if input.read(raw) != size { return None } } + Some(val.from_le()) } fn as_slice_then R>(&self, f: F) -> R { @@ -85,19 +85,15 @@ impl Slicable for T { } impl Slicable for Vec { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - u32::from_slice(value).and_then(move |len| { + fn decode(input: &mut I) -> Option { + u32::decode(input).and_then(move |len| { let len = len as usize; - - if value.len() < len { - *value = initial; - return None; + let mut vec = vec![0; len]; + if input.read(&mut vec[..len]) != len { + None + } else { + Some(vec) } - - let res = value[..len].to_vec(); - *value = &value[len..]; - Some(res) }) } fn as_slice_then R>(&self, f: F) -> R { @@ -120,13 +116,14 @@ macro_rules! impl_vec_simple_array { impl Slicable for Vec<[T; $size]> where [T; $size]: EndianSensitive { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - u32::from_slice(value).and_then(move |len| { - let len = len as usize; - let mut r = Vec::with_capacity(len); + fn decode(input: &mut I) -> Option { + u32::decode(input).and_then(move |len| { + let mut r = Vec::with_capacity(len as usize); for _ in 0..len { - r.push(try_decode!(initial, value)); + r.push(match Slicable::decode(input) { + Some(x) => x, + None => return None, + }); } Some(r) @@ -159,13 +156,14 @@ impl_vec_simple_array!(1 2 4 8 16 32 64); impl NonTrivialSlicable for Vec where Vec: Slicable {} impl Slicable for Vec { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - u32::from_slice(value).and_then(move |len| { - let len = len as usize; - let mut r = Vec::with_capacity(len); + fn decode(input: &mut I) -> Option { + u32::decode(input).and_then(move |len| { + let mut r = Vec::with_capacity(len as usize); for _ in 0..len { - r.push(try_decode!(initial, value)) + r.push(match T::decode(input) { + Some(x) => x, + None => return None, + }); } Some(r) @@ -191,7 +189,7 @@ impl Slicable for Vec { } impl Slicable for () { - fn from_slice(_value: &mut &[u8]) -> Option<()> { + fn decode(_: &mut I) -> Option<()> { Some(()) } @@ -207,8 +205,8 @@ impl Slicable for () { macro_rules! tuple_impl { ($one:ident,) => { impl<$one: Slicable> Slicable for ($one,) { - fn from_slice(value: &mut &[u8]) -> Option { - match $one::from_slice(value) { + fn decode(input: &mut I) -> Option { + match $one::decode(input) { None => None, Some($one) => Some(($one,)), } @@ -223,13 +221,15 @@ macro_rules! tuple_impl { impl<$first: Slicable, $($rest: Slicable),+> Slicable for ($first, $($rest),+) { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; + fn decode(input: &mut INPUT) -> Option { Some(( - try_decode!(initial, value), - $({ - let x: $rest = try_decode!(initial, value); - x + match $first::decode(input) { + Some(x) => x, + None => return None, + }, + $(match $rest::decode(input) { + Some(x) => x, + None => return None, },)+ )) } @@ -257,7 +257,7 @@ macro_rules! tuple_impl { #[allow(non_snake_case)] mod inner_tuple_impl { - use super::Slicable; + use super::{Input, Slicable}; tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,); } @@ -273,20 +273,4 @@ mod tests { assert_eq!(slice, &b"\x0b\0\0\0Hello world") ); } - - #[test] - fn failed_tuple_decode_does_not_munch() { - let encoded = (vec![1u8, 3, 5, 9], 6u64).to_vec(); - let mut data = &encoded[..]; - assert!(<(Vec, u64, Vec)>::from_slice(&mut data).is_none()); - - // failure to decode shouldn't munch anything. - assert_eq!(data.len(), encoded.len()); - - // full decoding should have munched everything. - let decoded = <(Vec, u64)>::from_slice(&mut data).unwrap(); - assert!(data.is_empty()); - - assert_eq!(decoded, (vec![1, 3, 5,9], 6)); - } } diff --git a/executor/src/native_executor.rs b/executor/src/native_executor.rs index 302bedf9c91ae..f988c91b195f4 100644 --- a/executor/src/native_executor.rs +++ b/executor/src/native_executor.rs @@ -46,6 +46,10 @@ impl CodeExecutor for NativeExecutor { "execute_block" => safe_call(|| api::execute_block(data)), "execute_transaction" => safe_call(|| api::execute_transaction(data)), "finalise_block" => safe_call(|| api::finalise_block(data)), + "validator_count" => safe_call(|| api::validator_count(data)), + "validators" => safe_call(|| api::validators(data)), + "authorities" => safe_call(|| api::authorities(data)), + "duty_roster" => safe_call(|| api::duty_roster(data)), _ => Err(ErrorKind::MethodNotFound(method.to_owned()).into()), }) } else { diff --git a/polkadot-primitives/src/block.rs b/polkadot-primitives/src/block.rs index 5fe01e1dbc579..489411351759b 100644 --- a/polkadot-primitives/src/block.rs +++ b/polkadot-primitives/src/block.rs @@ -20,7 +20,7 @@ use primitives::bytes; use primitives::H256; use rstd::vec::Vec; -use codec::Slicable; +use codec::{Input, Slicable}; use transaction::UncheckedTransaction; /// Used to refer to a block number. @@ -38,8 +38,8 @@ pub type TransactionHash = H256; pub struct Log(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); impl Slicable for Log { - fn from_slice(value: &mut &[u8]) -> Option { - Vec::::from_slice(value).map(Log) + fn decode(input: &mut I) -> Option { + Vec::::decode(input).map(Log) } fn as_slice_then R>(&self, f: F) -> R { @@ -58,8 +58,8 @@ pub struct Digest { } impl Slicable for Digest { - fn from_slice(value: &mut &[u8]) -> Option { - Vec::::from_slice(value).map(|logs| Digest { logs }) + fn decode(input: &mut I) -> Option { + Vec::::decode(input).map(|logs| Digest { logs }) } fn as_slice_then R>(&self, f: F) -> R { @@ -81,8 +81,8 @@ pub struct Block { } impl Slicable for Block { - fn from_slice(value: &mut &[u8]) -> Option { - let (header, transactions) = try_opt!(Slicable::from_slice(value)); + fn decode(input: &mut I) -> Option { + let (header, transactions) = try_opt!(Slicable::decode(input)); Some(Block { header, transactions }) } @@ -134,14 +134,13 @@ impl Header { } impl Slicable for Header { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; + fn decode(input: &mut I) -> Option { Some(Header { - parent_hash: try_decode!(initial, value), - number: try_decode!(initial, value), - state_root: try_decode!(initial, value), - transaction_root: try_decode!(initial, value), - digest: try_decode!(initial, value), + parent_hash: try_opt!(Slicable::decode(input)), + number: try_opt!(Slicable::decode(input)), + state_root: try_opt!(Slicable::decode(input)), + transaction_root: try_opt!(Slicable::decode(input)), + digest: try_opt!(Slicable::decode(input)), }) } @@ -191,6 +190,6 @@ mod tests { }"#); let v = header.to_vec(); - assert_eq!(Header::from_slice(&mut &v[..]).unwrap(), header); + assert_eq!(Header::decode(&mut &v[..]).unwrap(), header); } } diff --git a/polkadot-primitives/src/lib.rs b/polkadot-primitives/src/lib.rs index 56ae47e2b1738..14b8e78d30f40 100644 --- a/polkadot-primitives/src/lib.rs +++ b/polkadot-primitives/src/lib.rs @@ -33,7 +33,6 @@ extern crate substrate_primitives as primitives; #[cfg(test)] extern crate substrate_serializer; -#[macro_use] extern crate substrate_codec as codec; macro_rules! try_opt { diff --git a/polkadot-primitives/src/parachain.rs b/polkadot-primitives/src/parachain.rs index bd4310551ac86..6535e0f0e04f1 100644 --- a/polkadot-primitives/src/parachain.rs +++ b/polkadot-primitives/src/parachain.rs @@ -19,7 +19,7 @@ #[cfg(feature = "std")] use primitives::bytes; use primitives; -use codec::{Slicable, NonTrivialSlicable}; +use codec::{Input, Slicable, NonTrivialSlicable}; use rstd::vec::Vec; /// Unique identifier of a parachain. @@ -35,9 +35,9 @@ impl From for Id { fn from(x: u32) -> Self { Id(x) } } -impl ::codec::Slicable for Id { - fn from_slice(value: &mut &[u8]) -> Option { - u32::from_slice(value).map(Id) +impl Slicable for Id { + fn decode(input: &mut I) -> Option { + u32::decode(input).map(Id) } fn as_slice_then R>(&self, f: F) -> R { @@ -56,13 +56,12 @@ pub enum Chain { } impl Slicable for Chain { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - let disc = try_opt!(u8::from_slice(value)); + fn decode(input: &mut I) -> Option { + let disc = try_opt!(u8::decode(input)); match disc { 0 => Some(Chain::Relay), - 1 => Some(Chain::Parachain(try_decode!(initial, value))), + 1 => Some(Chain::Parachain(try_opt!(Slicable::decode(input)))), _ => None, } } @@ -99,12 +98,10 @@ pub struct DutyRoster { } impl Slicable for DutyRoster { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - + fn decode(input: &mut I) -> Option { Some(DutyRoster { - validator_duty: try_decode!(initial, value), - guarantor_duty: try_decode!(initial, value), + validator_duty: try_opt!(Slicable::decode(input)), + guarantor_duty: try_opt!(Slicable::decode(input)), }) } @@ -202,9 +199,9 @@ pub struct ValidationCode(#[cfg_attr(feature = "std", serde(with="bytes"))] pub #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] pub struct Activity(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); -impl ::codec::Slicable for Activity { - fn from_slice(value: &mut &[u8]) -> Option { - Vec::::from_slice(value).map(Activity) +impl Slicable for Activity { + fn decode(input: &mut I) -> Option { + Vec::::decode(input).map(Activity) } fn as_slice_then R>(&self, f: F) -> R { diff --git a/polkadot-primitives/src/transaction.rs b/polkadot-primitives/src/transaction.rs index b7bbc83bdd69b..0a68256f744d7 100644 --- a/polkadot-primitives/src/transaction.rs +++ b/polkadot-primitives/src/transaction.rs @@ -17,7 +17,7 @@ //! Transaction type. use rstd::vec::Vec; -use codec::Slicable; +use codec::{Input, Slicable}; #[cfg(feature = "std")] use std::fmt; @@ -91,25 +91,23 @@ pub enum Proposal { } impl Slicable for Proposal { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - - let id = try_opt!(u8::from_slice(value).and_then(InternalFunctionId::from_u8)); + fn decode(input: &mut I) -> Option { + let id = try_opt!(u8::decode(input).and_then(InternalFunctionId::from_u8)); let function = match id { InternalFunctionId::SystemSetCode => - Proposal::SystemSetCode(try_decode!(initial, value)), + Proposal::SystemSetCode(try_opt!(Slicable::decode(input))), InternalFunctionId::SessionSetLength => - Proposal::SessionSetLength(try_decode!(initial, value)), + Proposal::SessionSetLength(try_opt!(Slicable::decode(input))), InternalFunctionId::SessionForceNewSession => Proposal::SessionForceNewSession, InternalFunctionId::StakingSetSessionsPerEra => - Proposal::StakingSetSessionsPerEra(try_decode!(initial, value)), + Proposal::StakingSetSessionsPerEra(try_opt!(Slicable::decode(input))), InternalFunctionId::StakingSetBondingDuration => - Proposal::StakingSetBondingDuration(try_decode!(initial, value)), + Proposal::StakingSetBondingDuration(try_opt!(Slicable::decode(input))), InternalFunctionId::StakingSetValidatorCount => - Proposal::StakingSetValidatorCount(try_decode!(initial, value)), + Proposal::StakingSetValidatorCount(try_opt!(Slicable::decode(input))), InternalFunctionId::StakingForceNewEra => Proposal::StakingForceNewEra, InternalFunctionId::GovernanceSetApprovalPpmRequired => - Proposal::GovernanceSetApprovalPpmRequired(try_decode!(initial, value)), + Proposal::GovernanceSetApprovalPpmRequired(try_opt!(Slicable::decode(input))), }; Some(function) @@ -212,26 +210,25 @@ pub enum Function { } impl Slicable for Function { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - let id = try_opt!(u8::from_slice(value).and_then(FunctionId::from_u8)); + fn decode(input: &mut I) -> Option { + let id = try_opt!(u8::decode(input).and_then(FunctionId::from_u8)); Some(match id { FunctionId::TimestampSet => - Function::TimestampSet(try_decode!(initial, value)), + Function::TimestampSet(try_opt!(Slicable::decode(input))), FunctionId::SessionSetKey => - Function::SessionSetKey(try_decode!(initial, value)), + Function::SessionSetKey(try_opt!(Slicable::decode(input))), FunctionId::StakingStake => Function::StakingStake, FunctionId::StakingUnstake => Function::StakingUnstake, FunctionId::StakingTransfer => { - let to = try_decode!(initial, value); - let amount = try_decode!(initial, value); + let to = try_opt!(Slicable::decode(input)); + let amount = try_opt!(Slicable::decode(input)); Function::StakingTransfer(to, amount) } FunctionId::GovernancePropose => - Function::GovernancePropose(try_decode!(initial, value)), + Function::GovernancePropose(try_opt!(Slicable::decode(input))), FunctionId::GovernanceApprove => - Function::GovernanceApprove(try_decode!(initial, value)), + Function::GovernanceApprove(try_opt!(Slicable::decode(input))), }) } @@ -288,12 +285,11 @@ pub struct Transaction { } impl Slicable for Transaction { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; + fn decode(input: &mut I) -> Option { Some(Transaction { - signed: try_decode!(initial, value), - nonce: try_decode!(initial, value), - function: try_decode!(initial, value), + signed: try_opt!(Slicable::decode(input)), + nonce: try_opt!(Slicable::decode(input)), + function: try_opt!(Slicable::decode(input)), }) } @@ -325,18 +321,16 @@ pub struct UncheckedTransaction { } impl Slicable for UncheckedTransaction { - fn from_slice(value: &mut &[u8]) -> Option { - let initial = *value; - + fn decode(input: &mut I) -> Option { // This is a little more complicated than usua since the binary format must be compatible // with substrate's generic `Vec` type. Basically this just means accepting that there // will be a prefix of u32, which has the total number of bytes following (we don't need // to use this). - let _length_do_not_remove_me_see_above: u32 = try_decode!(initial, value); + let _length_do_not_remove_me_see_above: u32 = try_opt!(Slicable::decode(input)); Some(UncheckedTransaction { - transaction: try_decode!(initial, value), - signature: try_decode!(initial, value), + transaction: try_opt!(Slicable::decode(input)), + signature: try_opt!(Slicable::decode(input)), }) } @@ -404,6 +398,6 @@ mod tests { let v = Slicable::to_vec(&tx); println!("{}", HexDisplay::from(&v)); - assert_eq!(UncheckedTransaction::from_slice(&mut &v[..]).unwrap(), tx); + assert_eq!(UncheckedTransaction::decode(&mut &v[..]).unwrap(), tx); } } diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 4ac11f9c461f2..41957f577464a 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -19,7 +19,7 @@ #[cfg(feature = "std")] use bytes; use rstd::vec::Vec; -use codec::Slicable; +use codec::{Input, Slicable}; use hash::H256; /// Used to refer to a block number. @@ -37,8 +37,8 @@ pub type TransactionHash = H256; pub struct Transaction(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); impl Slicable for Transaction { - fn from_slice(value: &mut &[u8]) -> Option { - Vec::::from_slice(value).map(Transaction) + fn decode(input: &mut I) -> Option { + Vec::::decode(input).map(Transaction) } fn as_slice_then R>(&self, f: F) -> R { @@ -54,8 +54,8 @@ impl ::codec::NonTrivialSlicable for Transaction { } pub struct Log(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec); impl Slicable for Log { - fn from_slice(value: &mut &[u8]) -> Option { - Vec::::from_slice(value).map(Log) + fn decode(input: &mut I) -> Option { + Vec::::decode(input).map(Log) } fn as_slice_then R>(&self, f: F) -> R { @@ -74,8 +74,8 @@ pub struct Digest { } impl Slicable for Digest { - fn from_slice(value: &mut &[u8]) -> Option { - Vec::::from_slice(value).map(|logs| Digest { logs }) + fn decode(input: &mut I) -> Option { + Vec::::decode(input).map(|logs| Digest { logs }) } fn as_slice_then R>(&self, f: F) -> R { @@ -97,10 +97,10 @@ pub struct Block { } impl Slicable for Block { - fn from_slice(value: &mut &[u8]) -> Option { + fn decode(input: &mut I) -> Option { Some(Block { - header: try_opt!(Slicable::from_slice(value)), - transactions: try_opt!(Slicable::from_slice(value)), + header: try_opt!(Slicable::decode(input)), + transactions: try_opt!(Slicable::decode(input)), }) } @@ -152,13 +152,13 @@ impl Header { } impl Slicable for Header { - fn from_slice(value: &mut &[u8]) -> Option { + fn decode(input: &mut I) -> Option { Some(Header { - parent_hash: try_opt!(Slicable::from_slice(value)), - number: try_opt!(Slicable::from_slice(value)), - state_root: try_opt!(Slicable::from_slice(value)), - transaction_root: try_opt!(Slicable::from_slice(value)), - digest: try_opt!(Slicable::from_slice(value)), + parent_hash: try_opt!(Slicable::decode(input)), + number: try_opt!(Slicable::decode(input)), + state_root: try_opt!(Slicable::decode(input)), + transaction_root: try_opt!(Slicable::decode(input)), + digest: try_opt!(Slicable::decode(input)), }) } @@ -208,6 +208,6 @@ mod tests { }"#); let v = header.to_vec(); - assert_eq!(Header::from_slice(&mut &v[..]).unwrap(), header); + assert_eq!(Header::decode(&mut &v[..]).unwrap(), header); } } diff --git a/primitives/src/hash.rs b/primitives/src/hash.rs index 7a529b1a0c28b..b3c069f4a55d0 100644 --- a/primitives/src/hash.rs +++ b/primitives/src/hash.rs @@ -40,8 +40,8 @@ macro_rules! impl_rest { } impl ::codec::Slicable for $name { - fn from_slice(value: &mut &[u8]) -> Option { - <[u8; $len] as ::codec::Slicable>::from_slice(value).map($name) + fn decode(input: &mut I) -> Option { + <[u8; $len] as ::codec::Slicable>::decode(input).map($name) } fn as_slice_then R>(&self, f: F) -> R { diff --git a/runtime-io/with_std.rs b/runtime-io/with_std.rs index 1af52f70eacde..dd3cb183bd0a0 100644 --- a/runtime-io/with_std.rs +++ b/runtime-io/with_std.rs @@ -50,7 +50,7 @@ pub fn storage(key: &[u8]) -> Vec { } /// Get `key` from storage, placing the value into `value_out` (as much as possible) and return -/// the number of bytes that the key in storage was. +/// the number of bytes that the key in storage was beyond the offset. pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize { ext::with(|ext| { if let Ok(value) = ext.storage(key) { @@ -138,7 +138,7 @@ macro_rules! impl_stubs { $( /// Stub version of $name pub fn $new_name(mut input: &[u8]) -> Vec { - let input = match $crate::codec::Slicable::from_slice(&mut input) { + let input = match $crate::codec::Slicable::decode(&mut input) { Some(input) => input, None => panic!("Bad input data provided to {}", stringify!($name)), }; diff --git a/runtime-io/without_std.rs b/runtime-io/without_std.rs index bae16b48c40c6..26e2e98c47782 100644 --- a/runtime-io/without_std.rs +++ b/runtime-io/without_std.rs @@ -82,7 +82,7 @@ pub fn set_storage(key: &[u8], value: &[u8]) { } /// Get `key` from storage, placing the value into `value_out` (as much as possible) and return -/// the number of bytes that the key in storage was. +/// the number of bytes that the key in storage was beyond the offset. pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize { unsafe { ext_get_storage_into(key.as_ptr(), key.len() as u32, value_out.as_mut_ptr(), value_out.len() as u32, value_offset as u32) as usize diff --git a/runtime-std/with_std.rs b/runtime-std/with_std.rs index 01212679fc43e..960871665e2c4 100644 --- a/runtime-std/with_std.rs +++ b/runtime-std/with_std.rs @@ -14,12 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -pub use std::vec; -pub use std::rc; -pub use std::cell; pub use std::boxed; -pub use std::slice; +pub use std::cell; +pub use std::cmp; +pub use std::iter; pub use std::mem; pub use std::ops; -pub use std::iter; pub use std::ptr; +pub use std::rc; +pub use std::slice; +pub use std::vec; diff --git a/runtime-std/without_std.rs b/runtime-std/without_std.rs index ea0f278d37e26..68270807ab6a9 100644 --- a/runtime-std/without_std.rs +++ b/runtime-std/without_std.rs @@ -17,12 +17,13 @@ #[cfg(feature = "nightly")] extern crate alloc; -pub use alloc::vec; pub use alloc::boxed; pub use alloc::rc; -pub use core::mem; -pub use core::slice; +pub use alloc::vec; pub use core::cell; -pub use core::ops; +pub use core::cmp; pub use core::iter; +pub use core::mem; +pub use core::ops; pub use core::ptr; +pub use core::slice; diff --git a/wasm-runtime/polkadot/src/api.rs b/wasm-runtime/polkadot/src/api.rs index 98a7564b5d780..3ae07ac9173fa 100644 --- a/wasm-runtime/polkadot/src/api.rs +++ b/wasm-runtime/polkadot/src/api.rs @@ -22,6 +22,7 @@ impl_stubs!( execute_transaction => |(header, utx)| system::internal::execute_transaction(utx, header), finalise_block => |header| system::internal::finalise_block(header), validator_count => |()| session::validator_count(), + validators => |()| session::validators(), authorities => |()| consensus::authorities(), duty_roster => |()| parachains::calculate_duty_roster() ); diff --git a/wasm-runtime/polkadot/src/runtime/parachains.rs b/wasm-runtime/polkadot/src/runtime/parachains.rs index d75b702925766..539e3e722b256 100644 --- a/wasm-runtime/polkadot/src/runtime/parachains.rs +++ b/wasm-runtime/polkadot/src/runtime/parachains.rs @@ -55,8 +55,8 @@ pub fn calculate_duty_roster() -> DutyRoster { let remaining = (validator_count - i) as usize; // 4 * 2 32-bit ints per 256-bit seed. - let val_index = u32::from_slice(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; - let gua_index = u32::from_slice(&mut &seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; + let val_index = u32::decode(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; + let gua_index = u32::decode(&mut &seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining; if offset == 24 { // into the last 8 bytes - rehash to gather new entropy diff --git a/wasm-runtime/polkadot/src/support/storage.rs b/wasm-runtime/polkadot/src/support/storage.rs index 5878113b133fa..8306906914b87 100644 --- a/wasm-runtime/polkadot/src/support/storage.rs +++ b/wasm-runtime/polkadot/src/support/storage.rs @@ -18,14 +18,33 @@ use rstd::prelude::*; use runtime_io::{self, twox_128}; -use codec::{Slicable, KeyedVec}; +use codec::{Input, Slicable, KeyedVec}; // TODO: consider using blake256 to avoid possible preimage attack. +struct IncrementalInput<'a> { + key: &'a [u8], + pos: usize, +} + +impl<'a> Input for IncrementalInput<'a> { + fn read(&mut self, into: &mut [u8]) -> usize { + let len = runtime_io::read_storage(self.key, into, self.pos); + let read = ::rstd::cmp::min(len, into.len()); + self.pos += read; + read + } +} + /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { - let raw = runtime_io::storage(&twox_128(key)[..]); - Slicable::from_slice(&mut &raw[..]) + let key = twox_128(key); + let mut input = IncrementalInput { + key: &key[..], + pos: 0, + }; + + Slicable::decode(&mut input) } /// Return the value of the item in storage under `key`, or the type's default if there is no @@ -142,12 +161,16 @@ pub trait StorageVec { } pub mod unhashed { - use super::{runtime_io, Slicable, KeyedVec, Vec}; + use super::{runtime_io, Slicable, KeyedVec, Vec, IncrementalInput}; /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { - let raw = runtime_io::storage(key); - T::from_slice(&mut &raw[..]) + let mut input = IncrementalInput { + key, + pos: 0, + }; + + T::decode(&mut input) } /// Return the value of the item in storage under `key`, or the type's default if there is no From a339bd47a5014cec58b8fefac023e5147c90edae Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 17:23:50 +0100 Subject: [PATCH 07/10] minimal polkadot-client API --- Cargo.lock | 13 +++ Cargo.toml | 15 +-- polkadot/api/Cargo.toml | 13 +++ polkadot/api/src/lib.rs | 118 ++++++++++++++++++++++ polkadot/executor/src/lib.rs | 25 ++++- substrate/client/src/lib.rs | 26 +++-- substrate/executor/src/lib.rs | 2 +- substrate/executor/src/native_executor.rs | 15 +-- substrate/rpc/src/chain/mod.rs | 3 +- substrate/rpc/src/state/mod.rs | 5 +- 10 files changed, 201 insertions(+), 34 deletions(-) create mode 100644 polkadot/api/Cargo.toml create mode 100644 polkadot/api/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b2fca77787b12..9dd2bce216683 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -958,6 +958,19 @@ dependencies = [ "polkadot-network 0.1.0", ] +[[package]] +name = "polkadot-api" +version = "0.1.0" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-executor 0.1.0", + "polkadot-primitives 0.1.0", + "polkadot-runtime 0.1.0", + "substrate-client 0.1.0", + "substrate-executor 0.1.0", + "substrate-state-machine 0.1.0", +] + [[package]] name = "polkadot-candidate-agreement" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 1c3d80e3c7dd7..c866a54a64bf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,21 +14,22 @@ polkadot-network = { path = "polkadot/network" } [workspace] members = [ - "substrate/client", - "substrate/codec", - "substrate/environmental", - "substrate/executor", - "polkadot/network", + "polkadot/api", "polkadot/candidate-agreement", "polkadot/cli", "polkadot/collator", "polkadot/executor", - "polkadot/runtime", + "polkadot/network", "polkadot/primitives", + "polkadot/runtime", "polkadot/validator", + "substrate/client", + "substrate/codec", + "substrate/environmental", + "substrate/executor", "substrate/primitives", - "substrate/rpc", "substrate/rpc-servers", + "substrate/rpc", "substrate/runtime-io", "substrate/runtime-std", "substrate/serializer", diff --git a/polkadot/api/Cargo.toml b/polkadot/api/Cargo.toml new file mode 100644 index 0000000000000..de3e082548a75 --- /dev/null +++ b/polkadot/api/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "polkadot-api" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +error-chain = "0.11" +polkadot-executor = { path = "../executor" } +polkadot-runtime = { path = "../runtime" } +polkadot-primitives = { path = "../primitives" } +substrate-client = { path = "../../substrate/client" } +substrate-executor = { path = "../../substrate/executor" } +substrate-state-machine = { path = "../../substrate/state-machine" } diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs new file mode 100644 index 0000000000000..df1a084c4f998 --- /dev/null +++ b/polkadot/api/src/lib.rs @@ -0,0 +1,118 @@ +// 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 . + +//! Strongly typed API for Polkadot based around the locally-compiled native +//! runtime. + +extern crate polkadot_executor as p_executor; +extern crate polkadot_runtime ; +extern crate polkadot_primitives as primitives; +extern crate substrate_client as client; +extern crate substrate_executor as s_executor; +extern crate substrate_state_machine as state_machine; + +#[macro_use] +extern crate error_chain; + +use client::backend::Backend; +use client::blockchain::BlockId; +use client::Client; +use polkadot_runtime::runtime; +use p_executor::LocalNativeExecutionDispatch as LocalDispatch; +use s_executor::{NativeExecutionDispatch, NativeExecutor}; +use primitives::{AccountId, SessionKey}; +use primitives::parachain::DutyRoster; + +error_chain! { + errors { + /// Unknown runtime code. + UnknownRuntime { + description("Unknown runtime code") + display("Unknown runtime code") + } + UnknownBlock(b: BlockId) { + description("Unknown block") + display("Unknown block") + } + /// Some other error. + // TODO: allow to be specified as associated type of PolkadotApi + Other(e: Box<::std::error::Error + Send>) { + description("Other error") + display("Other error: {}", e.description()) + } + } + + links { + Executor(s_executor::error::Error, s_executor::error::ErrorKind); + } +} + +/// Trait encapsulating the Polkadot API. +/// +/// All calls should fail when the exact runtime is unknown. +pub trait PolkadotApi { + /// Get authorities at a given block. + fn authorities(&self, at: BlockId) -> Result>; + + /// Get validators at a given block. + fn validators(&self, at: BlockId) -> Result>; + + /// Get the authority duty roster at a block. + fn duty_roster(&self, at: BlockId) -> Result; +} + +fn convert_client_error(e: client::error::Error) -> Error { + match e { + client::error::Error(client::error::ErrorKind::UnknownBlock(b), _) => Error::from_kind(ErrorKind::UnknownBlock(b)), + other => Error::from_kind(ErrorKind::Other(Box::new(other) as Box<_>)), + } +} + +// set up the necessary scaffolding to execute the runtime. +macro_rules! with_runtime { + ($client: ident, $at: expr, $exec: expr) => {{ + // bail if the code is not the same as the natively linked. + if $client.code_at($at).map_err(convert_client_error)? != LocalDispatch::native_equivalent() { + bail!(ErrorKind::UnknownRuntime); + } + + $client.state_at($at).map_err(convert_client_error).and_then(|state| { + let mut changes = Default::default(); + let mut ext = state_machine::Ext { + overlay: &mut changes, + backend: &state, + }; + + LocalDispatch::execute_runtime(&mut ext, $exec).map_err(Into::into) + }) + }} +} + +impl PolkadotApi for Client> + where ::client::error::Error: From<<::State as state_machine::backend::Backend>::Error> +{ + fn authorities(&self, at: BlockId) -> Result> { + with_runtime!(self, at, ::runtime::consensus::authorities) + } + + fn validators(&self, at: BlockId) -> Result> { + with_runtime!(self, at, ::runtime::session::validators) + } + + fn duty_roster(&self, at: BlockId) -> Result { + with_runtime!(self, at, ::runtime::parachains::calculate_duty_roster) + } +} diff --git a/polkadot/executor/src/lib.rs b/polkadot/executor/src/lib.rs index 6d4f6c2110d3d..c9887eeb1d9f8 100644 --- a/polkadot/executor/src/lib.rs +++ b/polkadot/executor/src/lib.rs @@ -31,11 +31,25 @@ extern crate triehash; extern crate hex_literal; use polkadot_runtime as runtime; +use substrate_executor::error::{Error, ErrorKind}; use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; +use state_machine::Externalities; + +use std::panic::catch_unwind; + /// A null struct which implements `NativeExecutionDispatch` feeding in the hard-coded runtime. pub struct LocalNativeExecutionDispatch; +impl LocalNativeExecutionDispatch { + /// Set up the externalities and safe calling environment to execute calls to the runtime. + pub fn execute_runtime(ext: &mut Externalities, f: F) -> Result + where F: ::std::panic::UnwindSafe + FnOnce() -> U + { + runtime_io::with_externalities(ext, move || safe_call(f)) + } +} + impl NativeExecutionDispatch for LocalNativeExecutionDispatch { fn native_equivalent() -> &'static [u8] { // WARNING!!! This assumes that the runtime was built *before* the main project. Until we @@ -43,11 +57,18 @@ impl NativeExecutionDispatch for LocalNativeExecutionDispatch { include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm") } - fn dispatch(method: &str, data: &[u8]) -> Option> { - runtime::api::dispatch(method, data) + fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> Result, Error> { + LocalNativeExecutionDispatch::execute_runtime(ext, move || runtime::api::dispatch(method, data))? + .ok_or(ErrorKind::MethodNotFound(method.to_owned()).into()) } } +fn safe_call(f: F) -> Result + where F: ::std::panic::UnwindSafe + FnOnce() -> U +{ + catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()) +} + /// Creates new RustExecutor for contracts. pub fn executor() -> NativeExecutor { NativeExecutor { _dummy: ::std::marker::PhantomData } diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index 72314d4ce2f80..b818dc0466f45 100644 --- a/substrate/client/src/lib.rs +++ b/substrate/client/src/lib.rs @@ -139,22 +139,28 @@ impl Client where }) } - fn state_at(&self, hash: &block::HeaderHash) -> error::Result { - self.backend.state_at(BlockId::Hash(*hash)) + /// Get a reference to the state at a given block. + pub fn state_at(&self, block: BlockId) -> error::Result { + self.backend.state_at(block) } /// Return single storage entry of contract under given address in state in a block of given hash. - pub fn storage(&self, hash: &block::HeaderHash, key: &StorageKey) -> error::Result { - Ok(self.state_at(hash)? + pub fn storage(&self, block: BlockId, key: &StorageKey) -> error::Result { + Ok(self.state_at(block)? .storage(&key.0) .map(|x| StorageData(x.to_vec()))?) } + /// Get the code at a given block. + pub fn code_at(&self, block: BlockId) -> error::Result> { + self.storage(block, &StorageKey(b":code:".to_vec())).map(|data| data.0) + } + /// Execute a call to a contract on top of state in a block of given hash. /// /// No changes are made. - pub fn call(&self, hash: &block::HeaderHash, method: &str, call_data: &[u8]) -> error::Result { - let state = self.state_at(hash)?; + pub fn call(&self, block: BlockId, method: &str, call_data: &[u8]) -> error::Result { + let state = self.state_at(block)?; let mut changes = state_machine::OverlayedChanges::default(); let return_data = state_machine::execute( @@ -197,9 +203,9 @@ impl Client where } /// Get block status. - pub fn block_status(&self, hash: &block::HeaderHash) -> error::Result { + pub fn block_status(&self, block: BlockId) -> error::Result { // TODO: more efficient implementation - match self.backend.blockchain().header(BlockId::Hash(*hash)).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { + match self.backend.blockchain().header(block).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { true => Ok(BlockStatus::InChain), false => Ok(BlockStatus::Unknown), } @@ -211,7 +217,7 @@ impl Client where } /// Get block header by hash. - pub fn header(&self, hash: &block::HeaderHash) -> error::Result> { - self.backend.blockchain().header(BlockId::Hash(*hash)) + pub fn header(&self, block: BlockId) -> error::Result> { + self.backend.blockchain().header(block) } } diff --git a/substrate/executor/src/lib.rs b/substrate/executor/src/lib.rs index 266373f2b2297..d7e2ba33d582f 100644 --- a/substrate/executor/src/lib.rs +++ b/substrate/executor/src/lib.rs @@ -61,4 +61,4 @@ mod native_executor; pub mod error; pub use wasm_executor::WasmExecutor; -pub use native_executor::{NativeExecutionDispatch, NativeExecutor}; +pub use native_executor::{NativeExecutor, NativeExecutionDispatch}; diff --git a/substrate/executor/src/native_executor.rs b/substrate/executor/src/native_executor.rs index 0ee6a66864be2..74a950528e199 100644 --- a/substrate/executor/src/native_executor.rs +++ b/substrate/executor/src/native_executor.rs @@ -14,13 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use error::{Error, ErrorKind, Result}; -use runtime_io; +use error::{Error, Result}; use state_machine::{Externalities, CodeExecutor}; use wasm_executor::WasmExecutor; -use std::panic::catch_unwind; - /// Delegate for dispatching a CodeExecutor call to native code. pub trait NativeExecutionDispatch { /// Get the wasm code that the native dispatch will be equivalent to. @@ -28,7 +25,7 @@ pub trait NativeExecutionDispatch { /// Dispatch a method and input data to be executed natively. Returns `Some` result or `None` /// if the `method` is unknown. Panics if there's an unrecoverable error. - fn dispatch(method: &str, data: &[u8]) -> Option>; + fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> Result>; } /// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence @@ -38,10 +35,6 @@ pub struct NativeExecutor { pub _dummy: ::std::marker::PhantomData, } -fn safe_call Option>>(f: F) -> Result>> { - catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()) -} - impl CodeExecutor for NativeExecutor { type Error = Error; @@ -53,8 +46,8 @@ impl CodeExecutor for NativeExecutor Result> { if code == D::native_equivalent() { - runtime_io::with_externalities(ext, || safe_call(|| D::dispatch(method, data)))? - .ok_or(ErrorKind::MethodNotFound(method.to_owned()).into()) + // call native + D::dispatch(ext, method, data) } else { // call into wasm. WasmExecutor.call(ext, code, method, data) diff --git a/substrate/rpc/src/chain/mod.rs b/substrate/rpc/src/chain/mod.rs index 473b53aff0136..dfb6501264aac 100644 --- a/substrate/rpc/src/chain/mod.rs +++ b/substrate/rpc/src/chain/mod.rs @@ -18,6 +18,7 @@ use primitives::block; use client; +use client::blockchain::BlockId; use state_machine; mod error; @@ -42,6 +43,6 @@ impl ChainApi for client::Client where client::error::Error: From<<::State as state_machine::backend::Backend>::Error>, { fn header(&self, hash: block::HeaderHash) -> Result> { - client::Client::header(self, &hash).chain_err(|| "Blockchain error") + client::Client::header(self, BlockId::Hash(hash)).chain_err(|| "Blockchain error") } } diff --git a/substrate/rpc/src/state/mod.rs b/substrate/rpc/src/state/mod.rs index 2549f86a85895..109046ac74b76 100644 --- a/substrate/rpc/src/state/mod.rs +++ b/substrate/rpc/src/state/mod.rs @@ -22,6 +22,7 @@ mod error; mod tests; use client::{self, Client}; +use client::blockchain::BlockId; use primitives::block; use primitives::storage::{StorageKey, StorageData}; use state_machine; @@ -47,10 +48,10 @@ impl StateApi for Client where client::error::Error: From<<::State as state_machine::backend::Backend>::Error>, { fn storage(&self, key: StorageKey, block: block::HeaderHash) -> Result { - Ok(self.storage(&block, &key)?) + Ok(self.storage(BlockId::Hash(block), &key)?) } fn call(&self, method: String, data: Vec, block: block::HeaderHash) -> Result> { - Ok(self.call(&block, &method, &data)?.return_data) + Ok(self.call(BlockId::Hash(block), &method, &data)?.return_data) } } From 3a691b235c7427ec97ea41b2be4b233d7a57d0f9 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 17:59:45 +0100 Subject: [PATCH 08/10] fix WASM API generation --- polkadot/runtime/src/api.rs | 1 - polkadot/runtime/src/genesismap.rs | 6 +- polkadot/runtime/src/lib.rs | 4 +- .../release/polkadot_runtime.compact.wasm | Bin 70314 -> 77991 bytes .../release/polkadot_runtime.wasm | Bin 70393 -> 78040 bytes substrate/codec/src/lib.rs | 1 + substrate/codec/src/slicable.rs | 2 + substrate/runtime-io/without_std.rs | 56 +++++++++--------- substrate/runtime-std/src/lib.rs | 1 + substrate/runtime-std/without_std.rs | 1 + 10 files changed, 38 insertions(+), 34 deletions(-) diff --git a/polkadot/runtime/src/api.rs b/polkadot/runtime/src/api.rs index 3ae07ac9173fa..3f9ff71473dac 100644 --- a/polkadot/runtime/src/api.rs +++ b/polkadot/runtime/src/api.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use rstd::prelude::*; use runtime::{system, parachains, consensus, session}; impl_stubs!( diff --git a/polkadot/runtime/src/genesismap.rs b/polkadot/runtime/src/genesismap.rs index a12086cc47d7b..7cb0aba5b63ea 100644 --- a/polkadot/runtime/src/genesismap.rs +++ b/polkadot/runtime/src/genesismap.rs @@ -16,12 +16,12 @@ //! Tool for creating the genesis block. -use std::collections::HashMap; -use runtime_io::twox_128; use codec::{KeyedVec, Joiner}; -use support::Hashable; use polkadot_primitives::{BlockNumber, Block, AccountId}; +use std::collections::HashMap; +use runtime_io::twox_128; use runtime::staking::Balance; +use support::Hashable; /// Configuration of a general Polkadot genesis block. pub struct GenesisConfig { diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index 424cece1bba29..10d37aacaeadc 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -37,9 +37,11 @@ extern crate hex_literal; #[macro_use] pub mod support; pub mod runtime; -pub mod genesismap; pub mod api; +#[cfg(feature = "std")] +pub mod genesismap; + /// Type definitions and helpers for transactions. pub mod transaction { use rstd::ops; diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index 3afbce2059eec0c867d62180c89b42001c635e65..5418e0432c20b6e36fc79ff8ab4f65e793bcc78b 100644 GIT binary patch literal 77991 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 ir=v;R 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 E9}(@W7XSbN diff --git a/substrate/codec/src/lib.rs b/substrate/codec/src/lib.rs index c354f432b67ba..0c032bd5d813e 100644 --- a/substrate/codec/src/lib.rs +++ b/substrate/codec/src/lib.rs @@ -20,6 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc))] +#[macro_use] extern crate substrate_runtime_std as rstd; mod endiansensitive; diff --git a/substrate/codec/src/slicable.rs b/substrate/codec/src/slicable.rs index 6c15223332e18..81e7dfdf01f10 100644 --- a/substrate/codec/src/slicable.rs +++ b/substrate/codec/src/slicable.rs @@ -257,6 +257,8 @@ macro_rules! tuple_impl { #[allow(non_snake_case)] mod inner_tuple_impl { + use rstd::vec::Vec; + use super::{Input, Slicable}; tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,); } diff --git a/substrate/runtime-io/without_std.rs b/substrate/runtime-io/without_std.rs index afc9a45d58548..a4d8e4076d5ee 100644 --- a/substrate/runtime-io/without_std.rs +++ b/substrate/runtime-io/without_std.rs @@ -182,34 +182,32 @@ pub fn print(value: T) { #[macro_export] macro_rules! impl_stubs { ( $( $new_name:ident => $invoke:expr ),* ) => { - pub mod _internal { - $( - #[no_mangle] - pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { - let mut input = if input_len == 0 { - &[0u8; 0] - } else { - unsafe { - $crate::slice::from_raw_parts(input_data, input_len) - } - }; - - let input = match $crate::codec::Slicable::from_slice(&mut input) { - Some(input) => input, - None => panic!("Bad input data provided to {}", stringify!($name)), - }; - - let output = ($invoke)(input); - let output = $crate::codec::Slicable::to_vec(&output); - let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); - - // Leak the output vector to avoid it being freed. - // This is fine in a WASM context since the heap - // will be discarded after the call. - ::core::mem::forget(output); - res - } - )* - } + $( + #[no_mangle] + pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { + let mut input = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::slice::from_raw_parts(input_data, input_len) + } + }; + + let input = match $crate::codec::Slicable::decode(&mut input) { + Some(input) => input, + None => panic!("Bad input data provided to {}", stringify!($name)), + }; + + let output = ($invoke)(input); + let output = $crate::codec::Slicable::to_vec(&output); + let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); + + // Leak the output vector to avoid it being freed. + // This is fine in a WASM context since the heap + // will be discarded after the call. + ::core::mem::forget(output); + res + } + )* } } diff --git a/substrate/runtime-std/src/lib.rs b/substrate/runtime-std/src/lib.rs index 2dde712473aee..0128ebd31ae61 100644 --- a/substrate/runtime-std/src/lib.rs +++ b/substrate/runtime-std/src/lib.rs @@ -21,6 +21,7 @@ #![cfg_attr(not(feature = "std"), feature(lang_items))] #![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #![cfg_attr(not(feature = "std"), feature(alloc))] +#![cfg_attr(not(feature = "std"), feature(macro_reexport))] #![cfg_attr(feature = "std", doc = "Polkadot runtime standard library as compiled when linked with Rust's standard library.")] #![cfg_attr(not(feature = "std"), doc = "Polkadot's runtime standard library as compiled without Rust's standard library.")] diff --git a/substrate/runtime-std/without_std.rs b/substrate/runtime-std/without_std.rs index 4e5a3f739df80..b27db9bc7d217 100644 --- a/substrate/runtime-std/without_std.rs +++ b/substrate/runtime-std/without_std.rs @@ -15,6 +15,7 @@ // along with Substrate. If not, see . #[cfg(feature = "nightly")] +#[macro_reexport(vec)] extern crate alloc; #[cfg(feature = "nightly")] extern crate pwasm_libc; From cbf79ff8d720dc37d1b6f1c337cb194161089aa6 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 18:18:36 +0100 Subject: [PATCH 09/10] move native environment stuff to substrate executor --- polkadot/api/src/lib.rs | 12 ++++++------ polkadot/executor/src/lib.rs | 20 +------------------- substrate/executor/src/lib.rs | 2 +- substrate/executor/src/native_executor.rs | 17 ++++++++++++++++- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index ad876bd39bfb7..c79295823a7dd 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -17,11 +17,11 @@ //! Strongly typed API for Polkadot based around the locally-compiled native //! runtime. -extern crate polkadot_executor as p_executor; +extern crate polkadot_executor as polkadot_executor; extern crate polkadot_runtime ; extern crate polkadot_primitives as primitives; extern crate substrate_client as client; -extern crate substrate_executor as s_executor; +extern crate substrate_executor as substrate_executor; extern crate substrate_state_machine as state_machine; #[macro_use] @@ -31,8 +31,8 @@ use client::backend::Backend; use client::blockchain::BlockId; use client::Client; use polkadot_runtime::runtime; -use p_executor::LocalNativeExecutionDispatch as LocalDispatch; -use s_executor::{NativeExecutionDispatch, NativeExecutor}; +use polkadot_executor::LocalNativeExecutionDispatch as LocalDispatch; +use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; use primitives::{AccountId, SessionKey}; use primitives::parachain::DutyRoster; @@ -56,7 +56,7 @@ error_chain! { } links { - Executor(s_executor::error::Error, s_executor::error::ErrorKind); + Executor(substrate_executor::error::Error, substrate_executor::error::ErrorKind); } } @@ -96,7 +96,7 @@ macro_rules! with_runtime { backend: &state, }; - LocalDispatch::execute_runtime(&mut ext, $exec).map_err(Into::into) + ::substrate_executor::with_native_environment(&mut ext, $exec).map_err(Into::into) }) }} } diff --git a/polkadot/executor/src/lib.rs b/polkadot/executor/src/lib.rs index 93924657733b3..f4d5af026f55d 100644 --- a/polkadot/executor/src/lib.rs +++ b/polkadot/executor/src/lib.rs @@ -35,21 +35,9 @@ use substrate_executor::error::{Error, ErrorKind}; use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; use state_machine::Externalities; -use std::panic::catch_unwind; - - /// A null struct which implements `NativeExecutionDispatch` feeding in the hard-coded runtime. pub struct LocalNativeExecutionDispatch; -impl LocalNativeExecutionDispatch { - /// Set up the externalities and safe calling environment to execute calls to the runtime. - pub fn execute_runtime(ext: &mut Externalities, f: F) -> Result - where F: ::std::panic::UnwindSafe + FnOnce() -> U - { - runtime_io::with_externalities(ext, move || safe_call(f)) - } -} - impl NativeExecutionDispatch for LocalNativeExecutionDispatch { fn native_equivalent() -> &'static [u8] { // WARNING!!! This assumes that the runtime was built *before* the main project. Until we @@ -58,17 +46,11 @@ impl NativeExecutionDispatch for LocalNativeExecutionDispatch { } fn dispatch(ext: &mut Externalities, method: &str, data: &[u8]) -> Result, Error> { - LocalNativeExecutionDispatch::execute_runtime(ext, move || runtime::api::dispatch(method, data))? + ::substrate_executor::with_native_environment(ext, move || runtime::api::dispatch(method, data))? .ok_or_else(|| ErrorKind::MethodNotFound(method.to_owned()).into()) } } -fn safe_call(f: F) -> Result - where F: ::std::panic::UnwindSafe + FnOnce() -> U -{ - catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()) -} - /// Creates new RustExecutor for contracts. pub fn executor() -> NativeExecutor { NativeExecutor { _dummy: ::std::marker::PhantomData } diff --git a/substrate/executor/src/lib.rs b/substrate/executor/src/lib.rs index d7e2ba33d582f..d520048e74964 100644 --- a/substrate/executor/src/lib.rs +++ b/substrate/executor/src/lib.rs @@ -61,4 +61,4 @@ mod native_executor; pub mod error; pub use wasm_executor::WasmExecutor; -pub use native_executor::{NativeExecutor, NativeExecutionDispatch}; +pub use native_executor::{with_native_environment, NativeExecutor, NativeExecutionDispatch}; diff --git a/substrate/executor/src/native_executor.rs b/substrate/executor/src/native_executor.rs index 74a950528e199..0855e78e85869 100644 --- a/substrate/executor/src/native_executor.rs +++ b/substrate/executor/src/native_executor.rs @@ -14,10 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use error::{Error, Result}; +use error::{Error, ErrorKind, Result}; use state_machine::{Externalities, CodeExecutor}; use wasm_executor::WasmExecutor; +fn safe_call(f: F) -> Result + where F: ::std::panic::UnwindSafe + FnOnce() -> U +{ + ::std::panic::catch_unwind(f).map_err(|_| ErrorKind::Runtime.into()) +} + +/// Set up the externalities and safe calling environment to execute calls to a native runtime. +/// +/// If the inner closure panics, it will be caught and return an error. +pub fn with_native_environment(ext: &mut Externalities, f: F) -> Result + where F: ::std::panic::UnwindSafe + FnOnce() -> U +{ + ::runtime_io::with_externalities(ext, move || safe_call(f)) +} + /// Delegate for dispatching a CodeExecutor call to native code. pub trait NativeExecutionDispatch { /// Get the wasm code that the native dispatch will be equivalent to. From 71680b206ea529c7093db1df29a4e2fdf5f523d2 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Feb 2018 18:20:18 +0100 Subject: [PATCH 10/10] fix warnings and grumbles --- substrate/codec/src/lib.rs | 2 +- substrate/codec/src/slicable.rs | 4 ++-- substrate/rpc/src/chain/mod.rs | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/substrate/codec/src/lib.rs b/substrate/codec/src/lib.rs index 0c032bd5d813e..92df92d44927e 100644 --- a/substrate/codec/src/lib.rs +++ b/substrate/codec/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), feature(alloc))] -#[macro_use] +#[cfg_attr(not(feature = "std"), macro_use)] extern crate substrate_runtime_std as rstd; mod endiansensitive; diff --git a/substrate/codec/src/slicable.rs b/substrate/codec/src/slicable.rs index 81e7dfdf01f10..003e49c5f125e 100644 --- a/substrate/codec/src/slicable.rs +++ b/substrate/codec/src/slicable.rs @@ -111,7 +111,7 @@ impl Slicable for Vec { } macro_rules! impl_vec_simple_array { - ($($size:expr)*) => { + ($($size:expr),*) => { $( impl Slicable for Vec<[T; $size]> where [T; $size]: EndianSensitive @@ -151,7 +151,7 @@ macro_rules! impl_vec_simple_array { } } -impl_vec_simple_array!(1 2 4 8 16 32 64); +impl_vec_simple_array!(1, 2, 4, 8, 16, 32, 64); impl NonTrivialSlicable for Vec where Vec: Slicable {} diff --git a/substrate/rpc/src/chain/mod.rs b/substrate/rpc/src/chain/mod.rs index 523871d88e735..ef827696145d8 100644 --- a/substrate/rpc/src/chain/mod.rs +++ b/substrate/rpc/src/chain/mod.rs @@ -18,7 +18,6 @@ use primitives::block; use client; -use client::blockchain::BlockId; use state_machine; mod error;