Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Add Account and State classes. #37

Merged
merged 2 commits into from
Dec 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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<H256, H256>,
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;
}

// 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...

}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
93 changes: 93 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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<Address, Option<Account>>,

_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.
/// 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<Address, Account>) -> 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<Address, Account>) {
let r = self.root.clone(); // would prefer not to do this, really.
self.root = Self::commit(&mut self.db, r, accounts);
}
}