Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Update state tests execution model #9440

Merged
merged 15 commits into from
Sep 10, 2018
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ethcore/res/ethereum/eip161_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"eip161dTransition": "0x0",
"eip98Transition": "0x7fffffffffffffff",
"eip86Transition": "0x7fffffffffffffff",
"eip155Transition": "0x7fffffffffffffff",
"eip155Transition": "0x0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting to 0 enables the feature right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, otherwise some transaction tests for EIP158 spec (which for us is actaully eip161_test.json) fail, cause they assume that eip155 is enabled as well.

"maxCodeSize": 24576,
"maxCodeSizeTransition": "0x0"
},
Expand Down
2 changes: 1 addition & 1 deletion ethcore/res/ethereum/tests
Submodule tests updated 19225 files
26 changes: 18 additions & 8 deletions ethcore/src/client/evm_test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl fmt::Display for EvmTestError {
}

use ethereum;
use ethjson::state::test::ForkSpec;
use ethjson::spec::ForkSpec;

/// Simplified, single-block EVM test client.
pub struct EvmTestClient<'a> {
Expand All @@ -69,12 +69,12 @@ pub struct EvmTestClient<'a> {
}

impl<'a> fmt::Debug for EvmTestClient<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("EvmTestClient")
.field("state", &self.state)
.field("spec", &self.spec.name)
.finish()
}
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("EvmTestClient")
.field("state", &self.state)
.field("spec", &self.spec.name)
.finish()
}
}

