mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 18:11:10 +00:00
Implement basic block and tx processing
This commit is contained in:
@@ -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]);
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user