diff --git a/Cargo.toml b/Cargo.toml index 8450bf1b8..8dc2bd2a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ flate2 = "0.2" rocksdb = "0.2.1" evmjit = { path = "rust-evmjit", optional = true } +rustc-serialize = "0.3" [features] jit = ["evmjit"] diff --git a/src/account.rs b/src/account.rs index 065b899af..966b596d4 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use util::hash::*; +use util::sha3::*; use util::hashdb::*; use util::bytes::*; use util::trie::*; @@ -32,7 +33,7 @@ impl Account { nonce: nonce, storage_root: SHA3_NULL_RLP, storage_overlay: storage, - code_hash: None, + code_hash: Some(code.sha3()), code_cache: code } } @@ -49,6 +50,19 @@ impl Account { } } + /// Create a new account from RLP. + pub fn from_rlp(rlp: &[u8]) -> Account { + let r: Rlp = Rlp::new(rlp); + Account { + nonce: r.val_at(0), + balance: r.val_at(1), + storage_root: r.val_at(2), + storage_overlay: HashMap::new(), + code_hash: Some(r.val_at(3)), + code_cache: vec![], + } + } + /// Create a new contract account. /// NOTE: make sure you use `set_code` on this before `commit`ing. pub fn new_contract(balance: U256) -> Account { @@ -62,19 +76,6 @@ impl Account { } } - /// Create a new account from RLP. - pub fn from_rlp(_rlp: &[u8]) -> Account { -// unimplemented!(); - Account { - balance: U256::from(0u8), - nonce: U256::from(0u8), - storage_root: SHA3_NULL_RLP, - storage_overlay: HashMap::new(), - code_hash: Some(SHA3_EMPTY), - code_cache: vec![], - } - } - /// Set this account's code to the given code. /// NOTE: Account should have been created with `new_contract`. pub fn set_code(&mut self, code: Bytes) { @@ -82,6 +83,10 @@ impl Account { self.code_cache = code; } + pub fn set_storage(&mut self, key: H256, value: H256) { + self.storage_overlay.insert(key, value); + } + /// return the balance associated with this account. pub fn balance(&self) -> &U256 { &self.balance } /// return the nonce associated with this account. @@ -90,8 +95,44 @@ impl Account { pub fn code_hash(&self) -> H256 { self.code_hash.clone().unwrap_or(SHA3_EMPTY) } + /// returns the account's code. If `None` then the code cache isn't available - + /// get someone who knows to call `note_code`. + pub fn code(&self) -> Option<&[u8]> { + match self.code_hash { + Some(SHA3_EMPTY) | None if self.code_cache.is_empty() => Some(&self.code_cache), + Some(_) if !self.code_cache.is_empty() => Some(&self.code_cache), + _ => None, + } + } + + /// Provide a byte array which hashes to the `code_hash`. returns the hash as a result. + pub fn note_code(&mut self, code: Bytes) -> Result { + let h = code.sha3(); + match self.code_hash { + Some(ref i) if h == *i => { + self.code_cache = code; + Ok(h) + }, + _ => Err(h) + } + } + /// return the storage root associated with this account. + pub fn base_root(&self) -> &H256 { &self.storage_root } + /// return the storage root associated with this account or None if it has been altered via the overlay. pub fn storage_root(&self) -> Option<&H256> { if self.storage_overlay.is_empty() {Some(&self.storage_root)} else {None} } + /// rturn the storage overlay. + pub fn storage_overlay(&self) -> &HashMap { &self.storage_overlay } + + /// Increment the nonce of the account by one. + pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); } + + /// Increment the nonce of the account by one. + pub fn add_balance(&mut self, x: &U256) { self.balance = self.balance + *x; } + + /// Increment the nonce of the account by one. + pub fn sub_balance(&mut self, x: &U256) { self.balance = self.balance - *x; } + /// Commit the `storage_overlay` to the backing DB and update `storage_root`. pub fn commit_storage(&mut self, db: &mut HashDB) { @@ -104,7 +145,7 @@ impl Account { self.storage_overlay.clear(); } - /// Commit any unsaved code and ensure code is not `HashOrData::Data`. + /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. pub fn commit_code(&mut self, db: &mut HashDB) { if self.code_hash.is_none() && !self.code_cache.is_empty() { self.code_hash = Some(db.insert(&self.code_cache)); @@ -121,3 +162,27 @@ impl Account { stream.out() } } + +#[test] +fn playpen() { +} + +#[test] +fn new_account() { + use rustc_serialize::hex::ToHex; + + let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); + assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + assert_eq!(a.balance(), &U256::from(69u8)); + assert_eq!(a.nonce(), &U256::from(0u8)); + assert_eq!(a.code_hash(), SHA3_EMPTY); + assert_eq!(a.storage_root().unwrap(), &SHA3_NULL_RLP); +} + +#[test] +fn create_account() { + use rustc_serialize::hex::ToHex; + + let a = Account::new(U256::from(69u8), U256::from(0u8), HashMap::new(), Bytes::new()); + assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); +} diff --git a/src/blockheader.rs b/src/blockheader.rs index c57171f65..8d8cfef12 100644 --- a/src/blockheader.rs +++ b/src/blockheader.rs @@ -97,26 +97,26 @@ impl Header { impl Decodable for Header { fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_list(| d | { - let blockheader = Header { - parent_hash: try!(Decodable::decode(&d[0])), - uncles_hash: try!(Decodable::decode(&d[1])), - author: try!(Decodable::decode(&d[2])), - state_root: try!(Decodable::decode(&d[3])), - transactions_root: try!(Decodable::decode(&d[4])), - receipts_root: try!(Decodable::decode(&d[5])), - log_bloom: try!(Decodable::decode(&d[6])), - difficulty: try!(Decodable::decode(&d[7])), - number: try!(Decodable::decode(&d[8])), - gas_limit: try!(Decodable::decode(&d[9])), - gas_used: try!(Decodable::decode(&d[10])), - timestamp: try!(Decodable::decode(&d[11])), - extra_data: try!(Decodable::decode(&d[12])), - seal: vec![], - }; - // TODO: fill blockheader.seal with (raw) list items index 12..) - Ok(blockheader) - }) + let d = try!(decoder.as_list()); + + let blockheader = Header { + parent_hash: try!(Decodable::decode(&d[0])), + uncles_hash: try!(Decodable::decode(&d[1])), + author: try!(Decodable::decode(&d[2])), + state_root: try!(Decodable::decode(&d[3])), + transactions_root: try!(Decodable::decode(&d[4])), + receipts_root: try!(Decodable::decode(&d[5])), + log_bloom: try!(Decodable::decode(&d[6])), + difficulty: try!(Decodable::decode(&d[7])), + number: try!(Decodable::decode(&d[8])), + gas_limit: try!(Decodable::decode(&d[9])), + gas_used: try!(Decodable::decode(&d[10])), + timestamp: try!(Decodable::decode(&d[11])), + extra_data: try!(Decodable::decode(&d[12])), + seal: vec![], + }; + // TODO: fill blockheader.seal with (raw) list items index 12..) + Ok(blockheader) } } diff --git a/src/lib.rs b/src/lib.rs index cc1240233..3ba3b63be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ extern crate flate2; extern crate rocksdb; extern crate env_logger; +extern crate rustc_serialize; #[cfg(feature = "jit" )] extern crate evmjit; extern crate ethcore_util as util; diff --git a/src/transaction.rs b/src/transaction.rs index fc2159c8a..4fbd9588b 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -37,17 +37,18 @@ impl Encodable for Transaction { impl Decodable for Transaction { fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_list(| d | { - let transaction = Transaction { - nonce: try!(Decodable::decode(&d[0])), - gas_price: try!(Decodable::decode(&d[1])), - gas: try!(Decodable::decode(&d[2])), - receive_address: try!(Decodable::decode(&d[3])), - value: try!(Decodable::decode(&d[4])), - data: try!(Decodable::decode(&d[5])), - }; - Ok(transaction) - }) + let d = try!(decoder.as_list()); + + let transaction = Transaction { + nonce: try!(Decodable::decode(&d[0])), + gas_price: try!(Decodable::decode(&d[1])), + gas: try!(Decodable::decode(&d[2])), + receive_address: try!(Decodable::decode(&d[3])), + value: try!(Decodable::decode(&d[4])), + data: try!(Decodable::decode(&d[5])), + }; + + Ok(transaction) } }