Check logs in state tests.

This commit is contained in:
Gav Wood 2016-01-14 12:27:35 +01:00
parent 59e2df3e40
commit 507678aaa9
4 changed files with 108 additions and 62 deletions

View File

@ -36,6 +36,58 @@ pub struct PodAccount {
pub storage: BTreeMap<H256, H256>, pub storage: BTreeMap<H256, H256>,
} }
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<Address, PodAccount>);
pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap<H256, H256> {
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<Address, PodAccount>) -> 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<Address, PodAccount> { 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)] #[derive(Debug,Clone,PartialEq,Eq)]
pub struct AccountDiff { pub struct AccountDiff {
pub balance: Diff<U256>, // Allowed to be Same pub balance: Diff<U256>, // Allowed to be Same
@ -156,21 +208,21 @@ pub fn pod_diff(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option<A
} }
} }
pub fn pod_map_diff(pre: &BTreeMap<Address, PodAccount>, post: &BTreeMap<Address, PodAccount>) -> StateDiff { pub fn pod_state_diff(pre: &PodState, post: &PodState) -> StateDiff {
StateDiff(pre.keys().merge(post.keys()).filter_map(|acc| pod_diff(pre.get(acc), post.get(acc)).map(|d|(acc.clone(), d))).collect()) 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] #[test]
fn state_diff_create_delete() { fn state_diff_create_delete() {
let a = map![ let a = PodState(map![
x!(1) => PodAccount{ x!(1) => PodAccount{
balance: x!(69), balance: x!(69),
nonce: x!(0), nonce: x!(0),
code: vec![], code: vec![],
storage: map![] storage: map![]
} }
]; ]);
assert_eq!(pod_map_diff(&a, &map![]), StateDiff(map![ assert_eq!(pod_state_diff(&a, &PodState(map![])), StateDiff(map![
x!(1) => AccountDiff{ x!(1) => AccountDiff{
balance: Diff::Died(x!(69)), balance: Diff::Died(x!(69)),
nonce: Diff::Died(x!(0)), nonce: Diff::Died(x!(0)),
@ -178,7 +230,7 @@ fn state_diff_create_delete() {
storage: map![], storage: map![],
} }
])); ]));
assert_eq!(pod_map_diff(&map![], &a), StateDiff(map![ assert_eq!(pod_state_diff(&PodState(map![]), &a), StateDiff(map![
x!(1) => AccountDiff{ x!(1) => AccountDiff{
balance: Diff::Born(x!(69)), balance: Diff::Born(x!(69)),
nonce: Diff::Born(x!(0)), nonce: Diff::Born(x!(0)),
@ -190,15 +242,15 @@ fn state_diff_create_delete() {
#[test] #[test]
fn state_diff_cretae_delete_with_unchanged() { fn state_diff_cretae_delete_with_unchanged() {
let a = map![ let a = PodState(map![
x!(1) => PodAccount{ x!(1) => PodAccount{
balance: x!(69), balance: x!(69),
nonce: x!(0), nonce: x!(0),
code: vec![], code: vec![],
storage: map![] storage: map![]
} }
]; ]);
let b = map![ let b = PodState(map![
x!(1) => PodAccount{ x!(1) => PodAccount{
balance: x!(69), balance: x!(69),
nonce: x!(0), nonce: x!(0),
@ -211,8 +263,8 @@ fn state_diff_cretae_delete_with_unchanged() {
code: vec![], code: vec![],
storage: map![] storage: map![]
} }
]; ]);
assert_eq!(pod_map_diff(&a, &b), StateDiff(map![ assert_eq!(pod_state_diff(&a, &b), StateDiff(map![
x!(2) => AccountDiff{ x!(2) => AccountDiff{
balance: Diff::Born(x!(69)), balance: Diff::Born(x!(69)),
nonce: Diff::Born(x!(0)), nonce: Diff::Born(x!(0)),
@ -220,7 +272,7 @@ fn state_diff_cretae_delete_with_unchanged() {
storage: map![], storage: map![],
} }
])); ]));
assert_eq!(pod_map_diff(&b, &a), StateDiff(map![ assert_eq!(pod_state_diff(&b, &a), StateDiff(map![
x!(2) => AccountDiff{ x!(2) => AccountDiff{
balance: Diff::Died(x!(69)), balance: Diff::Died(x!(69)),
nonce: Diff::Died(x!(0)), nonce: Diff::Died(x!(0)),
@ -232,7 +284,7 @@ fn state_diff_cretae_delete_with_unchanged() {
#[test] #[test]
fn state_diff_change_with_unchanged() { fn state_diff_change_with_unchanged() {
let a = map![ let a = PodState(map![
x!(1) => PodAccount{ x!(1) => PodAccount{
balance: x!(69), balance: x!(69),
nonce: x!(0), nonce: x!(0),
@ -245,8 +297,8 @@ fn state_diff_change_with_unchanged() {
code: vec![], code: vec![],
storage: map![] storage: map![]
} }
]; ]);
let b = map![ let b = PodState(map![
x!(1) => PodAccount{ x!(1) => PodAccount{
balance: x!(69), balance: x!(69),
nonce: x!(1), nonce: x!(1),
@ -259,8 +311,8 @@ fn state_diff_change_with_unchanged() {
code: vec![], code: vec![],
storage: map![] storage: map![]
} }
]; ]);
assert_eq!(pod_map_diff(&a, &b), StateDiff(map![ assert_eq!(pod_state_diff(&a, &b), StateDiff(map![
x!(1) => AccountDiff{ x!(1) => AccountDiff{
balance: Diff::Same, balance: Diff::Same,
nonce: Diff::Changed(x!(0), x!(1)), nonce: Diff::Changed(x!(0), x!(1)),

View File

@ -2,7 +2,7 @@ use util::*;
use basic_types::LogBloom; use basic_types::LogBloom;
/// A single log's entry. /// A single log's entry.
#[derive(Debug)] #[derive(Debug,PartialEq,Eq)]
pub struct LogEntry { pub struct LogEntry {
pub address: Address, pub address: Address,
pub topics: Vec<H256>, pub topics: Vec<H256>,
@ -19,10 +19,6 @@ impl RlpStandard for LogEntry {
} }
impl 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. /// Create a new log entry.
pub fn new(address: Address, topics: Vec<H256>, data: Bytes) -> LogEntry { pub fn new(address: Address, topics: Vec<H256>, data: Bytes) -> LogEntry {
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. /// Returns reference to address.
pub fn address(&self) -> &Address { pub fn address(&self) -> &Address {
&self.address &self.address
@ -46,6 +52,11 @@ impl LogEntry {
pub fn data(&self) -> &Bytes { pub fn data(&self) -> &Bytes {
&self.data &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)] #[cfg(test)]

View File

@ -181,8 +181,8 @@ impl State {
} }
/// Populate the state from `accounts`. /// Populate the state from `accounts`.
pub fn populate_from(&mut self, accounts: BTreeMap<Address, PodAccount>) { pub fn populate_from(&mut self, accounts: PodState) {
for (add, acc) in accounts.into_iter() { for (add, acc) in accounts.drain().into_iter() {
self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc))); self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc)));
} }
} }
@ -199,14 +199,14 @@ impl State {
} }
/// Populate a PodAccount map from this state. /// Populate a PodAccount map from this state.
pub fn to_pod_map(&self) -> BTreeMap<Address, PodAccount> { pub fn to_pod(&self) -> PodState {
// TODO: handle database rather than just the cache. // 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 { if let &Some(ref acc) = opt {
m.insert(add.clone(), PodAccount::from_account(acc)); m.insert(add.clone(), PodAccount::from_account(acc));
} }
m m
}) }))
} }
/// Pull account `a` in our cache from the trie DB and return it. /// Pull account `a` in our cache from the trie DB and return it.

View File

@ -3,32 +3,6 @@ use state::*;
use executive::*; use executive::*;
use ethereum; use ethereum;
pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap<H256, H256> {
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<Address, PodAccount> {
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<String> { fn do_json_test(json_data: &[u8]) -> Vec<String> {
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid"); let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let mut failed = Vec::new(); let mut failed = Vec::new();
@ -43,9 +17,9 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
let env = EnvInfo::from_json(&test["env"]); let env = EnvInfo::from_json(&test["env"]);
let _out = bytes_from_json(&test["out"]); let _out = bytes_from_json(&test["out"]);
let post_state_root = h256_from_json(&test["postStateRoot"]); let post_state_root = h256_from_json(&test["postStateRoot"]);
let pre = pod_map_from_json(&test["pre"]); let pre = PodState::from_json(&test["pre"]);
let post = pod_map_from_json(&test["post"]); let post = PodState::from_json(&test["post"]);
// TODO: read test["logs"] let logs: Vec<_> = test["logs"].as_array().unwrap().iter().map(&LogEntry::from_json).collect();
//println!("Transaction: {:?}", t); //println!("Transaction: {:?}", t);
//println!("Env: {:?}", env); //println!("Env: {:?}", env);
@ -59,15 +33,24 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
let mut s = State::new_temp(); let mut s = State::new_temp();
s.populate_from(pre); s.populate_from(pre);
let r = s.apply(&env, engine.deref(), &t).unwrap();
s.apply(&env, engine.deref(), &t).unwrap(); if fail_unless(&r.state_root == &post_state_root) {
let our_post = s.to_pod_map(); println!("!!! {}: State mismatch (expect: {}, got: {}):", name, r.state_root, post_state_root);
let our_post = s.to_pod();
if fail_unless(s.root() == &post_state_root) { println!("Expect:\n{}\n", post);
println!("FAILED {}. Diff:\n{}", name, pod_map_diff(&post, &our_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() { for f in failed.iter() {
println!("FAILED: {:?}", f); println!("FAILED: {:?}", f);