diff --git a/src/basic_types.rs b/src/basic_types.rs new file mode 100644 index 000000000..2c18c59d2 --- /dev/null +++ b/src/basic_types.rs @@ -0,0 +1,7 @@ +use util::*; + +/// Type for a 2048-bit log-bloom, as used by our blocks. +pub type LogBloom = H2048; + +/// Constant 2048-bit datum for 0. Often used as a default. +pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]); diff --git a/src/block.rs b/src/block.rs index 3c42e4e27..aefa1da2f 100644 --- a/src/block.rs +++ b/src/block.rs @@ -1,9 +1,5 @@ -use util::*; -use transaction::*; -use receipt::*; +use common::*; use engine::*; -use header::*; -use env_info::*; use state::*; /// A transaction/receipt execution entry. @@ -71,7 +67,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: Vec
, + uncles: Bytes, } /// A block that has a valid seal. @@ -105,7 +101,7 @@ impl<'engine> OpenBlock<'engine> { timestamp: self.block.header.timestamp.clone(), difficulty: self.block.header.difficulty.clone(), last_hashes: self.last_hashes.clone(), - gas_used: if let Some(ref t) = self.block.archive.last() {t.receipt.gas_used} else {U256::from(0)}, + gas_used: self.block.archive.last().map(|t| t.receipt.gas_used).unwrap_or(U256::from(0)), gas_limit: self.block.header.gas_limit.clone(), } } @@ -123,8 +119,24 @@ impl<'engine> OpenBlock<'engine> { } } - /// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles. - pub fn close(self, _uncles: Vec
) -> ClosedBlock<'engine> { unimplemented!(); } + /// 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> { + 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.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.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(); + + ClosedBlock::new(s, uncle_bytes) + } } impl<'engine> IsBlock for OpenBlock<'engine> { @@ -132,6 +144,13 @@ impl<'engine> IsBlock for OpenBlock<'engine> { } 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!(); } @@ -155,11 +174,13 @@ impl IsBlock for SealedBlock { #[test] fn open_block() { - use super::*; use spec::*; - let engine = Spec::new_test().to_engine().unwrap(); + use ethereum::*; + let engine = new_morden().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()); } \ No newline at end of file diff --git a/src/common.rs b/src/common.rs index ec4eb14cd..106a7a3b9 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,4 +1,5 @@ pub use util::*; +pub use basic_types::*; pub use env_info::*; pub use evm_schedule::*; pub use views::*; @@ -6,3 +7,4 @@ pub use builtin::*; pub use header::*; pub use account::*; pub use transaction::*; +pub use receipt::*; diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 13b1f6a46..02a21a233 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -23,24 +23,18 @@ impl Engine for Ethash { /// Apply the block reward on finalisation of the block. fn on_close_block(&self, block: &mut Block) { let a = block.header().author.clone(); - block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("block_reward").unwrap())); + block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("blockReward").unwrap())); } } -// TODO: test for on_close_block. #[test] -fn playpen() { +fn on_close_block() { use super::*; - use state::*; let engine = new_morden().to_engine().unwrap(); let genesis_header = engine.spec().genesis_header(); let mut db = OverlayDB::new_temp(); engine.spec().ensure_db_good(&mut db); - assert!(SecTrieDB::new(&db, &genesis_header.state_root).contains(&address_from_hex("102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c"))); - { - let s = State::from_existing(db.clone(), genesis_header.state_root.clone(), engine.account_start_nonce()); - assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64)); - } let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]); -// let c = b.close(); + let b = b.close(vec![], Address::zero(), vec![]); + assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244F40000").unwrap()); } \ No newline at end of file diff --git a/src/ethereum/mod.rs b/src/ethereum/mod.rs index e743c6d09..ed1950d64 100644 --- a/src/ethereum/mod.rs +++ b/src/ethereum/mod.rs @@ -56,7 +56,7 @@ mod tests { let genesis = morden.genesis_block(); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); - morden.to_engine(); + let _ = morden.to_engine(); } #[test] @@ -67,6 +67,6 @@ mod tests { let genesis = frontier.genesis_block(); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap()); - frontier.to_engine(); + let _ = frontier.to_engine(); } } \ No newline at end of file diff --git a/src/header.rs b/src/header.rs index 80d91eb20..56f03460e 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,14 +1,5 @@ use util::*; - -/// Type for a 2048-bit log-bloom, as used by our blocks. -pub type LogBloom = H2048; - -/// Constant address for point 0. Often used as a default. -pub static ZERO_ADDRESS: Address = Address([0x00; 20]); -/// Constant 256-bit datum for 0. Often used as a default. -pub static ZERO_H256: H256 = H256([0x00; 32]); -/// Constant 2048-bit datum for 0. Often used as a default. -pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]); +use basic_types::*; /// A block header. /// @@ -39,6 +30,11 @@ pub struct Header { pub hash: RefCell>, //TODO: make this private } +pub enum Seal { + With, + Without, +} + impl Header { /// Create a new, default-valued, header. pub fn new() -> Header { @@ -64,19 +60,54 @@ impl Header { } } + /// 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 => { - let mut stream = RlpStream::new(); - stream.append(self); - let h = stream.as_raw().sha3(); - *hash = Some(h.clone()); - h.clone() + *hash = Some(self.rlp_sha3(Seal::With)); + 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; + } + + // TODO: get hash without seal. + + // TODO: make these functions traity + pub fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { + s.append_list(13 + match with_seal { Seal::With => self.seal.len(), _ => 0 }); + s.append(&self.parent_hash); + s.append(&self.uncles_hash); + s.append(&self.author); + s.append(&self.state_root); + s.append(&self.transactions_root); + s.append(&self.receipts_root); + s.append(&self.log_bloom); + s.append(&self.difficulty); + s.append(&self.number); + s.append(&self.gas_limit); + s.append(&self.gas_used); + s.append(&self.timestamp); + s.append(&self.extra_data); + match with_seal { + Seal::With => for b in self.seal.iter() { s.append_raw(&b, 1); }, + _ => {} + } + } + + pub fn rlp(&self, with_seal: Seal) -> Bytes { + let mut s = RlpStream::new(); + self.stream_rlp(&mut s, with_seal); + s.out() + } + + pub fn rlp_sha3(&self, with_seal: Seal) -> H256 { self.rlp(with_seal).sha3() } } impl Decodable for Header { diff --git a/src/lib.rs b/src/lib.rs index 698801d8e..f4621feeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![feature(cell_extras)] - +#![feature(augmented_assignments)] //! Ethcore's ethereum implementation //! //! ### Rust version @@ -85,6 +85,7 @@ extern crate evmjit; extern crate ethcore_util as util; pub mod common; +pub mod basic_types; pub mod env_info; pub mod engine; pub mod state; diff --git a/src/receipt.rs b/src/receipt.rs index fb72e2e18..6f91c14dc 100644 --- a/src/receipt.rs +++ b/src/receipt.rs @@ -1,8 +1,10 @@ use util::*; +use basic_types::LogBloom; /// Information describing execution of a transaction. pub struct Receipt { // TODO pub state_root: H256, pub gas_used: U256, + pub log_bloom: LogBloom, } diff --git a/src/state.rs b/src/state.rs index f0e10fa2d..3afa1d308 100644 --- a/src/state.rs +++ b/src/state.rs @@ -79,6 +79,11 @@ impl State { self.require_or_from(contract, false, || Account::new_contract(U256::from(0u8)), |r| r.reset_code()); } + /// Remove an existing account. + pub fn kill_account(&mut self, account: &Address) { + self.cache.borrow_mut().insert(account.clone(), None); + } + /// Get the balance of account `a`. pub fn balance(&self, a: &Address) -> U256 { self.get(a, false).as_ref().map(|account| account.balance().clone()).unwrap_or(U256::from(0u8)) @@ -228,12 +233,11 @@ use util::hash::*; use util::trie::*; use util::rlp::*; use util::uint::*; -use std::str::FromStr; use account::*; #[test] fn code_from_database() { - let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let a = Address::zero(); let (r, db) = { let mut s = State::new_temp(); s.require_or_from(&a, false, ||Account::new_contract(U256::from(42u32)), |_|{}); @@ -250,7 +254,7 @@ fn code_from_database() { #[test] fn storage_at_from_database() { - let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let a = Address::zero(); let (r, db) = { let mut s = State::new_temp(); s.set_storage(&a, H256::from(&U256::from(01u64)), H256::from(&U256::from(69u64))); @@ -264,7 +268,7 @@ fn storage_at_from_database() { #[test] fn get_from_database() { - let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let a = Address::zero(); let (r, db) = { let mut s = State::new_temp(); s.inc_nonce(&a); @@ -279,11 +283,45 @@ fn get_from_database() { assert_eq!(s.nonce(&a), U256::from(1u64)); } +#[test] +fn remove() { + let a = Address::zero(); + let mut s = State::new_temp(); + s.inc_nonce(&a); + assert_eq!(s.nonce(&a), U256::from(1u64)); + s.kill_account(&a); + assert_eq!(s.nonce(&a), U256::from(0u64)); +} + +#[test] +fn remove_from_database() { + let a = Address::zero(); + let (r, db) = { + let mut s = State::new_temp(); + s.inc_nonce(&a); + s.commit(); + assert_eq!(s.nonce(&a), U256::from(1u64)); + s.drop() + }; + + let (r, db) = { + let mut s = State::from_existing(db, r, U256::from(0u8)); + assert_eq!(s.nonce(&a), U256::from(1u64)); + s.kill_account(&a); + s.commit(); + assert_eq!(s.nonce(&a), U256::from(0u64)); + s.drop() + }; + + let s = State::from_existing(db, r, U256::from(0u8)); + assert_eq!(s.nonce(&a), U256::from(0u64)); +} + #[test] fn alter_balance() { let mut s = State::new_temp(); - let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); - let b = Address::from_str("0000000000000000000000000000000000000001").unwrap(); + let a = Address::zero(); + let b = address_from_u64(1u64); s.add_balance(&a, &U256::from(69u64)); assert_eq!(s.balance(&a), U256::from(69u64)); s.commit(); @@ -303,7 +341,7 @@ fn alter_balance() { #[test] fn alter_nonce() { let mut s = State::new_temp(); - let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let a = Address::zero(); s.inc_nonce(&a); assert_eq!(s.nonce(&a), U256::from(1u64)); s.inc_nonce(&a); @@ -319,7 +357,7 @@ fn alter_nonce() { #[test] fn balance_nonce() { let mut s = State::new_temp(); - let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let a = Address::zero(); assert_eq!(s.balance(&a), U256::from(0u64)); assert_eq!(s.nonce(&a), U256::from(0u64)); s.commit(); @@ -330,7 +368,7 @@ fn balance_nonce() { #[test] fn ensure_cached() { let mut s = State::new_temp(); - let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let a = Address::zero(); s.require(&a, false); s.commit(); assert_eq!(s.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785");