Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use codehash when deriving addresses #181

Merged
merged 13 commits into from
Feb 20, 2020
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`")
nhynes marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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};
nhynes marked this conversation as resolved.
Show resolved Hide resolved
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