Fix storage stuff and introduce per-item dirty-tracking.

This commit is contained in:
Gav Wood 2016-01-14 13:54:29 +01:00
parent 5a9ac6a329
commit 1b1038fe06
2 changed files with 39 additions and 24 deletions

View File

@ -386,6 +386,12 @@ fn account_diff_storage() {
})); }));
} }
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum Filth {
Clean,
Dirty,
}
/// Single account in the system. /// Single account in the system.
#[derive(Clone)] #[derive(Clone)]
pub struct Account { pub struct Account {
@ -395,8 +401,8 @@ pub struct Account {
nonce: U256, nonce: U256,
// Trie-backed storage. // Trie-backed storage.
storage_root: H256, storage_root: H256,
// Overlay on trie-backed storage. // Overlay on trie-backed storage - tuple is (<clean>, <value>).
storage_overlay: RefCell<HashMap<H256, H256>>, storage_overlay: RefCell<HashMap<H256, (Filth, H256)>>,
// Code hash of the account. If None, means that it's a contract whose code has not yet been set. // Code hash of the account. If None, means that it's a contract whose code has not yet been set.
code_hash: Option<H256>, code_hash: Option<H256>,
// Code cache of the account. // Code cache of the account.
@ -410,7 +416,7 @@ impl PodAccount {
PodAccount { PodAccount {
balance: acc.balance.clone(), balance: acc.balance.clone(),
nonce: acc.nonce.clone(), nonce: acc.nonce.clone(),
storage: acc.storage_overlay.borrow().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}), storage: acc.storage_overlay.borrow().iter().fold(BTreeMap::new(), |mut m, (k, &(_, ref v))| {m.insert(k.clone(), v.clone()); m}),
code: acc.code_cache.clone() code: acc.code_cache.clone()
} }
} }
@ -433,7 +439,7 @@ impl Account {
balance: balance, balance: balance,
nonce: nonce, nonce: nonce,
storage_root: SHA3_NULL_RLP, storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(storage), storage_overlay: RefCell::new(storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
code_hash: Some(code.sha3()), code_hash: Some(code.sha3()),
code_cache: code code_cache: code
} }
@ -445,7 +451,7 @@ impl Account {
balance: pod.balance, balance: pod.balance,
nonce: pod.nonce, nonce: pod.nonce,
storage_root: SHA3_NULL_RLP, storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(pod.storage.into_iter().fold(HashMap::new(), |mut m, (k, v)| {m.insert(k, v); m})), storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
code_hash: Some(pod.code.sha3()), code_hash: Some(pod.code.sha3()),
code_cache: pod.code code_cache: pod.code
} }
@ -505,14 +511,14 @@ impl Account {
/// Set (and cache) the contents of the trie's storage at `key` to `value`. /// Set (and cache) the contents of the trie's storage at `key` to `value`.
pub fn set_storage(&mut self, key: H256, value: H256) { pub fn set_storage(&mut self, key: H256, value: H256) {
self.storage_overlay.borrow_mut().insert(key, value); self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value));
} }
/// Get (and cache) the contents of the trie's storage at `key`. /// Get (and cache) the contents of the trie's storage at `key`.
pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 { pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 {
self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{ self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
H256::from_slice(SecTrieDB::new(db, &self.storage_root).get(key.bytes()).unwrap_or(&[0u8;32][..])) (Filth::Clean, H256::from(SecTrieDB::new(db, &self.storage_root).get(key.bytes()).map(|v| -> U256 {decode(v)}).unwrap_or(U256::zero())))
}).clone() }).1.clone()
} }
/// return the balance associated with this account. /// return the balance associated with this account.
@ -574,11 +580,17 @@ impl Account {
/// return the storage root associated with this account. /// return the storage root associated with this account.
pub fn base_root(&self) -> &H256 { &self.storage_root } pub fn base_root(&self) -> &H256 { &self.storage_root }
/// return the storage root associated with this account or None if it has been altered via the overlay. /// Determine whether there are any un-`commit()`-ed storage-setting operations.
pub fn storage_root(&self) -> Option<&H256> { if self.storage_overlay.borrow().is_empty() {Some(&self.storage_root)} else {None} } pub fn storage_is_clean(&self) -> bool { self.storage_overlay.borrow().iter().find(|&(_, &(f, _))| f == Filth::Dirty).is_none() }
/// rturn the storage overlay. /// return the storage root associated with this account or None if it has been altered via the overlay.
pub fn storage_overlay(&self) -> Ref<HashMap<H256, H256>> { self.storage_overlay.borrow() } pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
/// return the storage root associated with this account or None if it has been altered via the overlay.
pub fn recent_storage_root(&self) -> &H256 { &self.storage_root }
/// return the storage overlay.
pub fn storage_overlay(&self) -> Ref<HashMap<H256, (Filth, H256)>> { self.storage_overlay.borrow() }
/// Increment the nonce of the account by one. /// Increment the nonce of the account by one.
pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); } pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); }
@ -592,12 +604,14 @@ impl Account {
/// Commit the `storage_overlay` to the backing DB and update `storage_root`. /// Commit the `storage_overlay` to the backing DB and update `storage_root`.
pub fn commit_storage(&mut self, db: &mut HashDB) { pub fn commit_storage(&mut self, db: &mut HashDB) {
let mut t = SecTrieDBMut::new(db, &mut self.storage_root); let mut t = SecTrieDBMut::new(db, &mut self.storage_root);
for (k, v) in self.storage_overlay.borrow().iter() { for (k, &mut (ref mut f, ref mut v)) in self.storage_overlay.borrow_mut().iter_mut() {
// cast key and value to trait type, if f == &Filth::Dirty {
// so we can call overloaded `to_bytes` method // cast key and value to trait type,
t.insert(k, &encode(&U256::from(v.as_slice()))); // so we can call overloaded `to_bytes` method
t.insert(k, &encode(&U256::from(v.as_slice())));
*f = Filth::Clean;
}
} }
// self.storage_overlay.borrow_mut().clear();
} }
/// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this.
@ -654,7 +668,7 @@ fn storage_at() {
}; };
let a = Account::from_rlp(&rlp); let a = Account::from_rlp(&rlp);
assert_eq!(a.storage_root().unwrap().hex(), "3541f181d6dad5c504371884684d08c29a8bad04926f8ceddf5e279dbc3cc769"); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64))); assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64)));
assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x01u64))), H256::new()); assert_eq!(a.storage_at(&mut db, &H256::from(&U256::from(0x01u64))), H256::new());
} }
@ -684,7 +698,7 @@ fn commit_storage() {
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64))); a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
assert_eq!(a.storage_root(), None); assert_eq!(a.storage_root(), None);
a.commit_storage(&mut db); a.commit_storage(&mut db);
assert_eq!(a.storage_root().unwrap().hex(), "3541f181d6dad5c504371884684d08c29a8bad04926f8ceddf5e279dbc3cc769"); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
} }
#[test] #[test]

