Merge branch 'evm' into evm_factory_parametrized

Conflicts:
	src/executive.rs
This commit is contained in:
Tomusdrw 2016-01-14 15:01:58 +01:00
commit f96e598f20
7 changed files with 237 additions and 115 deletions

View File

@ -36,6 +36,58 @@ pub struct PodAccount {
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)]
pub struct AccountDiff {
pub balance: Diff<U256>, // Allowed to be Same
@ -54,12 +106,47 @@ impl AccountDiff {
}
}
fn format(u: &H256) -> String {
if u <= &H256::from(0xffffffff) {
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32())
} else if u <= &H256::from(u64::max_value()) {
format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u64(), U256::from(u.as_slice()).low_u64())
// } else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") {
// format!("@{}", Address::from(u))
} else {
format!("#{}", u)
}
}
#[derive(Debug,Clone,PartialEq,Eq)]
pub struct StateDiff (BTreeMap<Address, AccountDiff>);
impl fmt::Display for AccountDiff {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "...\n")
match self.nonce {
Diff::Born(ref x) => try!(write!(f, " non {}", x)),
Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))),
_ => {},
}
match self.balance {
Diff::Born(ref x) => try!(write!(f, " bal {}", x)),
Diff::Changed(ref pre, ref post) => try!(write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))),
_ => {},
}
match self.code {
Diff::Born(ref x) => try!(write!(f, " code {}", x.pretty())),
_ => {},
}
try!(write!(f, "\n"));
for (k, dv) in self.storage.iter() {
match dv {
&Diff::Born(ref v) => try!(write!(f, " + {} => {}\n", format(k), format(v))),
&Diff::Changed(ref pre, ref post) => try!(write!(f, " * {} => {} (was {})\n", format(k), format(post), format(pre))),
&Diff::Died(_) => try!(write!(f, " X {}\n", format(k))),
_ => {},
}
}
Ok(())
}
}
@ -68,7 +155,7 @@ impl fmt::Display for Existance {
match self {
&Existance::Born => try!(write!(f, "+++")),
&Existance::Alive => try!(write!(f, "***")),
&Existance::Died => try!(write!(f, "---")),
&Existance::Died => try!(write!(f, "XXX")),
}
Ok(())
}
@ -121,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 {
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)),
@ -143,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)),
@ -155,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),
@ -176,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)),
@ -185,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)),
@ -197,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),
@ -210,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),
@ -224,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)),
@ -299,6 +386,12 @@ fn account_diff_storage() {
}));
}
#[derive(PartialEq,Eq,Clone,Copy)]
pub enum Filth {
Clean,
Dirty,
}
/// Single account in the system.
#[derive(Clone)]
pub struct Account {
@ -308,8 +401,8 @@ pub struct Account {
nonce: U256,
// Trie-backed storage.
storage_root: H256,
// Overlay on trie-backed storage.
storage_overlay: RefCell<HashMap<H256, H256>>,
// Overlay on trie-backed storage - tuple is (<clean>, <value>).
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: Option<H256>,
// Code cache of the account.
@ -323,7 +416,7 @@ impl PodAccount {
PodAccount {
balance: acc.balance.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()
}
}
@ -346,7 +439,7 @@ impl Account {
balance: balance,
nonce: nonce,
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_cache: code
}
@ -358,7 +451,7 @@ impl Account {
balance: pod.balance,
nonce: pod.nonce,
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_cache: pod.code
}
@ -418,14 +511,14 @@ impl Account {
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
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`.
pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 {
self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
H256::from_slice(TrieDB::new(db, &self.storage_root).get(key.bytes()).unwrap_or(&[0u8;32][..]))
}).clone()
(Filth::Clean, H256::from(SecTrieDB::new(db, &self.storage_root).get(key.bytes()).map(|v| -> U256 {decode(v)}).unwrap_or(U256::zero())))
}).1.clone()
}
/// return the balance associated with this account.
@ -486,12 +579,18 @@ impl Account {
/// return the storage root associated with this account.
pub fn base_root(&self) -> &H256 { &self.storage_root }
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
pub fn storage_is_clean(&self) -> bool { self.storage_overlay.borrow().iter().find(|&(_, &(f, _))| f == Filth::Dirty).is_none() }
/// return the storage root associated with this account or None if it has been altered via the overlay.
pub fn storage_root(&self) -> Option<&H256> { if self.storage_overlay.borrow().is_empty() {Some(&self.storage_root)} else {None} }
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
/// rturn the storage overlay.
pub fn storage_overlay(&self) -> Ref<HashMap<H256, H256>> { self.storage_overlay.borrow() }
/// 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.
pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); }
@ -504,13 +603,15 @@ 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 = TrieDBMut::new(db, &mut self.storage_root);
for (k, v) in self.storage_overlay.borrow().iter() {
// cast key and value to trait type,
// so we can call overloaded `to_bytes` method
t.insert(k, v);
let mut t = SecTrieDBMut::new(db, &mut self.storage_root);
for (k, &mut (ref mut f, ref mut v)) in self.storage_overlay.borrow_mut().iter_mut() {
if f == &Filth::Dirty {
// cast key and value to trait type,
// 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.
@ -567,7 +668,7 @@ fn storage_at() {
};
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(0x01u64))), H256::new());
}
@ -597,7 +698,7 @@ fn commit_storage() {
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
assert_eq!(a.storage_root(), None);
a.commit_storage(&mut db);
assert_eq!(a.storage_root().unwrap().hex(), "3541f181d6dad5c504371884684d08c29a8bad04926f8ceddf5e279dbc3cc769");
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
}
#[test]

View File

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

View File

@ -74,7 +74,7 @@ trait Memory {
/// Resize (shrink or expand) the memory to specified size (fills 0)
fn resize(&mut self, new_size: usize);
/// Resize the memory only if its smaller
fn expand(&mut self, new_size: U256);
fn expand(&mut self, new_size: usize);
/// Write single byte to memory
fn write_byte(&mut self, offset: U256, value: U256);
/// Write a word to memory. Does not resize memory!
@ -141,8 +141,7 @@ impl Memory for Vec<u8> {
self.resize(new_size, 0);
}
fn expand(&mut self, new_size: U256) {
let size = new_size.low_u64() as usize;
fn expand(&mut self, size: usize) {
if size > self.len() {
Memory::resize(self, size)
}
@ -203,7 +202,7 @@ impl evm::Evm for Interpreter {
try!(self.verify_gas(&current_gas, &gas_cost));
current_gas = current_gas - gas_cost;
println!("Gas cost: {} (left: {})", gas_cost, current_gas);
println!("Executing: {} ", instructions::get_info(instruction).name);
println!("Executing: {} ({:x})", instructions::get_info(instruction).name, instruction);
// Execute instruction
let result = try!(self.exec_instruction(
current_gas, params, ext, instruction, &mut reader, &mut mem, &mut stack
@ -346,36 +345,37 @@ impl Interpreter {
Ok(gas)
},
InstructionCost::GasMem(gas, mem_size) => {
let mem_gas = self.mem_gas_cost(schedule, mem.size(), &mem_size);
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
// Expand after calculating the cost
mem.expand(mem_size);
mem.expand(new_mem_size);
Ok(gas + mem_gas)
},
InstructionCost::GasMemCopy(gas, mem_size, copy) => {
let mem_gas = self.mem_gas_cost(schedule, mem.size(), &mem_size);
let (mem_gas, new_mem_size) = self.mem_gas_cost(schedule, mem.size(), &mem_size);
let copy_gas = U256::from(schedule.copy_gas) * (add_u256_usize(&copy, 31) / U256::from(32));
// Expand after calculating the cost
mem.expand(mem_size);
mem.expand(new_mem_size);
Ok(gas + copy_gas + mem_gas)
}
}
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> U256 {
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> (U256, usize) {
let gas_for_mem = |mem_size: usize| {
let s = mem_size / 32;
schedule.memory_gas * s + s * s / schedule.quad_coeff_div
};
let mem_size_rounded = add_u256_usize(mem_size, 31).low_u64() as usize / 32 * 32;
let mem_gas = gas_for_mem(mem_size_rounded);
let req_mem_size = mem_size.low_u64() as usize;
let req_mem_size_rounded = (req_mem_size + 31) / 32 * 32;
let new_mem_gas = gas_for_mem(req_mem_size_rounded);
let current_mem_gas = gas_for_mem(current_mem_size);
U256::from(if mem_gas > current_mem_gas {
mem_gas - current_mem_gas
(if req_mem_size_rounded > current_mem_size {
U256::from(new_mem_gas - current_mem_gas)
} else {
0
})
U256::zero()
}, req_mem_size_rounded)
}
@ -582,9 +582,6 @@ impl Interpreter {
instructions::EXTCODECOPY => {
let address = u256_to_address(&stack.pop_back());
let code = ext.extcode(&address);
for b in &code {
println!("Code: {:x}", b);
}
self.copy_data_to_memory(mem, stack, &code);
},
instructions::GASPRICE => {
@ -820,6 +817,7 @@ fn address_to_u256(value: Address) -> U256 {
mod tests {
use common::*;
use super::*;
use evm;
#[test]
fn test_find_jump_destinations() {
@ -834,6 +832,22 @@ mod tests {
assert!(valid_jump_destinations.contains(&66));
}
#[test]
fn test_calculate_mem_cost() {
// given
let interpreter = Interpreter;
let schedule = evm::Schedule::default();
let current_mem_size = 0;
let mem_size = U256::from(5);
// when
let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size);
// then
assert_eq!(mem_cost, U256::from(3));
assert_eq!(mem_size, 32);
}
#[test]
fn test_memory_read_and_write() {
// given

View File

@ -106,6 +106,8 @@ impl<'a> Executive<'a> {
let sender = try!(t.sender());
let nonce = self.state.nonce(&sender);
// TODO: error on base gas required
// validate transaction nonce
if t.nonce != nonce {
return Err(From::from(ExecutionError::InvalidNonce { expected: nonce, is: t.nonce }));
@ -137,13 +139,16 @@ impl<'a> Executive<'a> {
let mut substate = Substate::new();
let backup = self.state.clone();
let schedule = self.engine.schedule(self.info);
let init_gas = t.gas - U256::from(t.gas_required(&schedule));
let res = match t.action() {
&Action::Create => {
let params = ActionParams {
address: contract_address(&sender, &nonce),
sender: sender.clone(),
origin: sender.clone(),
gas: t.gas,
gas: init_gas,
gas_price: t.gas_price,
value: t.value,
code: t.data.clone(),
@ -156,7 +161,7 @@ impl<'a> Executive<'a> {
address: address.clone(),
sender: sender.clone(),
origin: sender.clone(),
gas: t.gas,
gas: init_gas,
gas_price: t.gas_price,
value: t.value,
code: self.state.code(address).unwrap_or(vec![]),
@ -421,7 +426,7 @@ impl<'a> Ext for Externalities<'a> {
};
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.factory, self.depth);
ex.call(&params, self.substate, BytesRef::Fixed(output))
ex.call(&params, self.substate, BytesRef::Fixed(output)).map(|gas_left| gas + gas_left)
}
fn extcode(&self, address: &Address) -> Vec<u8> {

View File

@ -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<H256>,
@ -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<H256>, 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)]

View File

@ -183,8 +183,8 @@ impl State {
}
/// Populate the state from `accounts`.
pub fn populate_from(&mut self, accounts: BTreeMap<Address, PodAccount>) {
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)));
}
}
@ -201,14 +201,14 @@ impl 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.
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.

View File

@ -1,33 +1,8 @@
use super::test_common::*;
use state::*;
use executive::*;
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> {
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
let mut failed = Vec::new();
@ -42,29 +17,44 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
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);
//println!("Transaction: {:?}", t);
//println!("Env: {:?}", env);
{
let mut s = State::new_temp();
s.populate_from(post.clone());
s.commit();
assert_eq!(&post_state_root, s.root());
}
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!("DIFF:\n{}", pod_map_diff(&post, &our_post));
if fail_unless(&r.state_root == &post_state_root) {
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, r.state_root, post_state_root);
let our_post = s.to_pod();
println!("Got:\n{}", our_post);
println!("Expect:\n{}", post);
println!("Diff ---expect -> +++got:\n{}", pod_state_diff(&post, &our_post));
}
// TODO: Compare logs.
}
for f in failed.iter() {
println!("FAILED: {:?}", f);
if fail_unless(logs == r.logs) {
println!("!!! {}: Logs mismatch:", name);
println!("Got:\n{:?}", r.logs);
println!("Expect:\n{:?}", logs);
}
// TODO: Add extra APIs for output
//if fail_unless(out == r.)
}
println!("!!! {:?} tests from failed.", failed.len());
failed
}
declare_test!{StateTests_stExample, "StateTests/stExample"}
declare_test!{StateTests_stLogTests, "StateTests/stLogTests"}