diff --git a/src/account.rs b/src/account.rs new file mode 100644 index 000000000..995f1c71e --- /dev/null +++ b/src/account.rs @@ -0,0 +1,77 @@ +use std::collections::HashMap; +use util::hash::*; +use util::hashdb::*; +use util::bytes::*; +use util::trie::*; +use util::rlp::*; +use util::uint::*; + +enum HashOrData { + Hash(H256), + Data(Bytes), + Both(H256, Bytes), +} + +pub struct Account { + balance: U256, + nonce: U256, + // Trie-backed storage. + storage_root: H256, + // Overlay on trie-backed storage. + storage_overlay: HashMap, + code: HashOrData, +} + +impl Account { + pub fn new_with_balance(balance: U256) -> Account { + Account { + balance: balance, + nonce: U256::from(0u8), + code: HashOrData::Data(vec![]), + storage_root: SHA3_NULL_RLP, + storage_overlay: HashMap::new(), + } + } + + pub fn from_rlp(_rlp: &[u8]) -> Account { + //TODO + Account { + balance: U256::from(0u8), + nonce: U256::from(0u8), + code: HashOrData::Hash(SHA3_NULL_RLP), + storage_root: SHA3_NULL_RLP, + storage_overlay: HashMap::new(), + } + } + + 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, + } + } + 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`. + pub fn commit_storage(&mut self, db: &mut HashDB) { + let mut t = TrieDB::new(db, &mut self.storage_root); + for (k, v) in self.storage_overlay.iter() { + // cast key and value to trait type, + // so we can call overloaded `to_bytes` method + t.insert(k, v); + } + self.storage_overlay.clear(); + } + + /// 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; + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 07fea5389..8a562cec2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,6 +82,7 @@ pub use util::uint::*; pub use util::bytes::*; pub mod state; +pub mod account; pub mod blockheader; pub mod transaction; pub mod networkparams; diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 000000000..86331a5af --- /dev/null +++ b/src/state.rs @@ -0,0 +1,92 @@ +use std::collections::HashMap; +use util::hash::*; +use util::hashdb::*; +use util::overlaydb::*; +use util::trie::*; +use util::rlp::*; +use util::uint::*; +use account::Account; + +pub struct State { + db: OverlayDB, + root: H256, + _cache: HashMap>, + + _account_start_nonce: U256, +} + +impl State { + /// Creates new state with empty state root + pub fn new(mut db: OverlayDB, account_start_nonce: U256) -> State { + let mut root = H256::new(); + { + // init trie and reset root too null + let _ = TrieDB::new(&mut db, &mut root); + } + + State { + db: db, + root: root, + _cache: HashMap::new(), + _account_start_nonce: account_start_nonce, + } + } + + /// Creates new state with existing state root + pub fn new_existing(mut db: OverlayDB, mut root: H256, account_start_nonce: U256) -> State { + { + // trie should panic! if root does not exist + let _ = TrieDB::new_existing(&mut db, &mut root); + } + + State { + db: db, + root: root, + _cache: HashMap::new(), + _account_start_nonce: account_start_nonce, + } + } + + /// Create temporary state object + pub fn new_temp() -> State { + Self::new(OverlayDB::new_temp(), U256::from(0u8)) + } + + /// Return reference to root + pub fn root(&self) -> &H256 { + &self.root + } + + /// Commit everything to the disk + pub fn commit_db(&mut self) { + self.db.commit().expect("Number of kills exceeded number of inserts!"); + } + + /// Commit accounts to TrieDB. This is simplified version of + /// cpp-ethereum's dev::eth::commit. + pub fn commit(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); + } + + { + 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()); + } + } + root + } + + pub fn insert_accounts(&mut self, accounts: &mut HashMap) { + let r = self.root.clone(); + self.root = Self::commit(&mut self.db, r, accounts); + } +}