diff --git a/src/account.rs b/src/account.rs index 83b840191..cd951b15c 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,27 +1,85 @@ use std::collections::HashMap; -use util::uint::*; 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, - code: Vec, nonce: U256, - storage: HashMap + // 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, - code: vec![], nonce: U256::from(0u8), - storage: HashMap::new() + 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 code(&self) -> &[u8] { &self.code } pub fn nonce(&self) -> &U256 { &self.nonce } - pub fn storage(&self) -> &HashMap { &self.storage } -} + 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; + } + + // 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... + + } +} diff --git a/src/block.rs b/src/block.rs index 4663a0b00..efde03c2d 100644 --- a/src/block.rs +++ b/src/block.rs @@ -12,16 +12,12 @@ pub struct Block { impl Block { /// Creates block with empty state root pub fn new(db: OverlayDB) -> Block { - Block { - state: State::new(db) - } + unimplemented!() } /// Creates block with state root pub fn new_existing(db: OverlayDB, state_root: H256) -> Block { - Block { - state: State::new_existing(db, state_root) - } + unimplemented!() } /// Returns mutable reference to backing state diff --git a/src/blockchain.rs b/src/blockchain.rs index a81f3d857..fa6ff1782 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -105,7 +105,8 @@ impl BlockChain { } let mut block = Block::new(db.clone()); - block.mutable_state().insert_accounts(&self.genesis_state); + // TODO: commit + //block.mutable_state().insert_accounts(&self.genesis_state); block.mutable_state().commit_db(); // TODO: set previous block // TODO: reset current diff --git a/src/state.rs b/src/state.rs index 46c45415f..5225c56ac 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,22 +1,23 @@ use std::collections::HashMap; use util::hash::*; use util::hashdb::*; -use util::memorydb::*; use util::overlaydb::*; -use util::bytes::*; use util::trie::*; use util::rlp::*; -use util::sha3::*; -use account::*; +use util::uint::*; +use account::Account; pub struct State { db: OverlayDB, - root: H256 + root: H256, + _cache: HashMap>, + + _account_start_nonce: U256, } impl State { /// Creates new state with empty state root - pub fn new(mut db: OverlayDB) -> State { + pub fn new(mut db: OverlayDB, account_start_nonce: U256) -> State { let mut root = H256::new(); { // init trie and reset root too null @@ -25,12 +26,14 @@ impl State { State { db: db, - root: root + 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) -> State { + 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); @@ -38,13 +41,15 @@ impl State { State { db: db, - root: root + 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()) + Self::new(OverlayDB::new_temp(), U256::from(0u8)) } /// Return reference to root @@ -59,34 +64,30 @@ impl State { /// Commit accounts to TrieDB. This is simplified version of /// cpp-ethereum's dev::eth::commit. - pub fn insert_accounts(&mut self, accounts: &HashMap) { - let mut trie = TrieDB::new_existing(&mut self.db, &mut self.root); - - for (address, account) in accounts.iter() { - let mut stream = RlpStream::new_list(4); - stream.append(account.nonce()); - stream.append(account.balance()); - let mut root = H256::new(); - { - let mut db = MemoryDB::new(); - let mut t = TrieDB::new(&mut db, &mut root); - for (k, v) in account.storage().iter() { - // cast key and value to trait type, - // so we can call overloaded `to_bytes` method - let kas: &ToBytes = k; - let vas: &ToBytes = v; - t.insert(&kas.to_bytes(), &vas.to_bytes()); - } - } - stream.append(&root); - - let code_hash = account.code().sha3(); - stream.append(&code_hash); - - if account.code().len() > 0 { - trie.insert(&code_hash, account.code()); - } - trie.insert(address, &stream.out()); + /// 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 { + // 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(); // would prefer not to do this, really. + self.root = Self::commit(&mut self.db, r, accounts); } }