[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:
Arkadiy Paronyan
2017-09-16 12:21:35 +02:00
committed by GitHub
parent d8bf5fc848
commit bb311e838b
57 changed files with 1403 additions and 762 deletions

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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(&params.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(&params.address)? >= value.expect("value set for all but delegate call; qed");
(&params.address, &code_address, has_balance, CallType::Call)
},
@@ -372,10 +388,13 @@ impl<Cost: CostType> Interpreter<Cost> {
(&params.address, &params.address, has_balance, CallType::CallCode)
},
instructions::DELEGATECALL => (&params.sender, &params.address, true, CallType::DelegateCall),
instructions::STATICCALL => (&params.sender, &params.address, true, CallType::StaticCall),
instructions::STATICCALL => (&params.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 => {

View File

@@ -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,
}
}
}

View File

@@ -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()))
},
}
}