diff --git a/src/lib.rs b/src/lib.rs index 3ead3966d..76ef7f09b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,6 +109,9 @@ pub mod blockchain; pub mod extras; pub mod evm; +#[cfg(test)] +mod tests; + pub mod client; pub mod sync; pub mod block; diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 000000000..b048a1c16 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,4 @@ +#[macro_use] +mod test_common; + +mod transaction; \ No newline at end of file diff --git a/src/tests/test_common.rs b/src/tests/test_common.rs new file mode 100644 index 000000000..8c2f25d8a --- /dev/null +++ b/src/tests/test_common.rs @@ -0,0 +1,62 @@ +pub use common::*; + +pub fn clean(s: &str) -> &str { + if s.len() >= 2 && &s[0..2] == "0x" { + &s[2..] + } else { + s + } +} + +pub fn bytes_from_json(json: &Json) -> Bytes { + let s = json.as_string().unwrap(); + if s.len() % 2 == 1 { + FromHex::from_hex(&("0".to_string() + &(clean(s).to_string()))[..]).unwrap_or(vec![]) + } else { + FromHex::from_hex(clean(s)).unwrap_or(vec![]) + } +} + +pub fn address_from_json(json: &Json) -> Address { + let s = json.as_string().unwrap(); + if s.len() % 2 == 1 { + address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..]) + } else { + address_from_hex(clean(s)) + } +} + +pub fn u256_from_json(json: &Json) -> U256 { + let s = json.as_string().unwrap(); + if s.len() >= 2 && &s[0..2] == "0x" { + // hex + U256::from_str(&s[2..]).unwrap() + } + else { + // dec + U256::from_dec_str(s).unwrap() + } +} + +#[macro_export] +macro_rules! declare_test { + ($id: ident, $name: expr) => { + #[test] + #[allow(non_snake_case)] + fn $id() { + assert!(do_json_test(include_bytes!(concat!("../../res/ethereum/tests/", $name, ".json"))).len() == 0); + } + }; +} + +#[macro_export] +macro_rules! declare_test_ignore { + ($id: ident, $name: expr) => { + #[test] + #[ignore] + #[allow(non_snake_case)] + fn $id() { + assert!(do_json_test(include_bytes!(concat!("../../res/ethereum/tests/", $name, ".json"))).len() == 0); + } + }; +} diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs new file mode 100644 index 000000000..b6f592628 --- /dev/null +++ b/src/tests/transaction.rs @@ -0,0 +1,75 @@ +use super::test_common::*; +use evm; + +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(); + let old_schedule = evm::Schedule::new_frontier(); + let new_schedule = evm::Schedule::new_homestead(); + for (name, test) in json.as_object().unwrap() { + let mut fail = false; + let mut fail_unless = |cond: bool| if !cond && fail { failed.push(name.to_string()); fail = true }; + let schedule = match test.find("blocknumber") + .and_then(|j| j.as_string()) + .and_then(|s| BlockNumber::from_str(s).ok()) + .unwrap_or(0) { x if x < 900000 => &old_schedule, _ => &new_schedule }; + let rlp = bytes_from_json(&test["rlp"]); + let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(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 t = res.unwrap(); + fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender))); + fail_unless(t.data == bytes_from_json(&tx["data"])); + fail_unless(t.gas == u256_from_json(&tx["gasLimit"])); + fail_unless(t.gas_price == u256_from_json(&tx["gasPrice"])); + fail_unless(t.nonce == u256_from_json(&tx["nonce"])); + fail_unless(t.value == u256_from_json(&tx["value"])); + if let Action::Call(ref to) = t.action { + fail_unless(to == &address_from_json(&tx["to"])); + } else { + fail_unless(bytes_from_json(&tx["to"]).len() == 0); + } + } + } + for f in failed.iter() { + println!("FAILED: {:?}", f); + } + 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!{TransactionTests/Homestead/tt10mbDataField} +declare_test!{TransactionTests/Homestead/ttWrongRLPTransaction} +declare_test!{TransactionTests/RandomTests/tr201506052141PYTHON}*/ + +declare_test!{TransactionTests_ttTransactionTest, "TransactionTests/ttTransactionTest"} +declare_test_ignore!{TransactionTests_tt10mbDataField, "TransactionTests/tt10mbDataField"} +declare_test!{TransactionTests_ttWrongRLPTransaction, "TransactionTests/ttWrongRLPTransaction"} +declare_test!{TransactionTests_Homestead_ttTransactionTest, "TransactionTests/Homestead/ttTransactionTest"} +declare_test_ignore!{TransactionTests_Homestead_tt10mbDataField, "TransactionTests/Homestead/tt10mbDataField"} +declare_test!{TransactionTests_Homestead_ttWrongRLPTransaction, "TransactionTests/Homestead/ttWrongRLPTransaction"} +declare_test!{TransactionTests_RandomTests_tr201506052141PYTHON, "TransactionTests/RandomTests/tr201506052141PYTHON"} diff --git a/src/transaction.rs b/src/transaction.rs index 9a326211e..661c1355f 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -147,144 +147,16 @@ impl Decodable for Transaction { } } -pub fn clean(s: &str) -> &str { - if s.len() >= 2 && &s[0..2] == "0x" { - &s[2..] - } else { - s - } +#[test] +fn sender_test() { + let t: Transaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); + assert_eq!(t.data, b""); + assert_eq!(t.gas, U256::from(0x5208u64)); + assert_eq!(t.gas_price, U256::from(0x01u64)); + assert_eq!(t.nonce, U256::from(0x00u64)); + if let Action::Call(ref to) = t.action { + assert_eq!(*to, address_from_hex("095e7baea6a6c7c4c2dfeb977efac326af552d87")); + } else { panic!(); } + assert_eq!(t.value, U256::from(0x0au64)); + assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e")); } - -pub fn bytes_from_json(json: &Json) -> Bytes { - let s = json.as_string().unwrap(); - if s.len() % 2 == 1 { - FromHex::from_hex(&("0".to_string() + &(clean(s).to_string()))[..]).unwrap_or(vec![]) - } else { - FromHex::from_hex(clean(s)).unwrap_or(vec![]) - } -} - -pub fn address_from_json(json: &Json) -> Address { - let s = json.as_string().unwrap(); - if s.len() % 2 == 1 { - address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..]) - } else { - address_from_hex(clean(s)) - } -} - -pub fn u256_from_json(json: &Json) -> U256 { - let s = json.as_string().unwrap(); - if s.len() >= 2 && &s[0..2] == "0x" { - // hex - U256::from_str(&s[2..]).unwrap() - } - else { - // dec - U256::from_dec_str(s).unwrap() - } -} - -#[cfg(test)] -mod tests { - use util::*; - use evm::Schedule; - use header::BlockNumber; - use super::*; - - #[test] - fn sender_test() { - let t: Transaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); - assert_eq!(t.data, b""); - assert_eq!(t.gas, U256::from(0x5208u64)); - assert_eq!(t.gas_price, U256::from(0x01u64)); - assert_eq!(t.nonce, U256::from(0x00u64)); - if let Action::Call(ref to) = t.action { - assert_eq!(*to, address_from_hex("095e7baea6a6c7c4c2dfeb977efac326af552d87")); - } else { panic!(); } - assert_eq!(t.value, U256::from(0x0au64)); - assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e")); - } - - 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(); - let old_schedule = Schedule::new_frontier(); - let new_schedule = Schedule::new_homestead(); - for (name, test) in json.as_object().unwrap() { - let mut fail = false; - let mut fail_unless = |cond: bool| if !cond && fail { failed.push(name.to_string()); fail = true }; - let schedule = match test.find("blocknumber") - .and_then(|j| j.as_string()) - .and_then(|s| BlockNumber::from_str(s).ok()) - .unwrap_or(0) { x if x < 900000 => &old_schedule, _ => &new_schedule }; - let rlp = bytes_from_json(&test["rlp"]); - let res = UntrustedRlp::new(&rlp).as_val().map_err(|e| From::from(e)).and_then(|t: Transaction| t.validate(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 t = res.unwrap(); - fail_unless(t.sender().unwrap() == address_from_hex(clean(expect_sender))); - fail_unless(t.data == bytes_from_json(&tx["data"])); - fail_unless(t.gas == u256_from_json(&tx["gasLimit"])); - fail_unless(t.gas_price == u256_from_json(&tx["gasPrice"])); - fail_unless(t.nonce == u256_from_json(&tx["nonce"])); - fail_unless(t.value == u256_from_json(&tx["value"])); - if let Action::Call(ref to) = t.action { - fail_unless(to == &address_from_json(&tx["to"])); - } else { - fail_unless(bytes_from_json(&tx["to"]).len() == 0); - } - } - } - for f in failed.iter() { - println!("FAILED: {:?}", f); - } - 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!{TransactionTests/Homestead/tt10mbDataField} - declare_test!{TransactionTests/Homestead/ttWrongRLPTransaction} - declare_test!{TransactionTests/RandomTests/tr201506052141PYTHON}*/ - - macro_rules! declare_test { - ($id: ident, $name: expr) => { - #[test] - #[allow(non_snake_case)] - fn $id() { - assert!(do_json_test(include_bytes!(concat!("../res/ethereum/tests/", $name, ".json"))).len() == 0); - } - }; - } - declare_test!{TransactionTests_ttTransactionTest, "TransactionTests/ttTransactionTest"} - declare_test!{TransactionTests_tt10mbDataField, "TransactionTests/tt10mbDataField"} - declare_test!{TransactionTests_ttWrongRLPTransaction, "TransactionTests/ttWrongRLPTransaction"} - declare_test!{TransactionTests_Homestead_ttTransactionTest, "TransactionTests/Homestead/ttTransactionTest"} - declare_test!{TransactionTests_Homestead_tt10mbDataField, "TransactionTests/Homestead/tt10mbDataField"} - declare_test!{TransactionTests_Homestead_ttWrongRLPTransaction, "TransactionTests/Homestead/ttWrongRLPTransaction"} - declare_test!{TransactionTests_RandomTests_tr201506052141PYTHON, "TransactionTests/RandomTests/tr201506052141PYTHON"} -} \ No newline at end of file