diff --git a/src/block.rs b/src/block.rs index aefa1da2f..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,12 +141,14 @@ 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) => { - self.block.archive_set.insert(h.unwrap_or_else(||t.sha3())); + self.block.archive_set.insert(h.unwrap_or_else(||t.hash())); self.block.archive.push(Entry { transaction: t, receipt: x.receipt }); Ok(&self.block.archive.last().unwrap().receipt) } @@ -120,17 +157,14 @@ 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; - // populate rest of header. s.engine.on_close_block(&mut s.block); - s.block.header.author = author; -// s.header.transactions_root = ...; - let uncle_bytes = uncles.iter().fold(RlpStream::new_list(uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out(); + s.block.header.transactions_root = ordered_trie_root(s.block.archive.iter().map(|ref e| e.transaction.rlp_bytes()).collect()); + 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.header.receipts_root = ...; + 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}); s.block.header.gas_used = s.block.archive.last().map(|t| t.receipt.gas_used).unwrap_or(U256::from(0)); s.block.header.note_dirty(); @@ -143,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 { @@ -175,12 +227,11 @@ impl IsBlock for SealedBlock { #[test] fn open_block() { use spec::*; - use ethereum::*; - let engine = new_morden().to_engine().unwrap(); + let engine = Spec::new_test().to_engine().unwrap(); 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![]); - assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244F40000").unwrap()); + 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 7fc6e0b54..4fa8ab0ef 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()); } diff --git a/src/header.rs b/src/header.rs index a25bb2e39..80c38c05f 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, @@ -27,7 +28,7 @@ pub struct Header { pub difficulty: U256, pub seal: Vec, - pub hash: RefCell>, //TODO: make this private + pub hash: RefCell>, } pub enum Seal { @@ -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(); @@ -163,28 +174,6 @@ impl Encodable for Header { }) } } -/* -trait RlpStandard { - fn append(&self, s: &mut RlpStream); -} - -impl RlpStandard for Header { - fn append(&self, s: &mut RlpStream) { - s.append_list(13); - s.append(self.parent_hash); - s.append_raw(self.seal[0]); - s.append_standard(self.x); - } - fn populate(&mut self, s: &Rlp) { - } -} - -impl RlpStream { - fn append_standard(&mut self, o: &O) where O: RlpStandard { - o.append(self); - } -} -*/ #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index 5f5f06387..f199d53c3 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/receipt.rs b/src/receipt.rs index 6f91c14dc..5a188f93d 100644 --- a/src/receipt.rs +++ b/src/receipt.rs @@ -1,10 +1,47 @@ use util::*; use basic_types::LogBloom; +/// A single log's entry. +pub struct LogEntry { + pub address: Address, + pub topics: Vec, + pub data: Bytes, +} + +impl RlpStandard for LogEntry { + fn rlp_append(&self, s: &mut RlpStream) { + s.append_list(3); + s.append(&self.address); + s.append(&self.topics); + s.append(&self.data); + } +} + +impl LogEntry { + pub fn bloom(&self) -> LogBloom { + self.topics.iter().fold(LogBloom::from_bloomed(&self.address.sha3()), |b, t| b.with_bloomed(&t.sha3())) + } +} + /// Information describing execution of a transaction. pub struct Receipt { - // TODO pub state_root: H256, pub gas_used: U256, pub log_bloom: LogBloom, + pub logs: Vec, +} + +impl RlpStandard for Receipt { + fn rlp_append(&self, s: &mut RlpStream) { + s.append_list(4); + s.append(&self.state_root); + s.append(&self.gas_used); + s.append(&self.log_bloom); + // TODO: make work: + //s.append(&self.logs); + s.append_list(self.logs.len()); + for l in self.logs.iter() { + l.rlp_append(s); + } + } } diff --git a/src/spec.rs b/src/spec.rs index 8613da64a..d3f516eb3 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 { diff --git a/src/transaction.rs b/src/transaction.rs index b54329422..d13a728bb 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -9,6 +9,8 @@ pub struct Transaction { to: Option
, value: U256, data: Bytes, + + hash: RefCell>, //TODO: make this private } impl Transaction { @@ -21,10 +23,36 @@ impl Transaction { pub fn is_message_call(&self) -> bool { !self.is_contract_creation() } +} - /// Get the hash of this transaction. - pub fn sha3(&self) -> H256 { - unimplemented!(); +impl RlpStandard for Transaction { + fn rlp_append(&self, s: &mut RlpStream) { + s.append_list(6); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + s.append(&self.to); + s.append(&self.value); + s.append(&self.data); + } +} + +impl Transaction { + /// Get the hash of this header (sha3 of the RLP). + pub fn hash(&self) -> H256 { + let mut hash = self.hash.borrow_mut(); + match &mut *hash { + &mut Some(ref h) => h.clone(), + hash @ &mut None => { + *hash = Some(self.rlp_sha3()); + hash.as_ref().unwrap().clone() + } + } + } + + /// Note that some fields have changed. Resets the memoised hash. + pub fn note_dirty(&self) { + *self.hash.borrow_mut() = None; } } @@ -52,6 +80,7 @@ impl Decodable for Transaction { to: try!(Decodable::decode(&d[3])), value: try!(Decodable::decode(&d[4])), data: try!(Decodable::decode(&d[5])), + hash: RefCell::new(None) }; Ok(transaction)