This repository has been archived by the owner on Jul 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
359 additions
and
0 deletions.
There are no files selected for viewing
358 changes: 358 additions & 0 deletions
358
tests/integration_tests/complex_contracts/kakarot/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,358 @@ | ||
use std::str::FromStr; | ||
|
||
use cairo_vm::Felt252; | ||
use reth_primitives::{ | ||
sign_message, AccessList, Bytes, Signature, TransactionSigned, TxValue, B256, U256, | ||
}; | ||
use starknet::core::utils::{get_contract_address, get_storage_var_address, starknet_keccak}; | ||
use starknet_api::core::L2_ADDRESS_UPPER_BOUND; | ||
use starknet_crypto::{poseidon_hash_many, FieldElement}; | ||
use starknet_in_rust::definitions::block_context::{FeeTokenAddresses, StarknetOsConfig}; | ||
use starknet_in_rust::definitions::constants::EXECUTE_ENTRY_POINT_SELECTOR; | ||
use starknet_in_rust::transaction::Address; | ||
use starknet_in_rust::transaction::{ | ||
ClassHash, InvokeFunction, Transaction, VersionSpecificAccountTxFields, | ||
}; | ||
use starknet_in_rust::utils::{calculate_sn_keccak, felt_to_field_element, field_element_to_felt}; | ||
|
||
use crate::integration_tests::cairo_native::TestStateSetup; | ||
|
||
#[test] | ||
fn test_kakarot_contract() { | ||
// Evm constants | ||
let private_key: B256 = | ||
B256::from_str("0x6ae82d865482a203603ecbf25c865e082396d7705a6bbce92c1ff1d6ab9b503c") | ||
.unwrap(); | ||
let public_key: reth_primitives::Address = | ||
reth_primitives::Address::from_str("0x7513A12F74fFF533ee12F20EE524e4883CBd1945").unwrap(); | ||
let contract_address: reth_primitives::Address = | ||
reth_primitives::Address::from_str("0x0000000000000000000000000000000000001234").unwrap(); | ||
|
||
// Starknet constants | ||
let chain_id = Felt252::from_hex("0x4B4B5254").unwrap(); | ||
|
||
let kakarot_class_hash = ClassHash([1; 32]); | ||
let kakarot_address = Address(1.into()); | ||
let uninitialized_account_class_hash = ClassHash([3; 32]); | ||
|
||
// EOA | ||
let eoa_evm_address = Felt252::from_bytes_be_slice(public_key.0.as_slice()); | ||
let eoa_class_hash = ClassHash([2; 32]); | ||
let eoa_address = compute_starknet_address( | ||
&eoa_evm_address, | ||
&kakarot_address.0, | ||
&Felt252::from_bytes_be(&uninitialized_account_class_hash.0), | ||
); | ||
let eoa_address = Address(eoa_address); | ||
|
||
// Contract Account | ||
let contract_account_evm_address = Felt252::from_bytes_be_slice(contract_address.0.as_slice()); | ||
let contract_account_class_hash = ClassHash([3; 32]); | ||
let contract_account_address = compute_starknet_address( | ||
&contract_account_evm_address, | ||
&kakarot_address.0, | ||
&Felt252::from_bytes_be(&uninitialized_account_class_hash.0), | ||
); | ||
let contract_account_address = Address(contract_account_address); | ||
let bytecode = Felt252::from_bytes_be_slice(&[ | ||
0x60, 0x01, 0x60, 0x00, 0x55, 0x60, 0x02, 0x60, 0x00, 0x53, 0x60, 0x01, 0x60, 0x00, 0xf3, | ||
]); | ||
let bytecode_base_address = compute_storage_key("contract_account_bytecode", &[]); | ||
let bytecode_storage_key = field_element_to_felt(&poseidon_hash_many(&[ | ||
felt_to_field_element(&bytecode_base_address).unwrap(), | ||
FieldElement::ZERO, | ||
])); | ||
|
||
let fee_token_address = Address( | ||
Felt252::from_hex("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7") | ||
.unwrap(), | ||
); | ||
|
||
let mut state = TestStateSetup::default(); | ||
|
||
// Deploy Kakarot | ||
state | ||
.load_contract_at_address( | ||
kakarot_class_hash, | ||
kakarot_address.clone(), | ||
"starknet_programs/kakarot/contracts_KakarotCore.cairo", | ||
) | ||
.unwrap(); | ||
// Deploy EOA | ||
state | ||
.load_contract_at_address( | ||
eoa_class_hash, | ||
eoa_address.clone(), | ||
"starknet_programs/kakarot/contracts_ExternallyOwnedAccount.cairo", | ||
) | ||
.unwrap(); | ||
// Deploy Contract Account | ||
state | ||
.load_contract_at_address( | ||
contract_account_class_hash, | ||
contract_account_address.clone(), | ||
"starknet_programs/kakarot/contracts_ContractAccount.cairo", | ||
) | ||
.unwrap(); | ||
|
||
// Declare uninitialized account class hash | ||
state | ||
.load_contract( | ||
uninitialized_account_class_hash, | ||
"starknet_programs/kakarot/contracts_UninitializedAccount.cairo", | ||
) | ||
.unwrap(); | ||
|
||
// Prepare storage for Kakarot | ||
let kakarot_storage = vec![ | ||
( | ||
(kakarot_address.clone(), compute_storage_key("owner", &[])), | ||
eoa_address.0, | ||
), | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("chain_id", &[]), | ||
), | ||
chain_id, | ||
), | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("native_token", &[]), | ||
), | ||
fee_token_address.0, | ||
), | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("ca_class_hash", &[]), | ||
), | ||
Felt252::from_bytes_be(&contract_account_class_hash.0), | ||
), | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("eoa_class_hash", &[]), | ||
), | ||
Felt252::from_bytes_be(&eoa_class_hash.0), | ||
), | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("account_class_hash", &[]), | ||
), | ||
Felt252::from_bytes_be(&uninitialized_account_class_hash.0), | ||
), | ||
// Add the EOA to the address registry | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("address_registry", &[eoa_evm_address]), | ||
), | ||
Felt252::ONE, // Set account type to 1 (EOA) | ||
), | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("address_registry", &[eoa_evm_address]) + Felt252::ONE, | ||
), | ||
eoa_address.0, | ||
), | ||
// Add the contract account to the address registry | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("address_registry", &[contract_account_evm_address]), | ||
), | ||
Felt252::TWO, // Set account type to 1 (CA) | ||
), | ||
( | ||
( | ||
kakarot_address.clone(), | ||
compute_storage_key("address_registry", &[contract_account_evm_address]) | ||
+ Felt252::ONE, | ||
), | ||
contract_account_address.0, | ||
), | ||
]; | ||
|
||
// Prepare storage for EOA | ||
let eoa_storage = vec![ | ||
( | ||
(eoa_address.clone(), compute_storage_key("evm_address", &[])), | ||
eoa_evm_address, | ||
), | ||
( | ||
(eoa_address.clone(), compute_storage_key("chain_id", &[])), | ||
chain_id, | ||
), | ||
( | ||
( | ||
eoa_address.clone(), | ||
compute_storage_key("kakarot_core_address", &[]), | ||
), | ||
kakarot_address.clone().0, | ||
), | ||
]; | ||
|
||
// Prepare storage for Contract Account | ||
let contract_account_storage = vec![ | ||
( | ||
( | ||
contract_account_address.clone(), | ||
compute_storage_key("evm_address", &[]), | ||
), | ||
contract_account_evm_address, | ||
), | ||
( | ||
( | ||
contract_account_address.clone(), | ||
compute_storage_key("chain_id", &[]), | ||
), | ||
chain_id, | ||
), | ||
( | ||
( | ||
contract_account_address.clone(), | ||
compute_storage_key("kakarot_core_address", &[]), | ||
), | ||
kakarot_address.0, | ||
), | ||
// Set the bytecode | ||
( | ||
(contract_account_address.clone(), bytecode_storage_key), | ||
bytecode, | ||
), | ||
]; | ||
|
||
// Set the initial storage | ||
let mut state = state.finalize_with_starknet_os_config(StarknetOsConfig::new( | ||
chain_id, | ||
FeeTokenAddresses::new(fee_token_address.clone(), fee_token_address), | ||
Default::default(), | ||
)); | ||
|
||
let storage = [kakarot_storage, eoa_storage, contract_account_storage].concat(); | ||
for (k, v) in storage { | ||
state.insert_initial_storage_value((k.0, k.1.to_bytes_be()), v); | ||
} | ||
|
||
// Prepare the evm transaction | ||
let mut transaction = TransactionSigned { | ||
hash: B256::default(), | ||
signature: Signature::default(), | ||
transaction: reth_primitives::Transaction::Eip1559(reth_primitives::TxEip1559 { | ||
chain_id: *chain_id.to_le_digits().first().unwrap(), | ||
nonce: 0, | ||
gas_limit: 1_000_000, | ||
max_fee_per_gas: 0, | ||
max_priority_fee_per_gas: 0, | ||
to: reth_primitives::TransactionKind::Call(contract_address), | ||
value: TxValue::from(u8::MIN), | ||
access_list: AccessList::default(), | ||
input: Bytes::default(), | ||
}), | ||
}; | ||
let signature = sign_message(private_key, transaction.transaction.signature_hash()).unwrap(); | ||
transaction.signature = signature; | ||
|
||
// Prepare the starknet transaction | ||
let mut calldata = vec![]; | ||
transaction | ||
.transaction | ||
.encode_without_signature(&mut calldata); | ||
let mut calldata = calldata.into_iter().map(Felt252::from).collect::<Vec<_>>(); | ||
let mut execute_calldata = vec![ | ||
Felt252::ONE, // call array length | ||
kakarot_address.0, // contract address | ||
Felt252::from_bytes_be(&calculate_sn_keccak(b"eth_send_transaction")), // selector | ||
Felt252::from(calldata.len()), // calldata length | ||
]; | ||
execute_calldata.append(&mut calldata); | ||
|
||
let [r_low, r_high] = split_u256(signature.r); | ||
let [s_low, s_high] = split_u256(signature.s); | ||
let signature = [r_low, r_high, s_low, s_high, signature.odd_y_parity as u128] | ||
.into_iter() | ||
.map(Felt252::from) | ||
.collect::<Vec<_>>(); | ||
|
||
let tx = Transaction::InvokeFunction( | ||
InvokeFunction::new( | ||
eoa_address, | ||
*EXECUTE_ENTRY_POINT_SELECTOR, | ||
VersionSpecificAccountTxFields::Deprecated(0), | ||
Felt252::ONE, | ||
execute_calldata, | ||
signature, | ||
chain_id, | ||
Some(Felt252::ZERO), | ||
) | ||
.unwrap(), | ||
); | ||
|
||
let tx = tx.create_for_simulation(false, false, true, true, true); | ||
|
||
let execution_result = state.execute_transaction(tx).unwrap(); | ||
assert!(execution_result.revert_error.is_none()); | ||
|
||
// Check that the storage var was updated (contract bytecode should update storage var at key 0u256 to 1) | ||
let low_key = compute_poseidon_storage_base_address( | ||
"contract_account_storage_keys", | ||
&[Felt252::ZERO, Felt252::ZERO], | ||
); | ||
let (_, value) = state.storage_writes_at((contract_account_address, low_key.to_bytes_be())); | ||
assert_eq!(value.unwrap(), Felt252::ONE); | ||
} | ||
|
||
fn compute_storage_key(key: &str, args: &[Felt252]) -> Felt252 { | ||
let args = args | ||
.iter() | ||
.map(|arg| felt_to_field_element(arg).unwrap()) | ||
.collect::<Vec<_>>(); | ||
field_element_to_felt(&get_storage_var_address(key, &args).unwrap()) | ||
} | ||
|
||
fn compute_starknet_address( | ||
evm_address: &Felt252, | ||
kakarot_address: &Felt252, | ||
account_class_hash: &Felt252, | ||
) -> Felt252 { | ||
let salt = felt_to_field_element(evm_address).unwrap(); | ||
let class_hash = felt_to_field_element(account_class_hash).unwrap(); | ||
let deployer_address = felt_to_field_element(kakarot_address).unwrap(); | ||
|
||
let constructor_args = vec![deployer_address, salt]; | ||
|
||
let starknet_address = | ||
get_contract_address(salt, class_hash, &constructor_args, deployer_address); | ||
|
||
field_element_to_felt(&starknet_address) | ||
} | ||
|
||
fn split_u256(value: U256) -> [u128; 2] { | ||
[ | ||
(value & U256::from(u128::MAX)).try_into().unwrap(), // safe unwrap <= U128::MAX. | ||
(value >> U256::from(128)).try_into().unwrap(), // safe unwrap <= U128::MAX. | ||
] | ||
} | ||
|
||
fn compute_poseidon_storage_base_address(storage_var_name: &str, keys: &[Felt252]) -> Felt252 { | ||
let selector = starknet_keccak(storage_var_name.as_bytes()); | ||
|
||
let data: Vec<_> = keys | ||
.iter() | ||
.filter_map(|d| felt_to_field_element(d).ok()) | ||
.collect(); | ||
let data = [vec![selector], data].concat(); | ||
|
||
let key = poseidon_hash_many(&data); | ||
let key = field_element_to_felt(&key); | ||
|
||
key.mod_floor( | ||
&Felt252::from_bytes_le_slice(&L2_ADDRESS_UPPER_BOUND.to_bytes_be()) | ||
.try_into() | ||
.unwrap(), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub mod amm_contracts; | ||
pub mod erc20; | ||
pub mod kakarot; | ||
pub mod nft; | ||
pub mod utils; |