diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 43de6e25e0..7024d67425 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1,3 +1,13 @@ +[root] +name = "polkadot-validator" +version = "0.1.0" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-primitives 0.1.0", + "polkadot-serializer 0.1.0", + "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "aho-corasick" version = "0.6.3" @@ -11,6 +21,11 @@ name = "ansi_term" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "assert_matches" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "atty" version = "0.2.3" @@ -210,6 +225,19 @@ dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "polkadot-contracts" +version = "0.1.0" +dependencies = [ + "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-primitives 0.1.0", + "polkadot-serializer 0.1.0", + "polkadot-state-machine 0.1.0", + "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "polkadot-primitives" version = "0.1.0" @@ -234,13 +262,10 @@ dependencies = [ ] [[package]] -name = "polkadot-validator" +name = "polkadot-state-machine" version = "0.1.0" dependencies = [ - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "polkadot-primitives 0.1.0", - "polkadot-serializer 0.1.0", - "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -478,6 +503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" +"checksum assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e772942dccdf11b368c31e044e4fca9189f80a773d2f0808379de65894cbf57" "checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" "checksum backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99f2ce94e22b8e664d95c57fff45b98a966c2252b60691d0b7aeeccd88d70983" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 14a08ae612..f99dfd8ee5 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -8,7 +8,9 @@ polkadot-cli = { path = "cli", version = "0.1" } [workspace] members = [ + "contracts", "primitives", "serializer", + "state_machine", "validator", ] diff --git a/substrate/contracts/Cargo.toml b/substrate/contracts/Cargo.toml new file mode 100644 index 0000000000..2c23cdb896 --- /dev/null +++ b/substrate/contracts/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "polkadot-contracts" +version = "0.1.0" +authors = ["Parity Team "] + +[dependencies] +assert_matches = "1.1" +error-chain = "0.11" +polkadot-primitives = { path = "../primitives", version = "0.1" } +polkadot-serializer = { path = "../serializer", version = "0.1" } +polkadot-state-machine = { path = "../state_machine" , version = "0.1" } +serde = "1.0" +serde_derive = "1.0" diff --git a/substrate/contracts/src/auth.rs b/substrate/contracts/src/auth.rs new file mode 100644 index 0000000000..641290c6c9 --- /dev/null +++ b/substrate/contracts/src/auth.rs @@ -0,0 +1,38 @@ +// 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 . + +use primitives::Address; +use state_machine::Externalities; + +use error::Result; +use executor::RustExecutor; + +/// Data and some sort of Authentication Data +type DataAndAuth = (Vec, Vec); + +/// Authentication contract rust implementation. +#[derive(Debug, Default)] +pub struct Contract; +impl Contract { + /// Verify authentication data. + /// + /// Given Message and Authentication Data verifies it and returns: + /// 1. None in case it doesn't match (i.e. signature is invalid) + /// 2. A address who signed that Message. + pub fn check_auth>(&self, _ext: &E, _data: DataAndAuth) -> Result> { + unimplemented!() + } +} diff --git a/substrate/contracts/src/balances.rs b/substrate/contracts/src/balances.rs new file mode 100644 index 0000000000..c93999c24a --- /dev/null +++ b/substrate/contracts/src/balances.rs @@ -0,0 +1,64 @@ +// 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 . + +use primitives::Address; +use primitives::uint::U256; +use state_machine::Externalities; + +use error::Result; +use executor::RustExecutor; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Transfer { + /// Transfer value + value: U256, + /// Transfer destination + to: Address, + /// Replay protection + nonce: U256, + /// Data authorizing the transfer (we can derive sender from it) + authentication_data: Vec, +} + +/// Balances contract rust implementation. +#[derive(Debug, Default)] +pub struct Contract; +impl Contract { + /// Returns a balance of given address. + pub fn balance_of>(&self, _ext: &E, _data: Address) -> Result { + unimplemented!() + } + + /// Returns the next nonce to authorize the transfer from given address. + pub fn next_nonce>(&self, _ext: &E, _data: Address) -> Result { + unimplemented!() + } + + /// Checks preconditions for transfer. + /// Should verify: + /// - signature + /// - replay protection + /// - enough balance + pub fn transfer_preconditions>(&self, _db: &E, _data: Transfer) -> Result { + unimplemented!() + } + + /// Perform a transfer. + /// This should first make sure that precondtions are satisfied and later perform the transfer. + pub fn transfer>(&self, _ext: &mut E, _data: Transfer) -> Result { + unimplemented!() + } +} diff --git a/substrate/contracts/src/error.rs b/substrate/contracts/src/error.rs new file mode 100644 index 0000000000..a792ba6378 --- /dev/null +++ b/substrate/contracts/src/error.rs @@ -0,0 +1,50 @@ +// 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 . + +//! Rust executor possible errors. + +#![allow(missing_docs)] + +use serializer; +use state_machine; + +error_chain! { + foreign_links { + InvalidData(serializer::Error); + } + + errors { + /// Method is not found + MethodNotFound(t: String) { + description("method not found"), + display("Method not found: '{}'", t), + } + + /// Code is invalid (expected single byte) + InvalidCode(c: Vec) { + description("invalid code"), + display("Invalid Code: {:?}", c), + } + + /// Externalities have failed. + Externalities(e: Box) { + description("externalities failure"), + display("Externalities error: {}", e), + } + } +} + +impl state_machine::Error for Error {} diff --git a/substrate/contracts/src/executor.rs b/substrate/contracts/src/executor.rs new file mode 100644 index 0000000000..819bc37350 --- /dev/null +++ b/substrate/contracts/src/executor.rs @@ -0,0 +1,136 @@ +// 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 . + +//! Rust implementation of Polkadot contracts. + +use primitives::contract::{CallData, OutData}; +use serializer::{from_slice as de, to_vec as ser}; +use state_machine::{Externalities, Executor}; + +use error::{Error, ErrorKind, Result}; +use auth; +use balances; +use validator_set; + +/// Dummy rust executor for contracts. +/// +/// Instead of actually executing the provided code it just +/// dispatches the calls to pre-defined hardcoded implementations in rust. +#[derive(Debug, Default)] +pub struct RustExecutor { + auth: auth::Contract, + balance: balances::Contract, + validator_set: validator_set::Contract, +} + +impl RustExecutor { + const AUTH: u8 = 1; + const BALANCES: u8 = 2; + const VALIDATOR_SET: u8 = 3; +} + +impl Executor for RustExecutor { + type Error = Error; + + fn static_call>( + &self, + ext: &E, + code: &[u8], + method: &str, + data: &CallData, + ) -> Result { + ensure!(code.len() == 1, ErrorKind::InvalidCode(code.to_vec())); + + Ok(OutData(match code[0] { + Self::AUTH => match method { + "check_auth" => ser(&self.auth.check_auth(ext, de(&data.0)?)?), + m => bail!(ErrorKind::MethodNotFound(m.to_owned())), + }, + Self::BALANCES => match method { + "balance_of" => ser(&self.balance.balance_of(ext, de(&data.0)?)?), + "next_nonce" => ser(&self.balance.next_nonce(ext, de(&data.0)?)?), + "transfer_preconditions" => ser(&self.balance.transfer_preconditions(ext, de(&data.0)?)?), + m => bail!(ErrorKind::MethodNotFound(m.to_owned())), + }, + Self::VALIDATOR_SET => match method { + "validator_set" => ser(&self.validator_set.validator_set(ext, de(&data.0)?)?), + m => bail!(ErrorKind::MethodNotFound(m.to_owned())), + }, + c => bail!(ErrorKind::InvalidCode(vec![c])), + })) + } + + fn call>( + &self, + ext: &mut E, + code: &[u8], + method: &str, + data: &CallData, + ) -> Result { + ensure!(code.len() == 1, ErrorKind::InvalidCode(code.to_vec())); + + Ok(OutData(match code[0] { + Self::BALANCES=> match method { + "transfer" => ser(&self.balance.transfer(ext, de(&data.0)?)?), + m => bail!(ErrorKind::MethodNotFound(m.to_owned())), + }, + c => bail!(ErrorKind::InvalidCode(vec![c])), + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug, Default)] + struct TestExternalities; + impl Externalities for TestExternalities { + type Error = Error; + } + + #[test] + fn should_fail_for_empty_or_unknown_code() { + // given + let mut ext = TestExternalities::default(); + let executor = RustExecutor::default(); + + assert_matches!( + *executor.call(&mut ext, &[], "any", &CallData(vec![])).unwrap_err().kind(), + ErrorKind::InvalidCode(ref code) if code.is_empty() + ); + assert_matches!( + *executor.call(&mut ext, &[1, 2], "any", &CallData(vec![])).unwrap_err().kind(), + ErrorKind::InvalidCode(ref code) if code.len() == 2 + ); + assert_matches!( + *executor.call(&mut ext, &[255,], "any", &CallData(vec![])).unwrap_err().kind(), + ErrorKind::InvalidCode(_) + ); + } + + #[test] + fn should_fail_on_invalid_method() { + // given + let mut ext = TestExternalities::default(); + let executor = RustExecutor::default(); + + assert_matches!( + *executor.call(&mut ext, &[2], "any", &CallData(vec![])).unwrap_err().kind(), + ErrorKind::MethodNotFound(ref method) if &*method == "any" + ); + } +} diff --git a/substrate/contracts/src/lib.rs b/substrate/contracts/src/lib.rs new file mode 100644 index 0000000000..cacfc54dd6 --- /dev/null +++ b/substrate/contracts/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 . + +//! Temporary crate for contracts implementations. +//! +//! This will be replaced with WASM contracts stored on-chain. + +#![warn(missing_docs)] + +extern crate polkadot_primitives as primitives; +extern crate polkadot_serializer as serializer; +extern crate polkadot_state_machine as state_machine; +extern crate serde; + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate serde_derive; + +#[cfg(test)] +#[macro_use] +extern crate assert_matches; + +mod auth; +mod balances; +mod validator_set; + +pub mod error; +pub mod executor; + +/// Creates new RustExecutor for contracts. +pub fn new() -> executor::RustExecutor { + executor::RustExecutor::default() +} diff --git a/substrate/contracts/src/validator_set.rs b/substrate/contracts/src/validator_set.rs new file mode 100644 index 0000000000..b495ac7588 --- /dev/null +++ b/substrate/contracts/src/validator_set.rs @@ -0,0 +1,31 @@ +// 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 . + +use primitives::Address; +use state_machine::Externalities; + +use error::Result; +use executor::RustExecutor; + +/// Harcoded validator set contract. +#[derive(Debug, Default)] +pub struct Contract; +impl Contract { + /// Returns current validator set. + pub fn validator_set>(&self, _db: &E, _data: ()) -> Result> { + unimplemented!() + } +} diff --git a/substrate/primitives/src/contract.rs b/substrate/primitives/src/contract.rs new file mode 100644 index 0000000000..36d22c4ea0 --- /dev/null +++ b/substrate/primitives/src/contract.rs @@ -0,0 +1,27 @@ +// 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 . + +//! Contract execution data. + +use bytes; + +/// Contract call data. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CallData(#[serde(with="bytes")] pub Vec); + +/// Contract output data. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct OutData(#[serde(with="bytes")] pub Vec); diff --git a/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs index 08079e42fb..b3e4c36be9 100644 --- a/substrate/primitives/src/lib.rs +++ b/substrate/primitives/src/lib.rs @@ -41,6 +41,7 @@ extern crate pretty_assertions; mod bytes; pub mod block; +pub mod contract; pub mod hash; pub mod parachain; pub mod uint; diff --git a/substrate/state_machine/Cargo.toml b/substrate/state_machine/Cargo.toml new file mode 100644 index 0000000000..f7ccdc93b4 --- /dev/null +++ b/substrate/state_machine/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "polkadot-state-machine" +version = "0.1.0" +authors = ["Parity Team "] + +[dependencies] +polkadot-primitives = { path = "../primitives", version = "0.1" } diff --git a/substrate/state_machine/src/lib.rs b/substrate/state_machine/src/lib.rs new file mode 100644 index 0000000000..e6ac3df345 --- /dev/null +++ b/substrate/state_machine/src/lib.rs @@ -0,0 +1,61 @@ +// 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 state machine + +#![warn(missing_docs)] + +extern crate polkadot_primitives as primitives; + +use std::fmt; + +use primitives::contract::{CallData, OutData}; + +/// State Machine Error bound. +/// +/// This should reflect WASM error type bound for future compatibility. +pub trait Error: 'static + fmt::Debug + fmt::Display + Send {} + +/// Externalities +pub trait Externalities { + /// Externalities error type. + type Error: Error; +} + +/// Contract code executor. +pub trait Executor: Sized { + /// Error type for contract execution. + type Error: Error; + + /// Execute a contract in read-only mode. + /// The execution is not allowed to modify the state. + fn static_call>( + &self, + ext: &E, + code: &[u8], + method: &str, + data: &CallData, + ) -> Result; + + /// Execute a contract. + fn call>( + &self, + ext: &mut E, + code: &[u8], + method: &str, + data: &CallData, + ) -> Result; +}