View File

@ -30,22 +30,23 @@ impl EnvInfo {
number: 0, number: 0,
author: Address::new(), author: Address::new(),
timestamp: 0, timestamp: 0,
difficulty: U256::zero(), difficulty: x!(0),
gas_limit: U256::zero(), gas_limit: x!(0),
last_hashes: vec![], last_hashes: vec![],
gas_used: U256::zero() gas_used: x!(0),
} }
} }
pub fn from_json(json: &Json) -> EnvInfo { pub fn from_json(json: &Json) -> EnvInfo {
let current_number = u64_from_json(&json["currentNumber"]);
EnvInfo { EnvInfo {
number: u64_from_json(&json["currentNumber"]), number: current_number,
author: address_from_json(&json["currentCoinbase"]), author: address_from_json(&json["currentCoinbase"]),
difficulty: u256_from_json(&json["currentDifficulty"]), difficulty: u256_from_json(&json["currentDifficulty"]),
gas_limit: u256_from_json(&json["currentGasLimit"]), gas_limit: u256_from_json(&json["currentGasLimit"]),
timestamp: u64_from_json(&json["currentTimestamp"]), timestamp: u64_from_json(&json["currentTimestamp"]),
last_hashes: vec![h256_from_json(&json["previousHash"])], last_hashes: (1..257).map(|i| format!("{}", current_number - i).as_bytes().sha3()).collect(),
gas_used: U256::zero(), gas_used: x!(0),
} }
} }
} }