Implement basic block and tx processing

This commit is contained in:
Gav
2018-01-18 16:24:53 +01:00
parent 92d8712b2b
commit 7f8949bed1
5 changed files with 142 additions and 99 deletions
@@ -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<u8>;
}
impl KeyedVec for AccountID {
fn to_keyed_vec(&self, prepend_key: &[u8]) -> Vec<u8> {
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<u8> {
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]);
+4 -4
View File
@@ -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<u8>) -> Vec<u8> {
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<u8>) -> Vec<u8> {
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);
@@ -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<u8> {
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<u8> {
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<u8> {
// 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 {
@@ -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<Self> {
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<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
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<usize> {
let first_part = size_of::<Hash>() + size_of::<BlockNumber>() + size_of::<Hash>() + size_of::<Hash>();
let second_part = <Vec<Vec<u8>>>::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<T: Slicable> 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<UncheckedTransaction>,
}
impl Slicable for Block {
fn from_slice(value: &[u8]) -> Option<Self> {
let mut reader = StreamReader::new(value);
Some(Block {
header: reader.read()?,
transactions: reader.read()?,
})
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.header)
.join(&self.transactions)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = Header::size_of(data)?;
let second_part = <Vec<Transaction>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl NonTrivialSlicable for Block {}
impl<T: Slicable> NonTrivialSlicable for Vec<T> where Vec<T>: Slicable {}
impl<T: NonTrivialSlicable> Slicable for Vec<T> {
@@ -161,64 +240,6 @@ impl<T: NonTrivialSlicable> Slicable for Vec<T> {
}
}
impl Slicable for Header {
fn from_slice(value: &[u8]) -> Option<Self> {
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<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
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<usize> {
let first_part = size_of::<Hash>() + size_of::<BlockNumber>() + size_of::<Hash>() + size_of::<Hash>();
let second_part = <Vec<Vec<u8>>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl Slicable for Block {
fn from_slice(value: &[u8]) -> Option<Self> {
let mut reader = StreamReader::new(value);
Some(Block {
header: reader.read()?,
transactions: reader.read()?,
})
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
unimplemented!();
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&self.header)
.join(&self.transactions)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = Header::size_of(data)?;
let second_part = <Vec<Transaction>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -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<T: Default + Sized + EndianSensitive> 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<T: Storage>(key: &[u8]) -> T {
T::into(key)
impl Storage for [u8] {
fn store(&self, key: &[u8]) {
runtime_support::set_storage(key, self)
}
}