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

[beta] Byzantium updates #6529

Merged
merged 3 commits into from
Sep 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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