Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
EIP-211
Browse files Browse the repository at this point in the history
  • Loading branch information
arkpar committed May 30, 2017
1 parent e6a31e7 commit 2819508
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 56 deletions.
2 changes: 1 addition & 1 deletion ethcore/src/client/evm_test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl EvmTestClient {
let mut tracer = trace::NoopTracer;
let mut output = vec![];
let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine);
let gas_left = executive.call(
let (gas_left, _) = executive.call(
params,
&mut substate,
util::BytesRef::Flexible(&mut output),
Expand Down
50 changes: 44 additions & 6 deletions ethcore/src/evm/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,53 @@ impl fmt::Display for Error {
/// A specialized version of Result over EVM errors.
pub type Result<T> = ::std::result::Result<T, Error>;


/// Return data buffer. Holds memory from a previous call and a slice into that memory.
#[derive(Debug)]
pub struct ReturnData {
mem: Vec<u8>,
offset: usize,
size: usize,
}

impl ::std::ops::Deref for ReturnData {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.mem[self.offset..self.offset + self.size]
}
}

impl ReturnData {
/// Create empty `ReturnData`.
pub fn empty() -> Self {
ReturnData {
mem: Vec::new(),
offset: 0,
size: 0,
}
}
/// Create `ReturnData` from give buffer and slice.
pub fn new(mem: Vec<u8>, offset: usize, size: usize) -> Self {
ReturnData {
mem: mem,
offset: offset,
size: size,
}
}
}

/// Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum GasLeft<'a> {
#[derive(Debug)]
pub enum GasLeft {
/// Known gas left
Known(U256),
/// Return or Revert instruction must be processed.
NeedsReturn {
/// Amount of gas left.
gas_left: U256,
/// Return data buffer.
data: &'a [u8],
data: ReturnData,
/// Apply or revert state changes on revert.
apply_state: bool
},
Expand All @@ -122,6 +157,8 @@ pub struct FinalizationResult {
pub gas_left: U256,
/// Apply execution state changes or revert them.
pub apply_state: bool,
/// Return data buffer.
pub return_data: ReturnData,
}

/// Types that can be "finalized" using an EVM.
Expand All @@ -133,13 +170,14 @@ pub trait Finalize {
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult>;
}

impl<'a> Finalize for Result<GasLeft<'a>> {
impl Finalize for Result<GasLeft> {
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
match self {
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true }),
Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, data).map(|gas_left| FinalizationResult {
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true, return_data: ReturnData::empty() }),
Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, &data).map(|gas_left| FinalizationResult {
gas_left: gas_left,
apply_state: apply_state,
return_data: data,
}),
Err(err) => Err(err),
}
Expand Down
8 changes: 4 additions & 4 deletions ethcore/src/evm/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//! Interface for Evm externalities.

use util::*;
use evm::{self, Schedule};
use evm::{self, Schedule, ReturnData};
use env_info::*;
use types::executed::CallType;

Expand All @@ -34,8 +34,8 @@ pub enum ContractCreateResult {
/// Result of externalities call function.
pub enum MessageCallResult {
/// Returned when message call was successfull.
/// Contains gas left.
Success(U256),
/// Contains gas left and output data.
Success(U256, ReturnData),
/// Returned when message call failed.
/// VM doesn't have to know the reason.
Failed
Expand Down Expand Up @@ -109,7 +109,7 @@ 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(self, gas: &U256, data: &[u8]) -> evm::Result<U256> where Self: Sized;
fn ret(self, gas: &U256, data: &ReturnData) -> evm::Result<U256>;

/// Should be called when contract commits suicide.
/// Address to which funds should be refunded.
Expand Down
6 changes: 6 additions & 0 deletions ethcore/src/evm/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ lazy_static! {
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid);
arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid);
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low);
arr[RETURNDATASIZE as usize] = InstructionInfo::new("RETURNDATASIZE", 0, 0, 1, false, GasPriceTier::Base);
arr[RETURNDATACOPY as usize] = InstructionInfo::new("RETURNDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
arr[SHA3 as usize] = InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special);
arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base);
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Special);
Expand Down Expand Up @@ -362,6 +364,10 @@ pub const GASPRICE: Instruction = 0x3a;
pub const EXTCODESIZE: Instruction = 0x3b;
/// copy external code (from another contract)
pub const EXTCODECOPY: Instruction = 0x3c;
/// get the size of the return data buffer for the last call
pub const RETURNDATASIZE: Instruction = 0x3d;
/// copy return data buffer to memory
pub const RETURNDATACOPY: Instruction = 0x3e;

/// get hash of most recent complete block
pub const BLOCKHASH: Instruction = 0x40;
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/evm/interpreter/gasometer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ impl<Gas: CostType> Gasometer<Gas> {
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
},
instructions::CALLDATACOPY | instructions::CODECOPY => {
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => {
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
},
instructions::EXTCODECOPY => {
Expand Down
12 changes: 11 additions & 1 deletion ethcore/src/evm/interpreter/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use util::U256;
use evm::ReturnData;
use std::cmp::min;

pub trait Memory {
/// Retrieve current size of the memory
Expand All @@ -36,6 +38,8 @@ pub trait Memory {
/// Retrieve writeable part of memory
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
fn dump(&self);
/// Convert memory into return data.
fn into_return_data(self, offset: usize, size: usize) -> ReturnData;
}

/// Checks whether offset and size is valid memory range
Expand Down Expand Up @@ -109,6 +113,12 @@ impl Memory for Vec<u8> {
Memory::resize(self, size)
}
}

fn into_return_data(self, offset: usize, size: usize) -> ReturnData {
let offset = min(offset, self.len());
let size = min(size, self.len() - offset);
ReturnData::new(self, offset, size)
}
}

#[cfg(test)]
Expand Down Expand Up @@ -164,4 +174,4 @@ mod tests {
assert_eq!(mem.read_slice(U256::from(0), U256::from(7)), "a67890g".as_bytes());
}
}
}
}
29 changes: 18 additions & 11 deletions ethcore/src/evm/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use std::marker::PhantomData;
use action_params::{ActionParams, ActionValue};
use types::executed::CallType;
use evm::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType, CreateContractAddress};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType, CreateContractAddress, ReturnData};
use bit_set::BitSet;

