big refactor of executive in progress
This commit is contained in:
parent
b72da41ea7
commit
b273792ef0
@ -4,15 +4,26 @@ use util::uint::U256;
|
||||
use util::bytes::Bytes;
|
||||
use evm::{EvmParams, Ext};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum EvmResult {
|
||||
Stop { gas_left: U256 },
|
||||
Return(Bytes),
|
||||
Suicide,
|
||||
/// Evm errors.
|
||||
pub enum EvmError {
|
||||
/// Returned when transaction execution run out of gas.
|
||||
/// The state should be reverted to the state from before the
|
||||
/// transaction execution. But it does not mean that transaction
|
||||
/// was invalid. Balance still should be transfered and nonce
|
||||
/// should be increased.
|
||||
OutOfGas,
|
||||
InternalError
|
||||
/// Returned on evm internal error. Should never be ignored during development.
|
||||
/// Likely to cause consensus issues.
|
||||
Internal,
|
||||
}
|
||||
|
||||
/// Evm result.
|
||||
///
|
||||
/// Returns gas_left if execution is successfull, otherwise error.
|
||||
pub type EvmResult = Result<U256, EvmError>;
|
||||
|
||||
/// Evm interface.
|
||||
pub trait Evm {
|
||||
/// This function should be used to execute transaction.
|
||||
fn exec(&self, params: &EvmParams, ext: &mut Ext) -> EvmResult;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use std::cmp;
|
||||
use std::ptr;
|
||||
use util::hash::*;
|
||||
use util::uint::*;
|
||||
use util::rlp::*;
|
||||
@ -7,9 +8,10 @@ use util::sha3::*;
|
||||
use util::bytes::*;
|
||||
use state::*;
|
||||
use env_info::*;
|
||||
use evm_schedule::*;
|
||||
use engine::*;
|
||||
use transaction::*;
|
||||
use evm::{VmFactory, Ext, LogEntry, EvmParams, EvmResult};
|
||||
use evm::{VmFactory, Ext, LogEntry, EvmParams, EvmResult, EvmError};
|
||||
|
||||
/// Returns new address created from address and given nonce.
|
||||
pub fn contract_address(address: &Address, nonce: &U256) -> Address {
|
||||
@ -40,6 +42,7 @@ impl Substate {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
pub fn logs(&self) -> &[LogEntry] {
|
||||
&self.logs
|
||||
}
|
||||
@ -52,17 +55,59 @@ impl Substate {
|
||||
}
|
||||
}
|
||||
|
||||
/// Transaction execution result.
|
||||
pub struct Executed {
|
||||
/// Gas paid up front for execution of transaction.
|
||||
pub gas: U256,
|
||||
/// Gas used during execution of transaction.
|
||||
pub gas_used: U256,
|
||||
/// Gas refunded after the execution of transaction.
|
||||
/// To get gas that was required up front, add `refunded` and `gas_used`.
|
||||
pub refunded: U256,
|
||||
/// Cumulative gas used in current block so far.
|
||||
///
|
||||
/// cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)
|
||||
///
|
||||
/// where `tn` is current transaction.
|
||||
pub cumulative_gas_used: U256,
|
||||
/// Transaction output.
|
||||
pub output: Bytes,
|
||||
/// Vector of logs generated by transaction.
|
||||
pub logs: Vec<LogEntry>
|
||||
}
|
||||
|
||||
impl Executed {
|
||||
fn new() -> Executed {
|
||||
Executed {
|
||||
gas: U256::zero(),
|
||||
gas_used: U256::zero(),
|
||||
refunded: U256::zero(),
|
||||
cumulative_gas_used: U256::zero(),
|
||||
output: vec![],
|
||||
logs: vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of executing the transaction.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ExecutiveResult {
|
||||
Ok,
|
||||
pub enum ExecutionError {
|
||||
/// Returned when block (gas_used + gas) > gas_limit.
|
||||
///
|
||||
/// If gas =< gas_limit, upstream may try to execute the transaction
|
||||
/// in next block.
|
||||
BlockGasLimitReached { gas_limit: U256, gas_used: U256, gas: U256 },
|
||||
/// Returned when transaction nonce does not match state nonce.
|
||||
InvalidNonce { expected: U256, is: U256 },
|
||||
/// Returned when cost of transaction (value + gas_price * gas) exceeds
|
||||
/// current sender balance.
|
||||
NotEnoughCash { required: U256, is: U256 },
|
||||
OutOfGas,
|
||||
InternalError
|
||||
/// Returned when internal evm error occurs.
|
||||
Internal
|
||||
}
|
||||
|
||||
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
||||
|
||||
/// Message-call/contract-creation executor; useful for executing transactions.
|
||||
pub struct Executive<'a> {
|
||||
state: &'a mut State,
|
||||
@ -94,22 +139,24 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
|
||||
/// This funtion should be used to execute transaction.
|
||||
pub fn transact(e: &mut Executive<'a>, t: &Transaction) -> ExecutiveResult {
|
||||
// validate if transaction fits into given block
|
||||
if e.info.gas_used + t.gas > e.info.gas_limit {
|
||||
return ExecutiveResult::BlockGasLimitReached {
|
||||
gas_limit: e.info.gas_limit,
|
||||
gas_used: e.info.gas_used,
|
||||
gas: t.gas
|
||||
};
|
||||
}
|
||||
pub fn transact(e: &mut Executive<'a>, t: &Transaction) -> ExecutionResult {
|
||||
// TODO: validate transaction signature ?/ sender
|
||||
|
||||
let sender = t.sender();
|
||||
let nonce = e.state.nonce(&sender);
|
||||
|
||||
// validate transaction nonce
|
||||
if t.nonce != nonce {
|
||||
return ExecutiveResult::InvalidNonce { expected: nonce, is: t.nonce };
|
||||
return Err(ExecutionError::InvalidNonce { expected: nonce, is: t.nonce });
|
||||
}
|
||||
|
||||
// validate if transaction fits into given block
|
||||
if e.info.gas_used + t.gas > e.info.gas_limit {
|
||||
return Err(ExecutionError::BlockGasLimitReached {
|
||||
gas_limit: e.info.gas_limit,
|
||||
gas_used: e.info.gas_used,
|
||||
gas: t.gas
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: we might need bigints here, or at least check overflows.
|
||||
@ -119,9 +166,10 @@ impl<'a> Executive<'a> {
|
||||
|
||||
// avoid unaffordable transactions
|
||||
if balance < total_cost {
|
||||
return ExecutiveResult::NotEnoughCash { required: total_cost, is: balance };
|
||||
return Err(ExecutionError::NotEnoughCash { required: total_cost, is: balance });
|
||||
}
|
||||
|
||||
// NOTE: there can be no invalid transactions from this point.
|
||||
e.state.inc_nonce(&sender);
|
||||
let mut substate = Substate::new();
|
||||
|
||||
@ -137,7 +185,7 @@ impl<'a> Executive<'a> {
|
||||
code: t.data.clone(),
|
||||
data: vec![],
|
||||
};
|
||||
Executive::call(e, ¶ms, &mut substate)
|
||||
Executive::call(e, ¶ms, &mut substate, &mut [])
|
||||
},
|
||||
TransactionKind::MessageCall => {
|
||||
let params = EvmParams {
|
||||
@ -156,78 +204,51 @@ impl<'a> Executive<'a> {
|
||||
|
||||
// finalize here!
|
||||
e.finalize(substate, &sender, U256::zero(), U256::zero(), t.gas_price);
|
||||
res
|
||||
//res
|
||||
Ok(Executed::new())
|
||||
}
|
||||
|
||||
/// Calls contract function with given contract params.
|
||||
/// *Note. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||
fn call(e: &mut Executive<'a>, params: &EvmParams, substate: &mut Substate) -> ExecutiveResult {
|
||||
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||
/// Modifies the substate and the output.
|
||||
/// Returns either gas_left or `EvmError`.
|
||||
fn call(e: &mut Executive<'a>, params: &EvmParams, substate: &mut Substate, output: &mut [u8]) -> EvmResult {
|
||||
// at first, transfer value to destination
|
||||
e.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
||||
|
||||
// if destination is builtin, try to execute it, or quickly return
|
||||
if e.engine.is_builtin(¶ms.address) {
|
||||
return match e.engine.cost_of_builtin(¶ms.address, ¶ms.data) > params.gas {
|
||||
true => ExecutiveResult::OutOfGas,
|
||||
false => {
|
||||
// TODO: substract gas for execution
|
||||
let mut out = vec![];
|
||||
e.engine.execute_builtin(¶ms.address, ¶ms.data, &mut out);
|
||||
ExecutiveResult::Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise do `normal` execution if destination is a contract
|
||||
// TODO: is executing contract with no code different from not executing contract at all?
|
||||
// if yes, there is a logic issue here. mk
|
||||
if params.code.len() > 0 {
|
||||
return match {
|
||||
let mut ext = Externalities::new(e.state, e.info, e.engine, e.depth, params, substate);
|
||||
let evm = VmFactory::create();
|
||||
evm.exec(¶ms, &mut ext)
|
||||
} {
|
||||
EvmResult::Stop { gas_left } => ExecutiveResult::Ok,
|
||||
EvmResult::Return(_) => ExecutiveResult::Ok,
|
||||
EvmResult::Suicide => {
|
||||
substate.suicides.insert(params.address.clone());
|
||||
ExecutiveResult::Ok
|
||||
// if destination is builtin, try to execute it
|
||||
let cost = e.engine.cost_of_builtin(¶ms.address, ¶ms.data);
|
||||
match cost <= params.gas {
|
||||
true => {
|
||||
e.engine.execute_builtin(¶ms.address, ¶ms.data, output);
|
||||
Ok(params.gas - cost)
|
||||
},
|
||||
EvmResult::OutOfGas => ExecutiveResult::OutOfGas,
|
||||
_err => ExecutiveResult::InternalError
|
||||
false => Err(EvmError::OutOfGas)
|
||||
}
|
||||
} else if params.code.len() > 0 {
|
||||
// if destination is a contract, do normal message call
|
||||
let mut ext = Externalities::new(e.state, e.info, e.engine, e.depth, params, substate, OutputPolicy::Return(output));
|
||||
let evm = VmFactory::create();
|
||||
evm.exec(¶ms, &mut ext)
|
||||
} else {
|
||||
// otherwise, nothing
|
||||
Ok(params.gas)
|
||||
}
|
||||
|
||||
ExecutiveResult::Ok
|
||||
}
|
||||
|
||||
/// Creates contract with given contract params.
|
||||
/// *Note. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||
fn create(e: &mut Executive<'a>, params: &EvmParams, substate: &mut Substate) -> ExecutiveResult {
|
||||
/// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides).
|
||||
/// Modifies the substate.
|
||||
fn create(e: &mut Executive<'a>, params: &EvmParams, substate: &mut Substate) -> EvmResult {
|
||||
// at first create new contract
|
||||
e.state.new_contract(¶ms.address);
|
||||
// then transfer value to it
|
||||
e.state.transfer_balance(¶ms.sender, ¶ms.address, ¶ms.value);
|
||||
|
||||
match {
|
||||
let mut ext = Externalities::new(e.state, e.info, e.engine, e.depth, params, substate);
|
||||
let evm = VmFactory::create();
|
||||
evm.exec(¶ms, &mut ext)
|
||||
} {
|
||||
EvmResult::Stop { gas_left } => {
|
||||
ExecutiveResult::Ok
|
||||
},
|
||||
EvmResult::Return(output) => {
|
||||
e.state.init_code(¶ms.address, output);
|
||||
ExecutiveResult::Ok
|
||||
},
|
||||
EvmResult::Suicide => {
|
||||
substate.suicides.insert(params.address.clone());
|
||||
ExecutiveResult::Ok
|
||||
},
|
||||
EvmResult::OutOfGas => ExecutiveResult::OutOfGas,
|
||||
_err => ExecutiveResult::InternalError
|
||||
}
|
||||
let mut ext = Externalities::new(e.state, e.info, e.engine, e.depth, params, substate, OutputPolicy::InitContract);
|
||||
let evm = VmFactory::create();
|
||||
evm.exec(¶ms, &mut ext)
|
||||
}
|
||||
|
||||
/// Finalizes the transaction (does refunds and suicides).
|
||||
@ -256,6 +277,19 @@ impl<'a> Executive<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ExtMode {
|
||||
Call,
|
||||
Create
|
||||
}
|
||||
|
||||
/// Wrapper structure for evm return data to avoid unnecessary copying.
|
||||
pub enum OutputPolicy<'a> {
|
||||
/// Reference to fixed sized output of a message call.
|
||||
Return(&'a mut [u8]),
|
||||
/// Use it, if you want return code to initialize contract.
|
||||
InitContract
|
||||
}
|
||||
|
||||
/// Implementation of evm Externalities.
|
||||
pub struct Externalities<'a> {
|
||||
state: &'a mut State,
|
||||
@ -263,19 +297,29 @@ pub struct Externalities<'a> {
|
||||
engine: &'a Engine,
|
||||
depth: usize,
|
||||
params: &'a EvmParams,
|
||||
substate: &'a mut Substate
|
||||
substate: &'a mut Substate,
|
||||
schedule: EvmSchedule,
|
||||
output: OutputPolicy<'a>
|
||||
}
|
||||
|
||||
impl<'a> Externalities<'a> {
|
||||
/// Basic `Externalities` constructor.
|
||||
pub fn new(state: &'a mut State, info: &'a EnvInfo, engine: &'a Engine, depth: usize, params: &'a EvmParams, substate: &'a mut Substate) -> Self {
|
||||
pub fn new(state: &'a mut State,
|
||||
info: &'a EnvInfo,
|
||||
engine: &'a Engine,
|
||||
depth: usize,
|
||||
params: &'a EvmParams,
|
||||
substate: &'a mut Substate,
|
||||
output: OutputPolicy<'a>) -> Self {
|
||||
Externalities {
|
||||
state: state,
|
||||
info: info,
|
||||
engine: engine,
|
||||
depth: depth,
|
||||
params: params,
|
||||
substate: substate
|
||||
substate: substate,
|
||||
schedule: engine.evm_schedule(info),
|
||||
output: output
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -345,19 +389,18 @@ impl<'a> Ext for Externalities<'a> {
|
||||
println!("gas: {:?}", gas);
|
||||
println!("call_gas: {:?}", call_gas);
|
||||
|
||||
let schedule = self.engine.evm_schedule(self.info);
|
||||
let mut gas_cost = call_gas;
|
||||
let mut call_gas = call_gas;
|
||||
|
||||
let is_call = receive_address == code_address;
|
||||
if is_call && self.state.code(&code_address).is_none() {
|
||||
gas_cost = gas_cost + schedule.call_new_account_gas as u64;
|
||||
gas_cost = gas_cost + self.schedule.call_new_account_gas as u64;
|
||||
}
|
||||
|
||||
if *value > U256::zero() {
|
||||
assert!(schedule.call_value_transfer_gas > schedule.call_stipend, "overflow possible");
|
||||
gas_cost = gas_cost + schedule.call_value_transfer_gas as u64;
|
||||
call_gas = call_gas + schedule.call_stipend as u64;
|
||||
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;
|
||||
}
|
||||
|
||||
if gas_cost > gas {
|
||||
@ -387,9 +430,11 @@ impl<'a> Ext for Externalities<'a> {
|
||||
let mut substate = Substate::new();
|
||||
{
|
||||
let mut ex = Executive::from_parent(self);
|
||||
Executive::call(&mut ex, ¶ms, &mut substate);
|
||||
// TODO: take output into account
|
||||
Executive::call(&mut ex, ¶ms, &mut substate, &mut []);
|
||||
}
|
||||
|
||||
self.substate.accrue(substate);
|
||||
// TODO: replace call_gas with what's actually left
|
||||
Some((vec![], gas - gas_cost + call_gas))
|
||||
}
|
||||
@ -398,10 +443,44 @@ impl<'a> Ext for Externalities<'a> {
|
||||
self.state.code(address).unwrap_or(vec![])
|
||||
}
|
||||
|
||||
fn ret(&mut self, gas: u64, data: &[u8]) -> Option<u64> {
|
||||
match &mut self.output {
|
||||
&mut OutputPolicy::Return(ref mut slice) => unsafe {
|
||||
let len = cmp::min(slice.len(), data.len());
|
||||
ptr::copy(data.as_ptr(), slice.as_mut_ptr(), len);
|
||||
Some(gas)
|
||||
},
|
||||
&mut OutputPolicy::InitContract => {
|
||||
let return_cost = data.len() as u64 * self.schedule.create_data_gas as u64;
|
||||
if return_cost > gas {
|
||||
return None;
|
||||
}
|
||||
let mut code = vec![];
|
||||
code.reserve(data.len());
|
||||
unsafe {
|
||||
ptr::copy(data.as_ptr(), code.as_mut_ptr(), data.len());
|
||||
code.set_len(data.len());
|
||||
}
|
||||
let address = &self.params.address;
|
||||
self.state.init_code(address, code);
|
||||
Some(gas - return_cost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn log(&mut self, topics: Vec<H256>, data: Bytes) {
|
||||
let address = self.params.address.clone();
|
||||
self.substate.logs.push(LogEntry::new(address, topics, data));
|
||||
}
|
||||
|
||||
fn suicide(&mut self) {
|
||||
let address = self.params.address.clone();
|
||||
self.substate.suicides.insert(address);
|
||||
}
|
||||
|
||||
fn schedule(&self) -> &EvmSchedule {
|
||||
&self.schedule
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -462,7 +541,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
assert_eq!(Executive::create(&mut ex, ¶ms, &mut substate), ExecutiveResult::Ok);
|
||||
assert_eq!(Executive::create(&mut ex, ¶ms, &mut substate), ExecutionResult::Ok);
|
||||
}
|
||||
|
||||
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(&U256::from(0xf9u64)));
|
||||
@ -489,7 +568,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||
assert_eq!(Executive::create(&mut ex, ¶ms, &mut substate), ExecutiveResult::Ok);
|
||||
assert_eq!(Executive::create(&mut ex, ¶ms, &mut substate), ExecutionResult::Ok);
|
||||
}
|
||||
|
||||
assert_eq!(state.storage_at(&address, &H256::new()), H256::from(next_address.clone()));
|
||||
@ -537,7 +616,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let mut ex = Executive::new(&mut state, &info, engine.deref());
|
||||
assert_eq!(Executive::call(&mut ex, ¶ms, &mut substate), ExecutiveResult::Ok);
|
||||
assert_eq!(Executive::call(&mut ex, ¶ms, &mut substate), ExecutionResult::Ok);
|
||||
}
|
||||
|
||||
assert!(false);
|
||||
|
@ -3,6 +3,7 @@
|
||||
use util::hash::*;
|
||||
use util::uint::*;
|
||||
use util::bytes::*;
|
||||
use evm_schedule::*;
|
||||
|
||||
pub trait Ext {
|
||||
/// Returns a value for given key.
|
||||
@ -33,4 +34,14 @@ pub trait Ext {
|
||||
|
||||
/// Creates log entry with given topics and data
|
||||
fn log(&mut self, topics: Vec<H256>, data: Bytes);
|
||||
|
||||
/// 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]) -> Option<u64>;
|
||||
|
||||
/// Should be called when contract commits suicide.
|
||||
fn suicide(&mut self);
|
||||
|
||||
/// Returns schedule.
|
||||
fn schedule(&self) -> &EvmSchedule;
|
||||
}
|
||||
|
@ -316,11 +316,21 @@ impl evm::Evm for JitEvm {
|
||||
|
||||
let mut context = unsafe { evmjit::ContextHandle::new(data.into_jit(), &mut ext_handle) };
|
||||
match context.exec() {
|
||||
evmjit::ReturnCode::Stop => evm::EvmResult::Stop { gas_left: U256::from(context.gas_left()) },
|
||||
evmjit::ReturnCode::Return => evm::EvmResult::Return(context.output_data().to_vec()),
|
||||
evmjit::ReturnCode::Suicide => evm::EvmResult::Suicide,
|
||||
evmjit::ReturnCode::OutOfGas => evm::EvmResult::OutOfGas,
|
||||
_ => evm::EvmResult::InternalError
|
||||
evmjit::ReturnCode::Stop => Ok(evm::EvmOutput::new(U256::from(context.gas_left()), None)),
|
||||
evmjit::ReturnCode::Return => {
|
||||
if context.output_data().len() as u64 * ext.schedule().create_data_gas as u64 > context.gas_left() {
|
||||
return Err(evm::EvmError::OutOfGas);
|
||||
}
|
||||
|
||||
Ok(evm::EvmOutput::new(U256::from(context.gas_left()), Some(context.output_data().to_vec())))
|
||||
},
|
||||
evmjit::ReturnCode::Suicide => {
|
||||
// what if there is a suicide and we run out of gas just after?
|
||||
ext.suicide();
|
||||
Ok(evm::EvmOutput::new(U256::from(context.gas_left()), None))
|
||||
},
|
||||
evmjit::ReturnCode::OutOfGas => Err(evm::EvmError::OutOfGas),
|
||||
_err => Err(evm::EvmError::Internal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ pub mod params;
|
||||
#[cfg(feature = "jit" )]
|
||||
mod jit;
|
||||
|
||||
pub use self::evm::{Evm, EvmResult};
|
||||
pub use self::evm::{Evm, EvmError, EvmResult};
|
||||
pub use self::ext::{Ext};
|
||||
pub use self::logentry::LogEntry;
|
||||
pub use self::vmfactory::VmFactory;
|
||||
pub use self::executive::{Executive, ExecutiveResult, Externalities, Substate};
|
||||
pub use self::executive::{Executive, ExecutionResult, Externalities, Substate};
|
||||
pub use self::params::EvmParams;
|
||||
|
Loading…
Reference in New Issue
Block a user