2016-01-13 01:23:01 +01:00
|
|
|
use super::test_common::*;
|
|
|
|
use state::*;
|
|
|
|
use executive::*;
|
|
|
|
use spec::*;
|
|
|
|
use engine::*;
|
2016-01-13 13:16:53 +01:00
|
|
|
use evm;
|
2016-01-14 17:24:57 +01:00
|
|
|
use evm::{Schedule, Ext, Factory, VMType};
|
2016-01-13 01:23:01 +01:00
|
|
|
use ethereum;
|
2016-01-15 14:22:46 +01:00
|
|
|
use externalities::*;
|
|
|
|
use substate::*;
|
2016-01-13 01:23:01 +01:00
|
|
|
|
|
|
|
struct TestEngine {
|
2016-01-14 17:24:57 +01:00
|
|
|
vm_factory: Factory,
|
2016-01-13 01:23:01 +01:00
|
|
|
spec: Spec,
|
2016-01-14 12:33:49 +01:00
|
|
|
max_depth: usize
|
2016-01-13 01:23:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TestEngine {
|
2016-01-14 17:24:57 +01:00
|
|
|
fn new(max_depth: usize, vm_type: VMType) -> TestEngine {
|
2016-01-13 01:23:01 +01:00
|
|
|
TestEngine {
|
2016-01-14 17:24:57 +01:00
|
|
|
vm_factory: Factory::new(vm_type),
|
2016-01-13 01:23:01 +01:00
|
|
|
spec: ethereum::new_frontier_test(),
|
2016-01-14 12:33:49 +01:00
|
|
|
max_depth: max_depth
|
2016-01-13 01:23:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Engine for TestEngine {
|
|
|
|
fn name(&self) -> &str { "TestEngine" }
|
|
|
|
fn spec(&self) -> &Spec { &self.spec }
|
2016-01-14 17:24:57 +01:00
|
|
|
fn vm_factory(&self) -> &Factory { &self.vm_factory }
|
2016-01-13 01:23:01 +01:00
|
|
|
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
|
|
|
let mut schedule = Schedule::new_frontier();
|
2016-01-14 12:33:49 +01:00
|
|
|
schedule.max_depth = self.max_depth;
|
2016-01-13 01:23:01 +01:00
|
|
|
schedule
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 13:16:53 +01:00
|
|
|
struct CallCreate {
|
|
|
|
data: Bytes,
|
2016-01-14 23:36:35 +01:00
|
|
|
destination: Option<Address>,
|
2016-01-13 17:26:04 +01:00
|
|
|
_gas_limit: U256,
|
2016-01-13 13:16:53 +01:00
|
|
|
value: U256
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tiny wrapper around executive externalities.
|
|
|
|
/// Stores callcreates.
|
|
|
|
struct TestExt<'a> {
|
|
|
|
ext: Externalities<'a>,
|
|
|
|
callcreates: Vec<CallCreate>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TestExt<'a> {
|
|
|
|
fn new(ext: Externalities<'a>) -> TestExt {
|
|
|
|
TestExt {
|
|
|
|
ext: ext,
|
|
|
|
callcreates: vec![]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Ext for TestExt<'a> {
|
|
|
|
fn sload(&self, key: &H256) -> H256 {
|
|
|
|
self.ext.sload(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sstore(&mut self, key: H256, value: H256) {
|
|
|
|
self.ext.sstore(key, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn balance(&self, address: &Address) -> U256 {
|
|
|
|
self.ext.balance(address)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn blockhash(&self, number: &U256) -> H256 {
|
|
|
|
self.ext.blockhash(number)
|
|
|
|
}
|
|
|
|
|
2016-01-14 17:40:38 +01:00
|
|
|
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>) {
|
2016-01-13 13:16:53 +01:00
|
|
|
// in call and create we need to check if we exited with insufficient balance or max limit reached.
|
|
|
|
// in case of reaching max depth, we should store callcreates. Otherwise, ignore.
|
|
|
|
let res = self.ext.create(gas, value, code);
|
|
|
|
let ext = &self.ext;
|
|
|
|
match res {
|
|
|
|
// just record call create
|
2016-01-14 17:40:38 +01:00
|
|
|
(gas_left, Some(address)) => {
|
2016-01-13 13:16:53 +01:00
|
|
|
self.callcreates.push(CallCreate {
|
|
|
|
data: code.to_vec(),
|
2016-01-14 23:36:35 +01:00
|
|
|
destination: Some(address.clone()),
|
2016-01-13 22:29:49 +01:00
|
|
|
_gas_limit: *gas,
|
2016-01-13 13:16:53 +01:00
|
|
|
value: *value
|
|
|
|
});
|
2016-01-14 17:40:38 +01:00
|
|
|
(gas_left, Some(address))
|
2016-01-13 13:16:53 +01:00
|
|
|
},
|
2016-01-14 12:33:49 +01:00
|
|
|
// creation failed only due to reaching max_depth
|
2016-01-14 17:40:38 +01:00
|
|
|
(gas_left, None) if ext.state.balance(&ext.params.address) >= *value => {
|
2016-01-13 13:16:53 +01:00
|
|
|
self.callcreates.push(CallCreate {
|
|
|
|
data: code.to_vec(),
|
2016-01-14 23:36:35 +01:00
|
|
|
// callcreate test does not need an address
|
|
|
|
destination: None,
|
2016-01-13 22:29:49 +01:00
|
|
|
_gas_limit: *gas,
|
2016-01-13 13:16:53 +01:00
|
|
|
value: *value
|
|
|
|
});
|
2016-01-14 23:36:35 +01:00
|
|
|
let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address));
|
2016-01-14 17:40:38 +01:00
|
|
|
(gas_left, Some(address))
|
2016-01-13 13:16:53 +01:00
|
|
|
},
|
|
|
|
other => other
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn call(&mut self,
|
2016-01-13 22:36:59 +01:00
|
|
|
gas: &U256,
|
|
|
|
call_gas: &U256,
|
2016-01-13 13:16:53 +01:00
|
|
|
receive_address: &Address,
|
|
|
|
value: &U256,
|
|
|
|
data: &[u8],
|
|
|
|
code_address: &Address,
|
2016-01-14 21:21:46 +01:00
|
|
|
output: &mut [u8]) -> Result<(U256, bool), evm::Error> {
|
2016-01-13 13:16:53 +01:00
|
|
|
let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output);
|
|
|
|
let ext = &self.ext;
|
2016-01-14 21:21:46 +01:00
|
|
|
if let &Ok(_some) = &res {
|
2016-01-14 19:52:40 +01:00
|
|
|
if ext.state.balance(&ext.params.address) >= *value {
|
2016-01-13 13:16:53 +01:00
|
|
|
self.callcreates.push(CallCreate {
|
|
|
|
data: data.to_vec(),
|
2016-01-14 23:36:35 +01:00
|
|
|
destination: Some(receive_address.clone()),
|
2016-01-13 22:36:59 +01:00
|
|
|
_gas_limit: *call_gas,
|
2016-01-13 13:16:53 +01:00
|
|
|
value: *value
|
|
|
|
});
|
2016-01-14 19:52:40 +01:00
|
|
|
}
|
2016-01-13 13:16:53 +01:00
|
|
|
}
|
2016-01-14 21:21:46 +01:00
|
|
|
res
|
2016-01-13 13:16:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn extcode(&self, address: &Address) -> Vec<u8> {
|
|
|
|
self.ext.extcode(address)
|
|
|
|
}
|
|
|
|
|
2016-01-15 16:16:16 +01:00
|
|
|
fn log(&mut self, topics: Vec<H256>, data: Vec<u8>) {
|
2016-01-13 13:16:53 +01:00
|
|
|
self.ext.log(topics, data)
|
|
|
|
}
|
|
|
|
|
2016-01-13 22:16:24 +01:00
|
|
|
fn ret(&mut self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
|
2016-01-13 13:16:53 +01:00
|
|
|
self.ext.ret(gas, data)
|
|
|
|
}
|
|
|
|
|
2016-01-13 16:16:21 +01:00
|
|
|
fn suicide(&mut self, refund_address: &Address) {
|
|
|
|
self.ext.suicide(refund_address)
|
2016-01-13 13:16:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn schedule(&self) -> &Schedule {
|
|
|
|
self.ext.schedule()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn env_info(&self) -> &EnvInfo {
|
|
|
|
self.ext.env_info()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:23:01 +01:00
|
|
|
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
2016-01-14 18:29:18 +01:00
|
|
|
let vms = VMType::all();
|
|
|
|
vms
|
|
|
|
.iter()
|
|
|
|
.flat_map(|vm| do_json_test_for(vm, json_data))
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec<String> {
|
2016-01-13 01:23:01 +01:00
|
|
|
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
|
|
|
|
let mut failed = Vec::new();
|
|
|
|
for (name, test) in json.as_object().unwrap() {
|
2016-01-15 20:19:46 +01:00
|
|
|
// if name != "calldataload_BigOffset" {
|
2016-01-15 19:15:37 +01:00
|
|
|
// continue;
|
|
|
|
// }
|
2016-01-14 17:40:38 +01:00
|
|
|
println!("name: {:?}", name);
|
2016-01-13 17:26:04 +01:00
|
|
|
// sync io is usefull when something crashes in jit
|
2016-01-14 18:29:18 +01:00
|
|
|
// ::std::io::stdout().write(&name.as_bytes());
|
|
|
|
// ::std::io::stdout().write(b"\n");
|
|
|
|
// ::std::io::stdout().flush();
|
2016-01-13 01:23:01 +01:00
|
|
|
let mut fail = false;
|
|
|
|
//let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true };
|
2016-01-14 18:29:18 +01:00
|
|
|
let mut fail_unless = |cond: bool, s: &str | if !cond && !fail {
|
|
|
|
failed.push(format!("[{}] {}: {}", vm, name.to_string(), s));
|
|
|
|
fail = true
|
|
|
|
};
|
2016-01-13 01:23:01 +01:00
|
|
|
|
|
|
|
// test env
|
|
|
|
let mut state = State::new_temp();
|
|
|
|
|
|
|
|
test.find("pre").map(|pre| for (addr, s) in pre.as_object().unwrap() {
|
2016-01-13 22:06:05 +01:00
|
|
|
let address = Address::from(addr.as_ref());
|
2016-01-14 21:23:46 +01:00
|
|
|
let balance = xjson!(&s["balance"]);
|
2016-01-14 21:58:37 +01:00
|
|
|
let code = xjson!(&s["code"]);
|
2016-01-14 21:23:46 +01:00
|
|
|
let _nonce: U256 = xjson!(&s["nonce"]);
|
2016-01-13 01:23:01 +01:00
|
|
|
|
|
|
|
state.new_contract(&address);
|
|
|
|
state.add_balance(&address, &balance);
|
|
|
|
state.init_code(&address, code);
|
2016-01-14 21:58:37 +01:00
|
|
|
BTreeMap::from_json(&s["storage"]).into_iter().foreach(|(k, v)| state.set_storage(&address, k, v));
|
2016-01-13 01:23:01 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
let mut info = EnvInfo::new();
|
|
|
|
|
|
|
|
test.find("env").map(|env| {
|
2016-01-14 21:58:37 +01:00
|
|
|
info.author = xjson!(&env["currentCoinbase"]);
|
2016-01-14 21:23:46 +01:00
|
|
|
info.difficulty = xjson!(&env["currentDifficulty"]);
|
|
|
|
info.gas_limit = xjson!(&env["currentGasLimit"]);
|
|
|
|
info.number = xjson!(&env["currentNumber"]);
|
|
|
|
info.timestamp = xjson!(&env["currentTimestamp"]);
|
2016-01-13 01:23:01 +01:00
|
|
|
});
|
|
|
|
|
2016-01-14 18:29:18 +01:00
|
|
|
let engine = TestEngine::new(0, vm.clone());
|
2016-01-13 01:23:01 +01:00
|
|
|
|
|
|
|
// params
|
|
|
|
let mut params = ActionParams::new();
|
|
|
|
test.find("exec").map(|exec| {
|
2016-01-14 21:58:37 +01:00
|
|
|
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"]);
|
2016-01-14 21:23:46 +01:00
|
|
|
params.gas = xjson!(&exec["gas"]);
|
|
|
|
params.gas_price = xjson!(&exec["gasPrice"]);
|
|
|
|
params.value = xjson!(&exec["value"]);
|
2016-01-13 01:23:01 +01:00
|
|
|
});
|
|
|
|
|
2016-01-13 17:26:04 +01:00
|
|
|
let out_of_gas = test.find("callcreates").map(|_calls| {
|
2016-01-13 01:23:01 +01:00
|
|
|
}).is_none();
|
|
|
|
|
|
|
|
let mut substate = Substate::new();
|
2016-01-13 15:26:52 +01:00
|
|
|
let mut output = vec![];
|
2016-01-13 01:23:01 +01:00
|
|
|
|
|
|
|
// execute
|
2016-01-13 17:26:04 +01:00
|
|
|
let (res, callcreates) = {
|
2016-01-14 17:24:57 +01:00
|
|
|
let ex = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::Return(BytesRef::Flexible(&mut output)));
|
2016-01-13 13:16:53 +01:00
|
|
|
let mut test_ext = TestExt::new(ex);
|
2016-01-14 18:29:18 +01:00
|
|
|
let evm = engine.vm_factory().create();
|
2016-01-13 17:26:04 +01:00
|
|
|
let res = evm.exec(¶ms, &mut test_ext);
|
|
|
|
(res, test_ext.callcreates)
|
2016-01-13 01:23:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// then validate
|
|
|
|
match res {
|
|
|
|
Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
|
|
|
|
Ok(gas_left) => {
|
2016-01-15 21:46:08 +01:00
|
|
|
println!("name: {}, gas_left : {:?}", name, gas_left);
|
2016-01-13 01:23:01 +01:00
|
|
|
fail_unless(!out_of_gas, "expected to run out of gas.");
|
2016-01-14 21:23:46 +01:00
|
|
|
fail_unless(gas_left == xjson!(&test["gas"]), "gas_left is incorrect");
|
2016-01-14 21:58:37 +01:00
|
|
|
fail_unless(output == Bytes::from_json(&test["out"]), "output is incorrect");
|
2016-01-13 16:16:21 +01:00
|
|
|
|
|
|
|
test.find("post").map(|pre| for (addr, s) in pre.as_object().unwrap() {
|
2016-01-13 22:06:05 +01:00
|
|
|
let address = Address::from(addr.as_ref());
|
2016-01-13 16:30:19 +01:00
|
|
|
|
2016-01-14 21:58:37 +01:00
|
|
|
fail_unless(state.code(&address).unwrap_or(vec![]) == Bytes::from_json(&s["code"]), "code is incorrect");
|
2016-01-14 21:23:46 +01:00
|
|
|
fail_unless(state.balance(&address) == xjson!(&s["balance"]), "balance is incorrect");
|
|
|
|
fail_unless(state.nonce(&address) == xjson!(&s["nonce"]), "nonce is incorrect");
|
2016-01-14 21:58:37 +01:00
|
|
|
BTreeMap::from_json(&s["storage"]).iter().foreach(|(k, v)| fail_unless(&state.storage_at(&address, &k) == v, "storage is incorrect"));
|
2016-01-13 16:16:21 +01:00
|
|
|
});
|
|
|
|
|
2016-01-13 17:26:04 +01:00
|
|
|
let cc = test["callcreates"].as_array().unwrap();
|
|
|
|
fail_unless(callcreates.len() == cc.len(), "callcreates does not match");
|
|
|
|
for i in 0..cc.len() {
|
2016-01-14 23:41:48 +01:00
|
|
|
let callcreate = &callcreates[i];
|
2016-01-13 17:26:04 +01:00
|
|
|
let expected = &cc[i];
|
2016-01-14 23:41:48 +01:00
|
|
|
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");
|
2016-01-13 17:26:04 +01:00
|
|
|
|
|
|
|
// TODO: call_gas is calculated in externalities and is not exposed to TestExt.
|
|
|
|
// maybe move it to it's own function to simplify calculation?
|
2016-01-14 23:41:48 +01:00
|
|
|
//println!("name: {:?}, callcreate {:?}, expected: {:?}", name, callcreate.gas_limit, U256::from(&expected["gasLimit"]));
|
|
|
|
//fail_unless(callcreate.gas_limit == U256::from(&expected["gasLimit"]), "callcreates gas_limit is incorrect");
|
2016-01-13 17:26:04 +01:00
|
|
|
}
|
2016-01-13 01:23:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for f in failed.iter() {
|
|
|
|
println!("FAILED: {:?}", f);
|
|
|
|
}
|
|
|
|
|
|
|
|
//assert!(false);
|
|
|
|
failed
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
|
|
|
|
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
|
2016-01-13 13:16:53 +01:00
|
|
|
// this one crashes with some vm internal error. Separately they pass.
|
2016-01-13 16:16:21 +01:00
|
|
|
declare_test_ignore!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
|
2016-01-13 01:23:01 +01:00
|
|
|
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
|
2016-01-13 13:16:53 +01:00
|
|
|
declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
|
|
|
|
// this one take way too long.
|
2016-01-13 16:16:21 +01:00
|
|
|
declare_test_ignore!{ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"}
|
2016-01-13 13:16:53 +01:00
|
|
|
declare_test!{ExecutiveTests_vmLogTest, "VMTests/vmLogTest"}
|
|
|
|
declare_test!{ExecutiveTests_vmPerformanceTest, "VMTests/vmPerformanceTest"}
|
|
|
|
declare_test!{ExecutiveTests_vmPushDupSwapTest, "VMTests/vmPushDupSwapTest"}
|
|
|
|
declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"}
|
|
|
|
declare_test!{ExecutiveTests_vmSystemOperationsTest, "VMTests/vmSystemOperationsTest"}
|
|
|
|
declare_test!{ExecutiveTests_vmtests, "VMTests/vmtests"}
|