diff --git a/substrate/native-runtime/support/src/lib.rs b/substrate/native-runtime/support/src/lib.rs index 7bbd6d235e4dbcae423167e2e59eef8cde878590..38de2f29b6e90570af3fc2d8836126f06a2accbf 100644 --- a/substrate/native-runtime/support/src/lib.rs +++ b/substrate/native-runtime/support/src/lib.rs @@ -25,12 +25,24 @@ pub struct ExternalitiesHolder<'a> { declare_generic!(ext : ExternalitiesHolder); -pub fn storage(_key: &[u8]) -> Vec<u8> { - ext::with(|holder| holder.ext.storage(_key).ok().map(|s| s.to_vec())) +pub fn storage(key: &[u8]) -> Vec<u8> { + ext::with(|holder| holder.ext.storage(key).ok().map(|s| s.to_vec())) .unwrap_or(None) .unwrap_or_else(|| vec![]) } +pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize { + ext::with(|holder| { + if let Ok(value) = holder.ext.storage(key) { + let written = ::std::cmp::min(value.len(), value_out.len()); + value_out[0..written].copy_from_slice(&value[0..written]); + value.len() + } else { + 0 + } + }).unwrap_or(0) +} + pub fn storage_into<T: Sized>(_key: &[u8]) -> Option<T> { let size = size_of::<T>(); diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index 639cdcdbc48ce77e4cb83570ad391a58eee8a6e1..fa22929a79c845ee8ea20f5461dcaef7bbc5d3c7 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -26,9 +26,81 @@ pub enum Function { ConsensusSetSessionKey, } +impl Function { + fn from_u8(value: u8) -> Option<Function> { + 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<T: Slicable + Sized>(&mut self) -> Option<T> { + let size = size_of::<T>(); + let new_offset = self.offset + size; + let slice = &self.data[self.offset..new_offset]; + self.offset = new_offset; + Slicable::from_slice(slice) + } +} + +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<T: Slicable + Sized>(&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 = &self.data[self.offset..new_offset]; + self.offset = new_offset; + slice.copy_from_slice(s); + true + } else { + false + } + }) + } +} + +trait Joiner { + fn join<T: Slicable + Sized>(self, value: &T) -> Self; +} + +impl Joiner for Vec<u8> { + fn join<T: Slicable + Sized>(mut self, value: &T) -> Vec<u8> { + value.as_slice_then(|s| self.extend_from_slice(s)); + self + } +} + impl Function { /// Dispatch the function. - pub fn dispatch(&self, transactor: &AccountID, params: &[u8]) { + pub fn dispatch(&self, transactor: &AccountID, data: &[u8]) { + let mut params = StreamReader::new(data); match *self { Function::StakingStake => { staking::stake(transactor); @@ -37,14 +109,13 @@ impl Function { staking::unstake(transactor); } Function::StakingTransferStake => { - let dest = FromSlice::from_slice(¶ms[0..size_of::<AccountID>()]).unwrap(); - let value = FromSlice::from_slice(¶ms[size_of::<AccountID>()..size_of::<AccountID>() + 4]).unwrap(); + let dest = params.read().unwrap(); + let value = params.read().unwrap(); staking::transfer_stake(transactor, &dest, value); } Function::ConsensusSetSessionKey => { - let mut session = AccountID::default(); - session.copy_from_slice(¶ms[0..size_of::<AccountID>()]); - consensus::set_session_key(transactor, session); + let session = params.read().unwrap(); + consensus::set_session_key(transactor, &session); } } } @@ -128,6 +199,8 @@ trait EndianSensitive: Sized { fn to_be(self) -> Self { self } fn from_le(self) -> Self { self } fn from_be(self) -> Self { self } + fn as_be_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) } + fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { f(&self) } } macro_rules! impl_endians { @@ -137,6 +210,8 @@ macro_rules! impl_endians { 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, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_be(); f(&d) } + fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_le(); f(&d) } } )* } } @@ -151,23 +226,17 @@ impl_non_endians!(u8, i8, [u8; 20], [u8; 32]); trait Storage { fn storage_into(key: &[u8]) -> Self; - fn store(self, key: &[u8]); + fn store(&self, key: &[u8]); } -impl<T: Default + EndianSensitive> Storage for T { +impl<T: Default + Sized + EndianSensitive> Storage for T { fn storage_into(key: &[u8]) -> Self { - runtime_support::storage_into(key) - .map(EndianSensitive::from_le) + Slicable::set_as_slice(|out| runtime_support::read_storage(key, out) == out.len()) .unwrap_or_else(Default::default) } - fn store(self, key: &[u8]) { - let size = size_of::<Self>(); - 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 store(&self, key: &[u8]) { + self.as_slice_then(|slice| runtime_support::set_storage(key, slice)); } } @@ -175,24 +244,47 @@ fn storage_into<T: Storage>(key: &[u8]) -> T { T::storage_into(key) } -trait FromSlice: Sized { - fn from_slice(value: &[u8]) -> Option<Self>; +/// 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> { + Self::set_as_slice(|out| if value.len() == out.len() { + out.copy_from_slice(&value); + true + } else { + false + }) + } + fn to_vec(&self) -> Vec<u8> { + self.as_slice_then(|s| s.to_vec()) + } + fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(set_slice: F) -> Option<Self>; + fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + f(&self.to_vec()) + } } -impl<T: EndianSensitive> FromSlice for T { - fn from_slice(value: &[u8]) -> Option<Self> { +impl<T: EndianSensitive> Slicable for T { + fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(fill_slice: F) -> Option<Self> { let size = size_of::<T>(); - if value.len() == size { - unsafe { - let mut result: T = std::mem::uninitialized(); - std::slice::from_raw_parts_mut(transmute::<*mut T, *mut u8>(&mut result), size) - .copy_from_slice(&value); - Some(result.from_le()) - } + 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, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { + let size = size_of::<Self>(); + self.as_le_then(|le| { + let value_slice = unsafe { + std::slice::from_raw_parts(transmute::<*const Self, *const u8>(le), size) + }; + f(value_slice) + }) + } } trait KeyedVec { @@ -211,14 +303,11 @@ macro_rules! impl_endians { ( $( $t:ty ),* ) => { $( impl KeyedVec for $t { fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec<u8> { - let size = size_of::<Self>(); - let value_bytes = self.to_le(); - let value_slice = unsafe { - std::slice::from_raw_parts(transmute::<*const Self, *const u8>(&value_bytes), size) - }; - let mut r = prepend_key.to_vec(); - r.extend_from_slice(value_slice); - r + self.as_slice_then(|slice| { + let mut r = prepend_key.to_vec(); + r.extend_from_slice(slice); + r + }) } } )* } @@ -226,7 +315,6 @@ macro_rules! impl_endians { impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); -// TODO: include RLP implementation // TODO: add keccak256 (or some better hashing scheme) & ECDSA-recover (or some better sig scheme) pub fn execute_block(_input: Vec<u8>) -> Vec<u8> { @@ -353,7 +441,7 @@ pub mod consensus { /// 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) { + pub fn set_session_key(_transactor: &AccountID, _session: &AccountID) { unimplemented!() } @@ -523,4 +611,27 @@ mod tests { 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); + }); + } }