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

Commit

Permalink
[beta] Byzantium updates (#6529)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
arkpar authored Sep 16, 2017
1 parent d8bf5fc commit bb311e8
Show file tree
Hide file tree
Showing 57 changed files with 1,393 additions and 752 deletions.
23 changes: 21 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

97 changes: 97 additions & 0 deletions ethcore/benches/evm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.

// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

#![feature(test)]

extern crate test;
extern crate ethcore_util as util;
extern crate rand;
extern crate bn;
extern crate crypto;
extern crate rustc_serialize;
extern crate ethkey;

use self::test::{Bencher};
use rand::{StdRng};


#[bench]
fn bn_128_pairing(b: &mut Bencher) {
use bn::{pairing, G1, G2, Fr, Group};

let rng = &mut ::rand::thread_rng();

let sk0 = Fr::random(rng);
let sk1 = Fr::random(rng);

let pk0 = G1::one() * sk0;
let pk1 = G2::one() * sk1;

b.iter(|| {
let _ = pairing(pk0, pk1);
});
}

#[bench]
fn bn_128_mul(b: &mut Bencher) {
use bn::{AffineG1, G1, Fr, Group};

let mut rng = StdRng::new().unwrap();
let p: G1 = G1::random(&mut rng);
let fr = Fr::random(&mut rng);

b.iter(|| {
let _ = AffineG1::from_jacobian(p * fr);
});
}

#[bench]
fn sha256(b: &mut Bencher) {
use crypto::sha2::Sha256;
use crypto::digest::Digest;

let mut input: [u8; 256] = [0; 256];
let mut out = [0; 32];

b.iter(|| {
let mut sha = Sha256::new();
sha.input(&input);
sha.result(&mut input[0..32]);
});
}

#[bench]
fn ecrecover(b: &mut Bencher) {
use rustc_serialize::hex::FromHex;
use ethkey::{Signature, recover as ec_recover};
use util::H256;
let input = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
let hash = H256::from_slice(&input[0..32]);
let v = H256::from_slice(&input[32..64]);
let r = H256::from_slice(&input[64..96]);
let s = H256::from_slice(&input[96..128]);

let bit = match v[31] {
27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27,
_ => { return; },
};

let s = Signature::from_rsv(&r, &s, bit);
b.iter(|| {
let _ = ec_recover(&s, &hash);
});
}

8 changes: 7 additions & 1 deletion ethcore/evm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"),
}
}
}
Expand Down Expand Up @@ -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,
Expand Down
17 changes: 14 additions & 3 deletions ethcore/evm/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
42 changes: 37 additions & 5 deletions ethcore/evm/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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 => {
Expand All @@ -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())
};
Expand All @@ -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)
},
Expand All @@ -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());
Expand All @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand Down
9 changes: 6 additions & 3 deletions ethcore/evm/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -246,6 +248,7 @@ impl Schedule {
blockhash_gas: 20,
have_static_call: false,
kill_dust: CleanDustMode::Off,
eip86: false,
}
}
}
Expand Down
11 changes: 10 additions & 1 deletion ethcore/evm/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
},
}
}

Expand Down
Loading

0 comments on commit bb311e8

Please sign in to comment.