diff --git a/contracts/src/auth.rs b/contracts/src/auth.rs index a244526ddd83c..98b4ed65b2d98 100644 --- a/contracts/src/auth.rs +++ b/contracts/src/auth.rs @@ -15,13 +15,14 @@ // along with Polkadot. If not, see . use primitives::Address; +use primitives::hash::H256; use state_machine::StaticExternalities; use error::Result; use executor::RustExecutor; /// Data and some sort of Authentication Data -type DataAndAuth = (Vec, Vec); +type DataAndAuth = (H256, Vec); /// Authentication contract rust implementation. #[derive(Debug, Default)] diff --git a/contracts/src/balances.rs b/contracts/src/balances.rs index d5e3fd6b662fb..f9d5ece00f452 100644 --- a/contracts/src/balances.rs +++ b/contracts/src/balances.rs @@ -14,14 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use primitives::Address; +use primitives::contract::CallData; use primitives::uint::U256; -use state_machine::{Externalities, StaticExternalities}; +use primitives::{hash, Address}; +use serializer; +use state_machine::{self, Externalities, StaticExternalities}; -use error::Result; +use error::{Error, ErrorKind, Result, ResultExt}; use executor::RustExecutor; -#[derive(Debug, Serialize, Deserialize)] +/// Account entry +#[derive(Default, Debug, Serialize, Deserialize)] +struct Account { + /// Account balance + balance: U256, + /// Account nonce + nonce: U256, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Transfer { /// Transfer value value: U256, @@ -33,18 +44,36 @@ pub struct Transfer { authentication_data: Vec, } +fn externalities_error(e: E) -> Error { + ErrorKind::Externalities(Box::new(e)).into() +} + +fn address(x: u8) -> Address { + [x].as_ref().into() +} + +fn account>(ext: &E, address: Address) -> Result { + ext.storage(&address.into()) + .map_err(externalities_error) + .and_then(|x| if x.is_empty() { + Ok(Account::default()) + } else { + serializer::from_slice(x).chain_err(|| "Invalid internal structure.") + }) +} + /// 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!() + pub fn balance_of>(&self, ext: &E, data: Address) -> Result { + account(ext, data).map(|acc| acc.balance) } /// Returns the next nonce to authorize the transfer from given address. - pub fn next_nonce>(&self, _ext: &E, _data: Address) -> Result { - unimplemented!() + pub fn next_nonce>(&self, ext: &E, data: Address) -> Result { + account(ext, data).map(|acc| acc.nonce) } /// Checks preconditions for transfer. @@ -52,13 +81,172 @@ impl Contract { /// - signature /// - replay protection /// - enough balance - pub fn transfer_preconditions>(&self, _db: &E, _data: Transfer) -> Result { - unimplemented!() + pub fn transfer_preconditions>(&self, ext: &E, data: Transfer) -> Result> { + // Check the caller: + let sender = ext.sender(); + if ![ + address(RustExecutor::TOP_LEVEL), + address(RustExecutor::BALANCES), + ].contains(sender) { + return Ok(None) + } + + // check the signature + let mut bytes = [0u8; 32 + 20 + 32]; + data.value.to_big_endian(&mut bytes[0..32]); + bytes[32..52].clone_from_slice(&*data.to); + data.nonce.to_big_endian(&mut bytes[52..84]); + let hash = hash(&bytes); + + let sender = serializer::from_slice(&ext.call_static( + &address(RustExecutor::TOP_LEVEL), + "check_auth", + &CallData(serializer::to_vec(&(hash, data.authentication_data))), + ).map_err(externalities_error)?.0) + .chain_err(|| "Invalid auth contract response.")?; + + let account = account(ext, sender)?; + + // check nonce + if account.nonce != data.nonce { + return Ok(None) + } + + // check balance + if account.balance < data.value { + return Ok(None) + } + + // return sender and account + Ok(Some(sender)) } /// 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!() + pub fn transfer>(&self, ext: &mut E, data: Transfer) -> Result { + let from = match self.transfer_preconditions(ext, data.clone())? { + None => return Ok(false), + Some(address) => address, + }; + + let mut sender = account(ext, from)?; + let mut recipient = account(ext, data.to)?; + + // update nonce + sender.nonce = sender.nonce.checked_add(&1.into()) + .ok_or_else(|| ErrorKind::OperationOverflow)?; + + // update balances + sender.balance = sender.balance.checked_sub(&data.value) + .ok_or_else(|| ErrorKind::OperationOverflow)?; + recipient.balance = recipient.balance.checked_add(&data.value) + .ok_or_else(|| ErrorKind::OperationOverflow)?; + + // save changes to the storage + ext.set_storage(data.to.into(), serializer::to_vec(&recipient)); + ext.set_storage(from.into(), serializer::to_vec(&sender)); + + Ok(true) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use test_helpers::TestExternalities; + + #[test] + fn should_return_balance_of_and_do_a_transfer() { + // given + let mut ext = TestExternalities::default(); + ext.call_result = Some(serializer::to_vec(&Address::from(5))); + ext.storage.insert(5.into(), serializer::to_vec(&Account { + nonce: 0.into(), + balance: 10.into(), + })); + + let balances = Contract::default(); + let transfer = Transfer { + value: 5.into(), + to: 1.into(), + nonce: 0.into(), + authentication_data: vec![5], + }; + assert_eq!(balances.balance_of(&ext, 5.into()).unwrap(), 10.into()); + assert_eq!(balances.balance_of(&ext, 1.into()).unwrap(), 0.into()); + assert_eq!(balances.next_nonce(&ext, 5.into()).unwrap(), 0.into()); + + // when + assert!(balances.transfer_preconditions(&ext, transfer.clone()).unwrap().is_some()); + assert_eq!(balances.transfer(&mut ext, transfer).unwrap(), true); + + // then + assert_eq!(balances.balance_of(&ext, 5.into()).unwrap(), 5.into()); + assert_eq!(balances.balance_of(&ext, 1.into()).unwrap(), 5.into()); + assert_eq!(balances.next_nonce(&ext, 5.into()).unwrap(), 1.into()); + } + + #[test] + fn should_reject_on_invalid_nonce() { + // given + let mut ext = TestExternalities::default(); + ext.call_result = Some(serializer::to_vec(&Address::from(5))); + ext.storage.insert(5.into(), serializer::to_vec(&Account { + nonce: 0.into(), + balance: 10.into(), + })); + + let balances = Contract::default(); + let transfer = Transfer { + value: 5.into(), + to: 1.into(), + nonce: 1.into(), + authentication_data: vec![5], + }; + + assert!(balances.transfer_preconditions(&ext, transfer.clone()).unwrap().is_none()); + } + + #[test] + fn should_reject_on_insufficient_balance() { + // given + let mut ext = TestExternalities::default(); + ext.call_result = Some(serializer::to_vec(&Address::from(5))); + ext.storage.insert(5.into(), serializer::to_vec(&Account { + nonce: 0.into(), + balance: 4.into(), + })); + + let balances = Contract::default(); + let transfer = Transfer { + value: 5.into(), + to: 1.into(), + nonce: 0.into(), + authentication_data: vec![5], + }; + + assert!(balances.transfer_preconditions(&ext, transfer.clone()).unwrap().is_none()); + } + + #[test] + fn should_reject_on_non_top_level_call() { + // given + let mut ext = TestExternalities::default(); + ext.sender = 1.into(); + ext.call_result = Some(serializer::to_vec(&Address::from(5))); + ext.storage.insert(5.into(), serializer::to_vec(&Account { + nonce: 0.into(), + balance: 10.into(), + })); + + let balances = Contract::default(); + let transfer = Transfer { + value: 5.into(), + to: 1.into(), + nonce: 0.into(), + authentication_data: vec![5], + }; + + assert!(balances.transfer_preconditions(&ext, transfer.clone()).unwrap().is_none()); } } diff --git a/contracts/src/error.rs b/contracts/src/error.rs index 04e3144ed66ba..22a068bfbcbd8 100644 --- a/contracts/src/error.rs +++ b/contracts/src/error.rs @@ -42,5 +42,11 @@ error_chain! { description("externalities failure"), display("Externalities error: {}", e), } + + /// Operation overflow + OperationOverflow { + description("overflow"), + display("Operation overflow"), + } } } diff --git a/contracts/src/executor.rs b/contracts/src/executor.rs index dde20b42576fe..73c43b79ad0e2 100644 --- a/contracts/src/executor.rs +++ b/contracts/src/executor.rs @@ -37,9 +37,14 @@ pub struct RustExecutor { } impl RustExecutor { - const AUTH: u8 = 1; - const BALANCES: u8 = 2; - const VALIDATOR_SET: u8 = 3; + /// Call initiated by a transaction. + pub const TOP_LEVEL: u8 = 0; + /// Authentication contract. + pub const AUTH: u8 = 1; + /// Balances contract. + pub const BALANCES: u8 = 2; + /// Validator set contract. + pub const VALIDATOR_SET: u8 = 3; } impl CodeExecutor for RustExecutor { @@ -95,32 +100,7 @@ impl CodeExecutor for RustExecutor { #[cfg(test)] mod tests { use super::*; - use primitives::Address; - use primitives::hash::H256; - - #[derive(Debug, Default)] - struct TestExternalities; - impl Externalities for TestExternalities { - fn set_storage(&mut self, _key: H256, _value: Vec) { - unimplemented!() - } - - fn call(&mut self, _address: &Address, _method: &str, _data: &CallData) -> Result { - unimplemented!() - } - } - - impl StaticExternalities for TestExternalities { - type Error = Error; - - fn storage(&self, _key: &H256) -> Result<&[u8]> { - unimplemented!() - } - - fn call_static(&self, _address: &Address, _method: &str, _data: &CallData) -> Result { - unimplemented!() - } - } + use test_helpers::TestExternalities; #[test] fn should_fail_for_empty_or_unknown_code() { diff --git a/contracts/src/lib.rs b/contracts/src/lib.rs index 6226edf4fcc8a..1b29a1847185c 100644 --- a/contracts/src/lib.rs +++ b/contracts/src/lib.rs @@ -41,6 +41,9 @@ mod validator_set; pub mod error; pub mod executor; +#[cfg(test)] +mod test_helpers; + /// Creates new RustExecutor for contracts. pub fn executor() -> executor::RustExecutor { executor::RustExecutor::default() diff --git a/contracts/src/test_helpers.rs b/contracts/src/test_helpers.rs new file mode 100644 index 0000000000000..218820684cc60 --- /dev/null +++ b/contracts/src/test_helpers.rs @@ -0,0 +1,75 @@ +// 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 std::fmt; +use std::result::Result as StdResult; +use std::collections::HashMap; + +use primitives::Address; +use primitives::hash::H256; +use primitives::contract::{CallData, OutData}; +use state_machine; + +use executor::RustExecutor; + +#[derive(Debug)] +pub enum Error { + NoOutData +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::NoOutData => write!(fmt, "No output data"), + } + } +} + +#[derive(Debug, Default)] +pub struct TestExternalities { + pub sender: Address, + pub storage: HashMap>, + pub call_result: Option>, +} + +impl state_machine::StaticExternalities for TestExternalities { + type Error = Error; + + fn sender(&self) -> &Address { + &self.sender + } + + fn storage(&self, key: &H256) -> StdResult<&[u8], Self::Error> { + Ok(self.storage.get(key).map(|x| &**x).unwrap_or(&[])) + } + + fn call_static(&self, _address: &Address, _method: &str, _data: &CallData) -> StdResult { + self.call_result.clone() + .map(OutData) + .ok_or(Error::NoOutData) + } +} + +impl state_machine::Externalities for TestExternalities { + fn set_storage(&mut self, key: H256, value: Vec) { + self.storage.insert(key, value); + } + + fn call(&mut self, _address: &Address, _method: &str, _data: &CallData) -> StdResult { + self.call_result.take().map(OutData).ok_or(Error::NoOutData) + } +} + diff --git a/primitives/src/hash.rs b/primitives/src/hash.rs index 10b32f37411d2..52a267ec260dc 100644 --- a/primitives/src/hash.rs +++ b/primitives/src/hash.rs @@ -42,11 +42,28 @@ impl_serde!(H160, 20); impl_hash!(H256, 32); impl_serde!(H256, 32); + +impl From for H256 { + fn from(h: H160) -> Self { + let mut v = H256::default(); + v.0[12..32].copy_from_slice(&h.0); + v + } +} + #[cfg(test)] mod tests { use super::*; use polkadot_serializer as ser; + #[test] + fn test_conversion() { + let given = H160::from(5); + let expected = H256::from(5); + + assert_eq!(expected, H256::from(given)); + } + #[test] fn test_h160() { let tests = vec![ diff --git a/primitives/src/uint.rs b/primitives/src/uint.rs index cecf69dd2a9a7..58977d804543e 100644 --- a/primitives/src/uint.rs +++ b/primitives/src/uint.rs @@ -20,7 +20,7 @@ use serde::{Serialize, Serializer, Deserialize, Deserializer}; use bytes; -macro_rules! impl_serde { +macro_rules! impl_uint { ($name: ident, $len: expr) => { impl Serialize for $name { fn serialize(&self, serializer: S) -> Result where S: Serializer { @@ -36,13 +36,31 @@ macro_rules! impl_serde { .map(|x| (&*x).into()) } } + + impl $name { + /// Adds two uints together and returns the result iif it didn't overflow. + pub fn checked_add(&self, other: &Self) -> Option { + match self.overflowing_add(*other) { + (_, true) => None, + (res, _) => Some(res), + } + } + + /// Subtracts given uint and returns the result iif it didn't overflow. + pub fn checked_sub(&self, other: &Self) -> Option { + match self.overflowing_sub(*other) { + (_, true) => None, + (res, _) => Some(res), + } + } + } } } construct_uint!(U256, 4); -impl_serde!(U256, 4); +impl_uint!(U256, 4); construct_uint!(U512, 8); -impl_serde!(U512, 8); +impl_uint!(U512, 8); #[cfg(test)] mod tests { diff --git a/state_machine/src/ext.rs b/state_machine/src/ext.rs index 455fc4d22ef29..9513d0fdbfcf6 100644 --- a/state_machine/src/ext.rs +++ b/state_machine/src/ext.rs @@ -59,6 +59,8 @@ pub struct Ext<'a, B: 'a, E: 'a> { pub backend: &'a B, /// Contract code executor. pub exec: &'a E, + /// Sender address. + pub sender: Address, /// Contract address. pub local: Address, } @@ -68,6 +70,10 @@ impl<'a, B: 'a, E: 'a> StaticExternalities for Ext<'a, B, E> { type Error = Error; + fn sender(&self) -> &Address { + &self.sender + } + fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { match self.overlay.storage(&self.local, key) { Some(x) => Ok(x), @@ -79,6 +85,7 @@ impl<'a, B: 'a, E: 'a> StaticExternalities for Ext<'a, B, E> let inner_ext = StaticExt { backend: self.backend, exec: self.exec, + sender: self.local, local: address.clone(), overlay: self.overlay, }; @@ -117,6 +124,7 @@ impl<'a, B: 'a, E: 'a> Externalities for Ext<'a, B, E> let mut inner_ext = Ext { backend: self.backend, exec: self.exec, + sender: self.local, local: address.clone(), overlay: &mut *self.overlay, }; @@ -135,6 +143,7 @@ struct StaticExt<'a, B: 'a, E: 'a> { overlay: &'a OverlayedChanges, backend: &'a B, exec: &'a E, + sender: Address, local: Address, } @@ -143,6 +152,10 @@ impl<'a, B: 'a, E: 'a> StaticExternalities for StaticExt<'a, B, E> { type Error = Error; + fn sender(&self) -> &Address { + &self.sender + } + fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { match self.overlay.storage(&self.local, key) { Some(x) => Ok(x), @@ -154,6 +167,7 @@ impl<'a, B: 'a, E: 'a> StaticExternalities for StaticExt<'a, B, E> let inner_ext = StaticExt { backend: self.backend, exec: self.exec, + sender: self.local, local: address.clone(), overlay: self.overlay, }; diff --git a/state_machine/src/lib.rs b/state_machine/src/lib.rs index 9eb6039fa3210..7eb37dc309b72 100644 --- a/state_machine/src/lib.rs +++ b/state_machine/src/lib.rs @@ -163,21 +163,11 @@ impl Error for E where E: 'static + fmt::Debug + fmt::Display + Send {} /// Externalities: pinned to specific active address. pub trait Externalities: StaticExternalities { - /// Read storage of current contract being called. - fn storage(&self, key: &H256) -> Result<&[u8], Self::Error> { - StaticExternalities::storage(self, key) - } - /// Set storage of current contract being called. fn set_storage(&mut self, key: H256, value: Vec); /// Make a sub-call to another contract. fn call(&mut self, address: &Address, method: &str, data: &CallData) -> Result; - - /// Make a static (read-only) call to another contract. - fn call_static(&self, address: &Address, method: &str, data: &CallData) -> Result { - StaticExternalities::call_static(self, address, method, data) - } } /// Static externalities: used only for read-only requests. @@ -185,6 +175,13 @@ pub trait StaticExternalities { /// Externalities error type. type Error: Error; + /// A sender of the call. + /// + /// This might be set to one of the special addresses in case the contract is executed directly: + /// - as a transaction + /// - as part of the block processing + fn sender(&self) -> &Address; + /// Read storage of current contract being called. fn storage(&self, key: &H256) -> Result<&[u8], Self::Error>; @@ -217,6 +214,8 @@ pub trait CodeExecutor: Sized { ) -> Result; } +fn transaction_context_address() -> Address { 0.into() } + /// Execute a call using the given state backend, overlayed changes, and call executor. /// /// On an error, no prospective changes are written to the overlay. @@ -237,6 +236,7 @@ pub fn execute( let mut externalities = ext::Ext { backend, exec, + sender: transaction_context_address(), overlay: &mut *overlay, local: *address, };