diff --git a/src/account.rs b/src/account.rs index 6525beb93..d62b07d54 100644 --- a/src/account.rs +++ b/src/account.rs @@ -36,6 +36,58 @@ pub struct PodAccount { pub storage: BTreeMap, } +impl fmt::Display for PodAccount { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", self.balance, self.nonce, self.code.len(), self.code.sha3(), self.storage.len()) + } +} + +#[derive(Debug,Clone,PartialEq,Eq)] +pub struct PodState (BTreeMap); + +pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap { + json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut m, (key, value)| { + m.insert(H256::from(&u256_from_str(key)), H256::from(&u256_from_json(value))); + m + }) +} + +impl PodState { + /// Contruct a new object from the `m`. + pub fn from(m: BTreeMap) -> PodState { PodState(m) } + + /// Translate the JSON object into a hash map of account information ready for insertion into State. + pub fn from_json(json: &Json) -> PodState { + PodState(json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| { + let balance = acc.find("balance").map(&u256_from_json); + let nonce = acc.find("nonce").map(&u256_from_json); + let storage = acc.find("storage").map(&map_h256_h256_from_json);; + let code = acc.find("code").map(&bytes_from_json); + if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() { + state.insert(address_from_hex(address), PodAccount{ + balance: balance.unwrap_or(U256::zero()), + nonce: nonce.unwrap_or(U256::zero()), + storage: storage.unwrap_or(BTreeMap::new()), + code: code.unwrap_or(Vec::new()) + }); + } + state + })) + } + + /// Drain object to get the underlying map. + pub fn drain(self) -> BTreeMap { self.0 } +} + +impl fmt::Display for PodState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for (add, acc) in &self.0 { + try!(writeln!(f, "{} => {}", add, acc)); + } + Ok(()) + } +} + #[derive(Debug,Clone,PartialEq,Eq)] pub struct AccountDiff { pub balance: Diff, // Allowed to be Same @@ -156,21 +208,21 @@ pub fn pod_diff(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option, post: &BTreeMap) -> StateDiff { - StateDiff(pre.keys().merge(post.keys()).filter_map(|acc| pod_diff(pre.get(acc), post.get(acc)).map(|d|(acc.clone(), d))).collect()) +pub fn pod_state_diff(pre: &PodState, post: &PodState) -> StateDiff { + StateDiff(pre.0.keys().merge(post.0.keys()).filter_map(|acc| pod_diff(pre.0.get(acc), post.0.get(acc)).map(|d|(acc.clone(), d))).collect()) } #[test] fn state_diff_create_delete() { - let a = map![ + let a = PodState(map![ x!(1) => PodAccount{ balance: x!(69), nonce: x!(0), code: vec![], storage: map![] } - ]; - assert_eq!(pod_map_diff(&a, &map![]), StateDiff(map![ + ]); + assert_eq!(pod_state_diff(&a, &PodState(map![])), StateDiff(map![ x!(1) => AccountDiff{ balance: Diff::Died(x!(69)), nonce: Diff::Died(x!(0)), @@ -178,7 +230,7 @@ fn state_diff_create_delete() { storage: map![], } ])); - assert_eq!(pod_map_diff(&map![], &a), StateDiff(map![ + assert_eq!(pod_state_diff(&PodState(map![]), &a), StateDiff(map![ x!(1) => AccountDiff{ balance: Diff::Born(x!(69)), nonce: Diff::Born(x!(0)), @@ -190,15 +242,15 @@ fn state_diff_create_delete() { #[test] fn state_diff_cretae_delete_with_unchanged() { - let a = map![ + let a = PodState(map![ x!(1) => PodAccount{ balance: x!(69), nonce: x!(0), code: vec![], storage: map![] } - ]; - let b = map![ + ]); + let b = PodState(map![ x!(1) => PodAccount{ balance: x!(69), nonce: x!(0), @@ -211,8 +263,8 @@ fn state_diff_cretae_delete_with_unchanged() { code: vec![], storage: map![] } - ]; - assert_eq!(pod_map_diff(&a, &b), StateDiff(map![ + ]); + assert_eq!(pod_state_diff(&a, &b), StateDiff(map![ x!(2) => AccountDiff{ balance: Diff::Born(x!(69)), nonce: Diff::Born(x!(0)), @@ -220,7 +272,7 @@ fn state_diff_cretae_delete_with_unchanged() { storage: map![], } ])); - assert_eq!(pod_map_diff(&b, &a), StateDiff(map![ + assert_eq!(pod_state_diff(&b, &a), StateDiff(map![ x!(2) => AccountDiff{ balance: Diff::Died(x!(69)), nonce: Diff::Died(x!(0)), @@ -232,7 +284,7 @@ fn state_diff_cretae_delete_with_unchanged() { #[test] fn state_diff_change_with_unchanged() { - let a = map![ + let a = PodState(map![ x!(1) => PodAccount{ balance: x!(69), nonce: x!(0), @@ -245,8 +297,8 @@ fn state_diff_change_with_unchanged() { code: vec![], storage: map![] } - ]; - let b = map![ + ]); + let b = PodState(map![ x!(1) => PodAccount{ balance: x!(69), nonce: x!(1), @@ -259,8 +311,8 @@ fn state_diff_change_with_unchanged() { code: vec![], storage: map![] } - ]; - assert_eq!(pod_map_diff(&a, &b), StateDiff(map![ + ]); + assert_eq!(pod_state_diff(&a, &b), StateDiff(map![ x!(1) => AccountDiff{ balance: Diff::Same, nonce: Diff::Changed(x!(0), x!(1)), diff --git a/src/log_entry.rs b/src/log_entry.rs index 8602eeb66..b16dfe9fc 100644 --- a/src/log_entry.rs +++ b/src/log_entry.rs @@ -2,7 +2,7 @@ use util::*; use basic_types::LogBloom; /// A single log's entry. -#[derive(Debug)] +#[derive(Debug,PartialEq,Eq)] pub struct LogEntry { pub address: Address, pub topics: Vec, @@ -19,10 +19,6 @@ impl RlpStandard for LogEntry { } impl LogEntry { - pub fn bloom(&self) -> LogBloom { - self.topics.iter().fold(LogBloom::from_bloomed(&self.address.sha3()), |b, t| b.with_bloomed(&t.sha3())) - } - /// Create a new log entry. pub fn new(address: Address, topics: Vec, data: Bytes) -> LogEntry { LogEntry { @@ -32,6 +28,16 @@ impl LogEntry { } } + /// Convert given JSON object to a LogEntry. + pub fn from_json(json: &Json) -> LogEntry { + // TODO: check bloom. + LogEntry { + address: address_from_json(&json["address"]), + topics: vec_h256_from_json(&json["topics"]), + data: bytes_from_json(&json["data"]), + } + } + /// Returns reference to address. pub fn address(&self) -> &Address { &self.address @@ -46,6 +52,11 @@ impl LogEntry { pub fn data(&self) -> &Bytes { &self.data } + + /// Calculates the bloom of this log entry. + pub fn bloom(&self) -> LogBloom { + self.topics.iter().fold(LogBloom::from_bloomed(&self.address.sha3()), |b, t| b.with_bloomed(&t.sha3())) + } } #[cfg(test)] diff --git a/src/state.rs b/src/state.rs index a236e08c5..1f58f638c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -181,8 +181,8 @@ impl State { } /// Populate the state from `accounts`. - pub fn populate_from(&mut self, accounts: BTreeMap) { - for (add, acc) in accounts.into_iter() { + pub fn populate_from(&mut self, accounts: PodState) { + for (add, acc) in accounts.drain().into_iter() { self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc))); } } @@ -199,14 +199,14 @@ impl State { } /// Populate a PodAccount map from this state. - pub fn to_pod_map(&self) -> BTreeMap { + pub fn to_pod(&self) -> PodState { // TODO: handle database rather than just the cache. - self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { + PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { if let &Some(ref acc) = opt { m.insert(add.clone(), PodAccount::from_account(acc)); } m - }) + })) } /// Pull account `a` in our cache from the trie DB and return it. diff --git a/src/tests/state.rs b/src/tests/state.rs index 5be13d3da..dee20965d 100644 --- a/src/tests/state.rs +++ b/src/tests/state.rs @@ -3,32 +3,6 @@ use state::*; use executive::*; use ethereum; -pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap { - json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut m, (key, value)| { - m.insert(H256::from(&u256_from_str(key)), H256::from(&u256_from_json(value))); - m - }) -} - -/// Translate the JSON object into a hash map of account information ready for insertion into State. -pub fn pod_map_from_json(json: &Json) -> BTreeMap { - json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| { - let balance = acc.find("balance").map(&u256_from_json); - let nonce = acc.find("nonce").map(&u256_from_json); - let storage = acc.find("storage").map(&map_h256_h256_from_json);; - let code = acc.find("code").map(&bytes_from_json); - if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() { - state.insert(address_from_hex(address), PodAccount{ - balance: balance.unwrap_or(U256::zero()), - nonce: nonce.unwrap_or(U256::zero()), - storage: storage.unwrap_or(BTreeMap::new()), - code: code.unwrap_or(Vec::new()) - }); - } - state - }) -} - fn do_json_test(json_data: &[u8]) -> Vec { let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid"); let mut failed = Vec::new(); @@ -43,9 +17,9 @@ fn do_json_test(json_data: &[u8]) -> Vec { let env = EnvInfo::from_json(&test["env"]); let _out = bytes_from_json(&test["out"]); let post_state_root = h256_from_json(&test["postStateRoot"]); - let pre = pod_map_from_json(&test["pre"]); - let post = pod_map_from_json(&test["post"]); - // TODO: read test["logs"] + let pre = PodState::from_json(&test["pre"]); + let post = PodState::from_json(&test["post"]); + let logs: Vec<_> = test["logs"].as_array().unwrap().iter().map(&LogEntry::from_json).collect(); //println!("Transaction: {:?}", t); //println!("Env: {:?}", env); @@ -59,15 +33,24 @@ fn do_json_test(json_data: &[u8]) -> Vec { let mut s = State::new_temp(); s.populate_from(pre); + let r = s.apply(&env, engine.deref(), &t).unwrap(); - s.apply(&env, engine.deref(), &t).unwrap(); - let our_post = s.to_pod_map(); - - if fail_unless(s.root() == &post_state_root) { - println!("FAILED {}. Diff:\n{}", name, pod_map_diff(&post, &our_post)); + if fail_unless(&r.state_root == &post_state_root) { + println!("!!! {}: State mismatch (expect: {}, got: {}):", name, r.state_root, post_state_root); + let our_post = s.to_pod(); + println!("Expect:\n{}\n", post); + println!("Got:\n{}\n", our_post); + println!("Diff:\n{}", pod_state_diff(&post, &our_post)); } - // TODO: Compare logs. + if fail_unless(logs == r.logs) { + println!("!!! {}: Logs mismatch:", name); + println!("Expect:\n{:?}", logs); + println!("Got:\n{:?}", r.logs); + } + + // TODO: Add extra APIs for output + //if fail_unless(out == r.) } for f in failed.iter() { println!("FAILED: {:?}", f);