diff --git a/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs b/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs index fcc410e582..fad6b1956f 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/keyedvec.rs @@ -1,17 +1,20 @@ use runtime_support::Vec; -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_non_endians { + ( $( $t:ty ),* ) => { $( + impl KeyedVec for $t { + 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 { @@ -28,4 +31,7 @@ macro_rules! impl_endians { )* } } -impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); +impl_endians!(u8, i8, u16, u32, u64, usize, i16, i32, i64, isize); +impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], + [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], + [u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]); diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index 18ce4059a8..7653aaff9b 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -19,16 +19,16 @@ use slicable::Slicable; use primitives::{Block, UncheckedTransaction}; // TODO: add externals for: -// - keccak256 (or some better hashing scheme) // - trie rooting -// - ECDSA-recover (or some better sig scheme) pub fn execute_block(input: Vec) -> Vec { - runtime::system::execute_block(Block::from_slice(&input).unwrap()) + runtime::system::execute_block(Block::from_slice(&input).unwrap()); + Vec::new() } pub fn execute_transaction(input: Vec) -> Vec { - runtime::system::execute_transaction(&UncheckedTransaction::from_slice(&input).unwrap()) + runtime::system::execute_transaction(&UncheckedTransaction::from_slice(&input).unwrap()); + Vec::new() } impl_stubs!(execute_block, execute_transaction); diff --git a/substrate/wasm-runtime/polkadot/src/runtime/system.rs b/substrate/wasm-runtime/polkadot/src/runtime/system.rs index 3c4d020dee..2ddc83bdb2 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/system.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/system.rs @@ -1,7 +1,8 @@ -use primitives::{Block, BlockNumber, Hash, UncheckedTransaction}; +use primitives::{Block, BlockNumber, Hash, UncheckedTransaction, TxOrder, Hashable}; use runtime_support::{Vec, swap}; +use storage::Storage; +use keyedvec::KeyedVec; use environment::with_env; -use runtime_support; use runtime::staking; /// The current block number being processed. Set by `execute_block`. @@ -10,8 +11,8 @@ pub fn block_number() -> BlockNumber { } /// Get the block hash of a given block (uses storage). -pub fn block_hash(_number: BlockNumber) -> Hash { - unimplemented!() +pub fn block_hash(number: BlockNumber) -> Hash { + Storage::into(&number.to_keyed_vec(b"sys\0old\0")) } /// Deposits a log and ensures it matches the blocks log data. @@ -22,7 +23,7 @@ pub fn deposit_log(log: &[u8]) { }); } -pub fn execute_block(mut block: Block) -> Vec { +pub fn execute_block(mut block: Block) { // populate environment from header. with_env(|e| { e.block_number = block.header.number; @@ -30,20 +31,29 @@ pub fn execute_block(mut block: Block) -> Vec { e.next_log_index = 0; }); + let ref header = block.header; + + // check parent_hash is correct. + assert!( + header.number > 0 && block_hash(header.number - 1) == header.parent_hash, + "Parent hash should be valid." + ); + // TODO: check transaction trie root represents the transactions. - // TODO: store the header hash in storage. + // store the header hash in storage. + let header_hash_key = header.number.to_keyed_vec(b"sys\0old\0"); + header.keccak256().store(&header_hash_key); + + // execute transactions staking::pre_transactions(); - - block.transactions.iter().for_each(|tx| { execute_transaction(tx); }); - + block.transactions.iter().for_each(execute_transaction); staking::post_transactions(); + // any final checks final_checks(&block); - // TODO: check state root somehow - - Vec::new() + // TODO: check storage root somehow } fn final_checks(_block: &Block) { @@ -53,29 +63,34 @@ fn final_checks(_block: &Block) { } /// Execute a given transaction. -pub fn execute_transaction(_tx: &UncheckedTransaction) -> 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 +pub fn execute_transaction(utx: &UncheckedTransaction) { + // Verify the signature is good. + assert!(utx.ed25519_verify(), "All transactions should be properly signed"); - _tx.transaction.function.dispatch(&_tx.transaction.signed, &_tx.transaction.input_data); + let ref tx = utx.transaction; - // TODO: encode any return - Vec::new() + // check nonce + let nonce_key = tx.signed.to_keyed_vec(b"sys\0non\0"); + let expected_nonce: TxOrder = Storage::into(&nonce_key); + assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); + + // increment nonce in storage + (expected_nonce + 1).store(&nonce_key); + + // decode parameters and dispatch + tx.function.dispatch(&tx.signed, &tx.input_data); } /// Set the new code. pub fn set_code(new: &[u8]) { - runtime_support::set_storage(b"\0code", new) + new.store(b"\0code"); } #[cfg(test)] mod tests { use joiner::Joiner; use function::Function; + use codec::keyedvec::KeyedVec; use std::collections::HashMap; use runtime_support::{NoError, with_externalities, Externalities}; use primitives::{AccountID, UncheckedTransaction, Transaction}; @@ -111,7 +126,7 @@ mod tests { 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] + one.to_keyed_vec(b"sta\0bal\0") => vec![111u8, 0, 0, 0, 0, 0, 0, 0] ], }; let tx = UncheckedTransaction { diff --git a/substrate/wasm-runtime/polkadot/src/support/primitives.rs b/substrate/wasm-runtime/polkadot/src/support/primitives.rs index bb82da8f3b..4cf9dd9d32 100644 --- a/substrate/wasm-runtime/polkadot/src/support/primitives.rs +++ b/substrate/wasm-runtime/polkadot/src/support/primitives.rs @@ -3,7 +3,7 @@ use streamreader::StreamReader; use joiner::Joiner; use slicable::{Slicable, NonTrivialSlicable}; use function::Function; -use runtime_support::size_of; +use runtime_support::{size_of, keccak256, ed25519_verify}; #[cfg(test)] use std::fmt; @@ -36,6 +36,40 @@ pub struct Header { pub digest: Digest, } +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::new() + .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 NonTrivialSlicable for Header {} + #[cfg_attr(test, derive(PartialEq, Debug))] pub struct Transaction { pub signed: AccountID, @@ -74,6 +108,16 @@ impl Slicable for Transaction { } } +pub trait Hashable: Sized { + fn keccak256(&self) -> [u8; 32]; +} + +impl Hashable for T { + fn keccak256(&self) -> [u8; 32] { + keccak256(&self.to_vec()) + } +} + impl NonTrivialSlicable for Transaction {} pub struct UncheckedTransaction { @@ -81,6 +125,13 @@ pub struct UncheckedTransaction { pub signature: [u8; 64], } +impl UncheckedTransaction { + pub fn ed25519_verify(&self) -> bool { + let msg = self.transaction.to_vec(); + ed25519_verify(&self.signature, &msg, &self.transaction.signed) + } +} + #[cfg(test)] impl PartialEq for UncheckedTransaction { fn eq(&self, other: &Self) -> bool { @@ -129,6 +180,34 @@ pub struct Block { pub transactions: Vec, } +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::new() + .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) + } +} + +impl NonTrivialSlicable for Block {} + impl NonTrivialSlicable for Vec where Vec: Slicable {} impl Slicable for Vec { @@ -161,64 +240,6 @@ impl Slicable for Vec { } } -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::new() - .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::new() - .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::*; diff --git a/substrate/wasm-runtime/polkadot/src/support/storage.rs b/substrate/wasm-runtime/polkadot/src/support/storage.rs index 99c8bab880..becb4f7fb2 100644 --- a/substrate/wasm-runtime/polkadot/src/support/storage.rs +++ b/substrate/wasm-runtime/polkadot/src/support/storage.rs @@ -3,7 +3,7 @@ use endiansensitive::EndianSensitive; use runtime_support; pub trait Storage { - fn into(key: &[u8]) -> Self; + fn into(_key: &[u8]) -> Self where Self: Sized { unimplemented!() } fn store(&self, key: &[u8]); } @@ -12,12 +12,13 @@ impl Storage for T { 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::into(key) +impl Storage for [u8] { + fn store(&self, key: &[u8]) { + runtime_support::set_storage(key, self) + } }