diff --git a/src/account.rs b/src/account.rs index cd951b15c..065b899af 100644 --- a/src/account.rs +++ b/src/account.rs @@ -6,52 +6,91 @@ use util::trie::*; use util::rlp::*; use util::uint::*; -enum HashOrData { - Hash(H256), - Data(Bytes), - Both(H256, Bytes), -} +pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] ); +/// Single account in the system. pub struct Account { + // Balance of the account. balance: U256, + // Nonce of the account. nonce: U256, // Trie-backed storage. storage_root: H256, // Overlay on trie-backed storage. storage_overlay: HashMap, - code: HashOrData, + // Code hash of the account. If None, means that it's a contract whose code has not yet been set. + code_hash: Option, + // Code cache of the account. + code_cache: Bytes, } impl Account { - pub fn new_with_balance(balance: U256) -> Account { + /// General constructor. + pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes) -> Account { + Account { + balance: balance, + nonce: nonce, + storage_root: SHA3_NULL_RLP, + storage_overlay: storage, + code_hash: None, + code_cache: code + } + } + + /// Create a new account with the given balance. + pub fn new_basic(balance: U256) -> Account { Account { balance: balance, nonce: U256::from(0u8), - code: HashOrData::Data(vec![]), storage_root: SHA3_NULL_RLP, storage_overlay: HashMap::new(), + code_hash: Some(SHA3_EMPTY), + 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 { + Account { + balance: balance, + nonce: U256::from(0u8), + storage_root: SHA3_NULL_RLP, + storage_overlay: HashMap::new(), + code_hash: None, + code_cache: vec![], + } + } + + /// Create a new account from RLP. pub fn from_rlp(_rlp: &[u8]) -> Account { - //TODO +// unimplemented!(); Account { balance: U256::from(0u8), nonce: U256::from(0u8), - code: HashOrData::Hash(SHA3_NULL_RLP), storage_root: SHA3_NULL_RLP, storage_overlay: HashMap::new(), + code_hash: Some(SHA3_EMPTY), + code_cache: vec![], } } - pub fn balance(&self) -> &U256 { &self.balance } - pub fn nonce(&self) -> &U256 { &self.nonce } - pub fn code_hash(&self) -> Option<&H256> { - match self.code { - HashOrData::Hash(ref h) | HashOrData::Both(ref h, _) => Some(h), - _ => None, - } + /// 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) { + assert!(self.code_hash.is_none()); + self.code_cache = code; } + + /// return the balance associated with this account. + pub fn balance(&self) -> &U256 { &self.balance } + /// return the nonce associated with this account. + pub fn nonce(&self) -> &U256 { &self.nonce } + /// return the code hash associated with this account. + pub fn code_hash(&self) -> H256 { + self.code_hash.clone().unwrap_or(SHA3_EMPTY) + } + /// return the storage root associated with this account. pub fn storage_root(&self) -> Option<&H256> { if self.storage_overlay.is_empty() {Some(&self.storage_root)} else {None} } /// Commit the `storage_overlay` to the backing DB and update `storage_root`. @@ -67,19 +106,18 @@ impl Account { /// Commit any unsaved code and ensure code is not `HashOrData::Data`. pub fn commit_code(&mut self, db: &mut HashDB) { - if let Some(new_code) = match self.code { - HashOrData::Data(ref d) => { Some(HashOrData::Both(db.insert(d), d.clone())) }, - _ => None, - }{ - self.code = new_code; + if self.code_hash.is_none() && !self.code_cache.is_empty() { + self.code_hash = Some(db.insert(&self.code_cache)); } + } - // that nasty bit of code should really be: - /*if let HashOrData::Data(ref d) = self.code { - let h = db.insert(d); - self.code = HashOrData::Both(h, d.clone()); - }*/ - // a rewrite it closer to this would be good... - + /// Export to RLP. + pub fn rlp(&self) -> Bytes { + let mut stream = RlpStream::new_list(4); + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&self.storage_root); + stream.append(self.code_hash.as_ref().expect("Cannot form RLP of contract account without code.")); + stream.out() } } diff --git a/src/state.rs b/src/state.rs index 5225c56ac..04a284b8a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -7,10 +7,11 @@ use util::rlp::*; use util::uint::*; use account::Account; +/// Representation of the entire state of all accounts in the system. pub struct State { db: OverlayDB, root: H256, - _cache: HashMap>, + cache: HashMap>, _account_start_nonce: U256, } @@ -27,7 +28,7 @@ impl State { State { db: db, root: root, - _cache: HashMap::new(), + cache: HashMap::new(), _account_start_nonce: account_start_nonce, } } @@ -42,7 +43,7 @@ impl State { State { db: db, root: root, - _cache: HashMap::new(), + cache: HashMap::new(), _account_start_nonce: account_start_nonce, } } @@ -57,37 +58,42 @@ impl State { &self.root } - /// Commit everything to the disk - pub fn commit_db(&mut self) { - self.db.commit().expect("Number of kills exceeded number of inserts!"); + /// Expose the underlying database; good to use for calling `state.db().commit()`. + pub fn db(&mut self) -> &mut OverlayDB { + &mut self.db } /// Commit accounts to TrieDB. This is simplified version of /// cpp-ethereum's dev::eth::commit. /// accounts mutable because we may need to commit the code or storage and record that. - pub fn commit(db: &mut HashDB, mut root: H256, accounts: &mut HashMap) -> H256 { + pub fn commit_into(db: &mut HashDB, mut root: H256, accounts: &mut HashMap>) -> H256 { // first, commit the sub trees. - for (_, ref mut account) in accounts.iter_mut() { - account.commit_storage(db); - account.commit_code(db); + // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? + for (_, ref mut a) in accounts.iter_mut() { + match a { + &mut&mut Some(ref mut account) => { + account.commit_storage(db); + account.commit_code(db); + } + &mut&mut None => {} + } } { let mut trie = TrieDB::new_existing(db, &mut root); - for (address, account) in accounts.iter() { - let mut stream = RlpStream::new_list(4); - stream.append(account.nonce()); - stream.append(account.balance()); - stream.append(account.storage_root().unwrap()); - stream.append(account.code_hash().unwrap()); - trie.insert(address, &stream.out()); + for (address, ref a) in accounts.iter() { + match a { + &&Some(ref account) => trie.insert(address, &account.rlp()), + &&None => trie.remove(address), + } } } root } - pub fn insert_accounts(&mut self, accounts: &mut HashMap) { + /// Commits our cached account changes into the trie. + pub fn commit(&mut self) { let r = self.root.clone(); // would prefer not to do this, really. - self.root = Self::commit(&mut self.db, r, accounts); + self.root = Self::commit_into(&mut self.db, r, &mut self.cache); } }