diff --git a/crates/blockifier/src/transaction/account_transaction.rs b/crates/blockifier/src/transaction/account_transaction.rs index d81b80e14ae..ca746e68ed5 100644 --- a/crates/blockifier/src/transaction/account_transaction.rs +++ b/crates/blockifier/src/transaction/account_transaction.rs @@ -222,7 +222,7 @@ impl AccountTransaction { ] } Transaction::DeployAccount(_) => { - vec![TransactionVersion::ONE, TransactionVersion::THREE] + vec![TransactionVersion::ONE, TransactionVersion::THREE, TransactionVersion::FOUR] } Transaction::Invoke(_) => { vec![TransactionVersion::ZERO, TransactionVersion::ONE, TransactionVersion::THREE] diff --git a/crates/blockifier/src/transaction/account_transactions_test.rs b/crates/blockifier/src/transaction/account_transactions_test.rs index cde8699e02e..ae9367bd6d9 100644 --- a/crates/blockifier/src/transaction/account_transactions_test.rs +++ b/crates/blockifier/src/transaction/account_transactions_test.rs @@ -26,6 +26,7 @@ use starknet_api::contract_class::compiled_class_hash::{HashVersion, HashableCom use starknet_api::contract_class::ContractClass; use starknet_api::core::{ calculate_contract_address, + is_pedersen_reachable_address, AddressDerivationHash, ClassHash, CompiledClassHash, @@ -1593,6 +1594,61 @@ fn test_insufficient_max_fee_reverts( assert_eq!(tx_execution_info3.receipt.da_gas, tx_execution_info1.receipt.da_gas); } +#[rstest] +fn test_deploy_account_v4_blake_address( + block_context: BlockContext, + default_all_resource_bounds: ValidResourceBounds, +) { + let account = + FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1(RunnableCairo1::Casm)); + let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm)); + let class_hash = account.get_class_hash(); + let chain_info = &block_context.chain_info; + let mut state = test_state(chain_info, BALANCE, &[(account, 1), (test_contract, 1)]); + let mut nonce_manager = NonceManager::default(); + + let (deploy_account_tx, account_address) = deploy_and_fund_account( + &mut state, + &mut nonce_manager, + chain_info, + deploy_account_tx_args! { + class_hash, + resource_bounds: default_all_resource_bounds, + version: TransactionVersion::FOUR, + }, + ); + + // The v4 address derives with Blake2 and escapes the Pedersen image, so it differs from the + // Pedersen (v3) address of the same deployment arguments. + assert!(!is_pedersen_reachable_address(account_address.0.key())); + let pedersen_address = calculate_contract_address( + ContractAddressSalt::default(), + class_hash, + &Calldata::default(), + ContractAddress::default(), + AddressDerivationHash::Pedersen, + ) + .unwrap(); + assert_ne!(account_address, pedersen_address); + + let execution_info = deploy_account_tx.execute(&mut state, &block_context).unwrap(); + assert!(!execution_info.is_reverted()); + assert_eq!(state.get_class_hash_at(account_address).unwrap(), class_hash); + + // The deployed account is functional: an invoke sent from it succeeds. + run_invoke_tx( + &mut state, + &block_context, + invoke_tx_args! { + sender_address: account_address, + calldata: create_trivial_calldata(test_contract.get_instance_address(0)), + resource_bounds: default_all_resource_bounds, + nonce: nonce_manager.next(account_address), + }, + ) + .unwrap(); +} + #[rstest] #[case::cairo0(CairoVersion::Cairo0)] #[case::cairo1(CairoVersion::Cairo1(RunnableCairo1::Casm))] diff --git a/crates/blockifier/src/transaction/transaction_execution.rs b/crates/blockifier/src/transaction/transaction_execution.rs index 3a323aa7a0b..2ae1e3c0d7a 100644 --- a/crates/blockifier/src/transaction/transaction_execution.rs +++ b/crates/blockifier/src/transaction/transaction_execution.rs @@ -1,5 +1,5 @@ use starknet_api::contract_class::ClassInfo; -use starknet_api::core::{AddressDerivationHash, ContractAddress, Nonce}; +use starknet_api::core::{ContractAddress, Nonce}; use starknet_api::executable_transaction::{ AccountTransaction as ApiExecutableTransaction, DeclareTransaction, @@ -31,6 +31,10 @@ use crate::transaction::objects::{ }; use crate::transaction::transactions::ExecutableTransaction; +#[cfg(test)] +#[path = "transaction_execution_test.rs"] +mod transaction_execution_test; + // TODO(Gilad): Move into transaction.rs, makes more sense to be defined there. #[allow(clippy::large_enum_variant)] #[derive(Clone, Debug, derive_more::From)] @@ -103,7 +107,7 @@ impl Transaction { let contract_address = match deployed_contract_address { Some(address) => address, None => deploy_account - .calculate_contract_address(AddressDerivationHash::Pedersen)?, + .calculate_contract_address(deploy_account.address_derivation_hash())?, }; ApiExecutableTransaction::DeployAccount(DeployAccountTransaction { tx: deploy_account, diff --git a/crates/blockifier/src/transaction/transaction_execution_test.rs b/crates/blockifier/src/transaction/transaction_execution_test.rs new file mode 100644 index 00000000000..3c3d0661123 --- /dev/null +++ b/crates/blockifier/src/transaction/transaction_execution_test.rs @@ -0,0 +1,54 @@ +use std::sync::Arc; + +use rstest::rstest; +use starknet_api::core::{is_pedersen_reachable_address, ContractAddress}; +use starknet_api::test_utils::deploy_account::deploy_account_tx; +use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; +use starknet_api::transaction::{ + Transaction as StarknetApiTransaction, + TransactionHash, + TransactionVersion, +}; +use starknet_api::{class_hash, deploy_account_tx_args, nonce}; +use starknet_types_core::felt::Felt; + +use super::Transaction; +use crate::transaction::account_transaction::ExecutionFlags as AccountExecutionFlags; + +/// Without an externally supplied address, `from_api` must derive it with the scheme the +/// transaction version implies -- Blake2 for v4, not the Pedersen default. +#[rstest] +fn test_from_api_derives_v4_address_with_blake2() { + // Frozen vector: deployer = 0, salt = 771, class_hash = 0x4242, + // constructor_calldata = [42, 2^63, 1337], escaping after one increment. + let tx = deploy_account_tx( + deploy_account_tx_args! { + version: TransactionVersion::FOUR, + class_hash: class_hash!("0x4242"), + contract_address_salt: ContractAddressSalt(Felt::from(771_u16)), + constructor_calldata: Calldata(Arc::new(vec![ + Felt::from(42_u8), + Felt::from(1_u64 << 63), + Felt::from(1337_u16), + ])), + }, + nonce!(0_u8), + ); + + let tx = Transaction::from_api( + StarknetApiTransaction::DeployAccount(tx), + TransactionHash::default(), + None, + None, + None, + AccountExecutionFlags::default(), + ) + .unwrap(); + + let expected_address = ContractAddress::try_from(Felt::from_hex_unchecked( + "0x566c3e328f3fd5a311267250cadc3c1c4de799db54180fcf862fe90b622571d", + )) + .unwrap(); + assert_eq!(tx.sender_address(), expected_address); + assert!(!is_pedersen_reachable_address(expected_address.0.key())); +}