Skip to content

Commit

Permalink
Use codehash when deriving addresses (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
nhynes authored Feb 20, 2020
1 parent e969e6c commit 45f4144
Show file tree
Hide file tree
Showing 17 changed files with 296 additions and 144 deletions.
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
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
108 changes: 95 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 Expand Up @@ -2691,12 +2725,17 @@ mod tests {

macro_rules! create_wasm_executive {
($factory:ident -> ($state:ident, $exec:ident)) => {
create_wasm_executive!($factory -> ($state, $exec), |state| {});
};
($factory:ident -> ($state:ident, $exec:ident), |state| $state_init:block) => {
let mut info = EnvInfo::default();
info.number = 100; // wasm activated at block 10
info.last_hashes = Arc::new(vec![H256::zero()]);
info.gas_limit = U256::from(0xffffffffffffffffu64);

let machine = ::ethereum::new_kovan_wasm_test_machine();
let mut $state = get_temp_state_with_factory($factory);
$state_init
let mut $exec = Executive::new(&mut $state, &info, &machine);
};
}
Expand Down Expand Up @@ -2768,6 +2807,49 @@ mod tests {
);
}

evm_test! {test_wasm_c10l_transact: test_wasm_c10l_transact_int}
fn test_wasm_c10l_transact(factory: Factory) {
let mut initcode =
include_bytes!("../res/wasi-tests/target/service/create_ctor.wasm").to_vec();
let salt = [1u8; 32];
let mut code = crate::vm::OasisContract::make_header(
1,
serde_json::json!({ "salt_if_confidential": &salt }).to_string(),
);
code.append(&mut initcode);

let (expected_addr, expected_code_hash) = contract_address(
CreateContractAddress::FromSaltAndCodeHash(salt.into()),
&Default::default(),
&Default::default(),
&code,
);
let expected_code_hash = expected_code_hash.unwrap();

let keypair = Random.generate().unwrap();
let t = Transaction {
action: Action::Create,
value: U256::from(1),
data: code,
gas: U256::from(0xffffffffu64),
gas_price: U256::zero(),
nonce: U256::zero(),
}
.sign(keypair.secret(), None);

create_wasm_executive!(factory -> (state, exec), |state| {
state
.add_balance(&t.sender(), &U256::from(2), CleanupMode::NoEmpty)
.unwrap();
});

let executed = exec
.transact(&t, TransactOptions::with_no_tracing())
.unwrap();

assert_eq!(state.code_hash(&expected_addr), Ok(expected_code_hash));
}

evm_test! {test_wasm_xcc: test_wasm_xcc_int}
fn test_wasm_xcc(factory: Factory) {
let code_xcc =
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()
);
}
}
6 changes: 1 addition & 5 deletions ethcore/src/machine.rs
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 45f4144

Please sign in to comment.