executive create

This commit is contained in:
debris 2016-01-14 17:40:38 +01:00
parent 05246c4f7d
commit d59e074d65
6 changed files with 66 additions and 69 deletions

View File

@ -21,10 +21,8 @@ pub trait Ext {
/// Creates new contract. /// Creates new contract.
/// ///
/// If contract creation is successfull, return gas_left and contract address, /// Return gas_left and contract address if contract creation was succesfull.
/// If depth is too big or transfer value exceeds balance, return None fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>);
/// Otherwise return appropriate `Error`.
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), Error>;
/// Message call. /// Message call.
/// ///

View File

@ -209,23 +209,12 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
init_size: u64, init_size: u64,
address: *mut evmjit::H256) { address: *mut evmjit::H256) {
unsafe { unsafe {
match self.ext.create(&U256::from(*io_gas), &U256::from_jit(&*endowment), slice::from_raw_parts(init_beg, init_size as usize)) { let (gas_left, opt_addr) = 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.low_u64();
*io_gas = gas_left.low_u64(); *address = match opt_addr {
*address = match opt { Some(addr) => addr.into_jit(),
Some(addr) => addr.into_jit(), _ => Address::new().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;
},
Err(err) => *self.err = Some(err)
}
} }
} }
@ -254,11 +243,11 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> {
true true
}, },
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 = 0 as u64;
false false
}, },
Err(err) => { Err(err) => {
@ -337,6 +326,7 @@ impl evm::Evm for JitEvm {
let mut context = unsafe { evmjit::ContextHandle::new(data.into_jit(), &mut ext_handle) }; let mut context = unsafe { evmjit::ContextHandle::new(data.into_jit(), &mut ext_handle) };
let res = context.exec(); let res = context.exec();
println!("jit res: {:?}", res);
// check in adapter if execution of children contracts failed. // check in adapter if execution of children contracts failed.
if let Some(err) = optional_err { if let Some(err) = optional_err {

View File

@ -42,7 +42,7 @@ impl Ext for FakeExt {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone() self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
} }
fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> result::Result<(U256, Option<Address>), evm::Error> { fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> (U256, Option<Address>) {
unimplemented!(); unimplemented!();
} }

View File

@ -21,6 +21,8 @@ pub struct Substate {
logs: Vec<LogEntry>, logs: Vec<LogEntry>,
/// Refund counter of SSTORE nonzero->zero. /// Refund counter of SSTORE nonzero->zero.
refunds_count: U256, refunds_count: U256,
/// True if transaction, or one of it's subcalls runs out of gas.
out_of_gas: bool,
/// Created contracts. /// Created contracts.
contracts_created: Vec<Address> contracts_created: Vec<Address>
} }
@ -32,6 +34,7 @@ impl Substate {
suicides: HashSet::new(), suicides: HashSet::new(),
logs: vec![], logs: vec![],
refunds_count: U256::zero(), refunds_count: U256::zero(),
out_of_gas: false,
contracts_created: vec![] contracts_created: vec![]
} }
} }
@ -135,7 +138,6 @@ impl<'a> Executive<'a> {
self.state.sub_balance(&sender, &U256::from(gas_cost)); self.state.sub_balance(&sender, &U256::from(gas_cost));
let mut substate = Substate::new(); let mut substate = Substate::new();
let backup = self.state.clone();
let schedule = self.engine.schedule(self.info); let schedule = self.engine.schedule(self.info);
let init_gas = t.gas - U256::from(t.gas_required(&schedule)); let init_gas = t.gas - U256::from(t.gas_required(&schedule));
@ -172,7 +174,7 @@ impl<'a> Executive<'a> {
}; };
// finalize here! // finalize here!
Ok(try!(self.finalize(t, substate, backup, res))) Ok(try!(self.finalize(t, substate, res)))
} }
/// Calls contract function with given contract params. /// Calls contract function with given contract params.
@ -180,6 +182,9 @@ impl<'a> Executive<'a> {
/// Modifies the substate and the output. /// Modifies the substate and the output.
/// Returns either gas_left or `evm::Error`. /// Returns either gas_left or `evm::Error`.
pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result { pub fn call(&mut self, params: &ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result {
// backup used in case of running out of gas
let backup = self.state.clone();
// at first, transfer value to destination // at first, transfer value to destination
self.state.transfer_balance(&params.sender, &params.address, &params.value); self.state.transfer_balance(&params.sender, &params.address, &params.value);
@ -191,13 +196,18 @@ impl<'a> Executive<'a> {
self.engine.execute_builtin(&params.address, &params.data, &mut output); self.engine.execute_builtin(&params.address, &params.data, &mut output);
Ok(params.gas - cost) Ok(params.gas - cost)
}, },
false => Err(evm::Error::OutOfGas) // just drain the whole gas
false => Ok(U256::zero())
} }
} else if params.code.len() > 0 { } else if params.code.len() > 0 {
// if destination is a contract, do normal message call // if destination is a contract, do normal message call
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::Return(output));
let evm = Factory::create(); let res = {
evm.exec(&params, &mut ext) let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::Return(output));
let evm = Factory::create();
evm.exec(&params, &mut ext)
};
self.handle_out_of_gas(res, substate, backup)
} else { } else {
// otherwise, nothing // otherwise, nothing
Ok(params.gas) Ok(params.gas)
@ -208,44 +218,27 @@ impl<'a> Executive<'a> {
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
/// Modifies the substate. /// Modifies the substate.
fn create(&mut self, params: &ActionParams, substate: &mut Substate) -> evm::Result { fn create(&mut self, params: &ActionParams, substate: &mut Substate) -> evm::Result {
// backup used in case of running out of gas
let backup = self.state.clone();
// at first create new contract // at first create new contract
self.state.new_contract(&params.address); self.state.new_contract(&params.address);
// then transfer value to it // then transfer value to it
self.state.transfer_balance(&params.sender, &params.address, &params.value); self.state.transfer_balance(&params.sender, &params.address, &params.value);
let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract); let res = {
let evm = Factory::create(); let mut ext = Externalities::from_executive(self, params, substate, OutputPolicy::InitContract);
evm.exec(&params, &mut ext) let evm = Factory::create();
evm.exec(&params, &mut ext)
};
self.handle_out_of_gas(res, substate, backup)
} }
/// Finalizes the transaction (does refunds and suicides). /// Finalizes the transaction (does refunds and suicides).
fn finalize(&mut self, t: &Transaction, substate: Substate, mut backup: State, result: evm::Result) -> ExecutionResult { fn finalize(&mut self, t: &Transaction, substate: Substate, result: evm::Result) -> ExecutionResult {
match result { match result {
Err(evm::Error::Internal) => Err(ExecutionError::Internal), Err(evm::Error::Internal) => Err(ExecutionError::Internal),
Err(evm::Error::OutOfGas) => {
// apply first transfer to backup, and then revert everything
let sender = t.sender().unwrap();
match t.action() {
&Action::Create => {
let nonce = backup.nonce(&sender);
let address = contract_address(&sender, &nonce);
backup.new_contract(&address);
backup.transfer_balance(&sender, &address, &t.value);
}
&Action::Call(ref address) => backup.transfer_balance(&sender, address, &t.value)
}
self.state.revert(backup);
Ok(Executed {
gas: t.gas,
gas_used: t.gas,
refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas,
logs: vec![],
out_of_gas: true,
contracts_created: vec![]
})
},
Ok(gas_left) => { Ok(gas_left) => {
let schedule = self.engine.schedule(self.info); let schedule = self.engine.schedule(self.info);
@ -277,12 +270,21 @@ impl<'a> Executive<'a> {
refunded: refund, refunded: refund,
cumulative_gas_used: self.info.gas_used + gas_used, cumulative_gas_used: self.info.gas_used + gas_used,
logs: substate.logs, logs: substate.logs,
out_of_gas: false, out_of_gas: substate.out_of_gas,
contracts_created: substate.contracts_created contracts_created: substate.contracts_created
}) })
} },
_err => { unreachable!() }
} }
} }
pub fn handle_out_of_gas(&mut self, result: evm::Result, substate: &mut Substate, backup: State) -> evm::Result {
if let &Err(evm::Error::OutOfGas) = &result {
substate.out_of_gas = true;
self.state.revert(backup);
}
result
}
} }
/// Policy for handling output data on `RETURN` opcode. /// Policy for handling output data on `RETURN` opcode.
@ -366,10 +368,10 @@ impl<'a> Ext for Externalities<'a> {
} }
} }
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), evm::Error> { fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>) {
// if balance is insufficient or we are to deep, return // if balance is insufficient or we are to deep, return
if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth { if self.state.balance(&self.params.address) < *value || self.depth >= self.schedule.max_depth {
return Ok((*gas, None)); return (*gas, None);
} }
// create new contract address // create new contract address
@ -389,7 +391,11 @@ impl<'a> Ext for Externalities<'a> {
let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth); let mut ex = Executive::from_parent(self.state, self.info, self.engine, self.depth);
ex.state.inc_nonce(&self.params.address); ex.state.inc_nonce(&self.params.address);
ex.create(&params, self.substate).map(|gas_left| (gas_left, Some(address))) match ex.create(&params, self.substate) {
Ok(gas_left) => (gas_left, Some(address)),
_ => (U256::zero(), None)
}
//ex.create(&params, self.substate).map(|gas_left| (gas_left, Some(address)))
} }
fn call(&mut self, gas: &U256, call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<U256, evm::Error> { fn call(&mut self, gas: &U256, call_gas: &U256, receive_address: &Address, value: &U256, data: &[u8], code_address: &Address, output: &mut [u8]) -> Result<U256, evm::Error> {

View File

@ -71,24 +71,24 @@ impl<'a> Ext for TestExt<'a> {
self.ext.blockhash(number) self.ext.blockhash(number)
} }
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> Result<(U256, Option<Address>), evm::Error> { fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> (U256, Option<Address>) {
// in call and create we need to check if we exited with insufficient balance or max limit reached. // 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. // in case of reaching max depth, we should store callcreates. Otherwise, ignore.
let res = self.ext.create(gas, value, code); let res = self.ext.create(gas, value, code);
let ext = &self.ext; let ext = &self.ext;
match res { match res {
// just record call create // just record call create
Ok((gas_left, Some(address))) => { (gas_left, Some(address)) => {
self.callcreates.push(CallCreate { self.callcreates.push(CallCreate {
data: code.to_vec(), data: code.to_vec(),
destination: address.clone(), destination: address.clone(),
_gas_limit: *gas, _gas_limit: *gas,
value: *value value: *value
}); });
Ok((gas_left, Some(address))) (gas_left, Some(address))
}, },
// creation failed only due to reaching max_depth // creation failed only due to reaching max_depth
Ok((gas_left, None)) if ext.state.balance(&ext.params.address) >= *value => { (gas_left, None) if ext.state.balance(&ext.params.address) >= *value => {
let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address)); let address = contract_address(&ext.params.address, &ext.state.nonce(&ext.params.address));
self.callcreates.push(CallCreate { self.callcreates.push(CallCreate {
data: code.to_vec(), data: code.to_vec(),
@ -97,9 +97,10 @@ impl<'a> Ext for TestExt<'a> {
_gas_limit: *gas, _gas_limit: *gas,
value: *value value: *value
}); });
Ok((gas_left, Some(address))) (gas_left, Some(address))
}, },
other => other // compiler is wrong...
_other => { unreachable!() }
} }
} }
@ -156,6 +157,7 @@ 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() {
println!("name: {:?}", name);
// sync io is usefull when something crashes in jit // sync io is usefull when something crashes in jit
//::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");

View File

@ -10,6 +10,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
let engine = ethereum::new_frontier_test().to_engine().unwrap(); let engine = ethereum::new_frontier_test().to_engine().unwrap();
for (name, test) in json.as_object().unwrap() { for (name, test) in json.as_object().unwrap() {
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; true } else {false}; let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.to_string()); fail = true; true } else {false};