Merge pull request #853 from ethcore/json_tx_refactor
refactored loading transaction json tests
This commit is contained in:
commit
3e09f99845
@ -16,75 +16,54 @@
|
|||||||
|
|
||||||
use super::test_common::*;
|
use super::test_common::*;
|
||||||
use evm;
|
use evm;
|
||||||
|
use ethjson;
|
||||||
|
|
||||||
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 tests = ethjson::transaction::Test::load(json_data).unwrap();
|
||||||
let mut failed = Vec::new();
|
let mut failed = Vec::new();
|
||||||
let old_schedule = evm::Schedule::new_frontier();
|
let old_schedule = evm::Schedule::new_frontier();
|
||||||
let new_schedule = evm::Schedule::new_homestead();
|
let new_schedule = evm::Schedule::new_homestead();
|
||||||
let ot = RefCell::new(None);
|
for (name, test) in tests.into_iter() {
|
||||||
for (name, test) in json.as_object().unwrap() {
|
|
||||||
let mut fail = false;
|
let mut fail = false;
|
||||||
let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.clone()); println!("Transaction: {:?}", ot.borrow()); fail = true };
|
let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.clone()); println!("Transaction failed: {:?}", name); fail = true };
|
||||||
let schedule = match test.find("blocknumber")
|
|
||||||
.and_then(|j| j.as_string())
|
let number: Option<u64> = test.block_number.map(Into::into);
|
||||||
.and_then(|s| BlockNumber::from_str(s).ok())
|
let schedule = match number {
|
||||||
.unwrap_or(0) { x if x < 1_000_000 => &old_schedule, _ => &new_schedule };
|
None => &old_schedule,
|
||||||
let rlp = Bytes::from_json(&test["rlp"]);
|
Some(x) if x < 1_150_000 => &old_schedule,
|
||||||
let res = UntrustedRlp::new(&rlp).as_val().map_err(From::from).and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call));
|
Some(_) => &new_schedule
|
||||||
fail_unless(test.find("transaction").is_none() == res.is_err());
|
};
|
||||||
if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) {
|
|
||||||
|
let rlp: Vec<u8> = test.rlp.into();
|
||||||
|
let res = UntrustedRlp::new(&rlp)
|
||||||
|
.as_val()
|
||||||
|
.map_err(From::from)
|
||||||
|
.and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call));
|
||||||
|
|
||||||
|
fail_unless(test.transaction.is_none() == res.is_err());
|
||||||
|
if let (Some(tx), Some(sender)) = (test.transaction, test.sender) {
|
||||||
let t = res.unwrap();
|
let t = res.unwrap();
|
||||||
fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender)));
|
fail_unless(t.sender().unwrap() == sender.into());
|
||||||
fail_unless(t.data == Bytes::from_json(&tx["data"]));
|
let data: Vec<u8> = tx.data.into();
|
||||||
fail_unless(t.gas == xjson!(&tx["gasLimit"]));
|
fail_unless(t.data == data);
|
||||||
fail_unless(t.gas_price == xjson!(&tx["gasPrice"]));
|
fail_unless(t.gas_price == tx.gas_price.into());
|
||||||
fail_unless(t.nonce == xjson!(&tx["nonce"]));
|
fail_unless(t.nonce == tx.nonce.into());
|
||||||
fail_unless(t.value == xjson!(&tx["value"]));
|
fail_unless(t.value == tx.value.into());
|
||||||
if let Action::Call(ref to) = t.action {
|
let to: Option<_> = tx.to.into();
|
||||||
*ot.borrow_mut() = Some(t.clone());
|
let to: Option<Address> = to.map(Into::into);
|
||||||
fail_unless(to == &xjson!(&tx["to"]));
|
match t.action {
|
||||||
} else {
|
Action::Call(dest) => fail_unless(Some(dest) == to),
|
||||||
*ot.borrow_mut() = Some(t.clone());
|
Action::Create => fail_unless(None == to),
|
||||||
fail_unless(Bytes::from_json(&tx["to"]).is_empty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for f in &failed {
|
for f in &failed {
|
||||||
println!("FAILED: {:?}", f);
|
println!("FAILED: {:?}", f);
|
||||||
}
|
}
|
||||||
failed
|
failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once we have interpolate idents.
|
|
||||||
/*macro_rules! declare_test {
|
|
||||||
($test_set_name: ident / $name: ident) => {
|
|
||||||
#[test]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn $name() {
|
|
||||||
assert!(do_json_test(include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($name), ".json"))).len() == 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($test_set_name: ident / $prename: ident / $name: ident) => {
|
|
||||||
#[test]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
interpolate_idents! { fn [$prename _ $name]()
|
|
||||||
{
|
|
||||||
let json = include_bytes!(concat!("../res/ethereum/tests/", stringify!($test_set_name), "/", stringify!($prename), "/", stringify!($name), ".json"));
|
|
||||||
assert!(do_json_test(json).len() == 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_test!{TransactionTests/ttTransactionTest}
|
|
||||||
declare_test!{TransactionTests/tt10mbDataField}
|
|
||||||
declare_test!{TransactionTests/ttWrongRLPTransaction}
|
|
||||||
declare_test!{TransactionTests/Homestead/ttTransactionTest}
|
|
||||||
declare_test!{heavy => TransactionTests/Homestead/tt10mbDataField}
|
|
||||||
declare_test!{TransactionTests/Homestead/ttWrongRLPTransaction}
|
|
||||||
declare_test!{TransactionTests/RandomTests/tr201506052141PYTHON}*/
|
|
||||||
|
|
||||||
declare_test!{TransactionTests_ttTransactionTest, "TransactionTests/ttTransactionTest"}
|
declare_test!{TransactionTests_ttTransactionTest, "TransactionTests/ttTransactionTest"}
|
||||||
declare_test!{heavy => TransactionTests_tt10mbDataField, "TransactionTests/tt10mbDataField"}
|
declare_test!{heavy => TransactionTests_tt10mbDataField, "TransactionTests/tt10mbDataField"}
|
||||||
declare_test!{TransactionTests_ttWrongRLPTransaction, "TransactionTests/ttWrongRLPTransaction"}
|
declare_test!{TransactionTests_ttWrongRLPTransaction, "TransactionTests/ttWrongRLPTransaction"}
|
||||||
|
@ -97,33 +97,26 @@ impl From<ethjson::state::Transaction> for SignedTransaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromJson for SignedTransaction {
|
impl From<ethjson::transaction::Transaction> for SignedTransaction {
|
||||||
#[cfg_attr(feature="dev", allow(single_char_pattern))]
|
fn from(t: ethjson::transaction::Transaction) -> Self {
|
||||||
fn from_json(json: &Json) -> SignedTransaction {
|
let to: Option<_> = t.to.into();
|
||||||
let t = Transaction {
|
SignedTransaction {
|
||||||
nonce: xjson!(&json["nonce"]),
|
unsigned: Transaction {
|
||||||
gas_price: xjson!(&json["gasPrice"]),
|
nonce: t.nonce.into(),
|
||||||
gas: xjson!(&json["gasLimit"]),
|
gas_price: t.gas_price.into(),
|
||||||
action: match Bytes::from_json(&json["to"]) {
|
gas: t.gas_limit.into(),
|
||||||
ref x if x.is_empty() => Action::Create,
|
action: match to {
|
||||||
ref x => Action::Call(Address::from_slice(x)),
|
Some(to) => Action::Call(to.into()),
|
||||||
|
None => Action::Create
|
||||||
|
},
|
||||||
|
value: t.value.into(),
|
||||||
|
data: t.data.into(),
|
||||||
},
|
},
|
||||||
value: xjson!(&json["value"]),
|
r: t.r.into(),
|
||||||
data: xjson!(&json["data"]),
|
s: t.s.into(),
|
||||||
};
|
v: t.v.into(),
|
||||||
match json.find("secretKey") {
|
sender: Cell::new(None),
|
||||||
Some(&Json::String(ref secret_key)) => t.sign(&h256_from_hex(clean(secret_key))),
|
hash: Cell::new(None)
|
||||||
_ => SignedTransaction {
|
|
||||||
unsigned: t,
|
|
||||||
v: match json.find("v") { Some(ref j) => u16::from_json(j) as u8, None => 0 },
|
|
||||||
r: match json.find("r") { Some(j) => xjson!(j), None => x!(0) },
|
|
||||||
s: match json.find("s") { Some(j) => xjson!(j), None => x!(0) },
|
|
||||||
hash: Cell::new(None),
|
|
||||||
sender: match json.find("sender") {
|
|
||||||
Some(&Json::String(ref sender)) => Cell::new(Some(address_from_hex(clean(sender)))),
|
|
||||||
_ => Cell::new(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,10 @@ impl Visitor for BytesVisitor {
|
|||||||
let v = match value.len() {
|
let v = match value.len() {
|
||||||
0 => vec![],
|
0 => vec![],
|
||||||
2 if value.starts_with("0x") => vec![],
|
2 if value.starts_with("0x") => vec![],
|
||||||
|
_ if value.starts_with("0x") && value.len() % 2 == 1 => {
|
||||||
|
let v = "0".to_owned() + &value[2..];
|
||||||
|
FromHex::from_hex(v.as_ref() as &str).unwrap_or(vec![]),
|
||||||
|
},
|
||||||
_ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]),
|
_ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]),
|
||||||
_ => FromHex::from_hex(value).unwrap_or(vec![]),
|
_ => FromHex::from_hex(value).unwrap_or(vec![]),
|
||||||
};
|
};
|
||||||
@ -64,13 +68,14 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bytes_deserialization() {
|
fn bytes_deserialization() {
|
||||||
let s = r#"["", "0x", "0x12", "1234"]"#;
|
let s = r#"["", "0x", "0x12", "1234", "0x001"]"#;
|
||||||
let deserialized: Vec<Bytes> = serde_json::from_str(s).unwrap();
|
let deserialized: Vec<Bytes> = serde_json::from_str(s).unwrap();
|
||||||
assert_eq!(deserialized, vec![
|
assert_eq!(deserialized, vec![
|
||||||
Bytes(vec![]),
|
Bytes(vec![]),
|
||||||
Bytes(vec![]),
|
Bytes(vec![]),
|
||||||
Bytes(vec![0x12]),
|
Bytes(vec![0x12]),
|
||||||
Bytes(vec![0x12, 0x34])
|
Bytes(vec![0x12, 0x34]),
|
||||||
|
Bytes(vec![0, 1])
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,3 +27,4 @@ pub mod spec;
|
|||||||
pub mod vm;
|
pub mod vm;
|
||||||
pub mod maybe;
|
pub mod maybe;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
pub mod transaction;
|
||||||
|
25
json/src/transaction/mod.rs
Normal file
25
json/src/transaction/mod.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Transaction test deserialization.
|
||||||
|
|
||||||
|
mod transaction;
|
||||||
|
mod txtest;
|
||||||
|
mod test;
|
||||||
|
|
||||||
|
pub use self::transaction::Transaction;
|
||||||
|
pub use self::txtest::TransactionTest;
|
||||||
|
pub use self::test::Test;
|
43
json/src/transaction/test.rs
Normal file
43
json/src/transaction/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/>.
|
||||||
|
|
||||||
|
//! TransactionTest test deserializer.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::io::Read;
|
||||||
|
use serde_json;
|
||||||
|
use serde_json::Error;
|
||||||
|
use transaction::TransactionTest;
|
||||||
|
|
||||||
|
/// TransactionTest test deserializer.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Test(BTreeMap<String, TransactionTest>);
|
||||||
|
|
||||||
|
impl IntoIterator for Test {
|
||||||
|
type Item = <BTreeMap<String, TransactionTest> as IntoIterator>::Item;
|
||||||
|
type IntoIter = <BTreeMap<String, TransactionTest> 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)
|
||||||
|
}
|
||||||
|
}
|
70
json/src/transaction/transaction.rs
Normal file
70
json/src/transaction/transaction.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Transaction test transaction deserialization.
|
||||||
|
|
||||||
|
use uint::Uint;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use hash::Address;
|
||||||
|
use maybe::MaybeEmpty;
|
||||||
|
|
||||||
|
/// Transaction test transaction deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct Transaction {
|
||||||
|
/// Transaction data.
|
||||||
|
pub data: Bytes,
|
||||||
|
/// Gas limit.
|
||||||
|
#[serde(rename="gasLimit")]
|
||||||
|
pub gas_limit: Uint,
|
||||||
|
/// Gas price.
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Uint,
|
||||||
|
/// Nonce.
|
||||||
|
pub nonce: Uint,
|
||||||
|
/// To.
|
||||||
|
pub to: MaybeEmpty<Address>,
|
||||||
|
/// Value.
|
||||||
|
pub value: Uint,
|
||||||
|
/// R.
|
||||||
|
pub r: Uint,
|
||||||
|
/// S.
|
||||||
|
pub s: Uint,
|
||||||
|
/// V.
|
||||||
|
pub v: Uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use transaction::Transaction;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"data" : "0x",
|
||||||
|
"gasLimit" : "0xf388",
|
||||||
|
"gasPrice" : "0x09184e72a000",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"r" : "0x2c",
|
||||||
|
"s" : "0x04",
|
||||||
|
"to" : "",
|
||||||
|
"v" : "0x1b",
|
||||||
|
"value" : "0x00"
|
||||||
|
}"#;
|
||||||
|
let _deserialized: Transaction = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
64
json/src/transaction/txtest.rs
Normal file
64
json/src/transaction/txtest.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/>.
|
||||||
|
|
||||||
|
//! Transaction test deserialization.
|
||||||
|
|
||||||
|
use uint::Uint;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use hash::Address;
|
||||||
|
use transaction::Transaction;
|
||||||
|
|
||||||
|
/// Transaction test deserialization.
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct TransactionTest {
|
||||||
|
/// Block number.
|
||||||
|
#[serde(rename="blocknumber")]
|
||||||
|
pub block_number: Option<Uint>,
|
||||||
|
/// Transaction rlp.
|
||||||
|
pub rlp: Bytes,
|
||||||
|
/// Transaction sender.
|
||||||
|
pub sender: Option<Address>,
|
||||||
|
/// Transaction
|
||||||
|
pub transaction: Option<Transaction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use transaction::TransactionTest;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_deserialization() {
|
||||||
|
let s = r#"{
|
||||||
|
"blocknumber" : "0",
|
||||||
|
"rlp" : "0xf83f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870b801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a3664935301",
|
||||||
|
"sender" : "e115cf6bb5656786569dd273705242ca72d84bc0",
|
||||||
|
"transaction" : {
|
||||||
|
"data" : "",
|
||||||
|
"gasLimit" : "0x5208",
|
||||||
|
"gasPrice" : "0x01",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353",
|
||||||
|
"s" : "0x01",
|
||||||
|
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
|
||||||
|
"v" : "0x1b",
|
||||||
|
"value" : "0x0b"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let _deserialized: TransactionTest = serde_json::from_str(s).unwrap();
|
||||||
|
// TODO: validate all fields
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,12 @@ impl Into<u64> for Uint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for Uint {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
<Uint as Into<u64>>::into(self) as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Deserialize for Uint {
|
impl Deserialize for Uint {
|
||||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||||
where D: Deserializer {
|
where D: Deserializer {
|
||||||
|
Loading…
Reference in New Issue
Block a user