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

feat: add __execute__ for eoa #564

Merged
merged 14 commits into from
Nov 24, 2023
39 changes: 31 additions & 8 deletions crates/contracts/src/eoa.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ trait IExternallyOwnedAccount<TContractState> {
mod ExternallyOwnedAccount {
use contracts::components::upgradeable::IUpgradeable;
use contracts::components::upgradeable::upgradeable_component;
use contracts::kakarot_core::interface::{IKakarotCoreDispatcher, IKakarotCoreDispatcherTrait};

use starknet::account::{Call, AccountContract};

use starknet::{
ContractAddress, EthAddress, ClassHash, VALIDATED, get_caller_address, get_contract_address
};
use utils::eth_transaction::{EthTransactionTrait, EthereumTransaction};
use utils::helpers::{Felt252SpanExTrait, U8SpanExTrait};

component!(path: upgradeable_component, storage: upgradeable, event: UpgradeableEvent);

Expand Down Expand Up @@ -100,14 +104,33 @@ mod ExternallyOwnedAccount {


fn __execute__(ref self: ContractState, calls: Array<Call>) -> Array<Span<felt252>> {
// TODO

// Step 1:
// Decode RLP-encoded transaction
// Step 2:
// Call KakarotCore.send_eth_transaction with correct params

array![]
assert(calls.len() == 1, 'calls length is not 1');

let call = calls.at(0);
let calldata = call.calldata.span().to_bytes();

let EthereumTransaction{nonce,
gas_price,
gas_limit,
destination,
amount,
calldata,
chain_id } =
EthTransactionTrait::decode(
calldata.span()
)
.expect('rlp decoding of tx failed');

let kakarot_core_dispatcher = IKakarotCoreDispatcher {
contract_address: self.kakarot_core_address()
};

let result = kakarot_core_dispatcher
.eth_send_transaction(
Option::Some(destination), gas_limit, gas_price, amount, calldata
);

array![result.to_felt252_array().span()]
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/contracts/src/kakarot_core/kakarot.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod KakarotCore {
use contracts::kakarot_core::interface;
use core::starknet::SyscallResultTrait;
use core::zeroable::Zeroable;

use evm::context::Status;
use evm::errors::{EVMError, EVMErrorTrait, CALLING_FROM_CA, CALLING_FROM_UNDEPLOYED_ACCOUNT};
use evm::execution::execute;
Expand Down Expand Up @@ -284,6 +285,7 @@ mod KakarotCore {
// and is an EOA. Contracts are added to the registry ONLY if there are
// part of the Kakarot system and thus deployed by the main Kakarot contract
// itself.

let (caller_account_type, caller_starknet_address) = self
.address_registry(from.evm)
.expect('Fetching EOA failed');
Expand Down
51 changes: 51 additions & 0 deletions crates/contracts/src/tests/test_data.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -1021,3 +1021,54 @@ fn storage_evm_bytecode() -> Span<u8> {
]
.span()
}


// eip-2930 RLP encoded tx { unsigned }, calls the `inc` function of counter bytecode
enitrat marked this conversation as resolved.
Show resolved Hide resolved
// format: 0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])
// rlp decoding: [ '0x01', '0x', '0x3b9aca00', '0x1e8480', '0x0000006f746865725f65766d5f61646472657373', '0x', '0x371303c0', [] ]
fn eip_2930_rlp_encoded_counter_inc_tx() -> Span<u8> {
array![
1,
231,
1,
128,
132,
59,
154,
202,
0,
131,
30,
132,
128,
148,
0,
0,
0,
111,
116,
104,
101,
114,
95,
101,
118,
109,
95,
97,
100,
100,
114,
101,
115,
115,
128,
132,
55,
19,
3,
192,
192,
]
.span()
}
99 changes: 95 additions & 4 deletions crates/contracts/src/tests/test_eoa.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,40 @@ mod test_external_owned_account {
IExternallyOwnedAccountDispatcherTrait
};
use contracts::kakarot_core::kakarot::StoredAccountType;
use contracts::kakarot_core::{IKakarotCore, KakarotCore, KakarotCore::KakarotCoreInternal};
use contracts::kakarot_core::{
IKakarotCore, KakarotCore, KakarotCore::KakarotCoreInternal,
interface::IExtendedKakarotCoreDispatcherTrait
};
use contracts::tests::test_data::{counter_evm_bytecode, eip_2930_rlp_encoded_counter_inc_tx,};
use contracts::tests::test_upgradeable::{
IMockContractUpgradeableDispatcher, IMockContractUpgradeableDispatcherTrait,
MockContractUpgradeableV1
};
use contracts::tests::test_utils::setup_contracts_for_testing;
use contracts::tests::test_utils::deploy_contract_account;
use contracts::tests::test_utils::{setup_contracts_for_testing};
use contracts::uninitialized_account::{
IUninitializedAccountDispatcher, IUninitializedAccountDispatcherTrait, UninitializedAccount,
IUninitializedAccount
};
use evm::model::{Address, AddressTrait};
use evm::tests::test_utils::{kakarot_address, evm_address, eoa_address, chain_id};
use core::array::SpanTrait;
use core::starknet::account::{Call, AccountContractDispatcher, AccountContractDispatcherTrait};


//todo(harsh): remove
use debug::PrintTrait;

use evm::model::{Address, AddressTrait, ContractAccountTrait};
use evm::tests::test_utils::{
kakarot_address, evm_address, other_evm_address, eoa_address, chain_id, gas_limit, gas_price
};
use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait;
use starknet::class_hash::Felt252TryIntoClassHash;
use starknet::testing::{set_caller_address, set_contract_address};
use starknet::{
deploy_syscall, ContractAddress, ClassHash, get_contract_address, contract_address_const,
EthAddress
};
use utils::helpers::{U8SpanExTrait, u256_to_bytes_array};

fn deploy_eoa(eoa_address: EthAddress) -> IExternallyOwnedAccountDispatcher {
let kakarot_address = get_contract_address();
Expand Down Expand Up @@ -103,4 +119,79 @@ mod test_external_owned_account {

eoa_contract.upgrade(new_class_hash);
}

#[test]
#[available_gas(2000000000)]
fn test___execute__() {
let (_, kakarot_core) = setup_contracts_for_testing();

let evm_address = evm_address();
let eoa = kakarot_core.deploy_eoa(evm_address);

//todo(harsh): remove
other_evm_address().print();

let kakarot_address = kakarot_core.contract_address;

let account = deploy_contract_account(other_evm_address(), counter_evm_bytecode());

set_contract_address(eoa);
let eoa_contract = AccountContractDispatcher { contract_address: eoa };

// Then
// selector: function get()
let data_get_tx = array![0x6d, 0x4c, 0xe6, 0x3c].span();

// check counter value is 0 before doing inc
let return_data = kakarot_core
.eth_call(
from: evm_address,
to: Option::Some(other_evm_address()),
gas_limit: gas_limit(),
gas_price: gas_price(),
value: 0,
data: data_get_tx
);

assert(return_data == u256_to_bytes_array(0).span(), 'counter value not 0');

// perform inc on the counter
let encoded_tx = eip_2930_rlp_encoded_counter_inc_tx();

let call = Call {
to: kakarot_address,
selector: selector!("eth_send_transaction"),
calldata: encoded_tx.to_felt252_array()
};

let result = eoa_contract.__execute__(array![call]);

// check counter value has increased
let return_data = kakarot_core
.eth_call(
from: evm_address,
to: Option::Some(other_evm_address()),
gas_limit: gas_limit(),
gas_price: gas_price(),
value: 0,
data: data_get_tx
);

assert(return_data == u256_to_bytes_array(1).span(), 'counter value not 1');
}

#[test]
#[available_gas(2000000000)]
#[should_panic(expected: ('calls length is not 1', 'ENTRYPOINT_FAILED'))]
fn test___execute___should_fail_with_zero_calls() {
let (_, kakarot) = setup_contracts_for_testing();
let kakarot_address = kakarot.contract_address;

let eoa_contract = deploy_eoa(eoa_address());
let eoa_contract = AccountContractDispatcher {
contract_address: eoa_contract.contract_address
};

eoa_contract.__execute__(array![]);
}
}
27 changes: 23 additions & 4 deletions crates/contracts/src/tests/test_kakarot_core.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -266,23 +266,42 @@ fn test_eth_send_transaction() {
let gas_limit = test_utils::gas_limit();
let gas_price = test_utils::gas_price();
let value = 0;

// Then
// selector: function get()
let data_get_tx = array![0x6d, 0x4c, 0xe6, 0x3c].span();
enitrat marked this conversation as resolved.
Show resolved Hide resolved

// check counter value is 0 before doing inc
let return_data = kakarot_core
.eth_call(
from: evm_address,
to: Option::Some(account.evm),
gas_limit: gas_limit,
gas_price: gas_price,
value: 0,
data: data_get_tx
);

assert(return_data == u256_to_bytes_array(0).span(), 'counter value not 0');

// selector: function inc()
let data = array![0x37, 0x13, 0x03, 0xc0].span();
let data_inc_tx = array![0x37, 0x13, 0x03, 0xc0].span();
enitrat marked this conversation as resolved.
Show resolved Hide resolved

// When
testing::set_contract_address(eoa);
let return_data = kakarot_core.eth_send_transaction(:to, :gas_limit, :gas_price, :value, :data);
let return_data = kakarot_core
.eth_send_transaction(:to, :gas_limit, :gas_price, :value, data: data_inc_tx);

// Then
// selector: function get()
let data = array![0x6d, 0x4c, 0xe6, 0x3c].span();

// When
let return_data = kakarot_core
.eth_call(from: evm_address, :to, :gas_limit, :gas_price, :value, :data);
.eth_call(from: evm_address, :to, :gas_limit, :gas_price, :value, data: data_get_tx);

// Then
assert(return_data == u256_to_bytes_array(1).span(), 'wrong result');
assert(return_data == u256_to_bytes_array(1).span(), 'counter value is not 1');
}

#[test]
Expand Down
2 changes: 0 additions & 2 deletions crates/contracts/src/tests/test_upgradeable.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ fn test_upgradeable_update_contract() {

assert(version == 0, 'version is not 0');

let mut call_data: Array<felt252> = array![];
enitrat marked this conversation as resolved.
Show resolved Hide resolved

let new_class_hash: ClassHash = MockContractUpgradeableV1::TEST_CLASS_HASH.try_into().unwrap();

IUpgradeableDispatcher { contract_address: contract_address }.upgrade_contract(new_class_hash);
Expand Down
4 changes: 2 additions & 2 deletions crates/utils/src/eth_transaction.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ impl EncodedTransactionImpl of EncodedTransactionTrait {
}

#[generate_trait]
impl EthTransactionImpl of EthTransaction {
impl EthTransactionImpl of EthTransactionTrait {
/// Decode a raw Ethereum transaction
/// This function decodes a raw Ethereum transaction. It checks if the transaction
/// is a legacy transaction or a modern transaction, and calls the appropriate decode function
Expand Down Expand Up @@ -254,7 +254,7 @@ impl EthTransactionImpl of EthTransaction {
) -> Result<bool, EthTransactionError> {
let TransactionMetadata{address, account_nonce, chain_id, signature } = tx_metadata;

let decoded_tx = EthTransaction::decode(encoded_tx_data)?;
let decoded_tx = EthTransactionTrait::decode(encoded_tx_data)?;

if (decoded_tx.nonce != account_nonce) {
return Result::Err(EthTransactionError::IncorrectAccountNonce);
Expand Down
40 changes: 40 additions & 0 deletions crates/utils/src/helpers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,25 @@ impl U8SpanExImpl of U8SpanExTrait {

(u64_words, last_input_word, last_input_num_bytes)
}

fn to_felt252_array(self: Span<u8>) -> Array<felt252> {
let mut array: Array<felt252> = Default::default();

let mut i = 0;

loop {
if (i == self.len()) {
break ();
}

let value: felt252 = (*self[i]).into();
array.append(value);

i += 1;
};

array
}
}

#[generate_trait]
Expand Down Expand Up @@ -957,6 +976,27 @@ impl ResultExImpl<T, E, +Drop<T>, +Drop<E>> of ResultExTrait<T, E> {
}
}

#[generate_trait]
impl Felt252SpanExImpl of Felt252SpanExTrait {
fn to_bytes(self: Span<felt252>) -> Array<u8> {
enitrat marked this conversation as resolved.
Show resolved Hide resolved
let mut i = 0;
let mut bytes: Array<u8> = Default::default();

loop {
if (i == self.len()) {
break ();
};

let v: u8 = (*self[i]).try_into().unwrap();
bytes.append(v);

i += 1;
};

bytes
}
}

fn compute_starknet_address(
deployer: ContractAddress, evm_address: EthAddress, class_hash: ClassHash
) -> ContractAddress {
Expand Down
Loading
Loading