diff --git a/src/executive.rs b/src/executive.rs
index 9f04293a0..c4d3dbc18 100644
--- a/src/executive.rs
+++ b/src/executive.rs
@@ -14,7 +14,7 @@ pub fn contract_address(address: &Address, nonce: &U256) -> Address {
/// State changes which should be applied in finalize,
/// after transaction is fully executed.
-struct Substate {
+pub struct Substate {
/// Any accounts that have suicided.
suicides: HashSet
,
/// Any logs.
@@ -27,7 +27,7 @@ struct Substate {
impl Substate {
/// Creates new substate.
- fn new() -> Self {
+ pub fn new() -> Self {
Substate {
suicides: HashSet::new(),
logs: vec![],
@@ -172,7 +172,7 @@ impl<'a> Executive<'a> {
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`.
- fn call(&mut self, params: &ActionParams, substate: &mut Substate, output: &mut [u8]) -> evm::Result {
+ pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, output: &mut [u8]) -> evm::Result {
// at first, transfer value to destination
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
@@ -332,12 +332,12 @@ impl<'a> Ext for Externalities<'a> {
}
fn blockhash(&self, number: &U256) -> H256 {
- match *number < U256::from(self.info.number) {
- false => H256::from(&U256::zero()),
+ match *number < U256::from(self.info.number) && number.low_u64() >= cmp::max(256, self.info.number) - 256 {
true => {
- let index = U256::from(self.info.number) - *number - U256::one();
- self.info.last_hashes[index.low_u32() as usize].clone()
- }
+ let index = self.info.number - number.low_u64() - 1;
+ self.info.last_hashes[index as usize].clone()
+ },
+ false => H256::from(&U256::zero()),
}
}
@@ -415,7 +415,6 @@ impl<'a> Ext for Externalities<'a> {
}
fn ret(&mut self, gas: u64, data: &[u8]) -> Result {
- println!("ret");
match &mut self.output {
&mut OutputPolicy::Return(ref mut slice) => unsafe {
let len = cmp::min(slice.len(), data.len());
@@ -472,7 +471,7 @@ mod tests {
use engine::*;
use spec::*;
use evm::Schedule;
- use super::Substate;
+ //use super::Substate;
struct TestEngine {
spec: Spec,
@@ -482,7 +481,7 @@ mod tests {
impl TestEngine {
fn new(stack_limit: usize) -> TestEngine {
TestEngine {
- spec: ethereum::new_frontier(),
+ spec: ethereum::new_frontier_test(),
stack_limit: stack_limit
}
}
diff --git a/src/tests/executive.rs b/src/tests/executive.rs
new file mode 100644
index 000000000..eb3f1bcfd
--- /dev/null
+++ b/src/tests/executive.rs
@@ -0,0 +1,125 @@
+use super::test_common::*;
+use state::*;
+use executive::*;
+use spec::*;
+use engine::*;
+use evm::{Schedule};
+use ethereum;
+
+struct TestEngine {
+ spec: Spec,
+ stack_limit: usize
+}
+
+impl TestEngine {
+ fn new(stack_limit: usize) -> TestEngine {
+ TestEngine {
+ spec: ethereum::new_frontier_test(),
+ stack_limit: stack_limit
+ }
+ }
+}
+
+impl Engine for TestEngine {
+ fn name(&self) -> &str { "TestEngine" }
+ fn spec(&self) -> &Spec { &self.spec }
+ fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
+ let mut schedule = Schedule::new_frontier();
+ schedule.stack_limit = self.stack_limit;
+ schedule
+ }
+}
+
+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();
+ for (name, test) in json.as_object().unwrap() {
+ ::std::io::stdout().write(&name.as_bytes());
+ ::std::io::stdout().write(b"\n");
+ ::std::io::stdout().flush();
+ //println!("name: {:?}", name);
+ 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 { failed.push(name.to_string() + ": "+ s); fail = true };
+
+ // test env
+ let mut state = State::new_temp();
+
+ test.find("pre").map(|pre| for (addr, s) in pre.as_object().unwrap() {
+ let address = address_from_str(addr);
+ let balance = u256_from_json(&s["balance"]);
+ let code = bytes_from_json(&s["code"]);
+ let nonce = u256_from_json(&s["nonce"]);
+
+ state.new_contract(&address);
+ state.add_balance(&address, &balance);
+ state.init_code(&address, code);
+
+ for (k, v) in s["storage"].as_object().unwrap() {
+ let key = H256::from(&u256_from_str(k));
+ let val = H256::from(&u256_from_json(v));
+ state.set_storage(&address, key, val);
+ }
+ });
+
+ let mut info = EnvInfo::new();
+
+ test.find("env").map(|env| {
+ info.author = address_from_json(&env["currentCoinbase"]);
+ info.difficulty = u256_from_json(&env["currentDifficulty"]);
+ info.gas_limit = u256_from_json(&env["currentGasLimit"]);
+ info.number = u256_from_json(&env["currentNumber"]).low_u64();
+ info.timestamp = u256_from_json(&env["currentTimestamp"]).low_u64();
+ });
+
+ let engine = TestEngine::new(0);
+
+ // params
+ let mut params = ActionParams::new();
+ test.find("exec").map(|exec| {
+ params.address = address_from_json(&exec["address"]);
+ params.sender = address_from_json(&exec["caller"]);
+ params.origin = address_from_json(&exec["origin"]);
+ params.code = bytes_from_json(&exec["code"]);
+ params.data = bytes_from_json(&exec["data"]);
+ params.gas = u256_from_json(&exec["gas"]);
+ params.gas_price = u256_from_json(&exec["gasPrice"]);
+ params.value = u256_from_json(&exec["value"]);
+ });
+
+ let out_of_gas = test.find("callcreates").map(|calls| {
+ }).is_none();
+
+ let mut substate = Substate::new();
+
+ // execute
+ let res = {
+ let mut ex = Executive::new(&mut state, &info, &engine);
+ ex.call(¶ms, &mut substate, &mut [])
+ };
+
+ // then validate
+ match res {
+ Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
+ Ok(gas_left) => {
+ fail_unless(!out_of_gas, "expected to run out of gas.");
+ fail_unless(gas_left == u256_from_json(&test["gas"]), "gas_left is incorrect");
+ println!("name: {}, gas_left : {:?}, expected: {:?}", name, gas_left, u256_from_json(&test["gas"]));
+ }
+ }
+ }
+
+
+ for f in failed.iter() {
+ println!("FAILED: {:?}", f);
+ }
+
+ //assert!(false);
+ failed
+}
+
+declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
+declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"}
+declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
+//declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
+declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index b048a1c16..63f77837b 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -1,4 +1,5 @@
#[macro_use]
mod test_common;
-mod transaction;
\ No newline at end of file
+mod transaction;
+mod executive;
diff --git a/src/tests/test_common.rs b/src/tests/test_common.rs
index 8c2f25d8a..1f9b4c891 100644
--- a/src/tests/test_common.rs
+++ b/src/tests/test_common.rs
@@ -18,7 +18,10 @@ pub fn bytes_from_json(json: &Json) -> Bytes {
}
pub fn address_from_json(json: &Json) -> Address {
- let s = json.as_string().unwrap();
+ address_from_str(json.as_string().unwrap())
+}
+
+pub fn address_from_str<'a>(s: &'a str) -> Address {
if s.len() % 2 == 1 {
address_from_hex(&("0".to_string() + &(clean(s).to_string()))[..])
} else {
@@ -27,7 +30,10 @@ pub fn address_from_json(json: &Json) -> Address {
}
pub fn u256_from_json(json: &Json) -> U256 {
- let s = json.as_string().unwrap();
+ u256_from_str(json.as_string().unwrap())
+}
+
+pub fn u256_from_str<'a>(s: &'a str) -> U256 {
if s.len() >= 2 && &s[0..2] == "0x" {
// hex
U256::from_str(&s[2..]).unwrap()
diff --git a/src/tests/transaction.rs b/src/tests/transaction.rs
index b6f592628..7ec1f6802 100644
--- a/src/tests/transaction.rs
+++ b/src/tests/transaction.rs
@@ -8,7 +8,7 @@ fn do_json_test(json_data: &[u8]) -> Vec {
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 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())