From 71ddcd68561dfd6a8255f2f5adff1b32b6dd022f Mon Sep 17 00:00:00 2001 From: Mathieu <60658558+enitrat@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:49:13 +0100 Subject: [PATCH] refactor: account balances (#569) * refactor: account balances fix: test_kakarot_core_compute_starknet_address chore: update compute sn address script * fix test --- .tool-versions | 2 +- .../contracts/src/kakarot_core/kakarot.cairo | 2 +- crates/contracts/src/tests/test_eoa.cairo | 24 +-- crates/contracts/src/tests/test_utils.cairo | 22 +++ crates/evm/src/create_helpers.cairo | 2 +- .../src/instructions/block_information.cairo | 2 +- .../environmental_information.cairo | 3 +- .../src/instructions/system_operations.cairo | 4 +- crates/evm/src/model.cairo | 12 +- crates/evm/src/model/account.cairo | 100 +++++++----- crates/evm/src/model/contract_account.cairo | 4 +- crates/evm/src/state.cairo | 48 ++---- .../test_environment_information.cairo | 1 + .../test_memory_operations.cairo | 2 + .../test_system_operations.cairo | 6 +- crates/evm/src/tests/test_model.cairo | 8 +- .../test_model/test_contract_account.cairo | 6 +- crates/evm/src/tests/test_state.cairo | 152 +++++++++++------- crates/evm/src/tests/test_utils.cairo | 1 + 19 files changed, 213 insertions(+), 188 deletions(-) diff --git a/.tool-versions b/.tool-versions index 8b94fe0ef..7f7d2866d 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -scarb nightly-2023-11-22 +scarb nightly-2023-11-22 \ No newline at end of file diff --git a/crates/contracts/src/kakarot_core/kakarot.cairo b/crates/contracts/src/kakarot_core/kakarot.cairo index fd0b39ecf..5b902f359 100644 --- a/crates/contracts/src/kakarot_core/kakarot.cairo +++ b/crates/contracts/src/kakarot_core/kakarot.cairo @@ -203,7 +203,7 @@ mod KakarotCore { let address = Address { evm: evm_address, starknet: self.compute_starknet_address(evm_address) }; - address.balance().unwrap() + address.fetch_balance() } fn contract_account_storage_at( diff --git a/crates/contracts/src/tests/test_eoa.cairo b/crates/contracts/src/tests/test_eoa.cairo index d65a05a38..d3468a853 100644 --- a/crates/contracts/src/tests/test_eoa.cairo +++ b/crates/contracts/src/tests/test_eoa.cairo @@ -14,8 +14,9 @@ mod test_external_owned_account { IMockContractUpgradeableDispatcher, IMockContractUpgradeableDispatcherTrait, MockContractUpgradeableV1 }; - use contracts::tests::test_utils::deploy_contract_account; - use contracts::tests::test_utils::{setup_contracts_for_testing}; + use contracts::tests::test_utils::{ + setup_contracts_for_testing, deploy_eoa, deploy_contract_account + }; use contracts::uninitialized_account::{ IUninitializedAccountDispatcher, IUninitializedAccountDispatcherTrait, UninitializedAccount, IUninitializedAccount @@ -40,25 +41,6 @@ mod test_external_owned_account { }; use utils::helpers::{U8SpanExTrait, u256_to_bytes_array}; - fn deploy_eoa(eoa_address: EthAddress) -> IExternallyOwnedAccountDispatcher { - let kakarot_address = get_contract_address(); - let calldata: Span = array![kakarot_address.into(), eoa_address.into()].span(); - - let (starknet_address, _) = deploy_syscall( - UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap(), - evm_address().into(), - calldata, - false - ) - .expect('failed to deploy EOA'); - - let account = IUninitializedAccountDispatcher { contract_address: starknet_address }; - - account.initialize(ExternallyOwnedAccount::TEST_CLASS_HASH.try_into().unwrap()); - let eoa = IExternallyOwnedAccountDispatcher { contract_address: starknet_address }; - eoa.set_chain_id(chain_id()); - eoa - } #[test] #[available_gas(2000000000)] diff --git a/crates/contracts/src/tests/test_utils.cairo b/crates/contracts/src/tests/test_utils.cairo index 805c099bd..9ac0db21d 100644 --- a/crates/contracts/src/tests/test_utils.cairo +++ b/crates/contracts/src/tests/test_utils.cairo @@ -3,6 +3,7 @@ use contracts::contract_account::{ }; use contracts::eoa::{ExternallyOwnedAccount}; +use contracts::eoa::{IExternallyOwnedAccountDispatcher, IExternallyOwnedAccountDispatcherTrait}; use contracts::kakarot_core::{interface::IExtendedKakarotCoreDispatcher, KakarotCore}; use contracts::uninitialized_account::{ IUninitializedAccountDispatcher, IUninitializedAccountDispatcherTrait, UninitializedAccount @@ -111,6 +112,27 @@ fn deploy_contract_account(evm_address: EthAddress, bytecode: Span) -> Addre } +fn deploy_eoa(eoa_address: EthAddress) -> IExternallyOwnedAccountDispatcher { + let kakarot_address = get_contract_address(); + let calldata: Span = array![kakarot_address.into(), eoa_address.into()].span(); + + let (starknet_address, _) = deploy_syscall( + UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap(), + eoa_address.into(), + calldata, + false + ) + .expect('failed to deploy EOA'); + + let account = IUninitializedAccountDispatcher { contract_address: starknet_address }; + + account.initialize(ExternallyOwnedAccount::TEST_CLASS_HASH.try_into().unwrap()); + let eoa = IExternallyOwnedAccountDispatcher { contract_address: starknet_address }; + eoa.set_chain_id(chain_id()); + eoa +} + + fn fund_account_with_native_token( contract_address: ContractAddress, native_token: IERC20CamelDispatcher, amount: u256, ) { diff --git a/crates/evm/src/create_helpers.cairo b/crates/evm/src/create_helpers.cairo index ad3c8304d..753c1a09b 100644 --- a/crates/evm/src/create_helpers.cairo +++ b/crates/evm/src/create_helpers.cairo @@ -73,7 +73,7 @@ impl MachineCreateHelpersImpl of MachineCreateHelpers { let caller = self.address(); let mut caller_account = self.state.get_account(caller.evm); let caller_current_nonce = caller_account.nonce(); - let caller_balance = self.state.read_balance(caller.evm)?; + let caller_balance = caller_account.balance(); if caller_balance < create_args.value || target_account.nonce() == integer::BoundedInt::::max() { return self.stack.push(0); diff --git a/crates/evm/src/instructions/block_information.cairo b/crates/evm/src/instructions/block_information.cairo index 3a017844f..d47b6d3c9 100644 --- a/crates/evm/src/instructions/block_information.cairo +++ b/crates/evm/src/instructions/block_information.cairo @@ -106,7 +106,7 @@ impl BlockInformation of BlockInformationTrait { fn exec_selfbalance(ref self: Machine) -> Result<(), EVMError> { let evm_address = self.address().evm; - let balance = self.state.read_balance(evm_address)?; + let balance = self.state.get_account(evm_address).balance; self.stack.push(balance) } diff --git a/crates/evm/src/instructions/environmental_information.cairo b/crates/evm/src/instructions/environmental_information.cairo index f840cb2b3..3ba89d2fc 100644 --- a/crates/evm/src/instructions/environmental_information.cairo +++ b/crates/evm/src/instructions/environmental_information.cairo @@ -36,8 +36,7 @@ impl EnvironmentInformationImpl of EnvironmentInformationTrait { fn exec_balance(ref self: Machine) -> Result<(), EVMError> { let evm_address = self.stack.pop_eth_address()?; - let balance = self.state.read_balance(evm_address)?; - + let balance = self.state.get_account(evm_address).balance(); self.stack.push(balance) } diff --git a/crates/evm/src/instructions/system_operations.cairo b/crates/evm/src/instructions/system_operations.cairo index 726ebb105..ad021a801 100644 --- a/crates/evm/src/instructions/system_operations.cairo +++ b/crates/evm/src/instructions/system_operations.cairo @@ -92,7 +92,7 @@ impl SystemOperations of SystemOperationsTrait { // If sender_balance < value, return early, pushing // 0 on the stack to indicate call failure. let caller_address = self.address(); - let sender_balance = self.state.read_balance(caller_address.evm)?; + let sender_balance = self.state.get_account(caller_address.evm).balance(); if sender_balance < value { return self.stack.push(0); } @@ -161,7 +161,7 @@ impl SystemOperations of SystemOperationsTrait { Transfer { sender: account.address(), recipient, - amount: self.state.read_balance(account.address().evm)? + amount: self.state.get_account(account.address().evm).balance } ); diff --git a/crates/evm/src/model.cairo b/crates/evm/src/model.cairo index 4b6eb3c11..8b5fc1eda 100644 --- a/crates/evm/src/model.cairo +++ b/crates/evm/src/model.cairo @@ -9,9 +9,7 @@ use evm::model::account::{Account, AccountTrait}; use evm::model::contract_account::{ContractAccountTrait}; use evm::model::eoa::EOATrait; use evm::state::State; -use openzeppelin::token::erc20::interface::{ - IERC20CamelSafeDispatcher, IERC20CamelSafeDispatcherTrait -}; +use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use starknet::{EthAddress, get_contract_address, ContractAddress}; use utils::helpers::{ResultExTrait}; use utils::traits::{EthAddressDefault, ContractAddressDefault}; @@ -40,13 +38,11 @@ impl AddressImpl of AddressTrait { } } - fn balance(self: @Address) -> Result { + fn fetch_balance(self: @Address) -> u256 { let kakarot_state = KakarotCore::unsafe_new_contract_state(); let native_token_address = kakarot_state.native_token(); - let native_token = IERC20CamelSafeDispatcher { contract_address: native_token_address }; - native_token - .balanceOf(*self.starknet) - .map_err(EVMError::SyscallFailed(CONTRACT_SYSCALL_FAILED)) + let native_token = IERC20CamelDispatcher { contract_address: native_token_address }; + native_token.balanceOf(*self.starknet) } } diff --git a/crates/evm/src/model/account.cairo b/crates/evm/src/model/account.cairo index 81f4d3928..5a53f3a89 100644 --- a/crates/evm/src/model/account.cairo +++ b/crates/evm/src/model/account.cairo @@ -7,9 +7,7 @@ use contracts::kakarot_core::{KakarotCore, IKakarotCore}; use evm::errors::{EVMError, CONTRACT_SYSCALL_FAILED}; use evm::model::contract_account::{ContractAccountTrait}; use evm::model::{Address, AddressTrait, AccountType}; -use openzeppelin::token::erc20::interface::{ - IERC20CamelSafeDispatcher, IERC20CamelSafeDispatcherTrait -}; +use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use starknet::{ContractAddress, EthAddress, get_contract_address}; use utils::helpers::{ResultExTrait, ByteArrayExTrait, compute_starknet_address}; @@ -19,29 +17,51 @@ struct Account { address: Address, code: Span, nonce: u64, + balance: u256, selfdestruct: bool, } -struct ContractAccountBuilder { +#[derive(Drop)] +struct AccountBuilder { account: Account } #[generate_trait] -impl ContractAccountBuilderImpl of ContractAccountBuilderTrait { - fn new(address: Address) -> ContractAccountBuilder { - ContractAccountBuilder { +impl AccountBuilderImpl of AccountBuilderTrait { + fn new(address: Address) -> AccountBuilder { + AccountBuilder { account: Account { - account_type: AccountType::ContractAccount, + account_type: AccountType::Unknown, address: address, code: Default::default().span(), nonce: 0, + balance: 0, selfdestruct: false, } } } #[inline(always)] - fn fetch_nonce(mut self: ContractAccountBuilder) -> ContractAccountBuilder { + fn set_type(mut self: AccountBuilder, account_type: AccountType) -> AccountBuilder { + self.account.account_type = account_type; + self + } + + #[inline(always)] + fn fetch_balance(mut self: AccountBuilder) -> AccountBuilder { + let kakarot_state = KakarotCore::unsafe_new_contract_state(); + let native_token_address = kakarot_state.native_token(); + let native_token = IERC20CamelDispatcher { contract_address: native_token_address }; + self.account.balance = self.account.address.fetch_balance(); + self + } + + #[inline(always)] + fn fetch_nonce(mut self: AccountBuilder) -> AccountBuilder { + assert!( + self.account.account_type == AccountType::ContractAccount, + "Cannot fetch nonce of an EOA" + ); let contract_account = IContractAccountDispatcher { contract_address: self.account.address.starknet }; @@ -49,12 +69,18 @@ impl ContractAccountBuilderImpl of ContractAccountBuilderTrait { self } + #[inline(always)] + fn set_nonce(mut self: AccountBuilder, nonce: u64) -> AccountBuilder { + self.account.nonce = nonce; + self + } + /// Loads the bytecode of a ContractAccount from Kakarot Core's contract storage into a Span. /// # Arguments /// * `self` - The address of the Contract Account to load the bytecode from /// # Returns /// * The bytecode of the Contract Account as a ByteArray - fn fetch_bytecode(mut self: ContractAccountBuilder) -> ContractAccountBuilder { + fn fetch_bytecode(mut self: AccountBuilder) -> AccountBuilder { let contract_account = IContractAccountDispatcher { contract_address: self.account.address.starknet }; @@ -64,7 +90,7 @@ impl ContractAccountBuilderImpl of ContractAccountBuilderTrait { } #[inline(always)] - fn build(self: ContractAccountBuilder) -> Account { + fn build(self: AccountBuilder) -> Account { self.account } } @@ -89,13 +115,9 @@ impl AccountImpl of AccountTrait { // If no account exists at `address`, then we are trying to // access an undeployed account (CA or EOA). We create an // empty account with the correct address and return it. - Account { - account_type: AccountType::Unknown, - address: Address { starknet: starknet_address, evm: evm_address, }, - code: Default::default().span(), - nonce: 0, - selfdestruct: false, - } + AccountBuilderTrait::new(Address { starknet: starknet_address, evm: evm_address }) + .fetch_balance() + .build() } } } @@ -117,21 +139,21 @@ impl AccountImpl of AccountTrait { Option::Some(( account_type, starknet_address )) => { + let address = Address { evm: evm_address, starknet: starknet_address }; match account_type { AccountType::EOA => Option::Some( - Account { - account_type: AccountType::EOA, - address: Address { evm: evm_address, starknet: starknet_address }, - code: Default::default().span(), - nonce: 1, - selfdestruct: false, - } + AccountBuilderTrait::new(address) + .set_type(AccountType::EOA) + .set_nonce(1) + .fetch_balance() + .build() ), AccountType::ContractAccount => { - let address = Address { evm: evm_address, starknet: starknet_address }; - let account = ContractAccountBuilderTrait::new(address) + let account = AccountBuilderTrait::new(address) + .set_type(AccountType::ContractAccount) .fetch_nonce() .fetch_bytecode() + .fetch_balance() .build(); Option::Some(account) }, @@ -227,6 +249,16 @@ impl AccountImpl of AccountTrait { } } + #[inline(always)] + fn set_balance(ref self: Account, value: u256) { + self.balance = value; + } + + #[inline(always)] + fn balance(self: @Account) -> u256 { + *self.balance + } + #[inline(always)] fn address(self: @Account) -> Address { *self.address @@ -280,20 +312,6 @@ impl AccountImpl of AccountTrait { self.address().evm } - /// Returns the balance in native token for a given EVM account (EOA or CA) - /// This is equivalent to checking the balance in native coin, i.e. ETHER of an account in Ethereum - #[inline(always)] - fn balance(self: @Account) -> Result { - let kakarot_state = KakarotCore::unsafe_new_contract_state(); - let native_token_address = kakarot_state.native_token(); - let native_token = IERC20CamelSafeDispatcher { contract_address: native_token_address }; - //Note: Starknet OS doesn't allow error management of failed syscalls yet. - // If this call fails, the entire transaction will revert. - native_token - .balanceOf(self.address().starknet) - .map_err(EVMError::SyscallFailed(CONTRACT_SYSCALL_FAILED)) - } - /// Returns the bytecode of the EVM account (EOA or CA) #[inline(always)] fn bytecode(self: @Account) -> Span { diff --git a/crates/evm/src/model/contract_account.cairo b/crates/evm/src/model/contract_account.cairo index 3aa255f80..73560a7c6 100644 --- a/crates/evm/src/model/contract_account.cairo +++ b/crates/evm/src/model/contract_account.cairo @@ -23,9 +23,7 @@ use evm::errors::{ use evm::execution::execute; use evm::model::{Address, Account, AccountType, AccountTrait}; use hash::{HashStateTrait, HashStateExTrait}; -use openzeppelin::token::erc20::interface::{ - IERC20CamelSafeDispatcher, IERC20CamelSafeDispatcherTrait -}; +use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; use poseidon::PoseidonTrait; use starknet::{ deploy_syscall, StorageBaseAddress, storage_base_address_from_felt252, Store, EthAddress, diff --git a/crates/evm/src/state.cairo b/crates/evm/src/state.cairo index c35772a7a..833e17220 100644 --- a/crates/evm/src/state.cairo +++ b/crates/evm/src/state.cairo @@ -201,9 +201,6 @@ struct State { /// Account states /// Pending emitted events events: SimpleLog, - /// Account balances updates. This is only internal accounting and stored - /// balances are updated when commiting transfers. - balances: StateChangeLog, /// Pending transfers transfers: SimpleLog, } @@ -257,49 +254,35 @@ impl StateImpl of StateTrait { if (transfer.amount == 0 || transfer.sender.evm == transfer.recipient.evm) { return Result::Ok(()); } - let sender_balance = self.read_balance(transfer.sender.evm)?; - let recipient_balance = self.read_balance(transfer.recipient.evm)?; + let mut sender = self.get_account(transfer.sender.evm); + let mut recipient = self.get_account(transfer.recipient.evm); - let (new_sender_balance, underflow) = u256_overflow_sub(sender_balance, transfer.amount); + let (new_sender_balance, underflow) = u256_overflow_sub(sender.balance(), transfer.amount); if underflow { return Result::Err(EVMError::NumericOperations(INSUFFICIENT_BALANCE)); } let (new_recipient_balance, overflow) = u256_overflowing_add( - recipient_balance, transfer.amount + recipient.balance, transfer.amount ); if overflow { return Result::Err(EVMError::NumericOperations(BALANCE_OVERFLOW)); } - self.write_balance(transfer.sender.evm, new_sender_balance); - self.write_balance(transfer.recipient.evm, new_recipient_balance); + sender.set_balance(new_sender_balance); + recipient.set_balance(new_recipient_balance); + + self.set_account(sender); + self.set_account(recipient); self.transfers.append(transfer); Result::Ok(()) } - #[inline(always)] - fn read_balance(ref self: State, evm_address: EthAddress) -> Result { - match self.balances.read(evm_address.into()) { - Option::Some(value) => { Result::Ok(value) }, - Option::None => { - //TODO this function should compute the deterministic address of the evm_address's corresponding starknet contract to perform value transfers. However, the test runner doesn't deterministically calculate the starknet address from the evm address, so we can't test this yet. In the meantime, we'll use `fetch_or_create` - which is unoptimized as we don't want to load a CA's bytecode to perform value transfers. - // let kakarot_state = KakarotCore::unsafe_new_contract_state(); - // let starknet_address = kakarot_state.compute_starknet_address(evm_address); - let account = AccountTrait::fetch_or_create(evm_address); - let balance = account.balance()?; - self.write_balance(evm_address, balance); - Result::Ok(balance) - } - } - } - fn commit_context(ref self: State) { self.accounts.commit_context(); self.accounts_storage.commit_context(); self.events.commit_context(); self.transfers.commit_context(); - self.balances.commit_context(); } fn clear_context(ref self: State) { @@ -307,7 +290,6 @@ impl StateImpl of StateTrait { self.accounts_storage.clear_context(); self.events.clear_context(); self.transfers.clear_context(); - self.balances.clear_context(); } fn commit_state(ref self: State) -> Result<(), EVMError> { @@ -319,18 +301,6 @@ impl StateImpl of StateTrait { } #[generate_trait] impl StateInternalImpl of StateInternalTrait { - /// Writes a value to the `balances` StateChangeLog. - /// This should not be called autonomously and should be called only by - /// when performing value transfers, using `add_transfer`. - /// # Arguments - /// * `address` - The EVM address of the account to write the balance to. - /// * `value` - The new balance to write. - - #[inline(always)] - fn write_balance(ref self: State, evm_address: EthAddress, value: u256) { - self.balances.write(evm_address.into(), value) - } - /// Commits storage changes to the KakarotCore contract by writing pending /// state changes to Starknet Storage. /// commit_storage MUST be called after commit_accounts. diff --git a/crates/evm/src/tests/test_instructions/test_environment_information.cairo b/crates/evm/src/tests/test_instructions/test_environment_information.cairo index de059c062..f8925dd69 100644 --- a/crates/evm/src/tests/test_instructions/test_environment_information.cairo +++ b/crates/evm/src/tests/test_instructions/test_environment_information.cairo @@ -962,6 +962,7 @@ fn test_exec_extcodehash_selfdestructed() { address: ca_address, code: array![].span(), nonce: 1, + balance: 1, selfdestruct: false }; account.selfdestruct().expect('CA selfdestruct failed'); diff --git a/crates/evm/src/tests/test_instructions/test_memory_operations.cairo b/crates/evm/src/tests/test_instructions/test_memory_operations.cairo index 0b7083891..bb5c6e669 100644 --- a/crates/evm/src/tests/test_instructions/test_memory_operations.cairo +++ b/crates/evm/src/tests/test_instructions/test_memory_operations.cairo @@ -556,6 +556,7 @@ fn test_exec_sload_from_storage() { address: ca_address, code: array![0xab, 0xcd, 0xef].span(), nonce: 1, + balance: 0, selfdestruct: false }; let key: u256 = 0x100000000000000000000000000000001; @@ -627,6 +628,7 @@ fn test_exec_sstore_finalized() { address: ca_address, code: array![].span(), nonce: 1, + balance: 0, selfdestruct: false }; let key: u256 = 0x100000000000000000000000000000001; diff --git a/crates/evm/src/tests/test_instructions/test_system_operations.cairo b/crates/evm/src/tests/test_instructions/test_system_operations.cairo index 23184c22a..d1063c417 100644 --- a/crates/evm/src/tests/test_instructions/test_system_operations.cairo +++ b/crates/evm/src/tests/test_instructions/test_system_operations.cairo @@ -295,7 +295,7 @@ fn test_exec_staticcall() { #[test] -#[available_gas(50000000)] +#[available_gas(70000000)] fn test_exec_staticcall_no_return() { // Given let mut interpreter = EVMInterpreterTrait::new(); @@ -584,9 +584,7 @@ fn test_exec_selfdestruct_existing_ca() { let destructed = machine.state.get_account(ca_address.evm); assert(destructed.nonce() == 0, 'destructed nonce should be 0'); - assert( - destructed.balance().expect('couldnt get balance') == 0, 'destructed balance should be 0' - ); + assert(destructed.balance() == 0, 'destructed balance should be 0'); assert(destructed.bytecode().len() == 0, 'bytecode should be empty'); let recipient = machine.state.get_account(recipient.evm); diff --git a/crates/evm/src/tests/test_model.cairo b/crates/evm/src/tests/test_model.cairo index a3b13af21..91c852a1b 100644 --- a/crates/evm/src/tests/test_model.cairo +++ b/crates/evm/src/tests/test_model.cairo @@ -68,7 +68,7 @@ fn test_account_balance_eoa() { // When set_contract_address(kakarot_core.contract_address); let account = AccountTrait::fetch(evm_address()).unwrap(); - let balance = account.balance().unwrap(); + let balance = account.balance(); // Then assert(balance == native_token.balanceOf(eoa_address.starknet), 'wrong balance'); @@ -86,7 +86,7 @@ fn test_address_balance_eoa() { // When set_contract_address(kakarot_core.contract_address); let account = AccountTrait::fetch(evm_address()).unwrap(); - let balance = account.balance().unwrap(); + let balance = account.balance(); // Then assert(balance == native_token.balanceOf(eoa_address.starknet), 'wrong balance'); @@ -165,7 +165,7 @@ fn test_account_balance_contract_account() { // When let account = AccountTrait::fetch(evm_address()).unwrap(); - let balance = account.balance().unwrap(); + let balance = account.balance(); // Then assert(balance == native_token.balanceOf(ca_address.starknet), 'wrong balance'); @@ -253,7 +253,7 @@ fn test_address_balance_contract_account() { // When let account = AccountTrait::fetch(evm_address()).unwrap(); - let balance = account.balance().unwrap(); + let balance = account.balance(); // Then // Then diff --git a/crates/evm/src/tests/test_model/test_contract_account.cairo b/crates/evm/src/tests/test_model/test_contract_account.cairo index b35f1be5a..cc9dfcece 100644 --- a/crates/evm/src/tests/test_model/test_contract_account.cairo +++ b/crates/evm/src/tests/test_model/test_contract_account.cairo @@ -4,7 +4,7 @@ use contracts::kakarot_core::kakarot::StoredAccountType; use contracts::tests::test_data::counter_evm_bytecode; use contracts::tests::test_utils as contract_utils; use contracts::tests::test_utils::constants::EVM_ADDRESS; -use evm::model::account::{Account, ContractAccountBuilderTrait}; +use evm::model::account::{Account, AccountBuilderTrait}; use evm::model::contract_account::{ContractAccountTrait}; use evm::model::{AccountType}; use evm::tests::test_utils; @@ -24,7 +24,8 @@ fn test_contract_account_deploy() { let bytecode = counter_evm_bytecode(); let ca_address = contract_utils::deploy_contract_account(test_utils::evm_address(), bytecode); - let account = ContractAccountBuilderTrait::new(ca_address) + let account = AccountBuilderTrait::new(ca_address) + .set_type(AccountType::ContractAccount) .fetch_nonce() .fetch_bytecode() .build(); @@ -80,6 +81,7 @@ fn test_fetch_nonce() { address: ca, nonce: 1, code: Default::default().span(), + balance: 0, selfdestruct: false, }; diff --git a/crates/evm/src/tests/test_state.cairo b/crates/evm/src/tests/test_state.cairo index a6dfc7bf3..cca64de5b 100644 --- a/crates/evm/src/tests/test_state.cairo +++ b/crates/evm/src/tests/test_state.cairo @@ -1,4 +1,4 @@ -use contracts::tests::test_utils::{deploy_contract_account}; +use contracts::tests::test_utils::{deploy_contract_account, deploy_eoa}; use evm::state::compute_state_key; use evm::tests::test_utils; @@ -193,6 +193,7 @@ mod test_simple_log { } mod test_state { + use contracts::kakarot_core::interface::{IExtendedKakarotCoreDispatcherTrait}; use contracts::tests::test_utils as contract_utils; use contracts::uninitialized_account::UninitializedAccount; use evm::model::account::{Account, AccountType, AccountTrait}; @@ -224,6 +225,7 @@ mod test_state { address: Address { evm: evm_address, starknet: starknet_address }, code: Default::default().span(), nonce: 0, + balance: 0, selfdestruct: false }; @@ -251,6 +253,7 @@ mod test_state { address: Address { evm: evm_address, starknet: starknet_address }, code: array![0xab, 0xcd, 0xef].span(), nonce: 1, + balance: 420, selfdestruct: false }; @@ -261,6 +264,35 @@ mod test_state { assert(state.accounts.contextual_keyset.len() == 1, 'Account not written in context'); } + + #[test] + #[available_gas(200000000)] + fn test_get_account_when_deployed() { + let mut state: State = Default::default(); + let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing(); + let evm_address: EthAddress = test_utils::evm_address(); + let ca = contract_utils::deploy_contract_account( + evm_address, array![0xab, 0xcd, 0xef].span() + ); + contract_utils::fund_account_with_native_token(ca.starknet, native_token, 420); + + let starknet_address = kakarot_core.compute_starknet_address(evm_address); + let expected_type = AccountType::ContractAccount; + let expected_account = Account { + account_type: expected_type, + address: Address { evm: evm_address, starknet: starknet_address }, + code: array![0xab, 0xcd, 0xef].span(), + nonce: 1, + balance: 420, + selfdestruct: false + }; + + let account = state.get_account(evm_address); + + assert(account == expected_account, 'Account mismatch'); + assert(state.accounts.contextual_keyset.len() == 1, 'Account not written in context'); + } + #[test] #[available_gas(200000000)] fn test_write_read_cached_storage() { @@ -291,6 +323,7 @@ mod test_state { address: ca_address, code: array![0xab, 0xcd, 0xef].span(), nonce: 1, + balance: 0, selfdestruct: false }; account.store_storage(key, value); @@ -317,29 +350,25 @@ mod test_state { fn test_add_transfer() { //Given let mut state: State = Default::default(); - let deployer = test_utils::kakarot_address(); - set_contract_address(deployer); + let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing(); let sender_evm_address = test_utils::evm_address(); - let sender_starknet_address = compute_starknet_address( - deployer.into(), - sender_evm_address, - UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap() - ); - let sender = Address { evm: sender_evm_address, starknet: sender_starknet_address }; + let sender_starknet_address = contract_utils::deploy_eoa(sender_evm_address) + .contract_address; + let sender_address = Address { evm: sender_evm_address, starknet: sender_starknet_address }; let recipient_evm_address = test_utils::other_evm_address(); - let recipient_starknet_address = compute_starknet_address( - deployer.into(), - recipient_evm_address, - UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap() - ); - let recipient = Address { + let recipient_starknet_address = contract_utils::deploy_eoa(recipient_evm_address) + .contract_address; + let recipient_address = Address { evm: recipient_evm_address, starknet: recipient_starknet_address }; - let transfer = Transfer { sender, recipient, amount: 100 }; + let transfer = Transfer { + sender: sender_address, recipient: recipient_address, amount: 100 + }; // Write user balances in cache to avoid fetching from SN storage - state.write_balance(sender.evm, 300); - state.write_balance(recipient.evm, 0); + let mut sender = state.get_account(sender_address.evm); + sender.set_balance(300); + state.set_account(sender); // When state.add_transfer(transfer).unwrap(); @@ -348,8 +377,10 @@ mod test_state { assert(state.transfers.contextual_logs.len() == 1, 'Transfer not added'); assert(*state.transfers.contextual_logs[0] == transfer, 'Transfer mismatch'); - assert(state.read_balance(sender.evm).unwrap() == 200, 'Sender balance mismatch'); - assert(state.read_balance(recipient.evm).unwrap() == 100, 'Recipient balance mismatch'); + assert(state.get_account(sender_address.evm).balance() == 200, 'Sender balance mismatch'); + assert( + state.get_account(recipient_address.evm).balance() == 100, 'Recipient balance mismatch' + ); } #[test] @@ -357,22 +388,26 @@ mod test_state { fn test_add_transfer_with_same_sender_and_recipient() { //Given let mut state: State = Default::default(); - let deployer = test_utils::kakarot_address(); - set_contract_address(deployer); + let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing(); let sender_evm_address = test_utils::evm_address(); - let sender_starknet_address = compute_starknet_address( - deployer.into(), - sender_evm_address, - UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap() - ); - let sender = Address { evm: sender_evm_address, starknet: sender_starknet_address }; + let sender_starknet_address = contract_utils::deploy_eoa(sender_evm_address) + .contract_address; + let sender_address = Address { evm: sender_evm_address, starknet: sender_starknet_address }; + let recipient_evm_address = test_utils::other_evm_address(); + let recipient_starknet_address = contract_utils::deploy_eoa(recipient_evm_address) + .contract_address; + let recipient_address = Address { + evm: recipient_evm_address, starknet: recipient_starknet_address + }; // since sender and recipient is same - let transfer = Transfer { sender: sender, recipient: sender, amount: 100 }; + let transfer = Transfer { sender: sender_address, recipient: sender_address, amount: 100 }; // Write user balances in cache to avoid fetching from SN storage - state.write_balance(sender.evm, 300); + let mut sender = state.get_account(sender_address.evm); + sender.balance = 300; + state.set_account(sender); // When state.add_transfer(transfer).unwrap(); @@ -380,7 +415,7 @@ mod test_state { // Then, no transfer appended to log and cached balances updated assert(state.transfers.contextual_logs.len() == 0, 'Transfer added'); - assert(state.read_balance(sender.evm).unwrap() == 300, 'Sender balance mismatch'); + assert(state.get_account(sender_address.evm).balance() == 300, 'Sender balance mismatch'); } #[test] @@ -388,29 +423,27 @@ mod test_state { fn test_add_transfer_when_amount_is_zero() { //Given let mut state: State = Default::default(); - let deployer = test_utils::kakarot_address(); - set_contract_address(deployer); + let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing(); let sender_evm_address = test_utils::evm_address(); - let sender_starknet_address = compute_starknet_address( - deployer.into(), - sender_evm_address, - UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap() - ); - let sender = Address { evm: sender_evm_address, starknet: sender_starknet_address }; + let sender_starknet_address = contract_utils::deploy_eoa(sender_evm_address) + .contract_address; + let sender_address = Address { evm: sender_evm_address, starknet: sender_starknet_address }; let recipient_evm_address = test_utils::other_evm_address(); - let recipient_starknet_address = compute_starknet_address( - deployer.into(), - recipient_evm_address, - UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap() - ); - let recipient = Address { + let recipient_starknet_address = contract_utils::deploy_eoa(recipient_evm_address) + .contract_address; + let recipient_address = Address { evm: recipient_evm_address, starknet: recipient_starknet_address }; - let transfer = Transfer { sender, recipient, amount: 0 }; + let transfer = Transfer { sender: sender_address, recipient: recipient_address, amount: 0 }; // Write user balances in cache to avoid fetching from SN storage - state.write_balance(sender.evm, 300); - state.write_balance(recipient.evm, 0); + // Write user balances in cache to avoid fetching from SN storage + let mut sender = state.get_account(sender_address.evm); + sender.balance = 300; + state.set_account(sender); + let mut recipient = state.get_account(recipient_address.evm); + recipient.set_balance(0); + state.set_account(recipient); // When state.add_transfer(transfer).unwrap(); @@ -418,25 +451,28 @@ mod test_state { // Then, no transfer appended to log and cached balances updated assert(state.transfers.contextual_logs.len() == 0, 'Transfer added'); - assert(state.read_balance(sender.evm).unwrap() == 300, 'Sender balance mismatch'); - assert(state.read_balance(recipient.evm).unwrap() == 0, 'Recipient balance mismatch'); + assert(state.get_account(sender_address.evm).balance() == 300, 'Sender balance mismatch'); + assert( + state.get_account(recipient_address.evm).balance() == 0, 'Recipient balance mismatch' + ); } #[test] #[available_gas(200000000)] fn test_read_balance_cached() { let mut state: State = Default::default(); - let deployer = test_utils::kakarot_address(); - let evm_address: EthAddress = test_utils::evm_address(); - let starknet_address = compute_starknet_address( - deployer.into(), evm_address, UninitializedAccount::TEST_CLASS_HASH.try_into().unwrap() - ); + let (native_token, kakarot_core) = contract_utils::setup_contracts_for_testing(); + + let evm_address = test_utils::evm_address(); + let starknet_address = contract_utils::deploy_eoa(evm_address).contract_address; let address = Address { evm: evm_address, starknet: starknet_address }; let balance = 100; - state.write_balance(address.evm, balance); - let read_balance = state.read_balance(address.evm).unwrap(); + let mut account = state.get_account(address.evm); + account.set_balance(balance); + state.set_account(account); + let read_balance = state.get_account(address.evm).balance(); assert(balance == read_balance, 'Balance mismatch'); } @@ -456,7 +492,7 @@ mod test_state { // Revert back to contract_address = kakarot for the test set_contract_address(kakarot_core.contract_address); let mut state: State = Default::default(); - let read_balance = state.read_balance(evm_address).unwrap(); + let read_balance = state.get_account(evm_address).balance(); assert(read_balance == 10000, 'Balance mismatch'); } diff --git a/crates/evm/src/tests/test_utils.cairo b/crates/evm/src/tests/test_utils.cairo index 058289d35..9725e0007 100644 --- a/crates/evm/src/tests/test_utils.cairo +++ b/crates/evm/src/tests/test_utils.cairo @@ -265,6 +265,7 @@ fn initialize_contract_account( address: ca_address, code: array![0xab, 0xcd, 0xef].span(), nonce: 1, + balance: 0, selfdestruct: false }; let mut i = 0;