use util::*;
Expand Down Expand Up @@ -102,6 +102,7 @@ enum InstructionResult<Gas> {
pub struct Interpreter<Cost: CostType> {
mem: Vec<u8>,
cache: Arc<SharedCache>,
return_data: ReturnData,
_type: PhantomData<Cost>,
}

Expand Down Expand Up @@ -166,9 +167,10 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
},
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
informant.done();
let mem = mem::replace(&mut self.mem, Vec::new());
return Ok(GasLeft::NeedsReturn {
gas_left: gas.as_u256(),
data: self.mem.read_slice(init_off, init_size),
data: mem.into_return_data(usize::from_u256(init_off)?, usize::from_u256(init_size)?),
apply_state: apply
});
},
Expand All @@ -187,6 +189,7 @@ impl<Cost: CostType> Interpreter<Cost> {
Interpreter {
mem: Vec::new(),
cache: cache,
return_data: ReturnData::empty(),
_type: PhantomData::default(),
}
}
Expand Down Expand Up @@ -233,7 +236,7 @@ impl<Cost: CostType> Interpreter<Cost> {
match instruction {
instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)),
instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)),
instructions::CALLDATACOPY | instructions::CODECOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)),
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)),
instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)),
instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)),
instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)),
Expand Down Expand Up @@ -362,8 +365,9 @@ impl<Cost: CostType> Interpreter<Cost> {
};

return match call_result {
MessageCallResult::Success(gas_left) => {
MessageCallResult::Success(gas_left, data) => {
stack.push(U256::one());
self.return_data = data;
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one")))
},
MessageCallResult::Failed => {
Expand Down Expand Up @@ -501,15 +505,18 @@ impl<Cost: CostType> Interpreter<Cost> {
stack.push(U256::from(len));
},
instructions::CALLDATACOPY => {
self.copy_data_to_memory(stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
Self::copy_data_to_memory(&mut self.mem, stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
},
instructions::RETURNDATACOPY => {
Self::copy_data_to_memory(&mut self.mem, stack, &*self.return_data);
},
instructions::CODECOPY => {
self.copy_data_to_memory(stack, params.code.as_ref().map_or_else(|| &[] as &[u8], |c| &**c as &[u8]));
Self::copy_data_to_memory(&mut self.mem, stack, params.code.as_ref().map_or_else(|| &[] as &[u8], |c| &**c as &[u8]));
},
instructions::EXTCODECOPY => {
let address = u256_to_address(&stack.pop_back());
let code = ext.extcode(&address)?;
self.copy_data_to_memory(stack, &code);
Self::copy_data_to_memory(&mut self.mem, stack, &code);
},
instructions::GASPRICE => {
stack.push(params.gas_price.clone());
Expand Down Expand Up @@ -541,7 +548,7 @@ impl<Cost: CostType> Interpreter<Cost> {
Ok(InstructionResult::Ok)
}

fn copy_data_to_memory(&mut self, stack: &mut Stack<U256>, source: &[u8]) {
fn copy_data_to_memory(mem: &mut Vec<u8>, stack: &mut Stack<U256>, source: &[u8]) {
let dest_offset = stack.pop_back();
let source_offset = stack.pop_back();
let size = stack.pop_back();
Expand All @@ -550,9 +557,9 @@ impl<Cost: CostType> Interpreter<Cost> {
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
true => {
let zero_slice = if source_offset > source_size {
self.mem.writeable_slice(dest_offset, size)
mem.writeable_slice(dest_offset, size)
} else {
self.mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size)
mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size)
};
for i in zero_slice.iter_mut() {
*i = 0;
Expand All @@ -564,7 +571,7 @@ impl<Cost: CostType> Interpreter<Cost> {

if source_offset < source_size {
let output_begin = source_offset.low_u64() as usize;
self.mem.write_slice(dest_offset, &source[output_begin..output_end]);
mem.write_slice(dest_offset, &source[output_begin..output_end]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ mod tests;
#[cfg(all(feature="benches", test))]
mod benches;

pub use self::evm::{Evm, Error, Finalize, FinalizationResult, GasLeft, Result, CostType};
pub use self::evm::{Evm, Error, Finalize, FinalizationResult, GasLeft, Result, CostType, ReturnData};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress};
pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule;
Expand Down
6 changes: 3 additions & 3 deletions ethcore/src/evm/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use util::*;
use action_params::{ActionParams, ActionValue};
use env_info::EnvInfo;
use types::executed::CallType;
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress};
use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress, ReturnData};
use std::fmt::Debug;
use tests::helpers::*;
use types::transaction::SYSTEM_ADDRESS;
Expand Down Expand Up @@ -149,7 +149,7 @@ impl Ext for FakeExt {
data: data.to_vec(),
code_address: Some(code_address.clone())
});
MessageCallResult::Success(*gas)
MessageCallResult::Success(*gas, ReturnData::empty())
}

fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
Expand All @@ -167,7 +167,7 @@ impl Ext for FakeExt {
});
}

fn ret(self, _gas: &U256, _data: &[u8]) -> evm::Result<U256> {
fn ret(self, _gas: &U256, _data: &ReturnData) -> evm::Result<U256> {
unimplemented!();
}

Expand Down
Loading

0 comments on commit 2819508

Please sign in to comment.