From 4bbc0943a379a091bcc8071f5bd69dd34f3d61ca Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 8 Jan 2016 19:12:19 +0100 Subject: [PATCH] Initial sketch of Block struct. --- src/block.rs | 146 +++++++++++++++++++++++++++++++++++++++++++++ src/builtin.rs | 6 ++ src/engine.rs | 16 +++-- src/ethash.rs | 6 ++ src/spec.rs | 5 +- src/state.rs | 8 ++- src/transaction.rs | 4 ++ 7 files changed, 176 insertions(+), 15 deletions(-) create mode 100644 src/block.rs diff --git a/src/block.rs b/src/block.rs new file mode 100644 index 000000000..4f9ed52d6 --- /dev/null +++ b/src/block.rs @@ -0,0 +1,146 @@ +use transaction::*; +use engine::*; +use env_info::*; + +/// A transaction/receipt execution entry. +pub struct Entry { + transaction: Transaction, + receipt: Receipt, +} + +/// Internal type for a block's common elements. +pub struct Block { + header: Header, + + /// State is the most final state in the block. + state: State, + + archive: Vec, + archive_set: HashSet, +} + +impl Block { + fn new(header: Header, state: State) -> Block { + Block { + header: Header:new(), + state: state, + archive: Vec::new(), + archive_set: HashSet::new(), + } + } + + pub fn state_mut(&mut self) -> &mut State { self.state } +} + +/// Trait for a object that is_a `Block`. +trait IsBlock { + /// Get the block associated with this object. + fn block(&self) -> &Block; + + /// Get the header associated with this object's block. + pub fn header(&self) -> &Header { self.block().header } + + /// Get the final state associated with this object's block. + pub fn state(&self) -> &State { self.block().state } + + /// Get all information on transactions in this block. + pub fn archive(&self) -> &Vec { self.block().archive } +} + +impl IsBlock for Block { + fn block(&self) -> &Block { self } + fn block_mut(&self) -> &mut Block { self } +} + +/// Block that is ready for transactions to be added. +/// +/// It's a bit like a Vec, eccept that whenever a transaction is pushed, we execute it and +/// maintain the system `state()`. We also archive execution receipts in preparation for later block creation. +pub struct OpenBlock { + block: Block, + engine: &Engine, + last_hashes: LastHashes, +} + +/// Just like OpenBlock, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields, +/// and collected the uncles. +/// +/// There is no function available to push a transaction. If you want that you'll need to `reopen()` it. +pub struct ClosedBlock { + open_block: OpenBlock, + uncles: Vec
, +} + +/// A block that has a valid seal. +/// +/// The block's header has valid seal arguments. The block cannot be reversed into a ClosedBlock or OpenBlock. +pub struct SealedBlock { + block: Block, + bytes: Bytes, +} + +impl OpenBlock { + pub fn new(engine: &Engine, mut db: OverlayDB, parent: &Header, last_hashes: LastHashes) -> OpenBlock { + let mut r = OpenBlock { + block: Block::new(State::new_existing(db, parent.state_root.clone(), engine.account_start_nonce())), + engine: engine, + last_hashes: last_hashes, + } + + engine.populate_from_parent(r.block.header, parent); + engine.on_init_block(&mut r); + r + } + + /// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles. + pub fn push_transaction(&mut self, t: Transaction, mut h: Option) -> Result<&Receipt, EthcoreError> { + let env_info = EnvInfo{ + number: self.header.number, + author: self.header.author, + timestamp: self.header.timestamp, + difficulty: self.header.difficulty, + last_hashes: self.last_hashes.clone(), + gas_used: if let Some(ref t) = self.archive.last() {t.receipt.gas_used} else {U256::from(0)}, + }; + match self.state.apply(env_info, self.engine, t, true) { + Ok(x) => { + self.transactionHashes.insert(h.unwrap_or_else(||t.sha3())); + self.transactions.push(BlockTransaction{t, x.receipt}); + Ok(&self.transactions.last().unwrap().receipt) + } + Err(x) => Err(x) + } + } + + /// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles. + pub fn close(self, bc: &BlockChain) -> ClosedBlock { unimplemented!(); } +} + +impl IsBlock for OpenBlock { + fn block(&self) -> &Block { self.block } + fn block_mut(&self) -> &mut Block { self.block } +} + +impl ClosedBlock { + /// Get the hash of the header without seal arguments. + pub fn preseal_hash(&self) -> H256 { unimplemented!(); } + + /// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles. + pub fn seal(self, seal_fields: Vec) -> SealedBlock { unimplemented!(); } + + /// Turn this back into an `OpenBlock`. + pub fn reopen(self) -> OpenBlock { unimplemented!(); } +} + +impl IsBlock for ClosedBlock { + fn block(&self) -> &Block { self.open_block.block } + fn block_mut(&self) -> &mut Block { self.open_block.block } +} + +impl SealedBlock { +} + +impl IsBlock for SealedBlock { + fn block(&self) -> &Block { self.block } + fn block_mut(&self) -> &mut Block { self.block.block } +} diff --git a/src/builtin.rs b/src/builtin.rs index b6eb2591b..ade5aa8d2 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -58,6 +58,12 @@ impl Builtin { } None } + + /// Simple forwarder for cost. + pub fn cost(&self, s: usize) -> U256 { (*self.cost)(s) } + + /// Simple forwarder for execute. + pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); } } pub fn copy_to(src: &[u8], dest: &mut[u8]) { diff --git a/src/engine.rs b/src/engine.rs index 9632ba090..1b7d8d6b6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,5 +1,6 @@ use std::collections::hash_map::*; use util::bytes::*; +use util::hash::*; use util::uint::*; use util::rlp::*; use util::semantic_version::*; @@ -35,18 +36,16 @@ pub trait Engine { /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. fn maximum_extra_data_size(&self, _env_info: &EnvInfo) -> usize { decode(&self.spec().engine_params.get("maximum_extra_data_size").unwrap()) } fn account_start_nonce(&self, _env_info: &EnvInfo) -> U256 { decode(&self.spec().engine_params.get("account_start_nonce").unwrap()) } - // TODO: refactor in terms of `on_preseal_block` - fn block_reward(&self, _env_info: &EnvInfo) -> U256 { decode(&self.spec().engine_params.get("block_reward").unwrap()) } /// Block transformation functions, before and after the transactions. -// fn on_new_block(&self, _env_info: &EnvInfo, _block: &mut Block) -> Result<(), EthcoreError> {} -// fn on_preseal_block(&self, _env_info: &EnvInfo, _block: &mut Block) -> Result<(), EthcoreError> {} + fn on_new_block(&self, _block: &mut Block) -> Result<(), EthcoreError> { Ok(()) } + fn on_close_block(&self, _block: &mut Block) -> Result<(), EthcoreError> { Ok(()) } /// Verify that `header` is valid. /// `parent` (the parent header) and `block` (the header's full block) may be provided for additional /// checks. Returns either a null `Ok` or a general error detailing the problem with import. // TODO: consider including State in the params. - fn verify(&self, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), EthcoreError> { Ok(()) } + fn verify_block(&self, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), EthcoreError> { Ok(()) } /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. @@ -59,10 +58,9 @@ pub trait Engine { // TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic // from Spec into here and removing the Spec::builtins field. -/* fn is_builtin(&self, a: Address) -> bool; - fn cost_of_builtin(&self, a: Address, in: &[u8]) -> bignum; - fn execute_builtin(&self, a: Address, in: &[u8], out: &mut [u8]); -*/ + fn is_builtin(&self, a: &Address) -> bool { self.spec().builtins.contains_key(a) } + fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.spec().builtins.get(a).unwrap().cost(input.len()) } + fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.spec().builtins.get(a).unwrap().execute(input, output); } // TODO: sealing stuff - though might want to leave this for later. } diff --git a/src/ethash.rs b/src/ethash.rs index 27d39bdaa..1cb85cec6 100644 --- a/src/ethash.rs +++ b/src/ethash.rs @@ -19,4 +19,10 @@ impl Engine for Ethash { fn name(&self) -> &str { "Ethash" } fn spec(&self) -> &Spec { &self.spec } fn evm_schedule(&self, _env_info: &EnvInfo) -> EvmSchedule { EvmSchedule::new_frontier() } + + /// Apply the block reward on finalisation of the block. + fn on_close_block(&self, block: &mut Block) -> Result<(), EthcoreError> { + let a = block.header().author.clone(); + block.state_mut().add_balance(a, decode(&self.spec().engine_params.get("block_reward").unwrap())); + } } diff --git a/src/spec.rs b/src/spec.rs index 8682972c3..eee653966 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -370,9 +370,8 @@ mod tests { use super::*; #[test] - fn all() { + fn morden_manual() { let morden = Spec::new_morden_manual(); -// let engine = morden.to_engine(); // Ethash doesn't exist as an engine yet, so would fail. assert_eq!(*morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); let genesis = morden.genesis_block(); @@ -386,8 +385,6 @@ mod tests { assert_eq!(*morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); let genesis = morden.genesis_block(); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); - - let engine = morden.to_engine(); } #[test] diff --git a/src/state.rs b/src/state.rs index dbe349d77..c60e9e5c3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -15,7 +15,11 @@ use env_info::EnvInfo; use engine::Engine; /// Information concerning the result of the `State::apply` operation. -pub struct ApplyResult; // TODO +pub struct ApplyInfo { + r: Receipt, +} + +type ApplyResult = Result; /// Representation of the entire state of all accounts in the system. pub struct State { @@ -131,7 +135,7 @@ impl State { /// Execute a given transaction. /// This will change the state accordingly. - pub fn apply(_env_info: &EnvInfo, _engine: &Engine, _t: &Transaction, _is_permanent: bool) -> (ApplyResult, Receipt) { + pub fn apply(_env_info: &EnvInfo, _engine: &Engine, _t: &Transaction, _is_permanent: bool) -> ApplyResult { unimplemented!(); } diff --git a/src/transaction.rs b/src/transaction.rs index 82fa9ccb6..930fcc2e1 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -3,6 +3,10 @@ use util::bytes::*; use util::uint::*; use util::rlp::*; +struct Receipt { + gas_used: U256, +} + /// A set of information describing an externally-originating message call /// or contract creation operation. pub struct Transaction {