Skip to content

Commit

Permalink
feat: compute contract address (#563)
Browse files Browse the repository at this point in the history
* rename is_deployed / exists

* feat: compute_contract_address & rlp encode_sequence

* tests: comput address

* feat: compute contract address

* feat: compute contract address

* address pr review

* address pr review
  • Loading branch information
enitrat authored Nov 21, 2023
1 parent ad9ff2f commit c517060
Show file tree
Hide file tree
Showing 16 changed files with 487 additions and 191 deletions.
6 changes: 3 additions & 3 deletions crates/contracts/src/kakarot_core/kakarot.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ mod KakarotCore {
};
use super::{INVOKE_ETH_CALL_FORBIDDEN};
use super::{StoredAccountType};
use utils::helpers::{compute_starknet_address};
use utils::helpers::{compute_starknet_address, EthAddressExTrait};
use utils::rlp::RLPTrait;

component!(path: ownable_component, storage: ownable, event: OwnableEvent);
component!(path: upgradeable_component, storage: upgradeable, event: UpgradeableEvent);
Expand Down Expand Up @@ -392,8 +393,7 @@ mod KakarotCore {
return Result::Ok(execution_result);
},
Option::None => {
let bytecode = data;
// TODO: compute_evm_address
// Deploy tx case.
// HASH(RLP(deployer_address, deployer_nonce))[0..20]
//TODO manually set target account type to CA in state
panic_with_felt252('deploy tx flow unimplemented')
Expand Down
50 changes: 11 additions & 39 deletions crates/evm/src/create_helpers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ use evm::stack::StackTrait;
use evm::state::StateTrait;
use keccak::cairo_keccak;
use starknet::{EthAddress, get_tx_info};
use utils::address::{compute_contract_address, compute_create2_contract_address};
use utils::helpers::ArrayExtTrait;
use utils::helpers::{ResultExTrait, EthAddressExt, U256Trait, U8SpanExTrait};
use utils::helpers::{ResultExTrait, EthAddressExTrait, U256Trait, U8SpanExTrait};
use utils::traits::{
BoolIntoNumeric, EthAddressIntoU256, U256TryIntoResult, SpanU8TryIntoResultEthAddress
};
Expand Down Expand Up @@ -48,15 +49,16 @@ impl MachineCreateHelpersImpl of MachineCreateHelpers {

// TODO(state): when the tx starts,
// store get_tx_info().unbox().nonce inside the sender account nonce
let sender_nonce = get_tx_info().unbox().nonce;
// so that we can call self.nonce() instead of get_tx_info().unbox().nonce

let to = match create_type {
CreateType::CreateOrDeployTx => self
.get_create_address(self.address().evm, sender_nonce)?,
CreateType::Create2 => self
.get_create2_address(
self.address().evm, salt: self.stack.pop()?, bytecode: bytecode.span()
)?,
CreateType::CreateOrDeployTx => {
let nonce = self.state.get_account(self.address().evm)?.nonce();
compute_contract_address(self.address().evm, sender_nonce: nonce)
},
CreateType::Create2 => compute_create2_contract_address(
self.address().evm, salt: self.stack.pop()?, bytecode: bytecode.span()
)?,
};

Result::Ok(CreateArgs { to, value, bytecode: bytecode.span() })
Expand Down Expand Up @@ -98,7 +100,7 @@ impl MachineCreateHelpersImpl of MachineCreateHelpers {
// - contract is already deployed at this location (type fetched from storage)
// - Contract has been scheduled for deployment (type set in cache)
// If the AccountType is unknown, then there's no collision.
if target_account.is_deployed() {
if target_account.exists() {
return self.stack.push(0);
};

Expand Down Expand Up @@ -172,34 +174,4 @@ impl MachineCreateHelpersImpl of MachineCreateHelpers {
},
}
}

fn get_create_address(
ref self: Machine, sender_address: EthAddress, sender_nounce: felt252
) -> Result<EthAddress, EVMError> {
panic_with_felt252('get_create_address todo')
}


fn get_create2_address(
self: @Machine, sender_address: EthAddress, salt: u256, bytecode: Span<u8>
) -> Result<EthAddress, EVMError> {
let hash = bytecode.compute_keccak256_hash().to_bytes();

let sender_address = sender_address.to_bytes();

let salt = salt.to_bytes();

let mut preimage: Array<u8> = array![];

preimage.concat(array![0xff].span());
preimage.concat(sender_address);
preimage.concat(salt);
preimage.concat(hash);

let address_hash = preimage.span().compute_keccak256_hash().to_bytes();

let address: EthAddress = address_hash.slice(12, 20).try_into_result()?;

Result::Ok(address)
}
}
4 changes: 2 additions & 2 deletions crates/evm/src/model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ struct Address {

#[generate_trait]
impl AddressImpl of AddressTrait {
fn is_registered(evm_address: EthAddress) -> bool {
fn is_deployed(self: EthAddress) -> bool {
let mut kakarot_state = KakarotCore::unsafe_new_contract_state();
let maybe_account = kakarot_state.address_registry(evm_address);
let maybe_account = kakarot_state.address_registry(self);
match maybe_account {
Option::Some(_) => true,
Option::None => false
Expand Down
18 changes: 9 additions & 9 deletions crates/evm/src/model/account.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ impl AccountImpl of AccountTrait {
/// be registered already, and the nonce must not be 0 or the code must not
/// be empty
#[inline(always)]
fn should_deploy(self: @Account, is_registered: bool) -> bool {
if !is_registered && self.is_ca() && (*self.nonce != 0 || !(*self.code).is_empty()) {
fn should_deploy(self: @Account) -> bool {
if self.is_ca() && (*self.nonce != 0 || !(*self.code).is_empty()) {
return true;
};
false
Expand All @@ -181,9 +181,9 @@ impl AccountImpl of AccountTrait {
/// `Ok(())` if the commit was successful, otherwise an `EVMError`.
fn commit(self: @Account) -> Result<(), EVMError> {
// Case account exists and is already on chain
let is_registered = AddressTrait::is_registered(self.address().evm);
let is_deployed = self.address().evm.is_deployed();

if is_registered {
if is_deployed {
match self.account_type {
AccountType::EOA(eoa) => {
// no - op
Expand All @@ -203,7 +203,7 @@ impl AccountImpl of AccountTrait {
},
AccountType::Unknown => { Result::Ok(()) }
}
} else if self.should_deploy(is_registered) {
} else if self.should_deploy() {
//Case new account
// If SELFDESTRUCT, just do nothing
if (*self.selfdestruct == true) {
Expand Down Expand Up @@ -231,19 +231,19 @@ impl AccountImpl of AccountTrait {
false
}

/// Returns whether an accound is deployed at the given address.
/// Returns whether an accound is exists at the given address.
///
/// Based on the state of the account in the cache - the account can
/// not be commited on-chain, but already be deployed in the KakarotState.
/// not be deployed on-chain yet, but already exist in the KakarotState.
/// # Arguments
///
/// * `address` - The Ethereum address to look up.
///
/// # Returns
///
/// `true` if an account is deployed at this address, `false` otherwise.
/// `true` if an account exists at this address, `false` otherwise.
#[inline(always)]
fn is_deployed(self: @Account) -> bool {
fn exists(self: @Account) -> bool {
let is_known = *self.account_type != AccountType::Unknown;

//TODO(account) verify whether is_known is a sufficient condition
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/src/model/eoa.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl EOAImpl of EOATrait {
/// * `evm_address` - The EVM address of the EOA to deploy.
fn deploy(evm_address: EthAddress) -> Result<Address, EVMError> {
// Unlike CAs, there is not check for the existence of an EOA prealably to calling `EOATrait::deploy` - therefore, we need to check that there is no collision.
let mut is_deployed = AddressTrait::is_registered(evm_address);
let mut is_deployed = evm_address.is_deployed();
if is_deployed {
return Result::Err(EVMError::DeployError(EOA_EXISTS));
}
Expand Down
33 changes: 1 addition & 32 deletions crates/evm/src/tests/test_create_helpers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,5 @@ use contracts::tests::test_data::counter_evm_bytecode;
use evm::create_helpers::MachineCreateHelpers;
use evm::tests::test_utils::setup_machine;
use starknet::EthAddress;
use utils::address::{compute_contract_address, compute_create2_contract_address};

#[test]
#[available_gas(3_000_000_000_000)]
fn test_get_create2_address() {
let machine = setup_machine();
let bytecode = counter_evm_bytecode();
let salt = 0xbeef;
let from: EthAddress = 0xF39FD6E51AAD88F6F4CE6AB8827279CFFFB92266
.try_into()
.expect('Wrong Eth address');

let address = machine
.get_create2_address(from, salt, bytecode)
.expect('get_create2_address fail');

// TODO
// add SNJS script for:
// import { getContractAddress } from 'viem'
// const address = getContractAddress({
// bytecode: '0x6080604052348015600f57600080fd5b506004361060465760003560e01c806306661abd14604b578063371303c01460655780636d4ce63c14606d578063b3bcfa82146074575b600080fd5b605360005481565b60405190815260200160405180910390f35b606b607a565b005b6000546053565b606b6091565b6001600080828254608a919060b7565b9091555050565b6001600080828254608a919060cd565b634e487b7160e01b600052601160045260246000fd5b8082018082111560c75760c760a1565b92915050565b8181038181111560c75760c760a156fea2646970667358221220f379b9089b70e8e00da8545f9a86f648441fdf27ece9ade2c71653b12fb80c7964736f6c63430008120033',
// from: '0xF39FD6E51AAD88F6F4CE6AB8827279CFFFB92266',
// opcode: 'CREATE2',
// salt: '0xbeef',
// });

// console.log(address)
assert(
address == 0xaE6b9c5FD4C9037511100FFb6813D0f607a49f3A
.try_into()
.expect('Wrong Eth Address'),
'wrong create2 address'
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ fn test_exec_call() {
}

#[test]
#[available_gas(50000000)]
#[available_gas(70000000)]
fn test_exec_call_no_return() {
// Given
let mut interpreter = EVMInterpreterTrait::new();
Expand Down
47 changes: 32 additions & 15 deletions crates/evm/src/tests/test_model.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use starknet::testing::set_contract_address;

#[test]
#[available_gas(20000000)]
fn test_is_registered_eoa_exists() {
fn test_is_deployed_eoa_exists() {
// Given
let (native_token, kakarot_core) = setup_contracts_for_testing();
let eoa_address = EOATrait::deploy(evm_address()).expect('failed deploy contract account',);
Expand All @@ -19,39 +19,39 @@ fn test_is_registered_eoa_exists() {

// When
set_contract_address(kakarot_core.contract_address);
let is_registered = AddressTrait::is_registered(evm_address());
let is_deployed = evm_address().is_deployed();

// Then
assert(is_registered, 'account should be deployed');
assert(is_deployed, 'account should be deployed');
}

#[test]
#[available_gas(20000000)]
fn test_is_registered_ca_exists() {
fn test_is_deployed_ca_exists() {
// Given
let (native_token, kakarot_core) = setup_contracts_for_testing();
ContractAccountTrait::deploy(evm_address(), array![].span())
.expect('failed deploy contract account',);

// When
let is_registered = AddressTrait::is_registered(evm_address());
let is_deployed = evm_address().is_deployed();

// Then
assert(is_registered, 'account should be deployed');
assert(is_deployed, 'account should be deployed');
}

#[test]
#[available_gas(20000000)]
fn test_is_registered_undeployed() {
fn test_is_deployed_undeployed() {
// Given
let (native_token, kakarot_core) = setup_contracts_for_testing();

// When
set_contract_address(kakarot_core.contract_address);
let is_registered = AddressTrait::is_registered(evm_address());
let is_deployed = evm_address().is_deployed();

// Then
assert(!is_registered, 'account should be undeployed');
assert(!is_deployed, 'account should be undeployed');
}


Expand Down Expand Up @@ -94,7 +94,7 @@ fn test_address_balance_eoa() {

#[test]
#[available_gas(5000000)]
fn test_account_is_deployed_eoa() {
fn test_account_exists_eoa() {
// Given
let (native_token, kakarot_core) = setup_contracts_for_testing();
let mut eoa_address = EOATrait::deploy(evm_address()).expect('failed deploy eoa',);
Expand All @@ -103,13 +103,13 @@ fn test_account_is_deployed_eoa() {
let account = AccountTrait::fetch(evm_address()).unwrap().unwrap();

// Then
assert(account.is_deployed() == true, 'account should be deployed');
assert(account.exists() == true, 'account should be deployed');
}


#[test]
#[available_gas(5000000)]
fn test_account_is_deployed_contract_account() {
fn test_account_exists_contract_account() {
// Given
let (native_token, kakarot_core) = setup_contracts_for_testing();
let mut ca_address = ContractAccountTrait::deploy(evm_address(), array![].span())
Expand All @@ -119,21 +119,38 @@ fn test_account_is_deployed_contract_account() {
let account = AccountTrait::fetch(evm_address()).unwrap().unwrap();

// Then
assert(account.is_deployed() == true, 'account should be deployed');
assert(account.exists() == true, 'account should be deployed');
}


#[test]
#[available_gas(5000000)]
fn test_account_is_deployed_undeployed() {
fn test_account_exists_undeployed() {
// Given
let (native_token, kakarot_core) = setup_contracts_for_testing();

// When
let account = AccountTrait::fetch_or_create(evm_address()).unwrap();

// Then
assert(account.is_deployed() == false, 'account should be deployed');
assert(account.exists() == false, 'account should be deployed');
}

#[test]
#[available_gas(5000000)]
fn test_account_exists_account_to_deploy() {
// Given
let (native_token, kakarot_core) = setup_contracts_for_testing();

// When
let mut account = AccountTrait::fetch_or_create(evm_address()).unwrap();
// Mock account as an existing contract account in the cached state.
account.account_type = AccountType::ContractAccount;
account.nonce = 1;
account.code = array![0x1].span();

// Then
assert(account.exists() == true, 'account should exist');
}


Expand Down
Loading

0 comments on commit c517060

Please sign in to comment.