From 544356390362bfea7abdc3a9ab961da2e24da76d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 16 Dec 2015 18:20:23 +0100 Subject: [PATCH 1/6] State functions for balance and nonce operations. --- src/account.rs | 4 +-- src/state.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/src/account.rs b/src/account.rs index 5e2a20a3d..4a0d6b56b 100644 --- a/src/account.rs +++ b/src/account.rs @@ -140,7 +140,7 @@ impl Account { } /// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code. - pub fn ensure_cached(&mut self, db: &HashDB) -> bool { + pub fn cache_code(&mut self, db: &HashDB) -> bool { // TODO: fill out self.code_cache; /* return !self.is_cached() || match db.lookup(&self.code_hash.unwrap()) { // why doesn't this work? unwrap causes move?! @@ -247,7 +247,7 @@ fn note_code() { }; let mut a = Account::from_rlp(&rlp); - assert_eq!(a.ensure_cached(&db), true); + assert_eq!(a.cache_code(&db), true); let mut a = Account::from_rlp(&rlp); assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(())); diff --git a/src/state.rs b/src/state.rs index 5674035bf..64086de83 100644 --- a/src/state.rs +++ b/src/state.rs @@ -63,6 +63,31 @@ impl State { &mut self.db } + /// Get the balance of account `a`. + pub fn balance(&mut self, a: &Address) -> U256 { + self.get(a, false).as_ref().map(|account| account.balance().clone()).unwrap_or(U256::from(0u8)) + } + + /// Add `incr` to the balance of account `a`. + pub fn add_balance(&mut self, a: &Address, incr: &U256) { + self.require(a, false).add_balance(incr) + } + + /// Subtract `decr` from the balance of account `a`. + pub fn sub_balance(&mut self, a: &Address, decr: &U256) { + self.require(a, false).sub_balance(decr) + } + + /// Get the nonce of account `a`. + pub fn nonce(&mut self, a: &Address) -> U256 { + self.get(a, false).as_ref().map(|account| account.nonce().clone()).unwrap_or(U256::from(0u8)) + } + + /// Increment the nonce of account `a` by 1. + pub fn inc_nonce(&mut self, a: &Address) { + self.require(a, false).inc_nonce() + } + /// Commit accounts to TrieDB. This is similar to cpp-ethereum's dev::eth::commit. /// `accounts` is mutable because we may need to commit the code or storage and record that. pub fn commit_into(db: &mut HashDB, mut root: H256, accounts: &mut HashMap>) -> H256 { @@ -98,23 +123,40 @@ impl State { /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// `force_create` creates a new, empty basic account if there is not currently an active account. - fn ensure_cached(&mut self, a: &Address, require_code: bool, force_create: bool) { + fn get(&mut self, a: &Address, require_code: bool) -> Option<&mut Account> { + if self.cache.get(a).is_none() { + // load from trie. + self.cache.insert(a.clone(), TrieDB::new(&mut self.db, &mut self.root).at(&a).map(|rlp| Account::from_rlp(rlp))); + } + + let db = &self.db; + self.cache.get_mut(a).unwrap().as_mut().map(|account| { + if require_code { + account.cache_code(db); + } + account + }) + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + /// `force_create` creates a new, empty basic account if there is not currently an active account. + fn require(&mut self, a: &Address, require_code: bool) -> &mut Account { if self.cache.get(a).is_none() { // load from trie. self.cache.insert(a.clone(), TrieDB::new(&mut self.db, &mut self.root).at(&a).map(|rlp| Account::from_rlp(rlp))); } if self.cache.get(a).unwrap().is_none() { - if !force_create { return; } - // create a new account self.cache.insert(a.clone(), Some(Account::new_basic(U256::from(0u8)))); } - if require_code { - if let &mut Some(ref mut account) = self.cache.get_mut(a).unwrap() { - account.ensure_cached(&self.db); + let db = &self.db; + self.cache.get_mut(a).unwrap().as_mut().map(|account| { + if require_code { + account.cache_code(db); } - } + account + }).unwrap() } } @@ -125,17 +167,38 @@ use super::*; use util::hash::*; use util::trie::*; use util::rlp::*; +use util::uint::*; use std::str::FromStr; #[test] fn playpen() { + +} + +#[test] +fn add_balance() { + let mut s = State::new_temp(); + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + s.add_balance(&a, &U256::from(69u64)); + assert_eq!(s.balance(&a), U256::from(69u64)); + s.commit(); + assert_eq!(s.balance(&a), U256::from(69u64)); +} + +#[test] +fn balance() { + let mut s = State::new_temp(); + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + assert_eq!(s.balance(&a), U256::from(0u64)); + s.commit(); + assert_eq!(s.balance(&a), U256::from(0u64)); } #[test] fn ensure_cached() { let mut s = State::new_temp(); let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); - s.ensure_cached(&a, false, true); + s.require(&a, false); s.commit(); assert_eq!(s.root().hex(), "ec68b85fa2e0526dc0e821a5b33135459114f19173ce0479f5c09b21cc25b9a4"); } From d2f3942c47a02bf02fcdfefe9fb9833887ae236b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 16 Dec 2015 18:28:04 +0100 Subject: [PATCH 2/6] Tests for nonce & balance. --- src/state.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/state.rs b/src/state.rs index 64086de83..528aaf035 100644 --- a/src/state.rs +++ b/src/state.rs @@ -176,22 +176,44 @@ fn playpen() { } #[test] -fn add_balance() { +fn alter_balance() { let mut s = State::new_temp(); let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); s.add_balance(&a, &U256::from(69u64)); assert_eq!(s.balance(&a), U256::from(69u64)); s.commit(); assert_eq!(s.balance(&a), U256::from(69u64)); + s.sub_balance(&a, &U256::from(42u64)); + assert_eq!(s.balance(&a), U256::from(27u64)); + s.commit(); + assert_eq!(s.balance(&a), U256::from(27u64)); } #[test] -fn balance() { +fn alter_nonce() { + let mut s = State::new_temp(); + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + s.inc_nonce(&a); + assert_eq!(s.nonce(&a), U256::from(1u64)); + s.inc_nonce(&a); + assert_eq!(s.nonce(&a), U256::from(2u64)); + s.commit(); + assert_eq!(s.nonce(&a), U256::from(2u64)); + s.inc_nonce(&a); + assert_eq!(s.nonce(&a), U256::from(3u64)); + s.commit(); + assert_eq!(s.nonce(&a), U256::from(3u64)); +} + +#[test] +fn balance_nonce() { let mut s = State::new_temp(); let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); assert_eq!(s.balance(&a), U256::from(0u64)); + assert_eq!(s.nonce(&a), U256::from(0u64)); s.commit(); assert_eq!(s.balance(&a), U256::from(0u64)); + assert_eq!(s.nonce(&a), U256::from(0u64)); } #[test] From 8edd95e8cb19775e01a966193cc29c442623fad8 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 16 Dec 2015 20:02:28 +0100 Subject: [PATCH 3/6] Test and fixes for State's require function. --- src/account.rs | 1 + src/blockheader.rs | 2 -- src/state.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/account.rs b/src/account.rs index 4a0d6b56b..e342333ec 100644 --- a/src/account.rs +++ b/src/account.rs @@ -10,6 +10,7 @@ use util::uint::*; 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. +#[derive(Debug)] pub struct Account { // Balance of the account. balance: U256, diff --git a/src/blockheader.rs b/src/blockheader.rs index 6abbbdff7..566a77f69 100644 --- a/src/blockheader.rs +++ b/src/blockheader.rs @@ -102,6 +102,4 @@ impl Encodable for Header { #[cfg(test)] mod tests { - fn encoding_and_decoding() { - } } diff --git a/src/state.rs b/src/state.rs index 528aaf035..b5b323a1a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -5,7 +5,29 @@ use util::overlaydb::*; use util::trie::*; use util::rlp::*; use util::uint::*; +use std::mem; use account::Account; +/* +enum ValueOrRef<'self, 'db: 'self> { + Value(OverlayDB), + Ref(&'db mut OverlayDB) +} + +impl<'self, 'db> ValueOrRef<'self, 'db: 'self> { + pub fn get_mut(&mut self) -> &mut OverlayDB { + match self { + Value(ref mut x) => x, + Ref(x) => x, + } + } + pub fn get(&self) -> &OverlayDB { + match self { + Value(ref x) => x, + Ref(x) => x, + } + } +} +*/ /// Representation of the entire state of all accounts in the system. pub struct State { @@ -58,12 +80,24 @@ impl State { &self.root } + /// Desttroy the current database and return it. + /// WARNING: the struct should be dropped immediately following this. + pub fn take_db(&mut self) -> OverlayDB { + mem::replace(&mut self.db, OverlayDB::new_temp()) + } + + /// Destroy the current object and return root and database. + pub fn drop(mut self) -> (H256, OverlayDB) { + (mem::replace(&mut self.root, H256::new()), mem::replace(&mut self.db, OverlayDB::new_temp())) + } + /// Expose the underlying database; good to use for calling `state.db().commit()`. pub fn db(&mut self) -> &mut OverlayDB { &mut self.db } /// Get the balance of account `a`. + // TODO: make immutable pub fn balance(&mut self, a: &Address) -> U256 { self.get(a, false).as_ref().map(|account| account.balance().clone()).unwrap_or(U256::from(0u8)) } @@ -79,6 +113,7 @@ impl State { } /// Get the nonce of account `a`. + // TODO: make immutable pub fn nonce(&mut self, a: &Address) -> U256 { self.get(a, false).as_ref().map(|account| account.nonce().clone()).unwrap_or(U256::from(0u8)) } @@ -123,10 +158,12 @@ impl State { /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// `force_create` creates a new, empty basic account if there is not currently an active account. + // TODO: make immutable. fn get(&mut self, a: &Address, require_code: bool) -> Option<&mut Account> { if self.cache.get(a).is_none() { // load from trie. - self.cache.insert(a.clone(), TrieDB::new(&mut self.db, &mut self.root).at(&a).map(|rlp| Account::from_rlp(rlp))); + let t = TrieDB::new_existing(&mut self.db, &mut self.root); + self.cache.insert(a.clone(), t.at(&a).map(|rlp| { println!("RLP: {:?}", rlp); Account::from_rlp(rlp) })); } let db = &self.db; @@ -171,8 +208,20 @@ use util::uint::*; use std::str::FromStr; #[test] -fn playpen() { +fn revived() { + let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); + let (r, db) = { + let mut s = State::new_temp(); + s.inc_nonce(&a); + s.add_balance(&a, &U256::from(69u64)); + s.commit(); + assert_eq!(s.balance(&a), U256::from(69u64)); + (s.root().clone(), s.take_db()) + }; + let mut s = State::new_existing(db, r, U256::from(0u8)); + assert_eq!(s.balance(&a), U256::from(69u64)); + assert_eq!(s.nonce(&a), U256::from(1u64)); } #[test] From e08958d5024145c4f639156372cee54cf282e939 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 16 Dec 2015 20:11:37 +0100 Subject: [PATCH 4/6] Minor doc fix. --- src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state.rs b/src/state.rs index b5b323a1a..05615e0c0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -208,7 +208,7 @@ use util::uint::*; use std::str::FromStr; #[test] -fn revived() { +fn get_from_database() { let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); let (r, db) = { let mut s = State::new_temp(); From 8687d0d097547869a97a404c544bf7458e64c9ac Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 17 Dec 2015 12:32:35 +0100 Subject: [PATCH 5/6] Slightly better mutation semantics. --- src/account.rs | 2 +- src/state.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/account.rs b/src/account.rs index e342333ec..33d9406ca 100644 --- a/src/account.rs +++ b/src/account.rs @@ -97,7 +97,7 @@ impl Account { } // fetch - cannot be done in match because of the borrow rules. let t = TrieDB::new_existing(db, &mut self.storage_root); - let r = H256::from_slice(t.at(key.bytes()).unwrap_or(&[0u8;32][..])); + let r = H256::from_slice(t.get(key.bytes()).unwrap_or(&[0u8;32][..])); self.storage_overlay.insert(key, r.clone()); r } diff --git a/src/state.rs b/src/state.rs index 05615e0c0..dc9da6d65 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,6 +6,8 @@ use util::trie::*; use util::rlp::*; use util::uint::*; use std::mem; +//use std::cell::*; +//use std::ops::*; use account::Account; /* enum ValueOrRef<'self, 'db: 'self> { @@ -159,11 +161,11 @@ impl State { /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// `force_create` creates a new, empty basic account if there is not currently an active account. // TODO: make immutable. - fn get(&mut self, a: &Address, require_code: bool) -> Option<&mut Account> { + fn get(&mut self, a: &Address, require_code: bool) -> Option<&Account> { if self.cache.get(a).is_none() { // load from trie. let t = TrieDB::new_existing(&mut self.db, &mut self.root); - self.cache.insert(a.clone(), t.at(&a).map(|rlp| { println!("RLP: {:?}", rlp); Account::from_rlp(rlp) })); + self.cache.insert(a.clone(), t.get(&a).map(|rlp| { println!("RLP: {:?}", rlp); Account::from_rlp(rlp) })); } let db = &self.db; @@ -171,7 +173,7 @@ impl State { if require_code { account.cache_code(db); } - account + account as &Account }) } @@ -180,7 +182,7 @@ impl State { fn require(&mut self, a: &Address, require_code: bool) -> &mut Account { if self.cache.get(a).is_none() { // load from trie. - self.cache.insert(a.clone(), TrieDB::new(&mut self.db, &mut self.root).at(&a).map(|rlp| Account::from_rlp(rlp))); + self.cache.insert(a.clone(), TrieDB::new(&mut self.db, &mut self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); } if self.cache.get(a).unwrap().is_none() { From 58d8f13913aebb7c1c3155a750600a0a8c095c0d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 17 Dec 2015 12:43:01 +0100 Subject: [PATCH 6/6] TrieDBMut rather than TrieDB in preparation for immutable Trie type. --- src/account.rs | 4 ++-- src/state.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/account.rs b/src/account.rs index 33d9406ca..95f58ed3b 100644 --- a/src/account.rs +++ b/src/account.rs @@ -96,7 +96,7 @@ impl Account { _ => {} } // fetch - cannot be done in match because of the borrow rules. - let t = TrieDB::new_existing(db, &mut self.storage_root); + let t = TrieDBMut::new_existing(db, &mut self.storage_root); let r = H256::from_slice(t.get(key.bytes()).unwrap_or(&[0u8;32][..])); self.storage_overlay.insert(key, r.clone()); r @@ -177,7 +177,7 @@ impl Account { /// 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); + let mut t = TrieDBMut::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 diff --git a/src/state.rs b/src/state.rs index dc9da6d65..978b55fbf 100644 --- a/src/state.rs +++ b/src/state.rs @@ -46,7 +46,7 @@ impl State { let mut root = H256::new(); { // init trie and reset root too null - let _ = TrieDB::new(&mut db, &mut root); + let _ = TrieDBMut::new(&mut db, &mut root); } State { @@ -61,7 +61,7 @@ impl 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); + let _ = TrieDBMut::new_existing(&mut db, &mut root); } State { @@ -125,7 +125,7 @@ impl State { self.require(a, false).inc_nonce() } - /// Commit accounts to TrieDB. This is similar to cpp-ethereum's dev::eth::commit. + /// Commit accounts to TrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. /// `accounts` is mutable because we may need to commit the code or storage and record that. pub fn commit_into(db: &mut HashDB, mut root: H256, accounts: &mut HashMap>) -> H256 { // first, commit the sub trees. @@ -141,7 +141,7 @@ impl State { } { - let mut trie = TrieDB::new_existing(db, &mut root); + let mut trie = TrieDBMut::new_existing(db, &mut root); for (address, ref a) in accounts.iter() { match a { &&Some(ref account) => trie.insert(address, &account.rlp()), @@ -164,7 +164,7 @@ impl State { fn get(&mut self, a: &Address, require_code: bool) -> Option<&Account> { if self.cache.get(a).is_none() { // load from trie. - let t = TrieDB::new_existing(&mut self.db, &mut self.root); + let t = TrieDBMut::new_existing(&mut self.db, &mut self.root); self.cache.insert(a.clone(), t.get(&a).map(|rlp| { println!("RLP: {:?}", rlp); Account::from_rlp(rlp) })); } @@ -182,7 +182,7 @@ impl State { fn require(&mut self, a: &Address, require_code: bool) -> &mut Account { if self.cache.get(a).is_none() { // load from trie. - self.cache.insert(a.clone(), TrieDB::new(&mut self.db, &mut self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); + self.cache.insert(a.clone(), TrieDBMut::new(&mut self.db, &mut self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); } if self.cache.get(a).unwrap().is_none() {