impl<'a> EvmTestClient<'a> {
Expand Down Expand Up @@ -217,9 +217,19 @@ impl<'a> EvmTestClient<'a> {
let result = self.state.apply_with_tracing(&env_info, self.spec.engine.machine(), &transaction, tracer, vm_tracer);
let scheme = self.spec.engine.machine().create_address_scheme(env_info.number);

// Touch the coinbase at the end of the test to simulate
// miner reward.
// Details: https://github.com/paritytech/parity-ethereum/issues/9431
let schedule = self.spec.engine.machine().schedule(env_info.number);
self.state.add_balance(&env_info.author, &0.into(), if schedule.no_empty {
state::CleanupMode::NoEmpty
} else {
state::CleanupMode::ForceCreate
}).ok();
self.state.commit().ok();

match result {
Ok(result) => {
self.state.commit().ok();
TransactResult::Ok {
state_root: *self.state.root(),
gas_left: initial_gas - result.receipt.gas_used,
Expand Down
49 changes: 42 additions & 7 deletions ethcore/src/json_tests/difficulty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,61 @@ pub fn json_difficulty_test<H: FnMut(&str, HookType)>(json_data: &[u8], spec: Sp
vec![]
}

mod difficulty_test_byzantium {
macro_rules! difficulty_json_test {
( $spec:ident ) => {

use super::json_difficulty_test;
use tempdir::TempDir;
use json_tests::HookType;

fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], h: &mut H) -> Vec<String> {
json_difficulty_test(json_data, ::ethereum::new_byzantium_test(), h)
let tempdir = TempDir::new("").unwrap();
json_difficulty_test(json_data, ::ethereum::$spec(&tempdir.path()), h)
}

declare_test!{DifficultyTests_difficultyByzantium, "BasicTests/difficultyByzantium.json"}
}
}

mod difficulty_test_foundation {
macro_rules! difficulty_json_test_nopath {
( $spec:ident ) => {

use super::json_difficulty_test;
use tempdir::TempDir;
use json_tests::HookType;

fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], h: &mut H) -> Vec<String> {
let tempdir = TempDir::new("").unwrap();
json_difficulty_test(json_data, ::ethereum::new_foundation(&tempdir.path()), h)
json_difficulty_test(json_data, ::ethereum::$spec(), h)
}

}
}

mod difficulty_test {
difficulty_json_test!(new_foundation);
declare_test!{DifficultyTests_difficulty, "BasicTests/difficulty.json"}
}

mod difficulty_test_byzantium {
difficulty_json_test_nopath!(new_byzantium_test);
declare_test!{DifficultyTests_difficultyByzantium, "BasicTests/difficultyByzantium.json"}
}

mod difficulty_test_foundation {
difficulty_json_test!(new_foundation);
declare_test!{DifficultyTests_difficultyMainNetwork, "BasicTests/difficultyMainNetwork.json"}
}

mod difficulty_test_ropsten {
difficulty_json_test_nopath!(new_ropsten_test);
declare_test!{DifficultyTests_difficultyRopsten, "BasicTests/difficultyRopsten.json"}
}

mod difficulty_test_frontier {
difficulty_json_test_nopath!(new_frontier_test);
declare_test!{DifficultyTests_difficultyFrontier, "BasicTests/difficultyFrontier.json"}
}

mod difficulty_test_homestead {
difficulty_json_test_nopath!(new_homestead_test);
declare_test!{DifficultyTests_difficultyHomestead, "BasicTests/difficultyHomestead.json"}
}

14 changes: 14 additions & 0 deletions ethcore/src/json_tests/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,16 @@ mod state_tests {
json_chain_test(json_data, h)
}

declare_test!{GeneralStateTest_stArgsZeroOneBalance, "GeneralStateTests/stArgsZeroOneBalance/"}
declare_test!{GeneralStateTest_stAttackTest, "GeneralStateTests/stAttackTest/"}
declare_test!{GeneralStateTest_stBadOpcodeTest, "GeneralStateTests/stBadOpcode/"}
declare_test!{GeneralStateTest_stBugs, "GeneralStateTests/stBugs/"}
declare_test!{GeneralStateTest_stCallCodes, "GeneralStateTests/stCallCodes/"}
declare_test!{GeneralStateTest_stCallCreateCallCodeTest, "GeneralStateTests/stCallCreateCallCodeTest/"}
declare_test!{GeneralStateTest_stCallDelegateCodesCallCodeHomestead, "GeneralStateTests/stCallDelegateCodesCallCodeHomestead/"}
declare_test!{GeneralStateTest_stCallDelegateCodesHomestead, "GeneralStateTests/stCallDelegateCodesHomestead/"}
declare_test!{GeneralStateTest_stChangedEIP150, "GeneralStateTests/stChangedEIP150/"}
declare_test!{GeneralStateTest_stCodeCopyTest, "GeneralStateTests/stCodeCopyTest/"}
declare_test!{GeneralStateTest_stCodeSizeLimit, "GeneralStateTests/stCodeSizeLimit/"}
declare_test!{GeneralStateTest_stCreateTest, "GeneralStateTests/stCreateTest/"}
declare_test!{GeneralStateTest_stDelegatecallTestHomestead, "GeneralStateTests/stDelegatecallTestHomestead/"}
Expand All @@ -135,12 +139,15 @@ mod state_tests {
declare_test!{GeneralStateTest_stMemoryTest, "GeneralStateTests/stMemoryTest/"}
declare_test!{GeneralStateTest_stNonZeroCallsTest, "GeneralStateTests/stNonZeroCallsTest/"}
declare_test!{GeneralStateTest_stPreCompiledContracts, "GeneralStateTests/stPreCompiledContracts/"}
declare_test!{GeneralStateTest_stPreCompiledContracts2, "GeneralStateTests/stPreCompiledContracts2/"}
declare_test!{heavy => GeneralStateTest_stQuadraticComplexityTest, "GeneralStateTests/stQuadraticComplexityTest/"}
declare_test!{GeneralStateTest_stRandom, "GeneralStateTests/stRandom/"}
declare_test!{GeneralStateTest_stRandom2, "GeneralStateTests/stRandom2/"}
declare_test!{GeneralStateTest_stRecursiveCreate, "GeneralStateTests/stRecursiveCreate/"}
declare_test!{GeneralStateTest_stRefundTest, "GeneralStateTests/stRefundTest/"}
declare_test!{GeneralStateTest_stReturnDataTest, "GeneralStateTests/stReturnDataTest/"}
declare_test!{GeneralStateTest_stRevertTest, "GeneralStateTests/stRevertTest/"}
declare_test!{GeneralStateTest_stShift, "GeneralStateTests/stShift/"}
declare_test!{GeneralStateTest_stSolidityTest, "GeneralStateTests/stSolidityTest/"}
declare_test!{GeneralStateTest_stSpecialTest, "GeneralStateTests/stSpecialTest/"}
declare_test!{GeneralStateTest_stStackTests, "GeneralStateTests/stStackTests/"}
Expand All @@ -152,4 +159,11 @@ mod state_tests {
declare_test!{GeneralStateTest_stZeroCallsRevert, "GeneralStateTests/stZeroCallsRevert/"}
declare_test!{GeneralStateTest_stZeroCallsTest, "GeneralStateTests/stZeroCallsTest/"}
declare_test!{GeneralStateTest_stZeroKnowledge, "GeneralStateTests/stZeroKnowledge/"}

// Attempts to send a transaction that requires more than current balance:
// Tx:
// https://github.com/ethereum/tests/blob/726b161ba8a739691006cc1ba080672bb50a9d49/GeneralStateTests/stZeroKnowledge2/ecmul_0-3_5616_28000_96.json#L170
// Balance:
// https://github.com/ethereum/tests/blob/726b161ba8a739691006cc1ba080672bb50a9d49/GeneralStateTests/stZeroKnowledge2/ecmul_0-3_5616_28000_96.json#L126
declare_test!{ignore => GeneralStateTest_stZeroKnowledge2, "GeneralStateTests/stZeroKnowledge2/"}
}
8 changes: 7 additions & 1 deletion ethcore/src/json_tests/test_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub fn run_test_path<H: FnMut(&str, HookType)>(
os.push(".json");
os
}).collect();
let extension = path.extension().and_then(|s| s.to_str());
if path.is_dir() {
for p in read_dir(path).unwrap().filter_map(|e| {
let e = e.unwrap();
Expand All @@ -51,6 +52,8 @@ pub fn run_test_path<H: FnMut(&str, HookType)>(
}}) {
run_test_path(&p, skip, runner, start_stop_hook)
}
} else if extension == Some("swp") || extension == None {
// Ignore junk
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:(

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, there are some swp files or .stub files committed in the repo :(

} else {
let mut path = p.to_path_buf();
path.set_extension("json");
Expand All @@ -64,7 +67,10 @@ pub fn run_test_file<H: FnMut(&str, HookType)>(
start_stop_hook: &mut H
) {
let mut data = Vec::new();
let mut file = File::open(&path).expect("Error opening test file");
let mut file = match File::open(&path) {
Ok(file) => file,
Err(_) => panic!("Error opening test file at: {:?}", path),
};
file.read_to_end(&mut data).expect("Error reading test file");
let results = runner(&data, start_stop_hook);
let empty: [String; 0] = [];
Expand Down
112 changes: 58 additions & 54 deletions ethcore/src/json_tests/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@

use std::path::Path;
use super::test_common::*;
use evm;
use client::EvmTestClient;
use header::Header;
use ethjson;
use rlp::Rlp;
use transaction::{Action, UnverifiedTransaction, SignedTransaction};
use transaction::UnverifiedTransaction;

/// Run transaction jsontests on a given folder.
pub fn run_test_path<H: FnMut(&str, HookType)>(p: &Path, skip: &[&'static str], h: &mut H) {
Expand All @@ -34,52 +35,54 @@ pub fn run_test_file<H: FnMut(&str, HookType)>(p: &Path, h: &mut H) {
fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], start_stop_hook: &mut H) -> Vec<String> {
let tests = ethjson::transaction::Test::load(json_data).unwrap();
let mut failed = Vec::new();
let frontier_schedule = evm::Schedule::new_frontier();
let homestead_schedule = evm::Schedule::new_homestead();
let byzantium_schedule = evm::Schedule::new_byzantium();
for (name, test) in tests.into_iter() {
start_stop_hook(&name, HookType::OnStart);

let mut fail_unless = |cond: bool, title: &str| if !cond { failed.push(name.clone()); println!("Transaction failed: {:?}: {:?}", name, title); };

let number: Option<u64> = test.block_number.map(Into::into);
let schedule = match number {
None => &frontier_schedule,
Some(x) if x < 1_150_000 => &frontier_schedule,
Some(x) if x < 3_000_000 => &homestead_schedule,
Some(_) => &byzantium_schedule
};
let allow_chain_id_of_one = number.map_or(false, |n| n >= 2_675_000);
let allow_unsigned = number.map_or(false, |n| n >= 3_000_000);

let rlp: Vec<u8> = test.rlp.into();
let res = Rlp::new(&rlp)
.as_val()
.map_err(::error::Error::from)
.and_then(|t: UnverifiedTransaction| {
t.validate(schedule, schedule.have_delegate_call, allow_chain_id_of_one, allow_unsigned).map_err(Into::into)
});

fail_unless(test.transaction.is_none() == res.is_err(), "Validity different");
if let (Some(tx), Some(sender)) = (test.transaction, test.sender) {
let t = res.unwrap();
fail_unless(SignedTransaction::new(t.clone()).unwrap().sender() == sender.into(), "sender mismatch");
let is_acceptable_chain_id = match t.chain_id() {
None => true,
Some(1) if allow_chain_id_of_one => true,
_ => false,
for (spec_name, result) in test.post_state {
let spec = match EvmTestClient::spec_from_json(&spec_name) {
Some(spec) => spec,
None => {
println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name);
continue;
}
};
fail_unless(is_acceptable_chain_id, "Network ID unacceptable");
let data: Vec<u8> = tx.data.into();
fail_unless(t.data == data, "data mismatch");
fail_unless(t.gas_price == tx.gas_price.into(), "gas_price mismatch");
fail_unless(t.nonce == tx.nonce.into(), "nonce mismatch");
fail_unless(t.value == tx.value.into(), "value mismatch");
let to: Option<ethjson::hash::Address> = tx.to.into();
let to: Option<Address> = to.map(Into::into);
match t.action {
Action::Call(dest) => fail_unless(Some(dest) == to, "call/destination mismatch"),
Action::Create => fail_unless(None == to, "create mismatch"),

let mut fail_unless = |cond: bool, title: &str| if !cond {
failed.push(format!("{}-{:?}", name, spec_name));
println!("Transaction failed: {:?}-{:?}: {:?}", name, spec_name, title);
};

let rlp: Vec<u8> = test.rlp.clone().into();
let res = Rlp::new(&rlp)
.as_val()
.map_err(::error::Error::from)
.and_then(|t: UnverifiedTransaction| {
let mut header: Header = Default::default();
// Use high enough number to activate all required features.
header.set_number(0x6ffffffffffffe);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps make it this number a named const (for readability)? e.g. FEATURE_ENABLING_BLOCKNR or some such?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense.


let minimal = t.gas_required(&spec.engine.schedule(header.number())).into();
if t.gas < minimal {
return Err(::transaction::Error::InsufficientGas {
minimal, got: t.gas,
}.into());
}
spec.engine.verify_transaction_basic(&t, &header)?;
Ok(spec.engine.verify_transaction_unordered(t, &header)?)
});

match (res, result.hash, result.sender) {
(Ok(t), Some(hash), Some(sender)) => {
fail_unless(t.sender() == sender.into(), "sender mismatch");
fail_unless(t.hash() == hash.into(), "hash mismatch");
},
(Err(_), None, None) => {},
data => {
fail_unless(
false,
&format!("Validity different: {:?}", data)
);
}
}
}

Expand All @@ -92,13 +95,14 @@ fn do_json_test<H: FnMut(&str, HookType)>(json_data: &[u8], start_stop_hook: &mu
failed
}

declare_test!{TransactionTests_ttEip155VitaliksHomesead, "TransactionTests/ttEip155VitaliksHomesead"}
declare_test!{TransactionTests_ttEip155VitaliksEip158, "TransactionTests/ttEip155VitaliksEip158"}
declare_test!{TransactionTests_ttEip158, "TransactionTests/ttEip158"}
declare_test!{TransactionTests_ttFrontier, "TransactionTests/ttFrontier"}
declare_test!{TransactionTests_ttHomestead, "TransactionTests/ttHomestead"}
declare_test!{TransactionTests_ttVRuleEip158, "TransactionTests/ttVRuleEip158"}
declare_test!{TransactionTests_ttWrongRLPFrontier, "TransactionTests/ttWrongRLPFrontier"}
declare_test!{TransactionTests_ttWrongRLPHomestead, "TransactionTests/ttWrongRLPHomestead"}
declare_test!{TransactionTests_ttConstantinople, "TransactionTests/ttConstantinople"}
declare_test!{TransactionTests_ttSpecConstantinople, "TransactionTests/ttSpecConstantinople"}
declare_test!{TransactionTests_ttAddress, "TransactionTests/ttAddress"}
declare_test!{TransactionTests_ttData, "TransactionTests/ttData"}
declare_test!{TransactionTests_ttGasLimit, "TransactionTests/ttGasLimit"}
declare_test!{TransactionTests_ttGasPrice, "TransactionTests/ttGasPrice"}
declare_test!{TransactionTests_ttNonce, "TransactionTests/ttNonce"}
declare_test!{TransactionTests_ttRSValue, "TransactionTests/ttRSValue"}
declare_test!{TransactionTests_ttSignature, "TransactionTests/ttSignature"}
declare_test!{TransactionTests_ttValue, "TransactionTests/ttValue"}
declare_test!{TransactionTests_ttVValue, "TransactionTests/ttVValue"}
declare_test!{TransactionTests_ttWrongRLP, "TransactionTests/ttWrongRLP"}

17 changes: 0 additions & 17 deletions ethcore/transaction/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,23 +393,6 @@ impl UnverifiedTransaction {
Ok(recover(&self.signature(), &self.unsigned.hash(self.chain_id()))?)
}

/// Do basic validation, checking for valid signature and minimum gas,
// TODO: consider use in block validation.
#[cfg(feature = "json-tests")]
pub fn validate(self, schedule: &Schedule, require_low: bool, allow_chain_id_of_one: bool, allow_empty_signature: bool)
-> Result<UnverifiedTransaction, error::Error>
{
let chain_id = if allow_chain_id_of_one { Some(1) } else { None };
self.verify_basic(require_low, chain_id, allow_empty_signature)?;
if !allow_empty_signature || !self.is_unsigned() {
self.recover_public()?;
}
if self.gas < U256::from(self.gas_required(&schedule)) {
return Err(error::Error::InvalidGasLimit(::unexpected::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into())
}
Ok(self)
}

/// Verify basic signature params. Does not attempt sender recovery.
pub fn verify_basic(&self, check_low_s: bool, chain_id: Option<u64>, allow_empty_signature: bool) -> Result<(), error::Error> {
if check_low_s && !(allow_empty_signature && self.is_unsigned()) {
Expand Down
2 changes: 1 addition & 1 deletion evmbin/src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub fn run_action<T: Informant>(
pub fn run_transaction<T: Informant>(
name: &str,
idx: usize,
spec: &ethjson::state::test::ForkSpec,
spec: &ethjson::spec::ForkSpec,
pre_state: &pod_state::PodState,
post_root: H256,
env_info: &client::EnvInfo,
Expand Down
3 changes: 1 addition & 2 deletions json/src/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ use hash::H256;
use blockchain::state::State;
use blockchain::header::Header;
use blockchain::block::Block;
use state::test::ForkSpec;
use spec::{Genesis, Seal, Ethereum};
use spec::{ForkSpec, Genesis, Seal, Ethereum};

/// Blockchain deserialization.
#[derive(Debug, PartialEq, Deserialize)]
Expand Down
2 changes: 1 addition & 1 deletion json/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub use self::account::Account;
pub use self::builtin::{Builtin, Pricing, Linear};
pub use self::genesis::Genesis;
pub use self::params::Params;
pub use self::spec::Spec;
pub use self::spec::{Spec, ForkSpec};
pub use self::seal::{Seal, Ethereum, AuthorityRoundSeal, TendermintSeal};
pub use self::engine::Engine;
pub use self::state::State;
Expand Down
Loading