diff --git a/rust-evmjit/src/lib.rs b/rust-evmjit/src/lib.rs
index 670509d87..a4381619b 100644
--- a/rust-evmjit/src/lib.rs
+++ b/rust-evmjit/src/lib.rs
@@ -127,6 +127,12 @@ impl ContextHandle {
unsafe { std::slice::from_raw_parts(self.data_handle.call_data, self.data_handle.call_data_size as usize) }
}
+ /// Returns address to which funds should be transfered after suicide.
+ pub fn suicide_refund_address(&self) -> JitI256 {
+ // evmjit reuses data_handle address field to store suicide address
+ self.data_handle.address
+ }
+
/// Returns gas left.
pub fn gas_left(&self) -> u64 {
self.data_handle.gas as u64
diff --git a/src/evm/ext.rs b/src/evm/ext.rs
index b878c5819..52b8af24f 100644
--- a/src/evm/ext.rs
+++ b/src/evm/ext.rs
@@ -6,7 +6,6 @@ use util::bytes::*;
use evm::{Schedule, Error};
use env_info::*;
-// TODO: replace all u64 with u256
pub trait Ext {
/// Returns a value for given key.
fn sload(&self, key: &H256) -> H256;
@@ -25,20 +24,20 @@ pub trait Ext {
/// If contract creation is successfull, return gas_left and contract address,
/// If depth is too big or transfer value exceeds balance, return None
/// Otherwise return appropriate `Error`.
- fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option
), Error>;
+ fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option), Error>;
/// Message call.
///
/// If call is successfull, returns gas left.
/// otherwise `Error`.
fn call(&mut self,
- gas: u64,
- call_gas: u64,
+ gas: &U256,
+ call_gas: &U256,
receive_address: &Address,
value: &U256,
data: &[u8],
code_address: &Address,
- output: &mut [u8]) -> Result;
+ output: &mut [u8]) -> Result;
/// Returns code at given address
fn extcode(&self, address: &Address) -> Vec;
@@ -48,10 +47,11 @@ pub trait Ext {
/// Should be called when transaction calls `RETURN` opcode.
/// Returns gas_left if cost of returning the data is not too high.
- fn ret(&mut self, gas: u64, data: &[u8]) -> Result;
+ fn ret(&mut self, gas: &U256, data: &[u8]) -> Result;
/// Should be called when contract commits suicide.
- fn suicide(&mut self);
+ /// Address to which funds should be refunded.
+ fn suicide(&mut self, refund_address: &Address);
/// Returns schedule.
fn schedule(&self) -> &Schedule;
diff --git a/src/evm/jit.rs b/src/evm/jit.rs
index fb71f3363..f907c4be8 100644
--- a/src/evm/jit.rs
+++ b/src/evm/jit.rs
@@ -209,19 +209,20 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
init_size: u64,
address: *mut evmjit::H256) {
unsafe {
- match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
+ match self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
Ok((gas_left, opt)) => {
- *io_gas = gas_left;
- if let Some(addr) = opt {
- *address = addr.into_jit();
- }
+ *io_gas = gas_left.low_u64();
+ *address = match opt {
+ Some(addr) => addr.into_jit(),
+ _ => Address::new().into_jit()
+ };
},
Err(err @ evm::Error::OutOfGas) => {
*self.err = Some(err);
// hack to propagate `OutOfGas` to evmjit and stop
// the execution immediately.
// Works, cause evmjit uses i64, not u64
- *io_gas = -1i64 as u64
+ *io_gas = -1i64 as u64;
},
Err(err) => *self.err = Some(err)
}
@@ -239,8 +240,8 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
out_size: u64,
code_address: *const evmjit::H256) -> bool {
unsafe {
- let res = self.ext.call(*io_gas,
- call_gas,
+ let res = self.ext.call(&U256::from(*io_gas),
+ &U256::from(call_gas),
&Address::from_jit(&*receive_address),
&U256::from_jit(&*value),
slice::from_raw_parts(in_beg, in_size as usize),
@@ -249,7 +250,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
match res {
Ok(gas_left) => {
- *io_gas = gas_left;
+ *io_gas = gas_left.low_u64();
true
},
Err(err @ evm::Error::OutOfGas) => {
@@ -344,10 +345,9 @@ impl evm::Evm for JitEvm {
match res {
evmjit::ReturnCode::Stop => Ok(U256::from(context.gas_left())),
- evmjit::ReturnCode::Return => ext.ret(context.gas_left(), context.output_data()).map(|gas_left| U256::from(gas_left)),
+ evmjit::ReturnCode::Return => ext.ret(&U256::from(context.gas_left()), context.output_data()),
evmjit::ReturnCode::Suicide => {
- // what if there is a suicide and we run out of gas just after?
- ext.suicide();
+ ext.suicide(&Address::from_jit(&context.suicide_refund_address()));
Ok(U256::from(context.gas_left()))
},
evmjit::ReturnCode::OutOfGas => Err(evm::Error::OutOfGas),
diff --git a/src/evm/schedule.rs b/src/evm/schedule.rs
index 0f46bba12..d02fedc91 100644
--- a/src/evm/schedule.rs
+++ b/src/evm/schedule.rs
@@ -50,7 +50,7 @@ impl Schedule {
exceptional_failed_code_deposit: efcd,
have_delegate_call: hdc,
stack_limit: 1024,
- tier_step_gas: [0usize, 2, 3, 4, 5, 6, 10, 20],
+ tier_step_gas: [0usize, 2, 3, 4, 5, 8, 10, 20],
exp_gas: 10,
exp_byte_gas: 10,
sha3_gas: 30,
diff --git a/src/evm/tests.rs b/src/evm/tests.rs
index e3f81dd93..7aff9f407 100644
--- a/src/evm/tests.rs
+++ b/src/evm/tests.rs
@@ -42,18 +42,18 @@ impl Ext for FakeExt {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
}
- fn create(&mut self, _gas: u64, _value: &U256, _code: &[u8]) -> result::Result<(u64, Option), evm::Error> {
+ fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> result::Result<(U256, Option), evm::Error> {
unimplemented!();
}
fn call(&mut self,
- _gas: u64,
- _call_gas: u64,
+ _gas: &U256,
+ _call_gas: &U256,
_receive_address: &Address,
_value: &U256,
_data: &[u8],
_code_address: &Address,
- _output: &mut [u8]) -> result::Result {
+ _output: &mut [u8]) -> result::Result {
unimplemented!();
}
@@ -68,11 +68,11 @@ impl Ext for FakeExt {
});
}
- fn ret(&mut self, _gas: u64, _data: &[u8]) -> result::Result {
+ fn ret(&mut self, _gas: &U256, _data: &[u8]) -> result::Result {
unimplemented!();
}
- fn suicide(&mut self) {
+ fn suicide(&mut self, _refund_address: &Address) {
unimplemented!();
}
diff --git a/src/executive.rs b/src/executive.rs
index 9f04293a0..ea135194f 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![],
@@ -160,7 +160,9 @@ impl<'a> Executive<'a> {
code: self.state.code(address).unwrap_or(vec![]),
data: t.data.clone(),
};
- self.call(¶ms, &mut substate, &mut [])
+ // TODO: move output upstream
+ let mut out = vec![];
+ self.call(¶ms, &mut substate, BytesRef::Flexible(&mut out))
}
};
@@ -172,7 +174,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, mut output: BytesRef) -> evm::Result {
// at first, transfer value to destination
self.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
@@ -181,7 +183,7 @@ impl<'a> Executive<'a> {
let cost = self.engine.cost_of_builtin(¶ms.address, ¶ms.data);
match cost <= params.gas {
true => {
- self.engine.execute_builtin(¶ms.address, ¶ms.data, output);
+ self.engine.execute_builtin(¶ms.address, ¶ms.data, &mut output);
Ok(params.gas - cost)
},
false => Err(evm::Error::OutOfGas)
@@ -267,20 +269,26 @@ impl<'a> Executive<'a> {
}
/// Policy for handling output data on `RETURN` opcode.
-enum OutputPolicy<'a> {
+pub enum OutputPolicy<'a> {
/// Return reference to fixed sized output.
/// Used for message calls.
- Return(&'a mut [u8]),
+ Return(BytesRef<'a>),
/// Init new contract as soon as `RETURN` is called.
InitContract
}
/// Implementation of evm Externalities.
-struct Externalities<'a> {
+pub struct Externalities<'a> {
+ #[cfg(test)]
+ pub state: &'a mut State,
+ #[cfg(not(test))]
state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
+ #[cfg(test)]
+ pub params: &'a ActionParams,
+ #[cfg(not(test))]
params: &'a ActionParams,
substate: &'a mut Substate,
schedule: Schedule,
@@ -289,7 +297,7 @@ struct Externalities<'a> {
impl<'a> Externalities<'a> {
/// Basic `Externalities` constructor.
- fn new(state: &'a mut State,
+ pub fn new(state: &'a mut State,
info: &'a EnvInfo,
engine: &'a Engine,
depth: usize,
@@ -332,19 +340,19 @@ 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()),
}
}
- fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option), evm::Error> {
+ fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option), evm::Error> {
// if balance is insufficient or we are to deep, return
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
- return Ok((gas, None));
+ return Ok((*gas, None));
}
// create new contract address
@@ -355,7 +363,7 @@ impl<'a> Ext for Externalities<'a> {
address: address.clone(),
sender: self.params.address.clone(),
origin: self.params.origin.clone(),
- gas: U256::from(gas),
+ gas: *gas,
gas_price: self.params.gas_price.clone(),
value: value.clone(),
code: code.to_vec(),
@@ -364,29 +372,29 @@ impl<'a> Ext for Externalities<'a> {
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
ex.state.inc_nonce(&self.params.address);
- ex.create(¶ms, self.substate).map(|gas_left| (gas_left.low_u64(), Some(address)))
+ ex.create(¶ms, self.substate).map(|gas_left| (gas_left, Some(address)))
}
- fn call(&mut self, gas: u64, call_gas: u64, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result {
- let mut gas_cost = call_gas;
- let mut call_gas = call_gas;
+ fn call(&mut self, gas: &U256, call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result {
+ let mut gas_cost = *call_gas;
+ let mut call_gas = *call_gas;
let is_call = receive_address == code_address;
if is_call && !self.state.exists(&code_address) {
- gas_cost = gas_cost + self.schedule.call_new_account_gas as u64;
+ gas_cost = gas_cost + U256::from(self.schedule.call_new_account_gas);
}
if *value > U256::zero() {
assert!(self.schedule.call_value_transfer_gas > self.schedule.call_stipend, "overflow possible");
- gas_cost = gas_cost + self.schedule.call_value_transfer_gas as u64;
- call_gas = call_gas + self.schedule.call_stipend as u64;
+ gas_cost = gas_cost + U256::from(self.schedule.call_value_transfer_gas);
+ call_gas = call_gas + U256::from(self.schedule.call_stipend);
}
- if gas_cost > gas {
+ if gas_cost > *gas {
return Err(evm::Error::OutOfGas)
}
- let gas = gas - gas_cost;
+ let gas = *gas - gas_cost;
// if balance is insufficient or we are to deep, return
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.stack_limit {
@@ -397,7 +405,7 @@ impl<'a> Ext for Externalities<'a> {
address: receive_address.clone(),
sender: self.params.address.clone(),
origin: self.params.origin.clone(),
- gas: U256::from(call_gas),
+ gas: call_gas,
gas_price: self.params.gas_price.clone(),
value: value.clone(),
code: self.state.code(code_address).unwrap_or(vec![]),
@@ -405,29 +413,33 @@ impl<'a> Ext for Externalities<'a> {
};
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
- ex.call(¶ms, self.substate, output).map(|gas_left| {
- gas + gas_left.low_u64()
- })
+ ex.call(¶ms, self.substate, BytesRef::Fixed(output))
}
fn extcode(&self, address: &Address) -> Vec {
self.state.code(address).unwrap_or(vec![])
}
- fn ret(&mut self, gas: u64, data: &[u8]) -> Result {
- println!("ret");
+ fn ret(&mut self, gas: &U256, data: &[u8]) -> Result {
match &mut self.output {
- &mut OutputPolicy::Return(ref mut slice) => unsafe {
+ &mut OutputPolicy::Return(BytesRef::Fixed(ref mut slice)) => unsafe {
let len = cmp::min(slice.len(), data.len());
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
- Ok(gas)
+ Ok(*gas)
+ },
+ &mut OutputPolicy::Return(BytesRef::Flexible(ref mut vec)) => unsafe {
+ vec.clear();
+ vec.reserve(data.len());
+ ptr::copy(data.as_ptr(), vec.as_mut_ptr(), data.len());
+ vec.set_len(data.len());
+ Ok(*gas)
},
&mut OutputPolicy::InitContract => {
- let return_cost = data.len() as u64 * self.schedule.create_data_gas as u64;
- if return_cost > gas {
+ let return_cost = U256::from(data.len()) * U256::from(self.schedule.create_data_gas);
+ if return_cost > *gas {
return match self.schedule.exceptional_failed_code_deposit {
true => Err(evm::Error::OutOfGas),
- false => Ok(gas)
+ false => Ok(*gas)
}
}
let mut code = vec![];
@@ -439,7 +451,7 @@ impl<'a> Ext for Externalities<'a> {
let address = &self.params.address;
self.state.init_code(address, code);
self.substate.contracts_created.push(address.clone());
- Ok(gas - return_cost)
+ Ok(*gas - return_cost)
}
}
}
@@ -449,8 +461,10 @@ impl<'a> Ext for Externalities<'a> {
self.substate.logs.push(LogEntry::new(address, topics, data));
}
- fn suicide(&mut self) {
+ fn suicide(&mut self, refund_address: &Address) {
let address = self.params.address.clone();
+ let balance = self.balance(&address);
+ self.state.transfer_balance(&address, refund_address, &balance);
self.substate.suicides.insert(address);
}
@@ -472,7 +486,6 @@ mod tests {
use engine::*;
use spec::*;
use evm::Schedule;
- use super::Substate;
struct TestEngine {
spec: Spec,
@@ -482,7 +495,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
}
}
@@ -585,7 +598,60 @@ mod tests {
ex.create(¶ms, &mut substate).unwrap()
};
- assert_eq!(gas_left, U256::from(47_976));
+ assert_eq!(gas_left, U256::from(62_976));
+ // ended with max depth
+ assert_eq!(substate.contracts_created.len(), 0);
+ }
+
+ #[test]
+ fn test_create_contract_value_too_high() {
+ // code:
+ //
+ // 7c 601080600c6000396000f3006000355415600957005b60203560003555 - push 29 bytes?
+ // 60 00 - push 0
+ // 52
+ // 60 1d - push 29
+ // 60 03 - push 3
+ // 60 e6 - push 230
+ // f0 - create a contract trying to send 230.
+ // 60 00 - push 0
+ // 55 sstore
+ //
+ // other code:
+ //
+ // 60 10 - push 16
+ // 80 - duplicate first stack item
+ // 60 0c - push 12
+ // 60 00 - push 0
+ // 39 - copy current code to memory
+ // 60 00 - push 0
+ // f3 - return
+
+ let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap();
+
+ let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
+ let address = contract_address(&sender, &U256::zero());
+ // TODO: add tests for 'callcreate'
+ //let next_address = contract_address(&address, &U256::zero());
+ let mut params = ActionParams::new();
+ params.address = address.clone();
+ params.sender = sender.clone();
+ params.origin = sender.clone();
+ params.gas = U256::from(100_000);
+ params.code = code.clone();
+ params.value = U256::from(100);
+ let mut state = State::new_temp();
+ state.add_balance(&sender, &U256::from(100));
+ let info = EnvInfo::new();
+ let engine = TestEngine::new(0);
+ let mut substate = Substate::new();
+
+ let gas_left = {
+ let mut ex = Executive::new(&mut state, &info, &engine);
+ ex.create(¶ms, &mut substate).unwrap()
+ };
+
+ assert_eq!(gas_left, U256::from(62_976));
assert_eq!(substate.contracts_created.len(), 0);
}
@@ -691,7 +757,7 @@ mod tests {
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
- ex.call(¶ms, &mut substate, &mut []).unwrap()
+ ex.call(¶ms, &mut substate, BytesRef::Fixed(&mut [])).unwrap()
};
assert_eq!(gas_left, U256::from(73_237));
@@ -733,7 +799,7 @@ mod tests {
let gas_left = {
let mut ex = Executive::new(&mut state, &info, &engine);
- ex.call(¶ms, &mut substate, &mut []).unwrap()
+ ex.call(¶ms, &mut substate, BytesRef::Fixed(&mut [])).unwrap()
};
assert_eq!(gas_left, U256::from(59_870));
@@ -864,8 +930,8 @@ mod tests {
match res {
Err(Error::Execution(ExecutionError::NotEnoughCash { required , is }))
- if required == U512::zero() && is == U512::one() => (),
- _ => assert!(false, "Expected not enough cash error.")
+ if required == U512::from(100_018) && is == U512::from(100_017) => (),
+ _ => assert!(false, "Expected not enough cash error. {:?}", res)
}
}
}
diff --git a/src/tests/executive.rs b/src/tests/executive.rs
new file mode 100644
index 000000000..cfe80dfc1
--- /dev/null
+++ b/src/tests/executive.rs
@@ -0,0 +1,293 @@
+use super::test_common::*;
+use state::*;
+use executive::*;
+use spec::*;
+use engine::*;
+use evm;
+use evm::{Schedule, Ext, Factory};
+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
+ }
+}
+
+struct CallCreate {
+ data: Bytes,
+ destination: Address,
+ _gas_limit: U256,
+ value: U256
+}
+
+/// Tiny wrapper around executive externalities.
+/// Stores callcreates.
+struct TestExt<'a> {
+ ext: Externalities<'a>,
+ callcreates: Vec
+}
+
+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)
+ }
+
+ fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option), evm::Error> {
+ // 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
+ Ok((gas_left, Some(address))) => {
+ self.callcreates.push(CallCreate {
+ data: code.to_vec(),
+ destination: address.clone(),
+ _gas_limit: *gas,
+ value: *value
+ });
+ Ok((gas_left, Some(address)))
+ },
+ // creation failed only due to reaching stack_limit
+ Ok((gas_left, None)) if ext.state.balance(&ext.params.address) >= *value => {
+ let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address));
+ self.callcreates.push(CallCreate {
+ data: code.to_vec(),
+ // TODO: address is not stored here?
+ destination: Address::new(),
+ _gas_limit: *gas,
+ value: *value
+ });
+ Ok((gas_left, Some(address)))
+ },
+ other => other
+ }
+ }
+
+ fn call(&mut self,
+ gas: &U256,
+ call_gas: &U256,
+ receive_address: &Address,
+ value: &U256,
+ data: &[u8],
+ code_address: &Address,
+ output: &mut [u8]) -> Result {
+ let res = self.ext.call(gas, call_gas, receive_address, value, data, code_address, output);
+ let ext = &self.ext;
+ match res {
+ Ok(gas_left) if ext.state.balance(&ext.params.address) >= *value => {
+ self.callcreates.push(CallCreate {
+ data: data.to_vec(),
+ destination: receive_address.clone(),
+ _gas_limit: *call_gas,
+ value: *value
+ });
+ Ok(gas_left)
+ },
+ other => other
+ }
+ }
+
+ fn extcode(&self, address: &Address) -> Vec {
+ self.ext.extcode(address)
+ }
+
+ fn log(&mut self, topics: Vec, data: Bytes) {
+ self.ext.log(topics, data)
+ }
+
+ fn ret(&mut self, gas: &U256, data: &[u8]) -> Result {
+ self.ext.ret(gas, data)
+ }
+
+ fn suicide(&mut self, refund_address: &Address) {
+ self.ext.suicide(refund_address)
+ }
+
+ fn schedule(&self) -> &Schedule {
+ self.ext.schedule()
+ }
+
+ fn env_info(&self) -> &EnvInfo {
+ self.ext.env_info()
+ }
+}
+
+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() {
+ // sync io is usefull when something crashes in jit
+ //::std::io::stdout().write(&name.as_bytes());
+ //::std::io::stdout().write(b"\n");
+ //::std::io::stdout().flush();
+ 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(addr.as_ref());
+ 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();
+ let mut output = vec![];
+
+ // execute
+ let (res, callcreates) = {
+ let ex = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::Return(BytesRef::Flexible(&mut output)));
+ let mut test_ext = TestExt::new(ex);
+ let evm = Factory::create();
+ let res = evm.exec(¶ms, &mut test_ext);
+ (res, test_ext.callcreates)
+ };
+
+ // then validate
+ match res {
+ Err(_) => fail_unless(out_of_gas, "didn't expect to run out of gas."),
+ Ok(gas_left) => {
+ //println!("name: {}, gas_left : {:?}, expected: {:?}", name, gas_left, u256_from_json(&test["gas"]));
+ fail_unless(!out_of_gas, "expected to run out of gas.");
+ fail_unless(gas_left == u256_from_json(&test["gas"]), "gas_left is incorrect");
+ fail_unless(output == bytes_from_json(&test["out"]), "output is incorrect");
+
+
+ test.find("post").map(|pre| for (addr, s) in pre.as_object().unwrap() {
+ let address = Address::from(addr.as_ref());
+ //let balance = u256_from_json(&s["balance"]);
+
+ fail_unless(state.code(&address).unwrap_or(vec![]) == bytes_from_json(&s["code"]), "code is incorrect");
+ fail_unless(state.balance(&address) == u256_from_json(&s["balance"]), "balance is incorrect");
+ fail_unless(state.nonce(&address) == u256_from_json(&s["nonce"]), "nonce is incorrect");
+
+ 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));
+
+ fail_unless(state.storage_at(&address, &key) == val, "storage is incorrect");
+ }
+ });
+
+ let cc = test["callcreates"].as_array().unwrap();
+ fail_unless(callcreates.len() == cc.len(), "callcreates does not match");
+ for i in 0..cc.len() {
+ let is = &callcreates[i];
+ let expected = &cc[i];
+ fail_unless(is.data == bytes_from_json(&expected["data"]), "callcreates data is incorrect");
+ fail_unless(is.destination == address_from_json(&expected["destination"]), "callcreates destination is incorrect");
+ fail_unless(is.value == u256_from_json(&expected["value"]), "callcreates value is incorrect");
+
+ // TODO: call_gas is calculated in externalities and is not exposed to TestExt.
+ // maybe move it to it's own function to simplify calculation?
+ //println!("name: {:?}, is {:?}, expected: {:?}", name, is.gas_limit, u256_from_json(&expected["gasLimit"]));
+ //fail_unless(is.gas_limit == u256_from_json(&expected["gasLimit"]), "callcreates gas_limit is incorrect");
+ }
+ }
+ }
+ }
+
+
+ for f in failed.iter() {
+ println!("FAILED: {:?}", f);
+ }
+
+ //assert!(false);
+ failed
+}
+
+declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
+declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
+// this one crashes with some vm internal error. Separately they pass.
+declare_test_ignore!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
+declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
+declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
+// this one take way too long.
+declare_test_ignore!{ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"}
+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"}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 4c0b685a1..c30f7f9b8 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -2,4 +2,5 @@
mod test_common;
mod transaction;
-mod state;
\ No newline at end of file
+mod executive;
+mod state;
diff --git a/src/tests/state.rs b/src/tests/state.rs
index 197f2d4d8..6ae01a334 100644
--- a/src/tests/state.rs
+++ b/src/tests/state.rs
@@ -4,7 +4,7 @@ use ethereum;
pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap {
json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut m, (key, value)| {
- m.insert(H256::from(&u256_from_hex(key)), H256::from(&u256_from_json(value)));
+ m.insert(H256::from(&u256_from_str(key)), H256::from(&u256_from_json(value)));
m
})
}