Merge pull request #803 from ethcore/executive_tests
refactored loading of execution tests
This commit is contained in:
commit
100e6fa88f
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
//! Evm input params.
|
//! Evm input params.
|
||||||
use common::*;
|
use common::*;
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
/// Transaction value
|
/// Transaction value
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -67,3 +68,19 @@ impl Default for ActionParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ethjson::vm::Transaction> for ActionParams {
|
||||||
|
fn from(t: ethjson::vm::Transaction) -> Self {
|
||||||
|
ActionParams {
|
||||||
|
code_address: Address::new(),
|
||||||
|
address: t.address.into(),
|
||||||
|
sender: t.sender.into(),
|
||||||
|
origin: t.origin.into(),
|
||||||
|
code: Some(t.code.into()),
|
||||||
|
data: Some(t.data.into()),
|
||||||
|
gas: t.gas.into(),
|
||||||
|
gas_price: t.gas_price.into(),
|
||||||
|
value: ActionValue::Transfer(t.value.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
/// Simple vector of hashes, should be at most 256 items large, can be smaller if being used
|
/// Simple vector of hashes, should be at most 256 items large, can be smaller if being used
|
||||||
/// for a block whose number is less than 257.
|
/// for a block whose number is less than 257.
|
||||||
@ -69,6 +70,21 @@ impl FromJson for EnvInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ethjson::vm::Env> for EnvInfo {
|
||||||
|
fn from(e: ethjson::vm::Env) -> Self {
|
||||||
|
let number = e.number.into();
|
||||||
|
EnvInfo {
|
||||||
|
number: number,
|
||||||
|
author: e.author.into(),
|
||||||
|
difficulty: e.difficulty.into(),
|
||||||
|
gas_limit: e.gas_limit.into(),
|
||||||
|
timestamp: e.timestamp.into(),
|
||||||
|
last_hashes: (1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect(),
|
||||||
|
gas_used: U256::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
@ -28,7 +28,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
|||||||
let tests = ethjson::blockchain::Test::load(json_data).unwrap();
|
let tests = ethjson::blockchain::Test::load(json_data).unwrap();
|
||||||
let mut failed = Vec::new();
|
let mut failed = Vec::new();
|
||||||
|
|
||||||
for (name, blockchain) in tests.deref() {
|
for (name, blockchain) in tests.into_iter() {
|
||||||
let mut fail = false;
|
let mut fail = false;
|
||||||
{
|
{
|
||||||
let mut fail_unless = |cond: bool| if !cond && !fail {
|
let mut fail_unless = |cond: bool| if !cond && !fail {
|
||||||
@ -61,7 +61,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
|||||||
client.import_verified_blocks(&IoChannel::disconnected());
|
client.import_verified_blocks(&IoChannel::disconnected());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fail_unless(client.chain_info().best_block_hash == blockchain.best_block.clone().into());
|
fail_unless(client.chain_info().best_block_hash == blockchain.best_block.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ use ethereum;
|
|||||||
use externalities::*;
|
use externalities::*;
|
||||||
use substate::*;
|
use substate::*;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
struct TestEngineFrontier {
|
struct TestEngineFrontier {
|
||||||
vm_factory: Factory,
|
vm_factory: Factory,
|
||||||
@ -53,6 +54,7 @@ impl Engine for TestEngineFrontier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
struct CallCreate {
|
struct CallCreate {
|
||||||
data: Bytes,
|
data: Bytes,
|
||||||
destination: Option<Address>,
|
destination: Option<Address>,
|
||||||
@ -60,6 +62,18 @@ struct CallCreate {
|
|||||||
value: U256
|
value: U256
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ethjson::vm::Call> for CallCreate {
|
||||||
|
fn from(c: ethjson::vm::Call) -> Self {
|
||||||
|
let dst: Option<_> = c.destination.into();
|
||||||
|
CallCreate {
|
||||||
|
data: c.data.into(),
|
||||||
|
destination: dst.map(Into::into),
|
||||||
|
gas_limit: c.gas_limit.into(),
|
||||||
|
value: c.value.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tiny wrapper around executive externalities.
|
/// Tiny wrapper around executive externalities.
|
||||||
/// Stores callcreates.
|
/// Stores callcreates.
|
||||||
struct TestExt<'a> {
|
struct TestExt<'a> {
|
||||||
@ -174,58 +188,26 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
|
fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec<String> {
|
||||||
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
|
let tests = ethjson::vm::Test::load(json_data).unwrap();
|
||||||
let mut failed = Vec::new();
|
let mut failed = Vec::new();
|
||||||
for (name, test) in json.as_object().unwrap() {
|
|
||||||
|
for (name, vm) in tests.into_iter() {
|
||||||
println!("name: {:?}", name);
|
println!("name: {:?}", name);
|
||||||
// sync io is usefull when something crashes in jit
|
|
||||||
// ::std::io::stdout().write(&name.as_bytes());
|
|
||||||
// ::std::io::stdout().write(b"\n");
|
|
||||||
// ::std::io::stdout().flush();
|
|
||||||
let mut fail = false;
|
let mut fail = false;
|
||||||
//let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true };
|
|
||||||
let mut fail_unless = |cond: bool, s: &str | if !cond && !fail {
|
let mut fail_unless = |cond: bool, s: &str | if !cond && !fail {
|
||||||
failed.push(format!("[{}] {}: {}", vm, name, s));
|
failed.push(format!("[{}] {}: {}", vm_type, name, s));
|
||||||
fail = true
|
fail = true
|
||||||
};
|
};
|
||||||
|
|
||||||
// test env
|
let out_of_gas = vm.out_of_gas();
|
||||||
let mut state_result = get_temp_state();
|
let mut state_result = get_temp_state();
|
||||||
let mut state = state_result.reference_mut();
|
let mut state = state_result.reference_mut();
|
||||||
|
state.populate_from(From::from(vm.pre_state.clone()));
|
||||||
test.find("pre").map(|pre| for (addr, s) in pre.as_object().unwrap() {
|
let info = From::from(vm.env);
|
||||||
let address = Address::from(addr.as_ref());
|
let engine = TestEngineFrontier::new(1, vm_type.clone());
|
||||||
let balance = xjson!(&s["balance"]);
|
let params = ActionParams::from(vm.transaction);
|
||||||
let code = xjson!(&s["code"]);
|
|
||||||
let _nonce: U256 = xjson!(&s["nonce"]);
|
|
||||||
|
|
||||||
state.new_contract(&address, balance);
|
|
||||||
state.init_code(&address, code);
|
|
||||||
BTreeMap::from_json(&s["storage"]).into_iter().foreach(|(k, v)| state.set_storage(&address, k, v));
|
|
||||||
});
|
|
||||||
|
|
||||||
let info = test.find("env").map(|env| {
|
|
||||||
EnvInfo::from_json(env)
|
|
||||||
}).unwrap_or_default();
|
|
||||||
|
|
||||||
let engine = TestEngineFrontier::new(1, vm.clone());
|
|
||||||
|
|
||||||
// params
|
|
||||||
let mut params = ActionParams::default();
|
|
||||||
test.find("exec").map(|exec| {
|
|
||||||
params.address = xjson!(&exec["address"]);
|
|
||||||
params.sender = xjson!(&exec["caller"]);
|
|
||||||
params.origin = xjson!(&exec["origin"]);
|
|
||||||
params.code = xjson!(&exec["code"]);
|
|
||||||
params.data = xjson!(&exec["data"]);
|
|
||||||
params.gas = xjson!(&exec["gas"]);
|
|
||||||
params.gas_price = xjson!(&exec["gasPrice"]);
|
|
||||||
params.value = ActionValue::Transfer(xjson!(&exec["value"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
let out_of_gas = test.find("callcreates").map(|_calls| {
|
|
||||||
}).is_none();
|
|
||||||
|
|
||||||
let mut substate = Substate::new(false);
|
let mut substate = Substate::new(false);
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
@ -247,44 +229,37 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
|
|||||||
(res, ex.callcreates)
|
(res, ex.callcreates)
|
||||||
};
|
};
|
||||||
|
|
||||||
// then validate
|
|
||||||
match res {
|
match res {
|
||||||
Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
|
Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
|
||||||
Ok(gas_left) => {
|
Ok(gas_left) => {
|
||||||
// println!("name: {}, gas_left : {:?}", name, gas_left);
|
|
||||||
fail_unless(!out_of_gas, "expected to run out of gas.");
|
fail_unless(!out_of_gas, "expected to run out of gas.");
|
||||||
fail_unless(gas_left == xjson!(&test["gas"]), "gas_left is incorrect");
|
fail_unless(Some(gas_left) == vm.gas_left.map(Into::into), "gas_left is incorrect");
|
||||||
fail_unless(output == Bytes::from_json(&test["out"]), "output is incorrect");
|
let vm_output: Option<Vec<u8>> = vm.output.map(Into::into);
|
||||||
|
fail_unless(Some(output) == vm_output, "output is incorrect");
|
||||||
|
|
||||||
test.find("post").map(|pre| for (addr, s) in pre.as_object().unwrap() {
|
for (address, account) in vm.post_state.unwrap().into_iter() {
|
||||||
let address = Address::from(addr.as_ref());
|
let address = address.into();
|
||||||
|
let code: Vec<u8> = account.code.into();
|
||||||
fail_unless(state.code(&address).unwrap_or_else(|| vec![]) == Bytes::from_json(&s["code"]), "code is incorrect");
|
fail_unless(state.code(&address).unwrap_or_else(Vec::new) == code, "code is incorrect");
|
||||||
fail_unless(state.balance(&address) == xjson!(&s["balance"]), "balance is incorrect");
|
fail_unless(state.balance(&address) == account.balance.into(), "balance is incorrect");
|
||||||
fail_unless(state.nonce(&address) == xjson!(&s["nonce"]), "nonce is incorrect");
|
fail_unless(state.nonce(&address) == account.nonce.into(), "nonce is incorrect");
|
||||||
BTreeMap::from_json(&s["storage"]).iter().foreach(|(k, v)| fail_unless(&state.storage_at(&address, &k) == v, "storage is incorrect"));
|
account.storage.into_iter().foreach(|(k, v)| {
|
||||||
|
let key: U256 = k.into();
|
||||||
|
let value: U256 = v.into();
|
||||||
|
fail_unless(state.storage_at(&address, &From::from(key)) == From::from(value), "storage is incorrect");
|
||||||
});
|
});
|
||||||
|
|
||||||
let cc = test["callcreates"].as_array().unwrap();
|
|
||||||
fail_unless(callcreates.len() == cc.len(), "callcreates does not match");
|
|
||||||
for i in 0..cc.len() {
|
|
||||||
let callcreate = &callcreates[i];
|
|
||||||
let expected = &cc[i];
|
|
||||||
fail_unless(callcreate.data == Bytes::from_json(&expected["data"]), "callcreates data is incorrect");
|
|
||||||
fail_unless(callcreate.destination == xjson!(&expected["destination"]), "callcreates destination is incorrect");
|
|
||||||
fail_unless(callcreate.value == xjson!(&expected["value"]), "callcreates value is incorrect");
|
|
||||||
fail_unless(callcreate.gas_limit == xjson!(&expected["gasLimit"]), "callcreates gas_limit is incorrect");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let calls: Option<Vec<CallCreate>> = vm.calls.map(|c| c.into_iter().map(From::from).collect());
|
||||||
|
fail_unless(Some(callcreates) == calls, "callcreates does not match");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for f in &failed {
|
for f in &failed {
|
||||||
println!("FAILED: {:?}", f);
|
println!("FAILED: {:?}", f);
|
||||||
}
|
}
|
||||||
|
|
||||||
//assert!(false);
|
|
||||||
failed
|
failed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ use account::*;
|
|||||||
use account_db::*;
|
use account_db::*;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
|
||||||
#[derive(Debug,Clone,PartialEq,Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
/// An account, expressed as Plain-Old-Data (hence the name).
|
/// An account, expressed as Plain-Old-Data (hence the name).
|
||||||
/// Does not have a DB overlay cache, code hash or anything like that.
|
/// Does not have a DB overlay cache, code hash or anything like that.
|
||||||
pub struct PodAccount {
|
pub struct PodAccount {
|
||||||
|
@ -41,6 +41,14 @@ impl fmt::Display for StateDiff {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for StateDiff {
|
||||||
|
type Target = BTreeMap<Address, AccountDiff>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use common::*;
|
use common::*;
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
//! Blockchain test state deserializer.
|
//! Blockchain test state deserializer.
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::ops::Deref;
|
|
||||||
use hash::Address;
|
use hash::Address;
|
||||||
use blockchain::account::Account;
|
use blockchain::account::Account;
|
||||||
|
|
||||||
@ -25,10 +24,11 @@ use blockchain::account::Account;
|
|||||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||||
pub struct State(pub BTreeMap<Address, Account>);
|
pub struct State(pub BTreeMap<Address, Account>);
|
||||||
|
|
||||||
impl Deref for State {
|
impl IntoIterator for State {
|
||||||
type Target = BTreeMap<Address, Account>;
|
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;
|
||||||
|
type IntoIter = <BTreeMap<Address, Account> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
&self.0
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
//! Blockchain test deserializer.
|
//! Blockchain test deserializer.
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use serde_json::Error;
|
use serde_json::Error;
|
||||||
@ -27,11 +26,12 @@ use blockchain::blockchain::BlockChain;
|
|||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
pub struct Test(BTreeMap<String, BlockChain>);
|
pub struct Test(BTreeMap<String, BlockChain>);
|
||||||
|
|
||||||
impl Deref for Test {
|
impl IntoIterator for Test {
|
||||||
type Target = BTreeMap<String, BlockChain>;
|
type Item = <BTreeMap<String, BlockChain> as IntoIterator>::Item;
|
||||||
|
type IntoIter = <BTreeMap<String, BlockChain> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
&self.0
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ macro_rules! impl_hash {
|
|||||||
($name: ident, $inner: ident) => {
|
($name: ident, $inner: ident) => {
|
||||||
/// Lenient hash json deserialization for test json files.
|
/// Lenient hash json deserialization for test json files.
|
||||||
#[derive(Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
|
#[derive(Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
|
||||||
pub struct $name($inner);
|
pub struct $name(pub $inner);
|
||||||
|
|
||||||
impl Into<$inner> for $name {
|
impl Into<$inner> for $name {
|
||||||
fn into(self) -> $inner {
|
fn into(self) -> $inner {
|
||||||
|
@ -24,3 +24,5 @@ pub mod uint;
|
|||||||
pub mod bytes;
|
pub mod bytes;
|
||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
pub mod spec;
|
pub mod spec;
|
||||||
|
pub mod vm;
|
||||||
|
pub mod maybe;
|
||||||
|
83
json/src/maybe.rs
Normal file
83
json/src/maybe.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
//! Deserializer of empty string values into optionals.
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use serde::{Deserialize, Deserializer, Error};
|
||||||
|
use serde::de::Visitor;
|
||||||
|
use serde_json::Value;
|
||||||
|
use serde_json::value;
|
||||||
|
|
||||||
|
/// Deserializer of empty string values into optionals.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum MaybeEmpty<T> {
|
||||||
|
/// Some.
|
||||||
|
Some(T),
|
||||||
|
/// None.
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deserialize for MaybeEmpty<T> where T: Deserialize {
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer {
|
||||||
|
deserializer.deserialize(MaybeEmptyVisitor::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MaybeEmptyVisitor<T> {
|
||||||
|
_phantom: PhantomData<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MaybeEmptyVisitor<T> {
|
||||||
|
fn new() -> Self {
|
||||||
|
MaybeEmptyVisitor {
|
||||||
|
_phantom: PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Visitor for MaybeEmptyVisitor<T> where T: Deserialize {
|
||||||
|
type Value = MaybeEmpty<T>;
|
||||||
|
|
||||||
|
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: Error {
|
||||||
|
self.visit_string(value.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_string<E>(&mut self, value: String) -> Result<Self::Value, E> where E: Error {
|
||||||
|
match value.is_empty() {
|
||||||
|
true => Ok(MaybeEmpty::None),
|
||||||
|
false => {
|
||||||
|
let value = Value::String(value);
|
||||||
|
T::deserialize(&mut value::Deserializer::new(value)).map(MaybeEmpty::Some).map_err(|_| Error::custom("failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Into<Option<T>> for MaybeEmpty<T> {
|
||||||
|
fn into(self) -> Option<T> {
|
||||||
|
match self {
|
||||||
|
MaybeEmpty::Some(s) => Some(s),
|
||||||
|
MaybeEmpty::None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
use serde_json;
|
||||||
|
use util::hash;
|
||||||
|
use hash::H256;
|
||||||
|
use maybe::MaybeEmpty;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maybe_deserialization() {
|
||||||
|
let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#;
|
||||||
|
let deserialized: Vec<MaybeEmpty<H256>> = serde_json::from_str(s).unwrap();
|
||||||
|
assert_eq!(deserialized, vec![
|
||||||
|
MaybeEmpty::None,
|
||||||
|
MaybeEmpty::Some(H256(hash::H256::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae").unwrap()))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
json/src/vm/call.rs
Normal file
54
json/src/vm/call.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Vm call deserialization.
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use hash::Address;
|
||||||
|
use uint::Uint;
|
||||||
|
use maybe::MaybeEmpty;
|
||||||
|
|
||||||
|
/// Vm call deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Call {
|
||||||
|
/// Call data.
|
||||||
|
pub data: Bytes,
|
||||||
|
/// Call destination.
|
||||||
|
pub destination: MaybeEmpty<Address>,
|
||||||
|
/// Gas limit.
|
||||||
|
#[serde(rename="gasLimit")]
|
||||||
|
pub gas_limit: Uint,
|
||||||
|
/// Call value.
|
||||||
|
pub value: Uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use vm::Call;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"data" : "0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff",
|
||||||
|
"destination" : "",
|
||||||
|
"gasLimit" : "0x1748766aa5",
|
||||||
|
"value" : "0x00"
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Call = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
58
json/src/vm/env.rs
Normal file
58
json/src/vm/env.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Vm environment.
|
||||||
|
use hash::Address;
|
||||||
|
use uint::Uint;
|
||||||
|
|
||||||
|
/// Vm environment.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Env {
|
||||||
|
/// Address.
|
||||||
|
#[serde(rename="currentCoinbase")]
|
||||||
|
pub author: Address,
|
||||||
|
/// Difficulty
|
||||||
|
#[serde(rename="currentDifficulty")]
|
||||||
|
pub difficulty: Uint,
|
||||||
|
/// Gas limit.
|
||||||
|
#[serde(rename="currentGasLimit")]
|
||||||
|
pub gas_limit: Uint,
|
||||||
|
/// Number.
|
||||||
|
#[serde(rename="currentNumber")]
|
||||||
|
pub number: Uint,
|
||||||
|
/// Timestamp.
|
||||||
|
#[serde(rename="currentTimestamp")]
|
||||||
|
pub timestamp: Uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use vm::Env;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn env_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentDifficulty" : "0x0100",
|
||||||
|
"currentGasLimit" : "0x0f4240",
|
||||||
|
"currentNumber" : "0x00",
|
||||||
|
"currentTimestamp" : "0x01"
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Env = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
54
json/src/vm/log.rs
Normal file
54
json/src/vm/log.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Vm log deserialization.
|
||||||
|
|
||||||
|
use hash::{Address, H256, Bloom};
|
||||||
|
use bytes::Bytes;
|
||||||
|
|
||||||
|
/// Vm log deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Log {
|
||||||
|
/// Log address.
|
||||||
|
pub address: Address,
|
||||||
|
/// Log bloom.
|
||||||
|
pub bloom: Bloom,
|
||||||
|
/// Data.
|
||||||
|
pub data: Bytes,
|
||||||
|
/// Topics.
|
||||||
|
pub topics: Vec<H256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use vm::Log;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn log_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
|
||||||
|
"bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000800000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000",
|
||||||
|
"data" : "0xaabbffffffffffffffffffffffffffffffffffffffffffffffffffffffffccdd",
|
||||||
|
"topics" : [
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Log = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
31
json/src/vm/mod.rs
Normal file
31
json/src/vm/mod.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Vm test loader.
|
||||||
|
|
||||||
|
pub mod env;
|
||||||
|
pub mod transaction;
|
||||||
|
pub mod vm;
|
||||||
|
pub mod log;
|
||||||
|
pub mod call;
|
||||||
|
pub mod test;
|
||||||
|
|
||||||
|
pub use self::env::Env;
|
||||||
|
pub use self::transaction::Transaction;
|
||||||
|
pub use self::vm::Vm;
|
||||||
|
pub use self::log::Log;
|
||||||
|
pub use self::call::Call;
|
||||||
|
pub use self::test::Test;
|
43
json/src/vm/test.rs
Normal file
43
json/src/vm/test.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Vm test deserializer.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::io::Read;
|
||||||
|
use serde_json;
|
||||||
|
use serde_json::Error;
|
||||||
|
use vm::Vm;
|
||||||
|
|
||||||
|
/// Vm test deserializer.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Test(BTreeMap<String, Vm>);
|
||||||
|
|
||||||
|
impl IntoIterator for Test {
|
||||||
|
type Item = <BTreeMap<String, Vm> as IntoIterator>::Item;
|
||||||
|
type IntoIter = <BTreeMap<String, Vm> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Test {
|
||||||
|
/// Loads test from json.
|
||||||
|
pub fn load<R>(reader: R) -> Result<Self, Error> where R: Read {
|
||||||
|
serde_json::from_reader(reader)
|
||||||
|
}
|
||||||
|
}
|
64
json/src/vm/transaction.rs
Normal file
64
json/src/vm/transaction.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Executed transaction.
|
||||||
|
use hash::Address;
|
||||||
|
use uint::Uint;
|
||||||
|
use bytes::Bytes;
|
||||||
|
|
||||||
|
/// Executed transaction.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Transaction {
|
||||||
|
/// Contract address.
|
||||||
|
pub address: Address,
|
||||||
|
/// Transaction sender.
|
||||||
|
#[serde(rename="caller")]
|
||||||
|
pub sender: Address,
|
||||||
|
/// Contract code.
|
||||||
|
pub code: Bytes,
|
||||||
|
/// Input data.
|
||||||
|
pub data: Bytes,
|
||||||
|
/// Gas.
|
||||||
|
pub gas: Uint,
|
||||||
|
/// Gas price.
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Uint,
|
||||||
|
/// Transaction origin.
|
||||||
|
pub origin: Address,
|
||||||
|
/// Sent value.
|
||||||
|
pub value: Uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use vm::Transaction;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
|
||||||
|
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
|
||||||
|
"code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055",
|
||||||
|
"data" : "0x",
|
||||||
|
"gas" : "0x0186a0",
|
||||||
|
"gasPrice" : "0x5af3107a4000",
|
||||||
|
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
|
||||||
|
"value" : "0x0de0b6b3a7640000"
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Transaction = serde_json::from_str(s).unwrap();
|
||||||
|
}
|
||||||
|
}
|
112
json/src/vm/vm.rs
Normal file
112
json/src/vm/vm.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Vm execution env.
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use uint::Uint;
|
||||||
|
use blockchain::State;
|
||||||
|
use vm::{Transaction, Log, Call, Env};
|
||||||
|
|
||||||
|
/// Reporesents vm execution environment before and after exeuction of transaction.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Vm {
|
||||||
|
/// Contract calls made internaly by executed transaction.
|
||||||
|
#[serde(rename="callcreates")]
|
||||||
|
pub calls: Option<Vec<Call>>,
|
||||||
|
/// Env info.
|
||||||
|
pub env: Env,
|
||||||
|
/// Executed transaction
|
||||||
|
#[serde(rename="exec")]
|
||||||
|
pub transaction: Transaction,
|
||||||
|
/// Gas left after transaction execution.
|
||||||
|
#[serde(rename="gas")]
|
||||||
|
pub gas_left: Option<Uint>,
|
||||||
|
/// Logs created during execution of transaction.
|
||||||
|
pub logs: Option<Vec<Log>>,
|
||||||
|
/// Transaction output.
|
||||||
|
#[serde(rename="out")]
|
||||||
|
pub output: Option<Bytes>,
|
||||||
|
/// Post execution vm state.
|
||||||
|
#[serde(rename="post")]
|
||||||
|
pub post_state: Option<State>,
|
||||||
|
/// Pre execution vm state.
|
||||||
|
#[serde(rename="pre")]
|
||||||
|
pub pre_state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vm {
|
||||||
|
/// Returns true if transaction execution run out of gas.
|
||||||
|
pub fn out_of_gas(&self) -> bool {
|
||||||
|
self.calls.is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use vm::Vm;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vm_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"callcreates" : [
|
||||||
|
],
|
||||||
|
"env" : {
|
||||||
|
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentDifficulty" : "0x0100",
|
||||||
|
"currentGasLimit" : "0x0f4240",
|
||||||
|
"currentNumber" : "0x00",
|
||||||
|
"currentTimestamp" : "0x01"
|
||||||
|
},
|
||||||
|
"exec" : {
|
||||||
|
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
|
||||||
|
"caller" : "cd1722f2947def4cf144679da39c4c32bdc35681",
|
||||||
|
"code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055",
|
||||||
|
"data" : "0x",
|
||||||
|
"gas" : "0x0186a0",
|
||||||
|
"gasPrice" : "0x5af3107a4000",
|
||||||
|
"origin" : "cd1722f2947def4cf144679da39c4c32bdc35681",
|
||||||
|
"value" : "0x0de0b6b3a7640000"
|
||||||
|
},
|
||||||
|
"gas" : "0x013874",
|
||||||
|
"logs" : [
|
||||||
|
],
|
||||||
|
"out" : "0x",
|
||||||
|
"post" : {
|
||||||
|
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
|
||||||
|
"balance" : "0x0de0b6b3a7640000",
|
||||||
|
"code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
"0x00" : "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pre" : {
|
||||||
|
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
|
||||||
|
"balance" : "0x0de0b6b3a7640000",
|
||||||
|
"code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Vm = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user