EIP-211 RETURNDATACOPY and RETURNDATASIZE (#5678)
* EIP-211 * Optimized memory usage * Optimized truncation
This commit is contained in:
parent
241de230bb
commit
99bfef2801
@ -104,7 +104,7 @@ impl EvmTestClient {
|
|||||||
let mut tracer = trace::NoopTracer;
|
let mut tracer = trace::NoopTracer;
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine);
|
let mut executive = executive::Executive::new(&mut state, &info, &*self.spec.engine);
|
||||||
let gas_left = executive.call(
|
let (gas_left, _) = executive.call(
|
||||||
params,
|
params,
|
||||||
&mut substate,
|
&mut substate,
|
||||||
util::BytesRef::Flexible(&mut output),
|
util::BytesRef::Flexible(&mut output),
|
||||||
|
@ -97,10 +97,45 @@ impl fmt::Display for Error {
|
|||||||
/// A specialized version of Result over EVM errors.
|
/// A specialized version of Result over EVM errors.
|
||||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
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
|
/// Gas Left: either it is a known value, or it needs to be computed by processing
|
||||||
/// a return instruction.
|
/// a return instruction.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug)]
|
||||||
pub enum GasLeft<'a> {
|
pub enum GasLeft {
|
||||||
/// Known gas left
|
/// Known gas left
|
||||||
Known(U256),
|
Known(U256),
|
||||||
/// Return or Revert instruction must be processed.
|
/// Return or Revert instruction must be processed.
|
||||||
@ -108,7 +143,7 @@ pub enum GasLeft<'a> {
|
|||||||
/// Amount of gas left.
|
/// Amount of gas left.
|
||||||
gas_left: U256,
|
gas_left: U256,
|
||||||
/// Return data buffer.
|
/// Return data buffer.
|
||||||
data: &'a [u8],
|
data: ReturnData,
|
||||||
/// Apply or revert state changes on revert.
|
/// Apply or revert state changes on revert.
|
||||||
apply_state: bool
|
apply_state: bool
|
||||||
},
|
},
|
||||||
@ -122,6 +157,8 @@ pub struct FinalizationResult {
|
|||||||
pub gas_left: U256,
|
pub gas_left: U256,
|
||||||
/// Apply execution state changes or revert them.
|
/// Apply execution state changes or revert them.
|
||||||
pub apply_state: bool,
|
pub apply_state: bool,
|
||||||
|
/// Return data buffer.
|
||||||
|
pub return_data: ReturnData,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Types that can be "finalized" using an EVM.
|
/// Types that can be "finalized" using an EVM.
|
||||||
@ -133,13 +170,14 @@ pub trait Finalize {
|
|||||||
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult>;
|
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> {
|
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
|
||||||
match self {
|
match self {
|
||||||
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true }),
|
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 {
|
Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, &data).map(|gas_left| FinalizationResult {
|
||||||
gas_left: gas_left,
|
gas_left: gas_left,
|
||||||
apply_state: apply_state,
|
apply_state: apply_state,
|
||||||
|
return_data: data,
|
||||||
}),
|
}),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! Interface for Evm externalities.
|
//! Interface for Evm externalities.
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use evm::{self, Schedule};
|
use evm::{self, Schedule, ReturnData};
|
||||||
use env_info::*;
|
use env_info::*;
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
|
|
||||||
@ -34,8 +34,8 @@ pub enum ContractCreateResult {
|
|||||||
/// Result of externalities call function.
|
/// Result of externalities call function.
|
||||||
pub enum MessageCallResult {
|
pub enum MessageCallResult {
|
||||||
/// Returned when message call was successfull.
|
/// Returned when message call was successfull.
|
||||||
/// Contains gas left.
|
/// Contains gas left and output data.
|
||||||
Success(U256),
|
Success(U256, ReturnData),
|
||||||
/// Returned when message call failed.
|
/// Returned when message call failed.
|
||||||
/// VM doesn't have to know the reason.
|
/// VM doesn't have to know the reason.
|
||||||
Failed
|
Failed
|
||||||
@ -109,7 +109,7 @@ pub trait Ext {
|
|||||||
|
|
||||||
/// Should be called when transaction calls `RETURN` opcode.
|
/// Should be called when transaction calls `RETURN` opcode.
|
||||||
/// Returns gas_left if cost of returning the data is not too high.
|
/// 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.
|
/// Should be called when contract commits suicide.
|
||||||
/// Address to which funds should be refunded.
|
/// Address to which funds should be refunded.
|
||||||
|
@ -178,6 +178,8 @@ lazy_static! {
|
|||||||
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid);
|
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[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[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[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[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);
|
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Special);
|
||||||
@ -369,6 +371,10 @@ pub const GASPRICE: Instruction = 0x3a;
|
|||||||
pub const EXTCODESIZE: Instruction = 0x3b;
|
pub const EXTCODESIZE: Instruction = 0x3b;
|
||||||
/// copy external code (from another contract)
|
/// copy external code (from another contract)
|
||||||
pub const EXTCODECOPY: Instruction = 0x3c;
|
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
|
/// get hash of most recent complete block
|
||||||
pub const BLOCKHASH: Instruction = 0x40;
|
pub const BLOCKHASH: Instruction = 0x40;
|
||||||
|
@ -173,7 +173,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
|
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))?)
|
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))?)
|
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
|
||||||
},
|
},
|
||||||
instructions::EXTCODECOPY => {
|
instructions::EXTCODECOPY => {
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::U256;
|
use util::U256;
|
||||||
|
use evm::ReturnData;
|
||||||
|
|
||||||
|
const MAX_RETURN_WASTE_BYTES: usize = 16384;
|
||||||
|
|
||||||
pub trait Memory {
|
pub trait Memory {
|
||||||
/// Retrieve current size of the memory
|
/// Retrieve current size of the memory
|
||||||
@ -36,6 +39,8 @@ pub trait Memory {
|
|||||||
/// Retrieve writeable part of memory
|
/// Retrieve writeable part of memory
|
||||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
|
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
|
||||||
fn dump(&self);
|
fn dump(&self);
|
||||||
|
/// Convert memory into return data.
|
||||||
|
fn into_return_data(self, offset: U256, size: U256) -> ReturnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether offset and size is valid memory range
|
/// Checks whether offset and size is valid memory range
|
||||||
@ -109,6 +114,21 @@ impl Memory for Vec<u8> {
|
|||||||
Memory::resize(self, size)
|
Memory::resize(self, size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_return_data(mut self, offset: U256, size: U256) -> ReturnData {
|
||||||
|
let mut offset = offset.low_u64() as usize;
|
||||||
|
let size = size.low_u64() as usize;
|
||||||
|
if !is_valid_range(offset, size) {
|
||||||
|
return ReturnData::empty()
|
||||||
|
}
|
||||||
|
if self.len() - size > MAX_RETURN_WASTE_BYTES {
|
||||||
|
{ let _ = self.drain(..offset); }
|
||||||
|
self.truncate(size);
|
||||||
|
self.shrink_to_fit();
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
ReturnData::new(self, offset, size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -164,4 +184,4 @@ mod tests {
|
|||||||
assert_eq!(mem.read_slice(U256::from(0), U256::from(7)), "a67890g".as_bytes());
|
assert_eq!(mem.read_slice(U256::from(0), U256::from(7)), "a67890g".as_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ use std::marker::PhantomData;
|
|||||||
use action_params::{ActionParams, ActionValue};
|
use action_params::{ActionParams, ActionValue};
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
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 bit_set::BitSet;
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
@ -102,6 +102,7 @@ enum InstructionResult<Gas> {
|
|||||||
pub struct Interpreter<Cost: CostType> {
|
pub struct Interpreter<Cost: CostType> {
|
||||||
mem: Vec<u8>,
|
mem: Vec<u8>,
|
||||||
cache: Arc<SharedCache>,
|
cache: Arc<SharedCache>,
|
||||||
|
return_data: ReturnData,
|
||||||
_type: PhantomData<Cost>,
|
_type: PhantomData<Cost>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,9 +167,10 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
},
|
},
|
||||||
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
|
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
|
||||||
informant.done();
|
informant.done();
|
||||||
|
let mem = mem::replace(&mut self.mem, Vec::new());
|
||||||
return Ok(GasLeft::NeedsReturn {
|
return Ok(GasLeft::NeedsReturn {
|
||||||
gas_left: gas.as_u256(),
|
gas_left: gas.as_u256(),
|
||||||
data: self.mem.read_slice(init_off, init_size),
|
data: mem.into_return_data(init_off, init_size),
|
||||||
apply_state: apply
|
apply_state: apply
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -187,6 +189,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
Interpreter {
|
Interpreter {
|
||||||
mem: Vec::new(),
|
mem: Vec::new(),
|
||||||
cache: cache,
|
cache: cache,
|
||||||
|
return_data: ReturnData::empty(),
|
||||||
_type: PhantomData::default(),
|
_type: PhantomData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,7 +236,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
match instruction {
|
match instruction {
|
||||||
instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)),
|
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::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::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::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)),
|
instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)),
|
||||||
@ -362,8 +365,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return match call_result {
|
return match call_result {
|
||||||
MessageCallResult::Success(gas_left) => {
|
MessageCallResult::Success(gas_left, data) => {
|
||||||
stack.push(U256::one());
|
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")))
|
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one")))
|
||||||
},
|
},
|
||||||
MessageCallResult::Failed => {
|
MessageCallResult::Failed => {
|
||||||
@ -495,21 +499,27 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
instructions::CODESIZE => {
|
instructions::CODESIZE => {
|
||||||
stack.push(U256::from(code.len()));
|
stack.push(U256::from(code.len()));
|
||||||
},
|
},
|
||||||
|
instructions::RETURNDATASIZE => {
|
||||||
|
stack.push(U256::from(self.return_data.len()))
|
||||||
|
},
|
||||||
instructions::EXTCODESIZE => {
|
instructions::EXTCODESIZE => {
|
||||||
let address = u256_to_address(&stack.pop_back());
|
let address = u256_to_address(&stack.pop_back());
|
||||||
let len = ext.extcodesize(&address)?;
|
let len = ext.extcodesize(&address)?;
|
||||||
stack.push(U256::from(len));
|
stack.push(U256::from(len));
|
||||||
},
|
},
|
||||||
instructions::CALLDATACOPY => {
|
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 => {
|
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 => {
|
instructions::EXTCODECOPY => {
|
||||||
let address = u256_to_address(&stack.pop_back());
|
let address = u256_to_address(&stack.pop_back());
|
||||||
let code = ext.extcode(&address)?;
|
let code = ext.extcode(&address)?;
|
||||||
self.copy_data_to_memory(stack, &code);
|
Self::copy_data_to_memory(&mut self.mem, stack, &code);
|
||||||
},
|
},
|
||||||
instructions::GASPRICE => {
|
instructions::GASPRICE => {
|
||||||
stack.push(params.gas_price.clone());
|
stack.push(params.gas_price.clone());
|
||||||
@ -541,7 +551,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
Ok(InstructionResult::Ok)
|
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 dest_offset = stack.pop_back();
|
||||||
let source_offset = stack.pop_back();
|
let source_offset = stack.pop_back();
|
||||||
let size = stack.pop_back();
|
let size = stack.pop_back();
|
||||||
@ -550,9 +560,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
|
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
|
||||||
true => {
|
true => {
|
||||||
let zero_slice = if source_offset > source_size {
|
let zero_slice = if source_offset > source_size {
|
||||||
self.mem.writeable_slice(dest_offset, size)
|
mem.writeable_slice(dest_offset, size)
|
||||||
} else {
|
} 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() {
|
for i in zero_slice.iter_mut() {
|
||||||
*i = 0;
|
*i = 0;
|
||||||
@ -564,7 +574,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
|
|
||||||
if source_offset < source_size {
|
if source_offset < source_size {
|
||||||
let output_begin = source_offset.low_u64() as usize;
|
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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ mod tests;
|
|||||||
#[cfg(all(feature="benches", test))]
|
#[cfg(all(feature="benches", test))]
|
||||||
mod benches;
|
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::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress};
|
||||||
pub use self::vmtype::VMType;
|
pub use self::vmtype::VMType;
|
||||||
pub use self::factory::Factory;
|
pub use self::factory::Factory;
|
||||||
|
@ -18,7 +18,7 @@ use util::*;
|
|||||||
use action_params::{ActionParams, ActionValue};
|
use action_params::{ActionParams, ActionValue};
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
use types::executed::CallType;
|
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 std::fmt::Debug;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
use types::transaction::SYSTEM_ADDRESS;
|
use types::transaction::SYSTEM_ADDRESS;
|
||||||
@ -149,7 +149,7 @@ impl Ext for FakeExt {
|
|||||||
data: data.to_vec(),
|
data: data.to_vec(),
|
||||||
code_address: Some(code_address.clone())
|
code_address: Some(code_address.clone())
|
||||||
});
|
});
|
||||||
MessageCallResult::Success(*gas)
|
MessageCallResult::Success(*gas, ReturnData::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
|
||||||
@ -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!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ use engines::Engine;
|
|||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
use error::ExecutionError;
|
use error::ExecutionError;
|
||||||
use evm::{self, Ext, Finalize, CreateContractAddress, FinalizationResult};
|
use evm::{self, Ext, Finalize, CreateContractAddress, FinalizationResult, ReturnData};
|
||||||
use externalities::*;
|
use externalities::*;
|
||||||
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer};
|
||||||
use transaction::{Action, SignedTransaction};
|
use transaction::{Action, SignedTransaction};
|
||||||
@ -194,7 +194,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
|
|
||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
let (gas_left, output) = match t.action {
|
let (result, output) = match t.action {
|
||||||
Action::Create => {
|
Action::Create => {
|
||||||
let code_hash = t.data.sha3();
|
let code_hash = t.data.sha3();
|
||||||
let new_address = contract_address(self.engine.create_address_scheme(self.info.number), &sender, &nonce, &code_hash);
|
let new_address = contract_address(self.engine.create_address_scheme(self.info.number), &sender, &nonce, &code_hash);
|
||||||
@ -233,7 +233,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// finalize here!
|
// finalize here!
|
||||||
Ok(self.finalize(t, substate, gas_left, output, tracer.traces(), vm_tracer.drain())?)
|
Ok(self.finalize(t, substate, result, output, tracer.traces(), vm_tracer.drain())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_vm<T, V>(
|
fn exec_vm<T, V>(
|
||||||
@ -279,7 +279,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
mut output: BytesRef,
|
mut output: BytesRef,
|
||||||
tracer: &mut T,
|
tracer: &mut T,
|
||||||
vm_tracer: &mut V
|
vm_tracer: &mut V
|
||||||
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
|
) -> evm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer {
|
||||||
// backup used in case of running out of gas
|
// backup used in case of running out of gas
|
||||||
self.state.checkpoint();
|
self.state.checkpoint();
|
||||||
|
|
||||||
@ -329,7 +329,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(params.gas - cost)
|
Ok((params.gas - cost, ReturnData::empty()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// just drain the whole gas
|
// just drain the whole gas
|
||||||
@ -376,13 +376,13 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
|
|
||||||
self.enact_result(&res, substate, unconfirmed_substate);
|
self.enact_result(&res, substate, unconfirmed_substate);
|
||||||
trace!(target: "executive", "enacted: substate={:?}\n", substate);
|
trace!(target: "executive", "enacted: substate={:?}\n", substate);
|
||||||
res.map(|r| r.gas_left)
|
res.map(|r| (r.gas_left, r.return_data))
|
||||||
} else {
|
} else {
|
||||||
// otherwise it's just a basic transaction, only do tracing, if necessary.
|
// otherwise it's just a basic transaction, only do tracing, if necessary.
|
||||||
self.state.discard_checkpoint();
|
self.state.discard_checkpoint();
|
||||||
|
|
||||||
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
|
tracer.trace_call(trace_info, U256::zero(), trace_output, vec![]);
|
||||||
Ok(params.gas)
|
Ok((params.gas, ReturnData::empty()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,7 +396,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
substate: &mut Substate,
|
substate: &mut Substate,
|
||||||
tracer: &mut T,
|
tracer: &mut T,
|
||||||
vm_tracer: &mut V,
|
vm_tracer: &mut V,
|
||||||
) -> evm::Result<U256> where T: Tracer, V: VMTracer {
|
) -> evm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer {
|
||||||
|
|
||||||
let scheme = self.engine.create_address_scheme(self.info.number);
|
let scheme = self.engine.create_address_scheme(self.info.number);
|
||||||
if scheme != CreateContractAddress::FromSenderAndNonce && self.state.exists_and_has_code(¶ms.address)? {
|
if scheme != CreateContractAddress::FromSenderAndNonce && self.state.exists_and_has_code(¶ms.address)? {
|
||||||
@ -446,7 +446,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.enact_result(&res, substate, unconfirmed_substate);
|
self.enact_result(&res, substate, unconfirmed_substate);
|
||||||
res.map(|r| r.gas_left)
|
res.map(|r| (r.gas_left, r.return_data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalizes the transaction (does refunds and suicides).
|
/// Finalizes the transaction (does refunds and suicides).
|
||||||
@ -454,7 +454,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
t: &SignedTransaction,
|
t: &SignedTransaction,
|
||||||
mut substate: Substate,
|
mut substate: Substate,
|
||||||
result: evm::Result<U256>,
|
result: evm::Result<(U256, ReturnData)>,
|
||||||
output: Bytes,
|
output: Bytes,
|
||||||
trace: Vec<FlatTrace>,
|
trace: Vec<FlatTrace>,
|
||||||
vm_trace: Option<VMTrace>
|
vm_trace: Option<VMTrace>
|
||||||
@ -468,7 +468,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
|
|||||||
let refunds_bound = sstore_refunds + suicide_refunds;
|
let refunds_bound = sstore_refunds + suicide_refunds;
|
||||||
|
|
||||||
// real ammount to refund
|
// real ammount to refund
|
||||||
let gas_left_prerefund = match result { Ok(x) => x, _ => 0.into() };
|
let gas_left_prerefund = match result { Ok((x, _)) => x, _ => 0.into() };
|
||||||
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1);
|
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1);
|
||||||
let gas_left = gas_left_prerefund + refunded;
|
let gas_left = gas_left_prerefund + refunded;
|
||||||
|
|
||||||
@ -597,7 +597,7 @@ mod tests {
|
|||||||
let engine = TestEngine::new(0);
|
let engine = TestEngine::new(0);
|
||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
let gas_left = {
|
let (gas_left, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||||
};
|
};
|
||||||
@ -655,7 +655,7 @@ mod tests {
|
|||||||
let engine = TestEngine::new(0);
|
let engine = TestEngine::new(0);
|
||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
let gas_left = {
|
let (gas_left, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||||
};
|
};
|
||||||
@ -713,7 +713,7 @@ mod tests {
|
|||||||
let mut tracer = ExecutiveTracer::default();
|
let mut tracer = ExecutiveTracer::default();
|
||||||
let mut vm_tracer = ExecutiveVMTracer::toplevel();
|
let mut vm_tracer = ExecutiveVMTracer::toplevel();
|
||||||
|
|
||||||
let gas_left = {
|
let (gas_left, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
let output = BytesRef::Fixed(&mut[0u8;0]);
|
let output = BytesRef::Fixed(&mut[0u8;0]);
|
||||||
ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap()
|
ex.call(params, &mut substate, output, &mut tracer, &mut vm_tracer).unwrap()
|
||||||
@ -822,7 +822,7 @@ mod tests {
|
|||||||
let mut tracer = ExecutiveTracer::default();
|
let mut tracer = ExecutiveTracer::default();
|
||||||
let mut vm_tracer = ExecutiveVMTracer::toplevel();
|
let mut vm_tracer = ExecutiveVMTracer::toplevel();
|
||||||
|
|
||||||
let gas_left = {
|
let (gas_left, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap()
|
ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap()
|
||||||
};
|
};
|
||||||
@ -907,7 +907,7 @@ mod tests {
|
|||||||
let engine = TestEngine::new(0);
|
let engine = TestEngine::new(0);
|
||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
let gas_left = {
|
let (gas_left, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||||
};
|
};
|
||||||
@ -1018,7 +1018,7 @@ mod tests {
|
|||||||
let engine = TestEngine::new(0);
|
let engine = TestEngine::new(0);
|
||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
let gas_left = {
|
let (gas_left, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||||
};
|
};
|
||||||
@ -1062,7 +1062,7 @@ mod tests {
|
|||||||
let engine = TestEngine::new(0);
|
let engine = TestEngine::new(0);
|
||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
let gas_left = {
|
let (gas_left, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
ex.call(params, &mut substate, BytesRef::Fixed(&mut []), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||||
};
|
};
|
||||||
@ -1267,7 +1267,7 @@ mod tests {
|
|||||||
let mut substate = Substate::new();
|
let mut substate = Substate::new();
|
||||||
|
|
||||||
let mut output = [0u8; 14];
|
let mut output = [0u8; 14];
|
||||||
let result = {
|
let (result, _) = {
|
||||||
let mut ex = Executive::new(&mut state, &info, &engine);
|
let mut ex = Executive::new(&mut state, &info, &engine);
|
||||||
ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
ex.call(params, &mut substate, BytesRef::Fixed(&mut output), &mut NoopTracer, &mut NoopVMTracer).unwrap()
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@ use state::{Backend as StateBackend, State, Substate};
|
|||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
use executive::*;
|
use executive::*;
|
||||||
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress};
|
use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, ReturnData};
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
use types::transaction::UNSIGNED_SENDER;
|
use types::transaction::UNSIGNED_SENDER;
|
||||||
use trace::{Tracer, VMTracer};
|
use trace::{Tracer, VMTracer};
|
||||||
@ -212,7 +212,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
|
|
||||||
// TODO: handle internal error separately
|
// TODO: handle internal error separately
|
||||||
match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
|
match ex.create(params, self.substate, self.tracer, self.vm_tracer) {
|
||||||
Ok(gas_left) => {
|
Ok((gas_left, _)) => {
|
||||||
self.substate.contracts_created.push(address.clone());
|
self.substate.contracts_created.push(address.clone());
|
||||||
ContractCreateResult::Created(address, gas_left)
|
ContractCreateResult::Created(address, gas_left)
|
||||||
},
|
},
|
||||||
@ -261,7 +261,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
|
let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth);
|
||||||
|
|
||||||
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
|
match ex.call(params, self.substate, BytesRef::Fixed(output), self.tracer, self.vm_tracer) {
|
||||||
Ok(gas_left) => MessageCallResult::Success(gas_left),
|
Ok((gas_left, return_data)) => MessageCallResult::Success(gas_left, return_data),
|
||||||
_ => MessageCallResult::Failed
|
_ => MessageCallResult::Failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,10 +275,10 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
||||||
fn ret(mut self, gas: &U256, data: &[u8]) -> evm::Result<U256>
|
fn ret(mut self, gas: &U256, data: &ReturnData) -> evm::Result<U256>
|
||||||
where Self: Sized {
|
where Self: Sized {
|
||||||
let handle_copy = |to: &mut Option<&mut Bytes>| {
|
let handle_copy = |to: &mut Option<&mut Bytes>| {
|
||||||
to.as_mut().map(|b| **b = data.to_owned());
|
to.as_mut().map(|b| **b = data.to_vec());
|
||||||
};
|
};
|
||||||
match self.output {
|
match self.output {
|
||||||
OutputPolicy::Return(BytesRef::Fixed(ref mut slice), ref mut copy) => {
|
OutputPolicy::Return(BytesRef::Fixed(ref mut slice), ref mut copy) => {
|
||||||
@ -292,7 +292,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
|
|||||||
handle_copy(copy);
|
handle_copy(copy);
|
||||||
|
|
||||||
vec.clear();
|
vec.clear();
|
||||||
vec.extend_from_slice(data);
|
vec.extend_from_slice(&*data);
|
||||||
Ok(*gas)
|
Ok(*gas)
|
||||||
},
|
},
|
||||||
OutputPolicy::InitContract(ref mut copy) => {
|
OutputPolicy::InitContract(ref mut copy) => {
|
||||||
|
@ -21,7 +21,7 @@ use executive::*;
|
|||||||
use engines::Engine;
|
use engines::Engine;
|
||||||
use env_info::EnvInfo;
|
use env_info::EnvInfo;
|
||||||
use evm;
|
use evm;
|
||||||
use evm::{Schedule, Ext, Finalize, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress};
|
use evm::{Schedule, Ext, Finalize, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress, ReturnData};
|
||||||
use externalities::*;
|
use externalities::*;
|
||||||
use types::executed::CallType;
|
use types::executed::CallType;
|
||||||
use tests::helpers::*;
|
use tests::helpers::*;
|
||||||
@ -142,7 +142,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E>
|
|||||||
gas_limit: *gas,
|
gas_limit: *gas,
|
||||||
value: value.unwrap()
|
value: value.unwrap()
|
||||||
});
|
});
|
||||||
MessageCallResult::Success(*gas)
|
MessageCallResult::Success(*gas, ReturnData::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>> {
|
||||||
@ -157,7 +157,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for TestExt<'a, T, V, B, E>
|
|||||||
self.ext.log(topics, data)
|
self.ext.log(topics, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret(self, gas: &U256, data: &[u8]) -> Result<U256, evm::Error> {
|
fn ret(self, gas: &U256, data: &ReturnData) -> Result<U256, evm::Error> {
|
||||||
self.ext.ret(gas, data)
|
self.ext.ret(gas, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,8 @@ pub struct CommonParams {
|
|||||||
pub eip210_contract_code: Bytes,
|
pub eip210_contract_code: Bytes,
|
||||||
/// Gas allocated for EIP-210 blockhash update.
|
/// Gas allocated for EIP-210 blockhash update.
|
||||||
pub eip210_contract_gas: U256,
|
pub eip210_contract_gas: U256,
|
||||||
|
/// Number of first block where EIP-211 (Metropolis: RETURNDATASIZE/RETURNDATACOPY) rules begin.
|
||||||
|
pub eip211_transition: BlockNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethjson::spec::Params> for CommonParams {
|
impl From<ethjson::spec::Params> for CommonParams {
|
||||||
@ -94,6 +96,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
|| DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect("Default BLOCKHASH contract is valid"),
|
|| DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect("Default BLOCKHASH contract is valid"),
|
||||||
Into::into),
|
Into::into),
|
||||||
eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into),
|
eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into),
|
||||||
|
eip211_transition: p.eip211_transition.map_or(BlockNumber::max_value(), Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,9 @@ pub struct Params {
|
|||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
#[serde(rename="eip210ContractGas")]
|
#[serde(rename="eip210ContractGas")]
|
||||||
pub eip210_contract_gas: Option<Uint>,
|
pub eip210_contract_gas: Option<Uint>,
|
||||||
|
/// See `CommonParams` docs.
|
||||||
|
#[serde(rename="eip211Transition")]
|
||||||
|
pub eip211_transition: Option<Uint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user