Skip to content
2 changes: 1 addition & 1 deletion ethcore/evm/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ lazy_static! {
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special);
arr[STATICCALL as usize] = InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special);
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special);
arr[CREATE2 as usize] = InstructionInfo::new("CREATE2", 3, 1, GasPriceTier::Special);
arr[CREATE2 as usize] = InstructionInfo::new("CREATE2", 4, 1, GasPriceTier::Special);
arr[REVERT as usize] = InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero);
arr
};
Expand Down
6 changes: 5 additions & 1 deletion ethcore/evm/src/interpreter/gasometer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
}
instructions::CREATE | instructions::CREATE2 => {
let gas = Gas::from(schedule.create_gas);
let mem = mem_needed(stack.peek(1), stack.peek(2))?;
let mem = match instruction {
instructions::CREATE => mem_needed(stack.peek(1), stack.peek(2))?,
instructions::CREATE2 => mem_needed(stack.peek(2), stack.peek(3))?,
_ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"),
};

Request::GasMemProvide(gas, mem, None)
}
Expand Down
12 changes: 7 additions & 5 deletions ethcore/evm/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,13 @@ impl<Cost: CostType> Interpreter<Cost> {
}
instructions::CREATE | instructions::CREATE2 => {
let endowment = stack.pop_back();
let address_scheme = match instruction {
instructions::CREATE => CreateContractAddress::FromSenderAndNonce,
instructions::CREATE2 => {
CreateContractAddress::FromSenderSaltAndCodeHash(stack.pop_back().into())
}
_ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"),
};
let init_off = stack.pop_back();
let init_size = stack.pop_back();

Expand All @@ -381,11 +388,6 @@ impl<Cost: CostType> Interpreter<Cost> {
}

let contract_code = self.mem.read_slice(init_off, init_size);
let address_scheme = if instruction == instructions::CREATE {
CreateContractAddress::FromSenderAndNonce
} else {
CreateContractAddress::FromSenderAndCodeHash
};

let create_result = ext.create(
&create_gas.as_u256(),
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/engines/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {

/// Returns new contract address generation scheme at given block number.
fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress {
self.machine().create_address_scheme(number)
unreachable!("address scheme is created directly from `EthereumMachine`")
}

/// Verify a particular transaction is valid.
Expand Down
3 changes: 3 additions & 0 deletions ethcore/src/executed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ pub enum ExecutionError {
TransactionMalformed(String),
/// Returned when a non-confidential transaction execution is requested in confidential mode.
NotConfidential,
/// For confidential transactions, the code at address was not what was expected.
InvalidCode,
}

impl From<Box<trie::TrieError>> for ExecutionError {
Expand Down Expand Up @@ -169,6 +171,7 @@ impl fmt::Display for ExecutionError {
SenderMustExist => write!(f, "Transacting from an empty account"),
Internal(ref msg) => write!(f, "{}", msg),
TransactionMalformed(ref err) => write!(f, "Malformed transaction: {}", err),
InvalidCode => write!(f, "Invalid code found in state"),
NotConfidential => write!(
f,
"Tried executing a non-confidential transaction in confidential mode"
Expand Down
60 changes: 47 additions & 13 deletions ethcore/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,26 @@ pub fn contract_address(
stream.append(nonce);
(From::from(keccak(stream.as_raw())), None)
}
CreateContractAddress::FromCodeHash => {
CreateContractAddress::FromSenderSaltAndCodeHash(salt) => {
let code_hash = keccak(code);
let mut buffer = [0xffu8; 20 + 32];
&mut buffer[20..].copy_from_slice(&code_hash[..]);
let mut buffer = [0u8; 20 + 32 + 32];
buffer[0..20].copy_from_slice(&sender[..]);
buffer[20..(20 + 32)].copy_from_slice(&salt[..]);
buffer[(20 + 32)..].copy_from_slice(&code_hash[..]);
(From::from(keccak(&buffer[..])), Some(code_hash))
}
CreateContractAddress::FromSaltAndCodeHash(salt) => {
let code_hash = keccak(code);
let mut buffer = [0u8; 32 + 32];
buffer[0..32].copy_from_slice(&salt[..]);
buffer[32..].copy_from_slice(&code_hash[..]);
(From::from(keccak(&buffer[..])), Some(code_hash))
}
CreateContractAddress::FromSenderAndCodeHash => {
let code_hash = keccak(code);
let mut buffer = [0u8; 20 + 32];
&mut buffer[..20].copy_from_slice(&sender[..]);
&mut buffer[20..].copy_from_slice(&code_hash[..]);
buffer[..20].copy_from_slice(&sender[..]);
buffer[20..].copy_from_slice(&code_hash[..]);
(From::from(keccak(&buffer[..])), Some(code_hash))
}
}
Expand Down Expand Up @@ -332,7 +341,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
.map_err(|e| ExecutionError::TransactionMalformed(e))?;

let schedule = self.machine.schedule(self.info.number);
let confidential = oasis_contract.as_ref().map_or(false, |c| c.confidential);
let confidential = oasis_contract
.as_ref()
.map_or(false, |c| c.is_confidential());
let base_gas_required = U256::from(t.gas_required(&schedule, confidential));

if t.gas < base_gas_required {
Expand Down Expand Up @@ -386,7 +397,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
let mut substate = Substate::new();

// NOTE: there can be no invalid transactions from this point.
if !schedule.eip86 || !t.is_unsigned() {
if !t.is_unsigned() {
self.state.inc_nonce(&sender)?;
}
self.state.sub_balance(
Expand All @@ -397,12 +408,18 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {

let (result, output) = match t.action {
Action::Create => {
let (new_address, code_hash) = contract_address(
self.machine.create_address_scheme(self.info.number),
&sender,
&nonce,
&t.data,
);
let address_scheme = oasis_contract
.as_ref()
.and_then(|oasis_contract| {
oasis_contract
.salt_if_confidential
.map(|salt| CreateContractAddress::FromSaltAndCodeHash(salt.into()))
})
.unwrap_or_else(|| self.machine.create_address_scheme(self.info.number));

let (new_address, code_hash) =
contract_address(address_scheme, &sender, &nonce, &t.data);

let params = ActionParams {
code_address: new_address.clone(),
code_hash: code_hash,
Expand Down Expand Up @@ -443,6 +460,23 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
)
}
Action::Call(ref address) => {
if let Some(OasisContract {
salt_if_confidential: Some(salt),
code,
..
}) = &oasis_contract
{
let expected_address = contract_address(
CreateContractAddress::FromSaltAndCodeHash((*salt).into()),
&Default::default(), /* unused */
&Default::default(), /* unused */
&code,
)
.0;
if *address != expected_address {
return Err(ExecutionError::InvalidCode);
}
}
let params = ActionParams {
code_address: address.clone(),
address: address.clone(),
Expand Down
95 changes: 90 additions & 5 deletions ethcore/src/externalities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,15 @@ where
.borrow()
.activated()
{
let salt = match address_scheme {
CreateContractAddress::FromSaltAndCodeHash(salt) => salt,
_ => unreachable!(
"confidential `create` must have address derived from salt and code hash"
),
};
let mut header_code = OasisContract::make_header(
1,
json!({
"confidential": true
})
.to_string(),
json!({ "salt_if_confidential": salt }).to_string(),
);
header_code.append(&mut code.to_vec());
header_code
Expand Down Expand Up @@ -307,7 +310,7 @@ where
};

if !self.static_flag {
if !self.schedule.eip86 || params.sender != UNSIGNED_SENDER {
if params.sender != UNSIGNED_SENDER {
if let Err(e) = self.state.inc_nonce(&self.origin_info.address) {
debug!(target: "ext", "Database corruption encountered: {:?}", e);
return ContractCreateResult::Failed;
Expand Down Expand Up @@ -901,4 +904,86 @@ mod tests {

assert_eq!(setup.sub_state.suicides.len(), 1);
}

#[test]
fn can_create() {
use std::str::FromStr;

let mut setup = TestSetup::new();
let state = &mut setup.state;
let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let mut ext_tracer = NoopExtTracer;

let address = {
let mut ext = Externalities::new(
state,
&setup.env_info,
&setup.machine,
0,
get_test_origin(),
&mut setup.sub_state,
OutputPolicy::InitContract(None),
&mut tracer,
&mut vm_tracer,
&mut ext_tracer,
false,
);
match ext.create(
&U256::max_value(),
&U256::zero(),
&[],
CreateContractAddress::FromSenderAndNonce,
) {
ContractCreateResult::Created(address, _) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."),
}
};

assert_eq!(
address,
Address::from_str("bd770416a3345f91e4b34576cb804a576fa48eb1").unwrap()
);
}

#[test]
fn can_create2() {
use std::str::FromStr;

let mut setup = TestSetup::new();
let state = &mut setup.state;
let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer;
let mut ext_tracer = NoopExtTracer;

let address = {
let mut ext = Externalities::new(
state,
&setup.env_info,
&setup.machine,
0,
get_test_origin(),
&mut setup.sub_state,
OutputPolicy::InitContract(None),
&mut tracer,
&mut vm_tracer,
&mut ext_tracer,
false,
);
match ext.create(
&U256::max_value(),
&U256::zero(),
&[],
CreateContractAddress::FromSenderSaltAndCodeHash(H256::default()),
) {
ContractCreateResult::Created(address, _) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."),
}
};

assert_eq!(
address,
Address::from_str("b7c227636666831278bacdb8d7f52933b8698ab9").unwrap()
);
}
}
8 changes: 2 additions & 6 deletions ethcore/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use transaction::{self, SignedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS
// use tx_filter::TransactionFilter;

use bytes::BytesRef;
use ethereum_types::{Address, U256};
use ethereum_types::{Address, H256, U256};
use rlp::Rlp;
use vm::{ActionParams, ActionValue, CallType, OasisContract, ParamsType};
use vm::{CreateContractAddress, EnvInfo, Schedule};
Expand Down Expand Up @@ -372,11 +372,7 @@ impl EthereumMachine {

/// Returns new contract address generation scheme at given block number.
pub fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress {
if number >= self.params().eip86_transition {
CreateContractAddress::FromCodeHash
} else {
CreateContractAddress::FromSenderAndNonce
}
CreateContractAddress::FromSenderAndNonce
}

/// Verify a particular transaction is valid, regardless of order.
Expand Down
18 changes: 2 additions & 16 deletions ethcore/src/spec/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ pub struct CommonParams {
pub validate_receipts_transition: BlockNumber,
/// Validate transaction chain id.
pub validate_chain_id_transition: BlockNumber,
/// Number of first block where EIP-86 (Metropolis) rules begin.
pub eip86_transition: BlockNumber,
/// Number of first block where EIP-140 (Metropolis: REVERT opcode) rules begin.
pub eip140_transition: BlockNumber,
/// Number of first block where EIP-210 (Metropolis: BLOCKHASH changes) rules begin.
Expand Down Expand Up @@ -179,7 +177,7 @@ impl CommonParams {

/// Apply common spec config parameters to the schedule.
pub fn update_schedule(&self, block_number: u64, schedule: &mut ::vm::Schedule) {
schedule.have_create2 = block_number >= self.eip86_transition;
schedule.have_create2 = true;
schedule.have_revert = block_number >= self.eip140_transition;
schedule.have_static_call = block_number >= self.eip214_transition;
schedule.have_return_data = block_number >= self.eip211_transition;
Expand All @@ -200,16 +198,7 @@ impl CommonParams {

/// Whether these params contain any bug-fix hard forks.
pub fn contains_bugfix_hard_fork(&self) -> bool {
self.eip98_transition != 0
&& self.eip155_transition != 0
&& self.validate_receipts_transition != 0
&& self.eip86_transition != 0
&& self.eip140_transition != 0
&& self.eip210_transition != 0
&& self.eip211_transition != 0
&& self.eip214_transition != 0
&& self.validate_chain_id_transition != 0
&& self.dust_protection_transition != 0
true
}
}

Expand Down Expand Up @@ -239,9 +228,6 @@ impl From<ethjson::spec::Params> for CommonParams {
eip155_transition: p.eip155_transition.map_or(0, Into::into),
validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into),
validate_chain_id_transition: p.validate_chain_id_transition.map_or(0, Into::into),
eip86_transition: p
.eip86_transition
.map_or_else(BlockNumber::max_value, Into::into),
eip140_transition: p
.eip140_transition
.map_or_else(BlockNumber::max_value, Into::into),
Expand Down
6 changes: 5 additions & 1 deletion ethcore/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1355,7 +1355,7 @@ impl<B: Backend> State<B> {
}
};

Ok(contract.as_ref().map_or(false, |c| c.confidential))
Ok(contract.as_ref().map_or(false, |c| c.is_confidential()))
}

pub fn is_encrypting(&self) -> bool {
Expand Down Expand Up @@ -1602,6 +1602,10 @@ mod tests {
let mut state = get_temp_state();

let mut info = EnvInfo::default();
info.last_hashes = Arc::new(vec![H256::from_str(
"9f1897f227a8a843c0961a5227709fa962dc6860a2129750fdb450f48d7459bb",
)
.unwrap()]);
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);

Expand Down
11 changes: 7 additions & 4 deletions ethcore/vm/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ pub enum MessageCallResult {
/// Specifies how an address is calculated for a new contract.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum CreateContractAddress {
/// Address is calculated from nonce and sender. Pre EIP-86 (Metropolis)
/// Address is calculated from sender and nonce. Pre EIP-86 (Metropolis)
FromSenderAndNonce,
/// Address is calculated from code hash. Default since EIP-86
FromCodeHash,
/// Address is calculated from code hash and sender. Used by CREATE_P2SH instruction.
/// Address is calculated from sender, salt and code hash. EIP-86 CREATE2 scheme.
FromSenderSaltAndCodeHash(H256),
/// Address is calculated from salt and code hash. Similar to EIP-86 CREATE2 scheme
/// except that the Addreess MAC can be verified without knowing the creator.
FromSaltAndCodeHash(H256),
/// Address is calculated from code hash and sender. Used by pwasm create ext.
FromSenderAndCodeHash,
}

Expand Down
Loading