From caff772b838d6a80256d7c36648380da039c82be Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 16 Jan 2018 17:55:07 +0100 Subject: [PATCH] Big refactor. --- substrate/native-runtime/support/src/lib.rs | 3 +- .../wasm-runtime/polkadot/src/consensus.rs | 72 ++ .../polkadot/src/endiansensitive.rs | 29 + .../wasm-runtime/polkadot/src/environment.rs | 33 + .../wasm-runtime/polkadot/src/function.rs | 50 + .../wasm-runtime/polkadot/src/keyedvec.rs | 30 + substrate/wasm-runtime/polkadot/src/lib.rs | 994 +----------------- .../wasm-runtime/polkadot/src/primitives.rs | 378 +++++++ .../wasm-runtime/polkadot/src/slicable.rs | 69 ++ .../wasm-runtime/polkadot/src/staking.rs | 109 ++ .../wasm-runtime/polkadot/src/storage.rs | 23 + .../wasm-runtime/polkadot/src/streamreader.rs | 63 ++ substrate/wasm-runtime/polkadot/src/system.rs | 129 +++ .../wasm-runtime/polkadot/src/testing.rs | 19 + .../wasm-runtime/polkadot/src/timestamp.rs | 9 + 15 files changed, 1037 insertions(+), 973 deletions(-) create mode 100644 substrate/wasm-runtime/polkadot/src/consensus.rs create mode 100644 substrate/wasm-runtime/polkadot/src/endiansensitive.rs create mode 100644 substrate/wasm-runtime/polkadot/src/environment.rs create mode 100644 substrate/wasm-runtime/polkadot/src/function.rs create mode 100644 substrate/wasm-runtime/polkadot/src/keyedvec.rs create mode 100644 substrate/wasm-runtime/polkadot/src/primitives.rs create mode 100644 substrate/wasm-runtime/polkadot/src/slicable.rs create mode 100644 substrate/wasm-runtime/polkadot/src/staking.rs create mode 100644 substrate/wasm-runtime/polkadot/src/storage.rs create mode 100644 substrate/wasm-runtime/polkadot/src/streamreader.rs create mode 100644 substrate/wasm-runtime/polkadot/src/system.rs create mode 100644 substrate/wasm-runtime/polkadot/src/testing.rs create mode 100644 substrate/wasm-runtime/polkadot/src/timestamp.rs diff --git a/substrate/native-runtime/support/src/lib.rs b/substrate/native-runtime/support/src/lib.rs index c1805d2d22..795cae2ffd 100644 --- a/substrate/native-runtime/support/src/lib.rs +++ b/substrate/native-runtime/support/src/lib.rs @@ -6,7 +6,8 @@ pub use std::vec::Vec; pub use std::rc::Rc; pub use std::cell::RefCell; pub use std::boxed::Box; -pub use std::mem::{size_of, transmute, swap}; +pub use std::slice; +pub use std::mem::{size_of, transmute, swap, uninitialized}; pub use polkadot_state_machine::Externalities; use std::fmt; diff --git a/substrate/wasm-runtime/polkadot/src/consensus.rs b/substrate/wasm-runtime/polkadot/src/consensus.rs new file mode 100644 index 0000000000..ef4d5a9856 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/consensus.rs @@ -0,0 +1,72 @@ +use keyedvec::KeyedVec; +use storage::Storage; +use primitives::{AccountID, SessionKey, BlockNumber}; +use storage::storage_into; + +pub fn set_authority(index: u32, authority: AccountID) { + authority.store(&index.to_keyed_vec(b"con\0aut\0")); +} + +fn authority(index: u32) -> AccountID { + storage_into(&index.to_keyed_vec(b"con\0aut\0")) +} + +pub fn set_authority_count(count: u32) { + (count..authority_count()).for_each(|i| set_authority(i, SessionKey::default())); + count.store(b"con\0aut\0len"); +} + +fn authority_count() -> u32 { + storage_into(b"con\0aut\0len") +} + +/// Get the current set of authorities. These are the session keys. +pub fn authorities() -> Vec { + (0..authority_count()).into_iter().map(authority).collect() +} + +/// Set the current set of authorities' session keys. +/// +/// Called by `next_session` only. +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 +/// and will be mapped to a session key with the most recent `set_next_session_key`. +pub fn validators() -> Vec { + unimplemented!() +} + +/// Set the current set of validators. +/// +/// Called by staking::next_era() only. +pub fn set_validators(_new: &[AccountID]) { + unimplemented!() +} + +/// The number of blocks in each session. +pub fn session_length() -> BlockNumber { + storage_into(b"con\0bps") +} + +/// Sets the session key of `_transactor` to `_session`. This doesn't take effect until the next +/// session. +pub fn set_session_key(_transactor: &AccountID, _session: &AccountID) { + unimplemented!() +} + +/// Move onto next session: register the new authority set. +pub fn next_session() { + // TODO: Call set_authorities(). + unimplemented!() +} + +/// Hook to be called prior to transaction processing. +pub fn pre_transactions() {} + +/// Hook to be called after to transaction processing. +pub fn post_transactions() { + // TODO: check block number and call next_session if necessary. +} diff --git a/substrate/wasm-runtime/polkadot/src/endiansensitive.rs b/substrate/wasm-runtime/polkadot/src/endiansensitive.rs new file mode 100644 index 0000000000..c13a96f23d --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/endiansensitive.rs @@ -0,0 +1,29 @@ +pub 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 } + fn as_be_then T>(&self, f: F) -> T { f(&self) } + fn as_le_then T>(&self, f: F) -> T { f(&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) } + fn as_be_then T>(&self, f: F) -> T { let d = self.to_be(); f(&d) } + fn as_le_then T>(&self, f: F) -> T { let d = self.to_le(); f(&d) } + } + )* } +} +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl EndianSensitive for $t {} + )* } +} + +impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); +impl_non_endians!(u8, i8, [u8; 20], [u8; 32]); diff --git a/substrate/wasm-runtime/polkadot/src/environment.rs b/substrate/wasm-runtime/polkadot/src/environment.rs new file mode 100644 index 0000000000..4d8ac354a8 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/environment.rs @@ -0,0 +1,33 @@ +use runtime_support::{Rc, RefCell, transmute, Box}; +use primitives::{BlockNumber, Digest}; + +#[derive(Default)] +pub struct Environment { + pub block_number: BlockNumber, + pub digest: Digest, + pub next_log_index: usize, +} + +pub fn with_env T>(f: F) -> T { + let e = env(); + let mut eb = e.borrow_mut(); + f(&mut *eb) +} + +pub fn env() -> Rc> { + // Initialize it to a null value + static mut SINGLETON: *const Rc> = 0 as *const Rc>; + + unsafe { + if SINGLETON == 0 as *const Rc> { + // Make it + let singleton: Rc> = Rc::new(RefCell::new(Default::default())); + + // Put it in the heap so it can outlive this call + SINGLETON = transmute(Box::new(singleton)); + } + + // Now we give out a copy of the data that is safe to use concurrently. + (*SINGLETON).clone() + } +} diff --git a/substrate/wasm-runtime/polkadot/src/function.rs b/substrate/wasm-runtime/polkadot/src/function.rs new file mode 100644 index 0000000000..3f6845bfe6 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/function.rs @@ -0,0 +1,50 @@ +use staking; +use consensus; +use primitives::AccountID; +use streamreader::StreamReader; + +/// The functions that a transaction can call (and be dispatched to). +#[cfg_attr(test, derive(PartialEq, Debug))] +#[derive(Clone, Copy)] +pub enum Function { + StakingStake, + StakingUnstake, + StakingTransferStake, + ConsensusSetSessionKey, +} + +impl Function { + pub fn from_u8(value: u8) -> Option { + match value { + x if x == Function::StakingStake as u8 => Some(Function::StakingStake), + x if x == Function::StakingUnstake as u8 => Some(Function::StakingUnstake), + x if x == Function::StakingTransferStake as u8 => Some(Function::StakingTransferStake), + x if x == Function::ConsensusSetSessionKey as u8 => Some(Function::ConsensusSetSessionKey), + _ => None, + } + } +} + +impl Function { + /// Dispatch the function. + pub fn dispatch(&self, transactor: &AccountID, data: &[u8]) { + let mut params = StreamReader::new(data); + match *self { + Function::StakingStake => { + staking::stake(transactor); + } + Function::StakingUnstake => { + staking::unstake(transactor); + } + Function::StakingTransferStake => { + let dest = params.read().unwrap(); + let value = params.read().unwrap(); + staking::transfer_stake(transactor, &dest, value); + } + Function::ConsensusSetSessionKey => { + let session = params.read().unwrap(); + consensus::set_session_key(transactor, &session); + } + } + } +} diff --git a/substrate/wasm-runtime/polkadot/src/keyedvec.rs b/substrate/wasm-runtime/polkadot/src/keyedvec.rs new file mode 100644 index 0000000000..3b0e595628 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/keyedvec.rs @@ -0,0 +1,30 @@ +use primitives::AccountID; +use slicable::Slicable; + +pub 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 + } +} + +macro_rules! impl_endians { + ( $( $t:ty ),* ) => { $( + impl KeyedVec for $t { + fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { + self.as_slice_then(|slice| { + let mut r = prepend_key.to_vec(); + r.extend_from_slice(slice); + r + }) + } + } + )* } +} + +impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index 533a6ad927..e64a3c2a6b 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -3,480 +3,37 @@ #[macro_use] extern crate runtime_support; -use runtime_support::{Vec, size_of, Rc, RefCell, transmute, swap, Box}; -/// The hash of an ECDSA pub key which is used to identify an external transactor. -pub type AccountID = [u8; 32]; -/// The ECDSA pub key of an authority. This is what the external environment/consensus algorithm -/// refers to as a "authority". -pub type SessionKey = AccountID; -pub type Balance = u64; -pub type ChainID = u64; -pub type Hash = [u8; 32]; -pub type BlockNumber = u64; -pub type Timestamp = u64; -pub type TxOrder = u64; +mod endiansensitive; +mod streamreader; +mod slicable; +mod primitives; +mod keyedvec; +mod function; +mod environment; +mod storage; +mod testing; +#[allow(unused)] +mod system; +#[allow(unused)] +mod consensus; +#[allow(unused)] +mod staking; +#[allow(unused)] +mod timestamp; -/// The functions that a transaction can call (and be dispatched to). -#[cfg_attr(test, derive(PartialEq, Debug))] -#[derive(Clone, Copy)] -pub enum Function { - StakingStake, - StakingUnstake, - StakingTransferStake, - ConsensusSetSessionKey, -} - -impl Function { - fn from_u8(value: u8) -> Option { - match value { - x if x == Function::StakingStake as u8 => Some(Function::StakingStake), - x if x == Function::StakingUnstake as u8 => Some(Function::StakingUnstake), - x if x == Function::StakingTransferStake as u8 => Some(Function::StakingTransferStake), - x if x == Function::ConsensusSetSessionKey as u8 => Some(Function::ConsensusSetSessionKey), - _ => None, - } - } -} - -struct StreamReader<'a> { - data: &'a[u8], - offset: usize, -} - -impl<'a> StreamReader<'a> { - fn new(data: &'a[u8]) -> Self { - StreamReader { - data: data, - offset: 0, - } - } - fn read(&mut self) -> Option { - let size = T::size_of(&self.data[self.offset..])?; - let new_offset = self.offset + size; - let slice = &self.data[self.offset..new_offset]; - self.offset = new_offset; - Slicable::from_slice(slice) - } -} -/* -// Not in use yet -// TODO: introduce fn size_will_be(&self) -> usize; to Slicable trait and implement -struct StreamWriter<'a> { - data: &'a mut[u8], - offset: usize, -} - -impl<'a> StreamWriter<'a> { - fn new(data: &'a mut[u8]) -> Self { - StreamWriter { - data: data, - offset: 0, - } - } - fn write(&mut self, value: &T) -> bool { - value.as_slice_then(|s| { - let new_offset = self.offset + s.len(); - if self.data.len() <= new_offset { - let slice = &mut self.data[self.offset..new_offset]; - self.offset = new_offset; - slice.copy_from_slice(s); - true - } else { - false - } - }) - } -} -*/ -trait Joiner { - fn join(self, value: &T) -> Self; -} - -impl Joiner for Vec { - fn join(mut self, value: &T) -> Vec { - value.as_slice_then(|s| self.extend_from_slice(s)); - self - } -} - -impl Function { - /// Dispatch the function. - pub fn dispatch(&self, transactor: &AccountID, data: &[u8]) { - let mut params = StreamReader::new(data); - match *self { - Function::StakingStake => { - staking::stake(transactor); - } - Function::StakingUnstake => { - staking::unstake(transactor); - } - Function::StakingTransferStake => { - let dest = params.read().unwrap(); - let value = params.read().unwrap(); - staking::transfer_stake(transactor, &dest, value); - } - Function::ConsensusSetSessionKey => { - let session = params.read().unwrap(); - consensus::set_session_key(transactor, &session); - } - } - } -} - -#[derive(Clone, Default)] -#[cfg_attr(test, derive(PartialEq, Debug))] -pub struct Digest { - pub logs: Vec>, -} - -#[derive(Clone)] -#[cfg_attr(test, derive(PartialEq, Debug))] -pub struct Header { - pub parent_hash: Hash, - pub number: BlockNumber, - pub state_root: Hash, - pub transaction_root: Hash, - pub digest: Digest, -} - -#[cfg_attr(test, derive(PartialEq, Debug))] -pub struct Transaction { - pub signed: AccountID, - pub function: Function, - pub input_data: Vec, - pub nonce: TxOrder, -} - -#[cfg_attr(test, derive(PartialEq, Debug))] -pub struct Block { - pub header: Header, - pub transactions: Vec, -} - -impl Header { - pub fn from_rlp(_rlp: &[u8]) -> Self { - unimplemented!() - } -} - -impl Transaction { - pub fn from_rlp(_rlp: &[u8]) -> Self { - unimplemented!() - } -} - -impl Block { - pub fn from_rlp(_rlp: &[u8]) -> Self { - unimplemented!() - } -} - -#[derive(Default)] -struct Environment { - block_number: BlockNumber, - digest: Digest, - next_log_index: usize, -} - -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>; - - unsafe { - if SINGLETON == 0 as *const Rc> { - // Make it - let singleton: Rc> = Rc::new(RefCell::new(Default::default())); - - // Put it in the heap so it can outlive this call - SINGLETON = transmute(Box::new(singleton)); - } - - // Now we give out a copy of the data that is safe to use concurrently. - (*SINGLETON).clone() - } -} - -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 } - fn as_be_then T>(&self, f: F) -> T { f(&self) } - fn as_le_then T>(&self, f: F) -> T { f(&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) } - fn as_be_then T>(&self, f: F) -> T { let d = self.to_be(); f(&d) } - fn as_le_then T>(&self, f: F) -> T { let d = self.to_le(); f(&d) } - } - )* } -} -macro_rules! impl_non_endians { - ( $( $t:ty ),* ) => { $( - impl EndianSensitive for $t {} - )* } -} - -impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); -impl_non_endians!(u8, i8, [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 { - Slicable::set_as_slice(|out| runtime_support::read_storage(key, out) == out.len()) - .unwrap_or_else(Default::default) - } - - fn store(&self, key: &[u8]) { - self.as_slice_then(|slice| runtime_support::set_storage(key, slice)); - } -} - -fn storage_into(key: &[u8]) -> T { - T::storage_into(key) -} - -/// Trait that allows zero-copy read/write of value-references to/from slices in LE format. -trait Slicable: Sized { - fn from_slice(value: &[u8]) -> Option { - Self::set_as_slice(|out| if value.len() == out.len() { - out.copy_from_slice(&value); - true - } else { - false - }) - } - fn to_vec(&self) -> Vec { - self.as_slice_then(|s| s.to_vec()) - } - fn set_as_slice bool>(set_slice: F) -> Option; - fn as_slice_then R>(&self, f: F) -> R { - f(&self.to_vec()) - } - fn size_of(_value: &[u8]) -> Option; -} - -trait NonTrivialSlicable: Slicable {} - -impl Slicable for T { - fn set_as_slice bool>(fill_slice: F) -> Option { - let size = size_of::(); - let mut result: T = unsafe { std::mem::uninitialized() }; - let result_slice = unsafe { - std::slice::from_raw_parts_mut(transmute::<*mut T, *mut u8>(&mut result), size) - }; - if fill_slice(result_slice) { - Some(result.from_le()) - } else { - None - } - } - fn as_slice_then R>(&self, f: F) -> R { - let size = size_of::(); - self.as_le_then(|le| { - let value_slice = unsafe { - std::slice::from_raw_parts(transmute::<*const Self, *const u8>(le), size) - }; - f(value_slice) - }) - } - fn size_of(_value: &[u8]) -> Option { - Some(size_of::()) - } -} - -impl Slicable for Vec { - fn from_slice(value: &[u8]) -> Option { - Some(value[4..].to_vec()) - } - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - fn to_vec(&self) -> Vec { - let mut r = vec![].join(&(self.len() as u32)); - r.extend_from_slice(&self); - r - } - fn size_of(data: &[u8]) -> Option { - u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) - } -} - -impl Slicable for Transaction { - fn from_slice(value: &[u8]) -> Option { - let mut reader = StreamReader::new(value); - Some(Transaction { - signed: reader.read()?, - function: Function::from_u8(reader.read()?)?, - nonce: reader.read()?, - input_data: reader.read()?, - }) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - vec![] - .join(&self.signed) - .join(&(self.function as u8)) - .join(&self.nonce) - .join(&self.input_data) - } - - fn size_of(data: &[u8]) -> Option { - let first_part = size_of::() + size_of::() + size_of::(); - let second_part = >::size_of(&data[first_part..])?; - Some(first_part + second_part) - } -} - -impl NonTrivialSlicable for Transaction {} - -impl NonTrivialSlicable for Vec where Vec: Slicable {} - -impl Slicable for Vec { - fn from_slice(value: &[u8]) -> Option { - let len = Self::size_of(&value[0..4])?; - let mut off = 4; - let mut r = vec![]; - while off < len { - let element_len = T::size_of(&value[off..])?; - r.push(T::from_slice(&value[off..off + element_len])?); - off += element_len; - } - Some(r) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - let vecs = self.iter().map(Slicable::to_vec).collect::>(); - let len = vecs.iter().fold(0, |mut a, v| {a += v.len(); a}); - let mut r = vec![].join(&(len as u32)); - vecs.iter().for_each(|v| r.extend_from_slice(v)); - r - } - - fn size_of(data: &[u8]) -> Option { - u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) - } -} - -impl Slicable for Header { - fn from_slice(value: &[u8]) -> Option { - let mut reader = StreamReader::new(value); - Some(Header { - parent_hash: reader.read()?, - number: reader.read()?, - state_root: reader.read()?, - transaction_root: reader.read()?, - digest: Digest { logs: reader.read()?, }, - }) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - vec![] - .join(&self.parent_hash) - .join(&self.number) - .join(&self.state_root) - .join(&self.transaction_root) - .join(&self.digest.logs) - } - - fn size_of(data: &[u8]) -> Option { - let first_part = size_of::() + size_of::() + size_of::() + size_of::(); - let second_part = >>::size_of(&data[first_part..])?; - Some(first_part + second_part) - } -} - -impl Slicable for Block { - fn from_slice(value: &[u8]) -> Option { - let mut reader = StreamReader::new(value); - Some(Block { - header: reader.read()?, - transactions: reader.read()?, - }) - } - - fn set_as_slice bool>(_fill_slice: F) -> Option { - unimplemented!(); - } - - fn to_vec(&self) -> Vec { - vec![] - .join(&self.header) - .join(&self.transactions) - } - - fn size_of(data: &[u8]) -> Option { - let first_part = Header::size_of(data)?; - let second_part = >::size_of(&data[first_part..])?; - Some(first_part + second_part) - } -} - -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 - } -} - -macro_rules! impl_endians { - ( $( $t:ty ),* ) => { $( - impl KeyedVec for $t { - fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec { - self.as_slice_then(|slice| { - let mut r = prepend_key.to_vec(); - r.extend_from_slice(slice); - r - }) - } - } - )* } -} - -impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); +use runtime_support::Vec; +use slicable::Slicable; +use primitives::{ChainID, Block, Transaction}; // TODO: add keccak256 (or some better hashing scheme) & ECDSA-recover (or some better sig scheme) pub fn execute_block(input: Vec) -> Vec { - environment::execute_block(Block::from_slice(&input).unwrap()) + system::execute_block(Block::from_slice(&input).unwrap()) } pub fn execute_transaction(input: Vec) -> Vec { - environment::execute_transaction(&Transaction::from_slice(&input).unwrap()) + system::execute_transaction(&Transaction::from_slice(&input).unwrap()) } impl_stubs!(execute_block, execute_transaction); @@ -486,510 +43,3 @@ pub fn chain_id() -> ChainID { // TODO: retrieve from external unimplemented!() } - -pub mod environment { - use super::*; - - /// The current block number being processed. Set by `execute_block`. - pub fn block_number() -> BlockNumber { - with_env(|e| e.block_number) - } - - /// Get the block hash of a given block (uses storage). - pub fn block_hash(_number: BlockNumber) -> Hash { - unimplemented!() - } - - /// Deposits a log and ensures it matches the blocks log data. - pub fn deposit_log(log: &[u8]) { - with_env(|e| { - assert_eq!(log, &e.digest.logs[e.next_log_index][..]); - e.next_log_index += 1; - }); - } - - pub fn execute_block(mut block: Block) -> Vec { - // populate environment from header. - with_env(|e| { - e.block_number = block.header.number; - swap(&mut e.digest, &mut block.header.digest); - e.next_log_index = 0; - }); - - // TODO: check transaction trie root represents the transactions. - // TODO: store the header hash in storage. - - staking::pre_transactions(); - - block.transactions.iter().for_each(|tx| { execute_transaction(tx); }); - - staking::post_transactions(); - - final_checks(&block); - - // TODO: check state root somehow - - Vec::new() - } - - fn final_checks(_block: &Block) { - with_env(|e| { - assert_eq!(e.next_log_index, e.digest.logs.len()); - }); - } - - /// Execute a given transaction. - pub fn execute_transaction(_tx: &Transaction) -> Vec { - // TODO: decode data and ensure valid - // TODO: ensure signature valid and recover id (use authentication::authenticate) - // TODO: check nonce - // TODO: increment nonce in storage - // TODO: ensure target_function valid - // TODO: decode parameters - - _tx.function.dispatch(&_tx.signed, &_tx.input_data); - - // TODO: encode any return - Vec::new() - } - - /// Set the new code. - pub fn set_code(new: &[u8]) { - runtime_support::set_storage(b"\0code", new) - } -} - -pub mod consensus { - use super::*; - - pub fn set_authority(index: u32, authority: AccountID) { - authority.store(&index.to_keyed_vec(b"con\0aut\0")); - } - - fn authority(index: u32) -> AccountID { - storage_into(&index.to_keyed_vec(b"con\0aut\0")) - } - - pub fn set_authority_count(count: u32) { - (count..authority_count()).for_each(|i| set_authority(i, SessionKey::default())); - count.store(b"con\0aut\0len"); - } - - fn authority_count() -> u32 { - storage_into(b"con\0aut\0len") - } - - /// Get the current set of authorities. These are the session keys. - pub fn authorities() -> Vec { - (0..authority_count()).into_iter().map(authority).collect() - } - - /// Set the current set of authorities' session keys. - /// - /// Called by `next_session` only. - 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 - /// and will be mapped to a session key with the most recent `set_next_session_key`. - pub fn validators() -> Vec { - unimplemented!() - } - - /// Set the current set of validators. - /// - /// Called by staking::next_era() only. - pub fn set_validators(_new: &[AccountID]) { - unimplemented!() - } - - /// The number of blocks in each session. - pub fn session_length() -> BlockNumber { - storage_into(b"con\0bps") - } - - /// Sets the session key of `_transactor` to `_session`. This doesn't take effect until the next - /// session. - pub fn set_session_key(_transactor: &AccountID, _session: &AccountID) { - unimplemented!() - } - - /// Move onto next session: register the new authority set. - pub fn next_session() { - // TODO: Call set_authorities(). - unimplemented!() - } - - /// Hook to be called prior to transaction processing. - pub fn pre_transactions() {} - - /// Hook to be called after to transaction processing. - pub fn post_transactions() { - // TODO: check block number and call next_session if necessary. - } -} - -pub mod staking { - use super::*; - - /// The length of a staking era in blocks. - 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 { - storage_into(b"sta\0spe") - } - - /// The era has changed - enact new staking set. - /// - /// NOTE: This always happens on a session change. - pub fn next_era() { - unimplemented!() - } - - /// The balance of a given account. - 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) { - 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!() - } - - /// 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!() - } - - /// Hook to be called prior to transaction processing. - pub fn pre_transactions() { - consensus::pre_transactions(); - } - - /// Hook to be called after to transaction processing. - pub fn post_transactions() { - // TODO: check block number and call next_era if necessary. - consensus::post_transactions(); - } -} - -pub mod timestamp { - use super::*; - - 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); - }); - } - - #[test] - fn staking_balance_transfer_dispatch_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] - ], }; - - let tx = Transaction { - signed: one.clone(), - function: Function::StakingTransferStake, - input_data: vec![].join(&two).join(&69u64), - nonce: 0, - }; - - with_externalities(&mut t, || { - environment::execute_transaction(&tx); - assert_eq!(staking::balance(&one), 42); - assert_eq!(staking::balance(&two), 69); - }); - } - - #[test] - fn serialise_transaction_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx = Transaction { - signed: one.clone(), - function: Function::StakingTransferStake, - input_data: vec![].join(&two).join(&69u64), - nonce: 69, - }; - let serialised = tx.to_vec(); - assert_eq!(serialised, vec![ - 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, - 69, 0, 0, 0, 0, 0, 0, 0, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0 - ]); - } - - #[test] - fn deserialise_transaction_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx = Transaction { - signed: one.clone(), - function: Function::StakingTransferStake, - input_data: vec![].join(&two).join(&69u64), - nonce: 69, - }; - let data = [ - 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, - 69, 0, 0, 0, 0, 0, 0, 0, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0 - ]; - let deserialised = Transaction::from_slice(&data).unwrap(); - assert_eq!(deserialised, tx); - } - - #[test] - fn serialise_header_works() { - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let serialised = h.to_vec(); - assert_eq!(serialised, vec![ - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 - ]); - } - - #[test] - fn deserialise_header_works() { - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let data = [ - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 - ]; - let deserialised = Header::from_slice(&data).unwrap(); - assert_eq!(deserialised, h); - } - - #[test] - fn serialise_block_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx1 = Transaction { - signed: one.clone(), - function: Function::StakingTransferStake, - input_data: vec![].join(&two).join(&69u64), - nonce: 69, - }; - let tx2 = Transaction { - signed: two.clone(), - function: Function::StakingStake, - input_data: vec![], - nonce: 42, - }; - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let b = Block { - header: h, - transactions: vec![tx1, tx2], - }; - let serialised = b.to_vec(); - assert_eq!(serialised, vec![ - // header - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, - // transactions - 130, 0, 0, 0, - // tx1 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, - 69, 0, 0, 0, 0, 0, 0, 0, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0, - // tx2 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, - 42, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]); - } - - #[test] - fn deserialise_block_works() { - let one: AccountID = [1u8; 32]; - let two: AccountID = [2u8; 32]; - let tx1 = Transaction { - signed: one.clone(), - function: Function::StakingTransferStake, - input_data: vec![].join(&two).join(&69u64), - nonce: 69, - }; - let tx2 = Transaction { - signed: two.clone(), - function: Function::StakingStake, - input_data: vec![], - nonce: 42, - }; - let h = Header { - parent_hash: [4u8; 32], - number: 42, - state_root: [5u8; 32], - transaction_root: [6u8; 32], - digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, - }; - let b = Block { - header: h, - transactions: vec![tx1, tx2], - }; - let data = [ - // header - 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 42, 0, 0, 0, 0, 0, 0, 0, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 26, 0, 0, 0, - 7, 0, 0, 0, - 111, 110, 101, 32, 108, 111, 103, - 11, 0, 0, 0, - 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, - // transactions - 130, 0, 0, 0, - // tx1 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, - 69, 0, 0, 0, 0, 0, 0, 0, - 40, 0, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 69, 0, 0, 0, 0, 0, 0, 0, - // tx2 - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0, - 42, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ]; - let deserialised = Block::from_slice(&data).unwrap(); - assert_eq!(deserialised, b); - } -} diff --git a/substrate/wasm-runtime/polkadot/src/primitives.rs b/substrate/wasm-runtime/polkadot/src/primitives.rs new file mode 100644 index 0000000000..a689eb7dce --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/primitives.rs @@ -0,0 +1,378 @@ +use streamreader::{StreamReader, Joiner}; +use slicable::{Slicable, NonTrivialSlicable}; +use function::Function; +use runtime_support::size_of; + +/// The hash of an ECDSA pub key which is used to identify an external transactor. +pub type AccountID = [u8; 32]; +/// The ECDSA pub key of an authority. This is what the external environment/consensus algorithm +/// refers to as a "authority". +pub type SessionKey = AccountID; +pub type Balance = u64; +pub type ChainID = u64; +pub type Hash = [u8; 32]; +pub type BlockNumber = u64; +pub type Timestamp = u64; +pub type TxOrder = u64; + +#[derive(Clone, Default)] +#[cfg_attr(test, derive(PartialEq, Debug))] +pub struct Digest { + pub logs: Vec>, +} + +#[derive(Clone)] +#[cfg_attr(test, derive(PartialEq, Debug))] +pub struct Header { + pub parent_hash: Hash, + pub number: BlockNumber, + pub state_root: Hash, + pub transaction_root: Hash, + pub digest: Digest, +} + +#[cfg_attr(test, derive(PartialEq, Debug))] +pub struct Transaction { + pub signed: AccountID, + pub function: Function, + pub input_data: Vec, + pub nonce: TxOrder, +} + +#[cfg_attr(test, derive(PartialEq, Debug))] +pub struct Block { + pub header: Header, + pub transactions: Vec, +} + +impl Slicable for Transaction { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Transaction { + signed: reader.read()?, + function: Function::from_u8(reader.read()?)?, + nonce: reader.read()?, + input_data: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + vec![] + .join(&self.signed) + .join(&(self.function as u8)) + .join(&self.nonce) + .join(&self.input_data) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = size_of::() + size_of::() + size_of::(); + let second_part = >::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl NonTrivialSlicable for Transaction {} + +impl NonTrivialSlicable for Vec where Vec: Slicable {} + +impl Slicable for Vec { + fn from_slice(value: &[u8]) -> Option { + let len = Self::size_of(&value[0..4])?; + let mut off = 4; + let mut r = vec![]; + while off < len { + let element_len = T::size_of(&value[off..])?; + r.push(T::from_slice(&value[off..off + element_len])?); + off += element_len; + } + Some(r) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + let vecs = self.iter().map(Slicable::to_vec).collect::>(); + let len = vecs.iter().fold(0, |mut a, v| {a += v.len(); a}); + let mut r = vec![].join(&(len as u32)); + vecs.iter().for_each(|v| r.extend_from_slice(v)); + r + } + + fn size_of(data: &[u8]) -> Option { + u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) + } +} + +impl Slicable for Header { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Header { + parent_hash: reader.read()?, + number: reader.read()?, + state_root: reader.read()?, + transaction_root: reader.read()?, + digest: Digest { logs: reader.read()?, }, + }) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + vec![] + .join(&self.parent_hash) + .join(&self.number) + .join(&self.state_root) + .join(&self.transaction_root) + .join(&self.digest.logs) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = size_of::() + size_of::() + size_of::() + size_of::(); + let second_part = >>::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +impl Slicable for Block { + fn from_slice(value: &[u8]) -> Option { + let mut reader = StreamReader::new(value); + Some(Block { + header: reader.read()?, + transactions: reader.read()?, + }) + } + + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + + fn to_vec(&self) -> Vec { + vec![] + .join(&self.header) + .join(&self.transactions) + } + + fn size_of(data: &[u8]) -> Option { + let first_part = Header::size_of(data)?; + let second_part = >::size_of(&data[first_part..])?; + Some(first_part + second_part) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use streamreader::Joiner; + use function::Function; + + #[test] + fn serialise_transaction_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx = Transaction { + signed: one.clone(), + function: Function::StakingTransferStake, + input_data: vec![].join(&two).join(&69u64), + nonce: 69, + }; + let serialised = tx.to_vec(); + assert_eq!(serialised, vec![ + 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, + 69, 0, 0, 0, 0, 0, 0, 0, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0 + ]); + } + + #[test] + fn deserialise_transaction_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx = Transaction { + signed: one.clone(), + function: Function::StakingTransferStake, + input_data: vec![].join(&two).join(&69u64), + nonce: 69, + }; + let data = [ + 1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, + 69, 0, 0, 0, 0, 0, 0, 0, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0 + ]; + let deserialised = Transaction::from_slice(&data).unwrap(); + assert_eq!(deserialised, tx); + } + + #[test] + fn serialise_header_works() { + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let serialised = h.to_vec(); + assert_eq!(serialised, vec![ + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 + ]); + } + + #[test] + fn deserialise_header_works() { + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let data = [ + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103 + ]; + let deserialised = Header::from_slice(&data).unwrap(); + assert_eq!(deserialised, h); + } + + #[test] + fn serialise_block_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx1 = Transaction { + signed: one.clone(), + function: Function::StakingTransferStake, + input_data: vec![].join(&two).join(&69u64), + nonce: 69, + }; + let tx2 = Transaction { + signed: two.clone(), + function: Function::StakingStake, + input_data: vec![], + nonce: 42, + }; + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let b = Block { + header: h, + transactions: vec![tx1, tx2], + }; + let serialised = b.to_vec(); + assert_eq!(serialised, vec![ + // header + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, + // transactions + 130, 0, 0, 0, + // tx1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, + 69, 0, 0, 0, 0, 0, 0, 0, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0, + // tx2 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, + 42, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]); + } + + #[test] + fn deserialise_block_works() { + let one: AccountID = [1u8; 32]; + let two: AccountID = [2u8; 32]; + let tx1 = Transaction { + signed: one.clone(), + function: Function::StakingTransferStake, + input_data: vec![].join(&two).join(&69u64), + nonce: 69, + }; + let tx2 = Transaction { + signed: two.clone(), + function: Function::StakingStake, + input_data: vec![], + nonce: 42, + }; + let h = Header { + parent_hash: [4u8; 32], + number: 42, + state_root: [5u8; 32], + transaction_root: [6u8; 32], + digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], }, + }; + let b = Block { + header: h, + transactions: vec![tx1, tx2], + }; + let data = [ + // header + 4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 42, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 26, 0, 0, 0, + 7, 0, 0, 0, + 111, 110, 101, 32, 108, 111, 103, + 11, 0, 0, 0, + 97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103, + // transactions + 130, 0, 0, 0, + // tx1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, + 69, 0, 0, 0, 0, 0, 0, 0, + 40, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 69, 0, 0, 0, 0, 0, 0, 0, + // tx2 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, + 42, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ]; + let deserialised = Block::from_slice(&data).unwrap(); + assert_eq!(deserialised, b); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/slicable.rs b/substrate/wasm-runtime/polkadot/src/slicable.rs new file mode 100644 index 0000000000..0459881f0e --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/slicable.rs @@ -0,0 +1,69 @@ +use runtime_support::{Vec, size_of, transmute, uninitialized, slice}; +use streamreader::Joiner; +use endiansensitive::EndianSensitive; + +/// Trait that allows zero-copy read/write of value-references to/from slices in LE format. +pub trait Slicable: Sized { + fn from_slice(value: &[u8]) -> Option { + Self::set_as_slice(|out| if value.len() == out.len() { + out.copy_from_slice(&value); + true + } else { + false + }) + } + fn to_vec(&self) -> Vec { + self.as_slice_then(|s| s.to_vec()) + } + fn set_as_slice bool>(set_slice: F) -> Option; + fn as_slice_then R>(&self, f: F) -> R { + f(&self.to_vec()) + } + fn size_of(_value: &[u8]) -> Option; +} + +pub trait NonTrivialSlicable: Slicable {} + +impl Slicable for T { + fn set_as_slice bool>(fill_slice: F) -> Option { + let size = size_of::(); + let mut result: T = unsafe { uninitialized() }; + let result_slice = unsafe { + slice::from_raw_parts_mut(transmute::<*mut T, *mut u8>(&mut result), size) + }; + if fill_slice(result_slice) { + Some(result.from_le()) + } else { + None + } + } + fn as_slice_then R>(&self, f: F) -> R { + let size = size_of::(); + self.as_le_then(|le| { + let value_slice = unsafe { + slice::from_raw_parts(transmute::<*const Self, *const u8>(le), size) + }; + f(value_slice) + }) + } + fn size_of(_value: &[u8]) -> Option { + Some(size_of::()) + } +} + +impl Slicable for Vec { + fn from_slice(value: &[u8]) -> Option { + Some(value[4..].to_vec()) + } + fn set_as_slice bool>(_fill_slice: F) -> Option { + unimplemented!(); + } + fn to_vec(&self) -> Vec { + let mut r: Vec = vec![].join(&(self.len() as u32)); + r.extend_from_slice(&self); + r + } + fn size_of(data: &[u8]) -> Option { + u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize) + } +} diff --git a/substrate/wasm-runtime/polkadot/src/staking.rs b/substrate/wasm-runtime/polkadot/src/staking.rs new file mode 100644 index 0000000000..d8903504d2 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/staking.rs @@ -0,0 +1,109 @@ +use keyedvec::KeyedVec; +use storage::{Storage, storage_into}; +use primitives::{BlockNumber, Balance, AccountID}; +use consensus; + +/// The length of a staking era in blocks. +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 { + storage_into(b"sta\0spe") +} + +/// The era has changed - enact new staking set. +/// +/// NOTE: This always happens on a session change. +pub fn next_era() { + unimplemented!() +} + +/// The balance of a given account. +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) { + 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!() +} + +/// 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!() +} + +/// Hook to be called prior to transaction processing. +pub fn pre_transactions() { + consensus::pre_transactions(); +} + +/// Hook to be called after to transaction processing. +pub fn post_transactions() { + // TODO: check block number and call next_era if necessary. + consensus::post_transactions(); +} + + +#[cfg(test)] +mod tests { + use runtime_support::with_externalities; + use testing::TestExternalities; + use primitives::{AccountID}; + use staking; + + 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); + }); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/storage.rs b/substrate/wasm-runtime/polkadot/src/storage.rs new file mode 100644 index 0000000000..13a9074d95 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/storage.rs @@ -0,0 +1,23 @@ +use slicable::Slicable; +use endiansensitive::EndianSensitive; +use runtime_support; + +pub trait Storage { + fn storage_into(key: &[u8]) -> Self; + fn store(&self, key: &[u8]); +} + +impl Storage for T { + fn storage_into(key: &[u8]) -> Self { + Slicable::set_as_slice(|out| runtime_support::read_storage(key, out) == out.len()) + .unwrap_or_else(Default::default) + } + + fn store(&self, key: &[u8]) { + self.as_slice_then(|slice| runtime_support::set_storage(key, slice)); + } +} + +pub fn storage_into(key: &[u8]) -> T { + T::storage_into(key) +} diff --git a/substrate/wasm-runtime/polkadot/src/streamreader.rs b/substrate/wasm-runtime/polkadot/src/streamreader.rs new file mode 100644 index 0000000000..3bb427a1ef --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/streamreader.rs @@ -0,0 +1,63 @@ +use slicable::Slicable; + +pub struct StreamReader<'a> { + data: &'a[u8], + offset: usize, +} + +impl<'a> StreamReader<'a> { + pub fn new(data: &'a[u8]) -> Self { + StreamReader { + data: data, + offset: 0, + } + } + pub fn read(&mut self) -> Option { + let size = T::size_of(&self.data[self.offset..])?; + let new_offset = self.offset + size; + let slice = &self.data[self.offset..new_offset]; + self.offset = new_offset; + Slicable::from_slice(slice) + } +} + +pub trait Joiner { + fn join(self, value: &T) -> Self; +} + +impl Joiner for Vec { + fn join(mut self, value: &T) -> Vec { + value.as_slice_then(|s| self.extend_from_slice(s)); + self + } +} +/* +// Not in use yet +// TODO: introduce fn size_will_be(&self) -> usize; to Slicable trait and implement +struct StreamWriter<'a> { + data: &'a mut[u8], + offset: usize, +} + +impl<'a> StreamWriter<'a> { + pub fn new(data: &'a mut[u8]) -> Self { + StreamWriter { + data: data, + offset: 0, + } + } + pub fn write(&mut self, value: &T) -> bool { + value.as_slice_then(|s| { + let new_offset = self.offset + s.len(); + if self.data.len() <= new_offset { + let slice = &mut self.data[self.offset..new_offset]; + self.offset = new_offset; + slice.copy_from_slice(s); + true + } else { + false + } + }) + } +} +*/ diff --git a/substrate/wasm-runtime/polkadot/src/system.rs b/substrate/wasm-runtime/polkadot/src/system.rs new file mode 100644 index 0000000000..96169d09cc --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/system.rs @@ -0,0 +1,129 @@ +use primitives::{Block, BlockNumber, Hash, Transaction}; +use runtime_support::{Vec, swap}; +use environment::with_env; +use staking; +use runtime_support; + +/// The current block number being processed. Set by `execute_block`. +pub fn block_number() -> BlockNumber { + with_env(|e| e.block_number) +} + +/// Get the block hash of a given block (uses storage). +pub fn block_hash(_number: BlockNumber) -> Hash { + unimplemented!() +} + +/// Deposits a log and ensures it matches the blocks log data. +pub fn deposit_log(log: &[u8]) { + with_env(|e| { + assert_eq!(log, &e.digest.logs[e.next_log_index][..]); + e.next_log_index += 1; + }); +} + +pub fn execute_block(mut block: Block) -> Vec { + // populate environment from header. + with_env(|e| { + e.block_number = block.header.number; + swap(&mut e.digest, &mut block.header.digest); + e.next_log_index = 0; + }); + + // TODO: check transaction trie root represents the transactions. + // TODO: store the header hash in storage. + + staking::pre_transactions(); + + block.transactions.iter().for_each(|tx| { execute_transaction(tx); }); + + staking::post_transactions(); + + final_checks(&block); + + // TODO: check state root somehow + + Vec::new() +} + +fn final_checks(_block: &Block) { + with_env(|e| { + assert_eq!(e.next_log_index, e.digest.logs.len()); + }); +} + +/// Execute a given transaction. +pub fn execute_transaction(_tx: &Transaction) -> Vec { + // TODO: decode data and ensure valid + // TODO: ensure signature valid and recover id (use authentication::authenticate) + // TODO: check nonce + // TODO: increment nonce in storage + // TODO: ensure target_function valid + // TODO: decode parameters + + _tx.function.dispatch(&_tx.signed, &_tx.input_data); + + // TODO: encode any return + Vec::new() +} + +/// Set the new code. +pub fn set_code(new: &[u8]) { + runtime_support::set_storage(b"\0code", new) +} + +#[cfg(test)] +mod tests { + use streamreader::Joiner; + use function::Function; + use std::collections::HashMap; + use runtime_support::{NoError, with_externalities, Externalities}; + use primitives::{AccountID, Transaction}; + use system; + use staking; + + #[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_transfer_dispatch_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] + ], }; + + let tx = Transaction { + signed: one.clone(), + function: Function::StakingTransferStake, + input_data: vec![].join(&two).join(&69u64), + nonce: 0, + }; + + with_externalities(&mut t, || { + system::execute_transaction(&tx); + assert_eq!(staking::balance(&one), 42); + assert_eq!(staking::balance(&two), 69); + }); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/testing.rs b/substrate/wasm-runtime/polkadot/src/testing.rs new file mode 100644 index 0000000000..d73917eb16 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/testing.rs @@ -0,0 +1,19 @@ +use runtime_support::{NoError, Externalities}; +use std::collections::HashMap; + +#[derive(Debug, Default)] +pub struct TestExternalities { + pub 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); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/timestamp.rs b/substrate/wasm-runtime/polkadot/src/timestamp.rs new file mode 100644 index 0000000000..6a4043651d --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/timestamp.rs @@ -0,0 +1,9 @@ +use primitives::Timestamp; + +pub fn timestamp() -> Timestamp { + unimplemented!() +} + +pub fn set_timestamp(_now: Timestamp) { + unimplemented!() +}