[beta] Byzantium updates (#6529)
* fix modexp bug: return 0 if base=0 (#6424) * Running state test using parity-evm (#6355) * Initial version of state tests. * Refactor state to support tracing. * Unify TransactResult. * Add test. * Byzantium updates
This commit is contained in:
@@ -69,6 +69,10 @@ pub enum Error {
|
||||
Internal(String),
|
||||
/// Wasm runtime error
|
||||
Wasm(String),
|
||||
/// Out of bounds access in RETURNDATACOPY.
|
||||
OutOfBounds,
|
||||
/// Execution has been reverted with REVERT.
|
||||
Reverted,
|
||||
}
|
||||
|
||||
impl From<Box<trie::TrieError>> for Error {
|
||||
@@ -96,6 +100,8 @@ impl fmt::Display for Error {
|
||||
Internal(ref msg) => write!(f, "Internal error: {}", msg),
|
||||
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
|
||||
Wasm(ref msg) => write!(f, "Internal error: {}", msg),
|
||||
OutOfBounds => write!(f, "Out of bounds"),
|
||||
Reverted => write!(f, "Reverted"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +185,7 @@ 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, 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, apply_state).map(|gas_left| FinalizationResult {
|
||||
gas_left: gas_left,
|
||||
apply_state: apply_state,
|
||||
return_data: data,
|
||||
|
||||
@@ -29,7 +29,12 @@ pub enum ContractCreateResult {
|
||||
Created(Address, U256),
|
||||
/// Returned when contract creation failed.
|
||||
/// VM doesn't have to know the reason.
|
||||
Failed
|
||||
Failed,
|
||||
/// Returned when contract creation failed.
|
||||
/// VM doesn't have to know the reason.
|
||||
FailedInStaticCall,
|
||||
/// Reverted with REVERT.
|
||||
Reverted(U256, ReturnData),
|
||||
}
|
||||
|
||||
/// Result of externalities call function.
|
||||
@@ -39,7 +44,10 @@ pub enum MessageCallResult {
|
||||
Success(U256, ReturnData),
|
||||
/// Returned when message call failed.
|
||||
/// VM doesn't have to know the reason.
|
||||
Failed
|
||||
Failed,
|
||||
/// Returned when message call was reverted.
|
||||
/// Contains gas left and output data.
|
||||
Reverted(U256, ReturnData),
|
||||
}
|
||||
|
||||
/// Specifies how an address is calculated for a new contract.
|
||||
@@ -109,7 +117,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: &ReturnData) -> evm::Result<U256>;
|
||||
fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> evm::Result<U256>;
|
||||
|
||||
/// Should be called when contract commits suicide.
|
||||
/// Address to which funds should be refunded.
|
||||
@@ -138,4 +146,7 @@ pub trait Ext {
|
||||
|
||||
/// Trace the finalised execution of a single instruction.
|
||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||
|
||||
/// Check if running in static context.
|
||||
fn is_static(&self) -> bool;
|
||||
}
|
||||
|
||||
@@ -319,6 +319,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let contract_code = self.mem.read_slice(init_off, init_size);
|
||||
let can_create = ext.balance(¶ms.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||
|
||||
// clear return data buffer before creating new call frame.
|
||||
self.return_data = ReturnData::empty();
|
||||
|
||||
if !can_create {
|
||||
stack.push(U256::zero());
|
||||
return Ok(InstructionResult::UnusedGas(create_gas));
|
||||
@@ -330,10 +333,18 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
stack.push(address_to_u256(address));
|
||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
},
|
||||
ContractCreateResult::Reverted(gas_left, return_data) => {
|
||||
stack.push(U256::zero());
|
||||
self.return_data = return_data;
|
||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||
},
|
||||
ContractCreateResult::Failed => {
|
||||
stack.push(U256::zero());
|
||||
Ok(InstructionResult::Ok)
|
||||
}
|
||||
},
|
||||
ContractCreateResult::FailedInStaticCall => {
|
||||
Err(evm::Error::MutableCallInStaticContext)
|
||||
},
|
||||
};
|
||||
},
|
||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||
@@ -344,8 +355,10 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let code_address = stack.pop_back();
|
||||
let code_address = u256_to_address(&code_address);
|
||||
|
||||
let value = if instruction == instructions::DELEGATECALL || instruction == instructions::STATICCALL {
|
||||
let value = if instruction == instructions::DELEGATECALL {
|
||||
None
|
||||
} else if instruction == instructions::STATICCALL {
|
||||
Some(U256::zero())
|
||||
} else {
|
||||
Some(stack.pop_back())
|
||||
};
|
||||
@@ -364,6 +377,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
// Get sender & receive addresses, check if we have balance
|
||||
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
||||
instructions::CALL => {
|
||||
if ext.is_static() && value.map_or(false, |v| !v.is_zero()) {
|
||||
return Err(evm::Error::MutableCallInStaticContext);
|
||||
}
|
||||
let has_balance = ext.balance(¶ms.address)? >= value.expect("value set for all but delegate call; qed");
|
||||
(¶ms.address, &code_address, has_balance, CallType::Call)
|
||||
},
|
||||
@@ -372,10 +388,13 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
||||
},
|
||||
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
||||
instructions::STATICCALL => (¶ms.sender, ¶ms.address, true, CallType::StaticCall),
|
||||
instructions::STATICCALL => (¶ms.address, &code_address, true, CallType::StaticCall),
|
||||
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
|
||||
};
|
||||
|
||||
// clear return data buffer before creating new call frame.
|
||||
self.return_data = ReturnData::empty();
|
||||
|
||||
let can_call = has_balance && ext.depth() < ext.schedule().max_depth;
|
||||
if !can_call {
|
||||
stack.push(U256::zero());
|
||||
@@ -394,12 +413,17 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
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")))
|
||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")))
|
||||
},
|
||||
MessageCallResult::Reverted(gas_left, data) => {
|
||||
stack.push(U256::zero());
|
||||
self.return_data = data;
|
||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")))
|
||||
},
|
||||
MessageCallResult::Failed => {
|
||||
stack.push(U256::zero());
|
||||
Ok(InstructionResult::Ok)
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
instructions::RETURN => {
|
||||
@@ -537,6 +561,14 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
Self::copy_data_to_memory(&mut self.mem, stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
|
||||
},
|
||||
instructions::RETURNDATACOPY => {
|
||||
{
|
||||
let source_offset = stack.peek(1);
|
||||
let size = stack.peek(2);
|
||||
let return_data_len = U256::from(self.return_data.len());
|
||||
if source_offset.overflow_add(*size).0 > return_data_len {
|
||||
return Err(evm::Error::OutOfBounds);
|
||||
}
|
||||
}
|
||||
Self::copy_data_to_memory(&mut self.mem, stack, &*self.return_data);
|
||||
},
|
||||
instructions::CODECOPY => {
|
||||
|
||||
@@ -111,6 +111,8 @@ pub struct Schedule {
|
||||
pub have_return_data: bool,
|
||||
/// Kill basic accounts below this balance if touched.
|
||||
pub kill_dust: CleanDustMode,
|
||||
/// Enable EIP-86 rules
|
||||
pub eip86: bool,
|
||||
}
|
||||
|
||||
/// Dust accounts cleanup mode.
|
||||
@@ -184,17 +186,17 @@ impl Schedule {
|
||||
blockhash_gas: 20,
|
||||
have_static_call: false,
|
||||
kill_dust: CleanDustMode::Off,
|
||||
eip86: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule for the Metropolis of the Ethereum main net.
|
||||
pub fn new_metropolis() -> Schedule {
|
||||
/// Schedule for the Byzantium fork of the Ethereum main net.
|
||||
pub fn new_byzantium() -> Schedule {
|
||||
let mut schedule = Self::new_post_eip150(24576, true, true, true);
|
||||
schedule.have_create2 = true;
|
||||
schedule.have_revert = true;
|
||||
schedule.have_static_call = true;
|
||||
schedule.have_return_data = true;
|
||||
schedule.blockhash_gas = 350;
|
||||
schedule
|
||||
}
|
||||
|
||||
@@ -246,6 +248,7 @@ impl Schedule {
|
||||
blockhash_gas: 20,
|
||||
have_static_call: false,
|
||||
kill_dust: CleanDustMode::Off,
|
||||
eip86: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,16 @@ impl<'a> Runtime<'a> {
|
||||
ext::ContractCreateResult::Failed => {
|
||||
trace!(target: "wasm", "runtime: create contract fail");
|
||||
Ok(Some((-1i32).into()))
|
||||
}
|
||||
},
|
||||
ext::ContractCreateResult::Reverted(gas_left, _) => {
|
||||
trace!(target: "wasm", "runtime: create contract reverted");
|
||||
self.gas_counter = self.gas_limit - gas_left.low_u64();
|
||||
Ok(Some((-1i32).into()))
|
||||
},
|
||||
ext::ContractCreateResult::FailedInStaticCall => {
|
||||
trace!(target: "wasm", "runtime: create contract called in static context");
|
||||
Err(interpreter::Error::Trap("CREATE in static context".to_owned()))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user