From 76bb480afbfa340d5b55e5e008424d368f2d4fa6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 10 Jan 2016 14:05:39 +0100 Subject: [PATCH] Block sealing, tests updated. --- src/block.rs | 112 ++++++++++++++++++++++++++++++----------- src/common.rs | 3 +- src/engine.rs | 9 ++-- src/error.rs | 52 +++++++++++++++++++ src/ethereum/ethash.rs | 4 +- src/header.rs | 11 ++++ src/lib.rs | 1 + src/spec.rs | 4 +- src/state.rs | 8 +-- 9 files changed, 160 insertions(+), 44 deletions(-) create mode 100644 src/error.rs diff --git a/src/block.rs b/src/block.rs index f30509698..ac43be23b 100644 --- a/src/block.rs +++ b/src/block.rs @@ -17,6 +17,8 @@ pub struct Block { archive: Vec, archive_set: HashSet, + + uncles: Vec
, } impl Block { @@ -26,6 +28,7 @@ impl Block { state: state, archive: Vec::new(), archive_set: HashSet::new(), + uncles: Vec::new(), } } @@ -45,6 +48,9 @@ pub trait IsBlock { /// Get all information on transactions in this block. fn archive(&self) -> &Vec { &self.block().archive } + + /// Get all uncles in this block. + fn uncles(&self) -> &Vec
{ &self.block().uncles } } impl IsBlock for Block { @@ -67,7 +73,7 @@ pub struct OpenBlock<'engine> { /// There is no function available to push a transaction. If you want that you'll need to `reopen()` it. pub struct ClosedBlock<'engine> { open_block: OpenBlock<'engine>, - uncles: Bytes, + uncle_bytes: Bytes, } /// A block that has a valid seal. @@ -75,23 +81,52 @@ pub struct ClosedBlock<'engine> { /// 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, + uncle_bytes: Bytes, } impl<'engine> OpenBlock<'engine> { /// Create a new OpenBlock ready for transaction pushing. - pub fn new<'a>(engine: &'a Engine, db: OverlayDB, parent: &Header, last_hashes: LastHashes) -> OpenBlock<'a> { + pub fn new<'a>(engine: &'a Engine, db: OverlayDB, parent: &Header, last_hashes: LastHashes, author: Address, extra_data: Bytes) -> OpenBlock<'a> { let mut r = OpenBlock { block: Block::new(State::from_existing(db, parent.state_root.clone(), engine.account_start_nonce())), engine: engine, last_hashes: last_hashes, }; + r.block.header.set_author(author); + r.block.header.set_extra_data(extra_data); engine.populate_from_parent(&mut r.block.header, parent); engine.on_new_block(&mut r.block); r } + /// Alter the author for the block. + pub fn set_author(&mut self, author: Address) { self.block.header.set_author(author); } + + /// Alter the extra_data for the block. + pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> { + if extra_data.len() > self.engine.maximum_extra_data_size() { + Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: 0, max: self.engine.maximum_extra_data_size(), found: extra_data.len()})) + } else { + self.block.header.set_extra_data(extra_data); + Ok(()) + } + } + + /// Add an uncle to the block, if possible. + /// + /// NOTE Will check chain constraints and the uncle number but will NOT check + /// that the header itself is actually valid. + pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> { + if self.block.uncles.len() >= self.engine.maximum_uncle_count() { + return Err(BlockError::TooManyUncles); + } + // TODO: check number + // TODO: check not a direct ancestor (use last_hashes for that) + self.block.uncles.push(valid_uncle_header); + Ok(()) + } + /// Get the environment info concerning this block. pub fn env_info(&self) -> EnvInfo { // TODO: memoise. @@ -106,8 +141,10 @@ impl<'engine> OpenBlock<'engine> { } } - /// Push a transaction into the block. It will be executed, and archived together with the receipt. - pub fn push_transaction(&mut self, t: Transaction, h: Option) -> Result<&Receipt, EthcoreError> { + /// Push a transaction into the block. + /// + /// If valid, it will be executed, and archived together with the receipt. + pub fn push_transaction(&mut self, t: Transaction, h: Option) -> Result<&Receipt, Error> { let env_info = self.env_info(); match self.block.state.apply(&env_info, self.engine, &t, true) { Ok(x) => { @@ -120,14 +157,12 @@ impl<'engine> OpenBlock<'engine> { } /// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure out the uncles. - pub fn close(self, uncles: Vec
, author: Address, extra_data: Bytes) -> ClosedBlock<'engine> { + pub fn close(self) -> ClosedBlock<'engine> { let mut s = self; s.engine.on_close_block(&mut s.block); - s.block.header.author = author; s.block.header.transactions_root = ordered_trie_root(s.block.archive.iter().map(|ref e| e.transaction.rlp_bytes()).collect()); - let uncle_bytes = uncles.iter().fold(RlpStream::new_list(uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out(); + let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out(); s.block.header.uncles_hash = uncle_bytes.sha3(); - s.block.header.extra_data = extra_data; s.block.header.state_root = s.block.state.root().clone(); s.block.header.receipts_root = ordered_trie_root(s.block.archive.iter().map(|ref e| e.receipt.rlp_bytes()).collect()); s.block.header.log_bloom = s.block.archive.iter().fold(LogBloom::zero(), |mut b, e| {b |= &e.receipt.log_bloom; b}); @@ -142,29 +177,47 @@ impl<'engine> IsBlock for OpenBlock<'engine> { fn block(&self) -> &Block { &self.block } } -impl<'engine> ClosedBlock<'engine> { - fn new<'a>(open_block: OpenBlock<'a>, uncles: Bytes) -> ClosedBlock<'a> { - ClosedBlock { - open_block: open_block, - uncles: uncles, - } - } - - /// 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<'engine> { unimplemented!(); } -} - impl<'engine> IsBlock for ClosedBlock<'engine> { fn block(&self) -> &Block { &self.open_block.block } } +impl<'engine> ClosedBlock<'engine> { + fn new<'a>(open_block: OpenBlock<'a>, uncle_bytes: Bytes) -> ClosedBlock<'a> { + ClosedBlock { + open_block: open_block, + uncle_bytes: uncle_bytes, + } + } + + /// Get the hash of the header without seal arguments. + pub fn hash(&self) -> H256 { self.header().rlp_sha3(Seal::Without) } + + /// Provide a valid seal in order to turn this into a `SealedBlock`. + /// + /// NOTE: This does not check the validity of `seal` with the engine. + pub fn seal(self, seal: Vec) -> Result { + let mut s = self; + if seal.len() != s.open_block.engine.seal_fields() { + return Err(BlockError::InvalidSealArity(Mismatch{expected: s.open_block.engine.seal_fields(), found: seal.len()})); + } + s.open_block.block.header.set_seal(seal); + Ok(SealedBlock { block: s.open_block.block, uncle_bytes: s.uncle_bytes }) + } + + /// Turn this back into an `OpenBlock`. + pub fn reopen(self) -> OpenBlock<'engine> { self.open_block } +} + impl SealedBlock { + /// Get the RLP-encoding of the block. + pub fn rlp_bytes(&self) -> Bytes { + let mut block_rlp = RlpStream::new_list(3); + self.block.header.stream_rlp(&mut block_rlp, Seal::With); + block_rlp.append_list(self.block.archive.len()); + for e in self.block.archive.iter() { e.transaction.rlp_append(&mut block_rlp); } + block_rlp.append_raw(&self.uncle_bytes, 1); + block_rlp.out() + } } impl IsBlock for SealedBlock { @@ -178,6 +231,7 @@ fn open_block() { let genesis_header = engine.spec().genesis_header(); let mut db = OverlayDB::new_temp(); engine.spec().ensure_db_good(&mut db); - let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]); - let _ = b.close(vec![], Address::zero(), vec![]); + let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]); + let b = b.close(); + let _ = b.seal(vec![]); } \ No newline at end of file diff --git a/src/common.rs b/src/common.rs index 106a7a3b9..468a722d7 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,5 +1,6 @@ pub use util::*; pub use basic_types::*; +pub use error::*; pub use env_info::*; pub use evm_schedule::*; pub use views::*; @@ -7,4 +8,4 @@ pub use builtin::*; pub use header::*; pub use account::*; pub use transaction::*; -pub use receipt::*; +pub use receipt::*; \ No newline at end of file diff --git a/src/engine.rs b/src/engine.rs index 7fee085e9..bfaf46348 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -11,7 +11,7 @@ pub trait Engine { fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) } /// The number of additional header fields required for this engine. - fn seal_fields(&self) -> u32 { 0 } + fn seal_fields(&self) -> usize { 0 } /// Default values of the additional fields RLP-encoded in a raw (non-list) harness. fn seal_rlp(&self) -> Bytes { vec![] } @@ -25,7 +25,8 @@ pub trait Engine { fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule; /// 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("maximumExtraDataSize").unwrap()) } + fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) } + fn maximum_uncle_count(&self) -> usize { 2 } fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) } /// Block transformation functions, before and after the transactions. @@ -36,12 +37,12 @@ pub trait Engine { /// `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_block(&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<(), Error> { Ok(()) } /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. // TODO: consider including State in the params. - fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), EthcoreError> { Ok(()) } + fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) } /// Don't forget to call Super::populateFromParent when subclassing & overriding. // TODO: consider including State in the params. diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..68b75ab5b --- /dev/null +++ b/src/error.rs @@ -0,0 +1,52 @@ +//! General error types for use in ethcore. + +use util::*; + +#[derive(Debug)] +pub struct Mismatch { + pub expected: T, + pub found: T, +} + +#[derive(Debug)] +pub struct OutOfBounds { + pub min: T, + pub max: T, + pub found: T, +} + +#[derive(Debug)] +pub enum BlockError { + TooManyUncles, + UncleWrongGeneration, + ExtraDataOutOfBounds(OutOfBounds), + InvalidSealArity(Mismatch), +} + +#[derive(Debug)] +/// General error type which should be capable of representing all errors in ethcore. +pub enum Error { + Util(UtilError), + Block(BlockError), + UnknownEngineName(String), +} + +impl From for Error { + fn from(err: BlockError) -> Error { + Error::Block(err) + } +} + +// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted. +/*#![feature(concat_idents)] +macro_rules! assimilate { + ($name:ident) => ( + impl From for Error { + fn from(err: concat_idents!($name, Error)) -> Error { + Error:: $name (err) + } + } + ) +} +assimilate!(FromHex); +assimilate!(BaseData);*/ diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 02a21a233..f959c3d1d 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -34,7 +34,7 @@ fn on_close_block() { let genesis_header = engine.spec().genesis_header(); let mut db = OverlayDB::new_temp(); engine.spec().ensure_db_good(&mut db); - let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]); - let b = b.close(vec![], Address::zero(), vec![]); + let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]); + let b = b.close(); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244F40000").unwrap()); } \ No newline at end of file diff --git a/src/header.rs b/src/header.rs index a1153d880..a1666ef04 100644 --- a/src/header.rs +++ b/src/header.rs @@ -9,6 +9,7 @@ use basic_types::*; /// Doesn't do all that much on its own. #[derive(Debug)] pub struct Header { + // TODO: make all private. pub parent_hash: H256, pub timestamp: U256, pub number: U256, @@ -60,6 +61,16 @@ impl Header { } } + pub fn author(&self) -> &Address { &self.author } + pub fn extra_data(&self) -> &Bytes { &self.extra_data } + pub fn seal(&self) -> &Vec { &self.seal } + + // TODO: seal_at, set_seal_at &c. + + pub fn set_author(&mut self, a: Address) { if a != self.author { self.author = a; self.note_dirty(); } } + pub fn set_extra_data(&mut self, a: Bytes) { if a != self.extra_data { self.extra_data = a; self.note_dirty(); } } + pub fn set_seal(&mut self, a: Vec) { self.seal = a; self.note_dirty(); } + /// Get the hash of this header (sha3 of the RLP). pub fn hash(&self) -> H256 { let mut hash = self.hash.borrow_mut(); diff --git a/src/lib.rs b/src/lib.rs index f4621feeb..0227d5e25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ extern crate ethcore_util as util; pub mod common; pub mod basic_types; +pub mod error; pub mod env_info; pub mod engine; pub mod state; diff --git a/src/spec.rs b/src/spec.rs index 2af075b1b..377b9c0df 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -73,11 +73,11 @@ pub struct Spec { impl Spec { /// Convert this object into a boxed Engine of the right underlying type. // TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. - pub fn to_engine(self) -> Result, EthcoreError> { + pub fn to_engine(self) -> Result, Error> { match self.engine_name.as_ref() { "NullEngine" => Ok(NullEngine::new_boxed(self)), "Ethash" => Ok(super::ethereum::Ethash::new_boxed(self)), - _ => Err(EthcoreError::UnknownName) + _ => Err(Error::UnknownEngineName(self.engine_name.clone())) } } diff --git a/src/state.rs b/src/state.rs index 3afa1d308..569de13bd 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,8 +1,4 @@ -use util::*; -use account::Account; -use transaction::Transaction; -use receipt::Receipt; -use env_info::EnvInfo; +use common::*; use engine::Engine; /// Information concerning the result of the `State::apply` operation. @@ -10,7 +6,7 @@ pub struct ApplyInfo { pub receipt: Receipt, } -pub type ApplyResult = Result; +pub type ApplyResult = Result; /// Representation of the entire state of all accounts in the system. pub struct State {