gas_left is correct in all tests
This commit is contained in:
parent
68a2c843ef
commit
920d8c51db
@ -212,16 +212,17 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
|
|||||||
match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
|
match self.ext.create(*io_gas, &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) {
|
||||||
Ok((gas_left, opt)) => {
|
Ok((gas_left, opt)) => {
|
||||||
*io_gas = gas_left;
|
*io_gas = gas_left;
|
||||||
if let Some(addr) = opt {
|
*address = match opt {
|
||||||
*address = addr.into_jit();
|
Some(addr) => addr.into_jit(),
|
||||||
}
|
_ => Address::new().into_jit()
|
||||||
|
};
|
||||||
},
|
},
|
||||||
Err(err @ evm::Error::OutOfGas) => {
|
Err(err @ evm::Error::OutOfGas) => {
|
||||||
*self.err = Some(err);
|
*self.err = Some(err);
|
||||||
// hack to propagate `OutOfGas` to evmjit and stop
|
// hack to propagate `OutOfGas` to evmjit and stop
|
||||||
// the execution immediately.
|
// the execution immediately.
|
||||||
// Works, cause evmjit uses i64, not u64
|
// Works, cause evmjit uses i64, not u64
|
||||||
*io_gas = -1i64 as u64
|
*io_gas = -1i64 as u64;
|
||||||
},
|
},
|
||||||
Err(err) => *self.err = Some(err)
|
Err(err) => *self.err = Some(err)
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ impl<'a> Executive<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Policy for handling output data on `RETURN` opcode.
|
/// Policy for handling output data on `RETURN` opcode.
|
||||||
enum OutputPolicy<'a> {
|
pub enum OutputPolicy<'a> {
|
||||||
/// Return reference to fixed sized output.
|
/// Return reference to fixed sized output.
|
||||||
/// Used for message calls.
|
/// Used for message calls.
|
||||||
Return(&'a mut [u8]),
|
Return(&'a mut [u8]),
|
||||||
@ -276,12 +276,12 @@ enum OutputPolicy<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of evm Externalities.
|
/// Implementation of evm Externalities.
|
||||||
struct Externalities<'a> {
|
pub struct Externalities<'a> {
|
||||||
state: &'a mut State,
|
pub state: &'a mut State,
|
||||||
info: &'a EnvInfo,
|
info: &'a EnvInfo,
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
params: &'a ActionParams,
|
pub params: &'a ActionParams,
|
||||||
substate: &'a mut Substate,
|
substate: &'a mut Substate,
|
||||||
schedule: Schedule,
|
schedule: Schedule,
|
||||||
output: OutputPolicy<'a>
|
output: OutputPolicy<'a>
|
||||||
@ -289,7 +289,7 @@ struct Externalities<'a> {
|
|||||||
|
|
||||||
impl<'a> Externalities<'a> {
|
impl<'a> Externalities<'a> {
|
||||||
/// Basic `Externalities` constructor.
|
/// Basic `Externalities` constructor.
|
||||||
fn new(state: &'a mut State,
|
pub fn new(state: &'a mut State,
|
||||||
info: &'a EnvInfo,
|
info: &'a EnvInfo,
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
@ -584,7 +584,60 @@ mod tests {
|
|||||||
ex.create(¶ms, &mut substate).unwrap()
|
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);
|
assert_eq!(substate.contracts_created.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -863,8 +916,8 @@ mod tests {
|
|||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(Error::Execution(ExecutionError::NotEnoughCash { required , is }))
|
Err(Error::Execution(ExecutionError::NotEnoughCash { required , is }))
|
||||||
if required == U512::zero() && is == U512::one() => (),
|
if required == U512::from(100_018) && is == U512::from(100_017) => (),
|
||||||
_ => assert!(false, "Expected not enough cash error.")
|
_ => assert!(false, "Expected not enough cash error. {:?}", res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ use state::*;
|
|||||||
use executive::*;
|
use executive::*;
|
||||||
use spec::*;
|
use spec::*;
|
||||||
use engine::*;
|
use engine::*;
|
||||||
use evm::{Schedule};
|
use evm;
|
||||||
|
use evm::{Schedule, Ext, Factory};
|
||||||
use ethereum;
|
use ethereum;
|
||||||
|
|
||||||
struct TestEngine {
|
struct TestEngine {
|
||||||
@ -30,13 +31,134 @@ impl Engine for TestEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(&mut self, gas: u64, value: &U256, code: &[u8]) -> Result<(u64, Option<Address>), 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: U256::from(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: U256::from(gas),
|
||||||
|
value: *value
|
||||||
|
});
|
||||||
|
Ok((gas_left, Some(address)))
|
||||||
|
},
|
||||||
|
other => other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self,
|
||||||
|
gas: u64,
|
||||||
|
call_gas: u64,
|
||||||
|
receive_address: &Address,
|
||||||
|
value: &U256,
|
||||||
|
data: &[u8],
|
||||||
|
code_address: &Address,
|
||||||
|
output: &mut [u8]) -> Result<u64, evm::Error> {
|
||||||
|
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: U256::from(call_gas),
|
||||||
|
value: *value
|
||||||
|
});
|
||||||
|
Ok(gas_left)
|
||||||
|
},
|
||||||
|
other => other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extcode(&self, address: &Address) -> Vec<u8> {
|
||||||
|
self.ext.extcode(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&mut self, topics: Vec<H256>, data: Bytes) {
|
||||||
|
self.ext.log(topics, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ret(&mut self, gas: u64, data: &[u8]) -> Result<u64, evm::Error> {
|
||||||
|
self.ext.ret(gas, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suicide(&mut self) {
|
||||||
|
self.ext.suicide()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule(&self) -> &Schedule {
|
||||||
|
self.ext.schedule()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn env_info(&self) -> &EnvInfo {
|
||||||
|
self.ext.env_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
||||||
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
|
let json = Json::from_str(::std::str::from_utf8(json_data).unwrap()).expect("Json is invalid");
|
||||||
let mut failed = Vec::new();
|
let mut failed = Vec::new();
|
||||||
for (name, test) in json.as_object().unwrap() {
|
for (name, test) in json.as_object().unwrap() {
|
||||||
::std::io::stdout().write(&name.as_bytes());
|
//::std::io::stdout().write(&name.as_bytes());
|
||||||
::std::io::stdout().write(b"\n");
|
//::std::io::stdout().write(b"\n");
|
||||||
::std::io::stdout().flush();
|
//::std::io::stdout().flush();
|
||||||
//println!("name: {:?}", name);
|
//println!("name: {:?}", name);
|
||||||
let mut fail = false;
|
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 };
|
||||||
@ -94,8 +216,10 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
|
|
||||||
// execute
|
// execute
|
||||||
let res = {
|
let res = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let ex = Externalities::new(&mut state, &info, &engine, 0, ¶ms, &mut substate, OutputPolicy::Return(&mut []));
|
||||||
ex.call(¶ms, &mut substate, &mut [])
|
let mut test_ext = TestExt::new(ex);
|
||||||
|
let evm = Factory::create();
|
||||||
|
evm.exec(¶ms, &mut test_ext)
|
||||||
};
|
};
|
||||||
|
|
||||||
// then validate
|
// then validate
|
||||||
@ -119,7 +243,16 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
|
declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"}
|
||||||
declare_test!{ExecutiveTests_vmSha3Test, "VMTests/vmSha3Test"}
|
|
||||||
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
|
declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"}
|
||||||
|
// this one crashes with some vm internal error. Separately they pass.
|
||||||
//declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
|
//declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"}
|
||||||
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
|
declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"}
|
||||||
|
declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"}
|
||||||
|
// this one take way too long.
|
||||||
|
//declare_test!{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"}
|
||||||
|
Loading…
Reference in New Issue
Block a user