diff --git a/substrate/native-runtime/support/src/lib.rs b/substrate/native-runtime/support/src/lib.rs index 93947dde68..7bbd6d235e 100644 --- a/substrate/native-runtime/support/src/lib.rs +++ b/substrate/native-runtime/support/src/lib.rs @@ -8,7 +8,7 @@ pub use std::cell::RefCell; pub use std::boxed::Box; pub use std::mem::{size_of, transmute}; -use polkadot_state_machine::Externalities; +pub use polkadot_state_machine::Externalities; use std::fmt; // TODO: use the real error, not NoError. diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index fb8e5b86f8..205d2e31a7 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -3,7 +3,7 @@ #[macro_use] extern crate runtime_support; -use runtime_support::{set_storage, storage, storage_into, Vec, size_of}; +use runtime_support::{Vec, size_of}; use runtime_support::{Rc, RefCell, transmute, Box}; /// The hash of an ECDSA pub key which is used to identify an external transactor. @@ -93,6 +93,12 @@ struct Environment { block_number: BlockNumber, } +fn with_env T>(f: F) -> T { + let e = env(); + let mut eb = e.borrow_mut(); + f(&mut *eb) +} + fn env() -> Rc> { // Initialize it to a null value static mut SINGLETON: *const Rc> = 0 as *const Rc>; @@ -111,6 +117,77 @@ fn env() -> Rc> { } } +fn value_vec(mut value: u32, initial: Vec) -> Vec { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; + } + acc +} + +trait EndianSensitive: Sized { + fn to_le(self) -> Self { self } + fn to_be(self) -> Self { self } + fn from_le(self) -> Self { self } + fn from_be(self) -> Self { self } +} + +macro_rules! impl_endians { + ( $( $t:ty ),* ) => { $( + impl EndianSensitive for $t { + fn to_le(self) -> Self { <$t>::to_le(self) } + fn to_be(self) -> Self { <$t>::to_be(self) } + fn from_le(self) -> Self { <$t>::from_le(self) } + fn from_be(self) -> Self { <$t>::from_be(self) } + } + )* } +} +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl EndianSensitive for $t {} + )* } +} + +impl_endians!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize); +impl_non_endians!([u8; 20], [u8; 32]); + +trait Storage { + fn storage_into(key: &[u8]) -> Self; + fn store(self, key: &[u8]); +} + +impl Storage for T { + fn storage_into(key: &[u8]) -> Self { + runtime_support::storage_into(key).map(EndianSensitive::from_le).unwrap_or_else(Default::default) + } + + fn store(self, key: &[u8]) { + let size = size_of::(); + let value_bytes = self.to_le(); + let value_slice = unsafe { + std::slice::from_raw_parts(transmute::<*const Self, *const u8>(&value_bytes), size) + }; + runtime_support::set_storage(key, value_slice); + } +} + +fn storage_into(key: &[u8]) -> T { + T::storage_into(key) +} + + +trait KeyedVec { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec; +} +impl KeyedVec for AccountID { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { + let mut r = prepend_key.to_vec(); + r.extend_from_slice(self); + r + } +} + // TODO: include RLP implementation // TODO: add keccak256 (or some better hashing scheme) & ECDSA-recover (or some better sig scheme) @@ -127,26 +204,27 @@ pub fn execute_transaction(_input: Vec) -> Vec { impl_stubs!(execute_block, execute_transaction); /// The current relay chain identifier. -fn chain_id() -> ChainID { +pub fn chain_id() -> ChainID { // TODO: retrieve from external unimplemented!() } -mod environment { +pub mod environment { use super::*; /// The current block number being processed. Set by `execute_block`. - pub fn block_number() -> BlockNumber { let e = env(); let eb = e.borrow(); eb.block_number } + pub fn block_number() -> BlockNumber { + with_env(|e| e.block_number) + } /// Get the block hash of a given block. - pub fn block_hash(_number: BlockNumber) -> Hash { unimplemented!() } + pub fn block_hash(_number: BlockNumber) -> Hash { + unimplemented!() + } pub fn execute_block(_block: &Block) -> Vec { // populate environment from header. - { - let e = env(); - e.borrow_mut().block_number = _block.header.number; - } + with_env(|e| e.block_number = _block.header.number); staking::pre_transactions(); @@ -174,7 +252,7 @@ mod environment { /// Set the new code. pub fn set_code(new: &[u8]) { - set_storage(b"\0code", new) + runtime_support::set_storage(b"\0code", new) } /// Set the light-client digest for the header. @@ -184,33 +262,24 @@ mod environment { } } -mod consensus { +pub mod consensus { use super::*; - fn value_vec(mut value: usize, initial: Vec) -> Vec { - let mut acc = initial; - while value > 0 { - acc.push(value as u8); - value /= 256; - } - acc + pub fn set_authority(index: u32, authority: AccountID) { + runtime_support::set_storage(&value_vec(index, b"\0authority".to_vec()), &authority[..]); } - fn set_authority(index: usize, authority: AccountID) { - set_storage(&value_vec(index, b"\0authority".to_vec()), &authority[..]); + fn authority(index: u32) -> AccountID { + runtime_support::storage_into(&value_vec(index, b"\0authority".to_vec())).unwrap() } - fn authority(index: usize) -> AccountID { - storage_into(&value_vec(index, b"\0authority".to_vec())).unwrap() - } - - fn set_authority_count(count: usize) { + pub fn set_authority_count(count: u32) { (count..authority_count()).for_each(|i| set_authority(i, SessionKey::default())); - set_storage(b"\0authority_count", &value_vec(count, Vec::new())); + runtime_support::set_storage(b"\0authority_count", &value_vec(count, Vec::new())); } - fn authority_count() -> usize { - storage(b"\0authority_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) + fn authority_count() -> u32 { + runtime_support::storage(b"\0authority_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as u32)) } /// Get the current set of authorities. These are the session keys. @@ -221,9 +290,9 @@ mod consensus { /// Set the current set of authorities' session keys. /// /// Called by `next_session` only. - fn set_authorities(authorities: &[AccountID]) { - set_authority_count(authorities.len()); - authorities.iter().enumerate().for_each(|(v, &i)| set_authority(v, i)); + pub fn set_authorities(authorities: &[AccountID]) { + set_authority_count(authorities.len() as u32); + authorities.iter().enumerate().for_each(|(v, &i)| set_authority(v as u32, i)); } /// Get the current set of validators. These are the long-term identifiers for the validators @@ -265,35 +334,56 @@ mod consensus { } } -mod staking { +pub mod staking { use super::*; /// The length of a staking era in blocks. - pub fn era_length() -> BlockNumber { sessions_per_era() * consensus::session_length() } + pub fn era_length() -> BlockNumber { + sessions_per_era() * consensus::session_length() + } /// The length of a staking era in sessions. - pub fn sessions_per_era() -> BlockNumber { 10 } + pub fn sessions_per_era() -> BlockNumber { + 10 + } /// The era has changed - enact new staking set. /// /// NOTE: This always happens on a session change. - fn next_era() { unimplemented!() } + pub fn next_era() { + unimplemented!() + } /// The balance of a given account. - fn balance(_who: AccountID) -> Balance { unimplemented!() } + pub fn balance(who: &AccountID) -> Balance { + storage_into(&who.to_keyed_vec(b"sta\0bal\0")) + } /// Transfer some unlocked staking balance to another staker. - pub fn transfer_stake(_transactor: AccountID, _dest: AccountID, _value: Balance) { unimplemented!() } + pub fn transfer_stake(transactor: &AccountID, dest: &AccountID, value: Balance) { + let from_key = transactor.to_keyed_vec(b"sta\0bal\0"); + let from_balance: Balance = storage_into(&from_key); + assert!(from_balance >= value); + let to_key = dest.to_keyed_vec(b"sta\0bal\0"); + let to_balance: Balance = storage_into(&to_key); + assert!(to_balance + value > to_balance); // no overflow + (from_balance - value).store(&from_key); + (to_balance + value).store(&to_key); + } /// Declare the desire to stake for the transactor. /// /// Effects will be felt at the beginning of the next era. - pub fn stake(_transactor: &AccountID) { unimplemented!() } + pub fn stake(_transactor: &AccountID) { + unimplemented!() + } /// Retract the desire to stake for the transactor. /// /// Effects will be felt at the beginning of the next era. - pub fn unstake(_transactor: &AccountID) { unimplemented!() } + pub fn unstake(_transactor: &AccountID) { + unimplemented!() + } /// Hook to be called prior to transaction processing. pub fn pre_transactions() { @@ -307,17 +397,92 @@ mod staking { } } -mod authentication { +pub mod authentication { use super::*; - fn validate_signature(_tx: Transaction) -> ( AccountID, TxOrder ) { unimplemented!() } - fn nonce(_id: AccountID) -> TxOrder { unimplemented!() } - fn authenticate(_tx: Transaction) -> AccountID { unimplemented!() } + pub fn validate_signature(_tx: Transaction) -> ( AccountID, TxOrder ) { + unimplemented!() + } + + pub fn nonce(_id: AccountID) -> TxOrder { + unimplemented!() + } + + pub fn authenticate(_tx: Transaction) -> AccountID { + unimplemented!() + } } -mod timestamp { +pub mod timestamp { use super::*; - fn timestamp() -> Timestamp { unimplemented!() } - fn set_timestamp(_now: Timestamp) { unimplemented!() } + pub fn timestamp() -> Timestamp { + unimplemented!() + } + + pub fn set_timestamp(_now: Timestamp) { + unimplemented!() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + use std::collections::HashMap; + use runtime_support::{NoError, with_externalities, Externalities}; + + #[derive(Debug, Default)] + struct TestExternalities { + storage: HashMap, Vec>, + } + impl Externalities for TestExternalities { + type Error = NoError; + + fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> { + Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice)) + } + + fn set_storage(&mut self, key: Vec, value: Vec) { + self.storage.insert(key, value); + } + } + + macro_rules! map { + ($( $name:expr => $value:expr ),*) => ( + vec![ $( ( $name, $value ) ),* ].into_iter().collect() + ) + } + + #[test] + fn staking_balance_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + + let mut t = TestExternalities { storage: map![ + { let mut r = b"sta\0bal\0".to_vec(); r.extend_from_slice(&one); r } => vec![42u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + with_externalities(&mut t, || { + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 0); + }); + } + + #[test] + fn staking_balance_transfer_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + + let mut t = TestExternalities { storage: map![ + { let mut r = b"sta\0bal\0".to_vec(); r.extend_from_slice(&one); r } => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ], }; + + with_externalities(&mut t, || { + staking::transfer_stake(&one, &two, 69); + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 69); + }); + } }