diff --git a/CHANGELOG.md b/CHANGELOG.md index d789a4d79..194890f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Next release +- test: Adding txv3 tests - feat: L1 gas price/fix ## v0.8.0 diff --git a/crates/pallets/starknet/src/tests/declare_tx.rs b/crates/pallets/starknet/src/tests/declare_tx.rs index 1103be3a7..538868b00 100644 --- a/crates/pallets/starknet/src/tests/declare_tx.rs +++ b/crates/pallets/starknet/src/tests/declare_tx.rs @@ -9,19 +9,60 @@ use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction, }; use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce, PatriciaKey}; +use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::hash::StarkFelt; use starknet_api::transaction::{ - DeclareTransaction as StarknetApiDeclareTransaction, DeclareTransactionV0V1, DeclareTransactionV2, Fee, - TransactionSignature, + DeclareTransaction as StarknetApiDeclareTransaction, DeclareTransactionV0V1, DeclareTransactionV2, + DeclareTransactionV3, Fee, TransactionSignature, }; use starknet_crypto::FieldElement; use super::mock::default_mock::*; use super::mock::*; -use super::utils::{get_contract_class, sign_message_hash}; +use super::utils::{create_resource_bounds, get_contract_class, sign_message_hash}; use crate::tests::{set_infinite_tokens, set_nonce}; use crate::Error; +fn create_declare_erc20_v3_transaction( + chain_id: Felt252Wrapper, + account_type: AccountType, + sender_address: Option, + signature: Option, + nonce: Option, +) -> BlockifierDeclareTransaction { + let sender_address = sender_address.unwrap_or_else(|| get_account_address(None, account_type)); + let erc20_class = get_contract_class("erc20.casm.json", 1); + let erc20_class_hash = + ClassHash(StarkFelt::try_from("0x057eca87f4b19852cfd4551cf4706ababc6251a8781733a0a11cf8e94211da95").unwrap()); + + let compiled_erc20_class_hash = CompiledClassHash( + StarkFelt::try_from("0x00df4d3042eec107abe704619f13d92bbe01a58029311b7a1886b23dcbb4ea87").unwrap(), + ); + + let mut tx = StarknetApiDeclareTransaction::V3(DeclareTransactionV3 { + resource_bounds: create_resource_bounds(), + tip: starknet_api::transaction::Tip::default(), + signature: TransactionSignature::default(), + nonce: nonce.unwrap_or_default(), + class_hash: erc20_class_hash, + compiled_class_hash: compiled_erc20_class_hash, + sender_address, + nonce_data_availability_mode: DataAvailabilityMode::L1, + fee_data_availability_mode: DataAvailabilityMode::L1, + paymaster_data: starknet_api::transaction::PaymasterData(vec![]), + account_deployment_data: starknet_api::transaction::AccountDeploymentData(vec![]), + }); + + let tx_hash = tx.compute_hash(chain_id, false); + // Force to do that because ComputeTransactionHash cannot be implemented on DeclareTransactionV0V1 + // directly... + if let StarknetApiDeclareTransaction::V3(tx) = &mut tx { + tx.signature = signature.unwrap_or_else(|| sign_message_hash(tx_hash)); + } + + BlockifierDeclareTransaction::new(tx, tx_hash, ClassInfo::new(&erc20_class, 1, 1).unwrap()).unwrap() +} + fn create_declare_erc20_v1_transaction( chain_id: Felt252Wrapper, account_type: AccountType, @@ -395,3 +436,26 @@ fn test_declare_using_transaction_v0() { assert!(Starknet::validate_unsigned(TransactionSource::InBlock, &crate::Call::declare { transaction }).is_ok()); }); } + +#[test] +fn given_contract_declare_tx3_than_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + let none_origin = RuntimeOrigin::none(); + let chain_id = Starknet::chain_id(); + + let transaction = create_declare_erc20_v3_transaction( + chain_id, + AccountType::V1(AccountTypeV1Inner::NoValidate), + None, + None, + None, + ); + let class_hash = transaction.class_hash(); + let contract_class = transaction.contract_class(); + + assert_ok!(Starknet::declare(none_origin.clone(), transaction.clone())); + assert_eq!(Starknet::contract_class_by_class_hash(class_hash.0).unwrap(), contract_class); + }); +} diff --git a/crates/pallets/starknet/src/tests/deploy_account_tx.rs b/crates/pallets/starknet/src/tests/deploy_account_tx.rs index 149d89f88..3d00ef498 100644 --- a/crates/pallets/starknet/src/tests/deploy_account_tx.rs +++ b/crates/pallets/starknet/src/tests/deploy_account_tx.rs @@ -7,17 +7,18 @@ use mp_transactions::compute_hash::ComputeTransactionHash; use sp_runtime::traits::ValidateUnsigned; use sp_runtime::transaction_validity::{InvalidTransaction, TransactionSource, TransactionValidityError}; use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress, Nonce}; +use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::hash::StarkFelt; use starknet_api::transaction::{ - Calldata, ContractAddressSalt, DeployAccountTransactionV1, Event as StarknetEvent, EventContent, EventData, - EventKey, Fee, TransactionSignature, + Calldata, ContractAddressSalt, DeployAccountTransactionV1, DeployAccountTransactionV3, Event as StarknetEvent, + EventContent, EventData, EventKey, Fee, TransactionSignature, }; use starknet_core::utils::get_selector_from_name; use starknet_crypto::FieldElement; use super::mock::default_mock::*; use super::mock::*; -use super::utils::{sign_message_hash, sign_message_hash_braavos}; +use super::utils::{create_resource_bounds, sign_message_hash, sign_message_hash_braavos}; use crate::tests::constants::{ACCOUNT_PUBLIC_KEY, SALT, TRANSFER_SELECTOR_NAME}; use crate::tests::{get_deploy_account_dummy, set_infinite_tokens, set_nonce}; use crate::{Error, StorageView}; @@ -42,6 +43,26 @@ fn deploy_v1_to_blockifier_deploy( ) } +fn deploy_v3_to_blockifier_deploy( + tx: DeployAccountTransactionV3, + chain_id: Felt252Wrapper, +) -> DeployAccountTransaction { + let tx_hash = tx.compute_hash(chain_id, false); + let contract_address = calculate_contract_address( + tx.contract_address_salt, + tx.class_hash, + &tx.constructor_calldata, + Default::default(), + ) + .unwrap(); + + DeployAccountTransaction::new( + starknet_api::transaction::DeployAccountTransaction::V3(tx), + tx_hash, + contract_address, + ) +} + fn helper_create_deploy_account_tx( chain_id: Felt252Wrapper, salt: ContractAddressSalt, @@ -60,6 +81,28 @@ fn helper_create_deploy_account_tx( deploy_v1_to_blockifier_deploy(tx, chain_id) } +fn helper_create_deploy_account_tx3( + chain_id: Felt252Wrapper, + salt: ContractAddressSalt, + calldata: Calldata, + account_class_hash: ClassHash, +) -> DeployAccountTransaction { + let tx = DeployAccountTransactionV3 { + resource_bounds: create_resource_bounds(), + tip: starknet_api::transaction::Tip::default(), + signature: TransactionSignature::default(), + nonce: Nonce(StarkFelt::ZERO), + class_hash: account_class_hash, + contract_address_salt: salt, + constructor_calldata: calldata, + nonce_data_availability_mode: DataAvailabilityMode::L1, + fee_data_availability_mode: DataAvailabilityMode::L1, + paymaster_data: starknet_api::transaction::PaymasterData(vec![]), + }; + + deploy_v3_to_blockifier_deploy(tx, chain_id) +} + #[test] fn given_contract_run_deploy_account_tx_works() { new_test_ext::().execute_with(|| { @@ -509,3 +552,26 @@ fn test_verify_nonce_in_unsigned_tx() { ); }); } + +#[test] +fn given_contract_run_deploy_account_tx3_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + let chain_id = Starknet::chain_id(); + // TEST ACCOUNT CONTRACT + // - ref testnet tx(0x0751b4b5b95652ad71b1721845882c3852af17e2ed0c8d93554b5b292abb9810) + let salt = ContractAddressSalt( + StarkFelt::try_from("0x03b37cbe4e9eac89d54c5f7cc6329a63a63e8c8db2bf936f981041e086752463").unwrap(), + ); + let (account_class_hash, calldata) = account_helper(AccountType::V0(AccountTypeV0Inner::NoValidate)); + + let deploy_tx = helper_create_deploy_account_tx3(chain_id, salt, calldata, account_class_hash); + let contract_address = deploy_tx.contract_address; + + set_infinite_tokens::(&contract_address); + + assert_ok!(Starknet::deploy_account(none_origin, deploy_tx)); + assert_eq!(Starknet::contract_class_hash_by_address(contract_address), account_class_hash.0); + }); +} diff --git a/crates/pallets/starknet/src/tests/invoke_tx.rs b/crates/pallets/starknet/src/tests/invoke_tx.rs index 22c99e3a0..0e0a370e1 100644 --- a/crates/pallets/starknet/src/tests/invoke_tx.rs +++ b/crates/pallets/starknet/src/tests/invoke_tx.rs @@ -12,6 +12,7 @@ use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction, }; use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce, PatriciaKey}; +use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::hash::StarkFelt; use starknet_api::state::StorageKey; use starknet_api::transaction::{ @@ -28,7 +29,8 @@ use super::constants::{ use super::mock::default_mock::*; use super::mock::*; use super::utils::{ - get_balance_contract_call, get_contract_class, set_account_erc20_balance_to_zero, sign_message_hash, + create_resource_bounds, get_balance_contract_call, get_contract_class, set_account_erc20_balance_to_zero, + sign_message_hash, }; use crate::tests::constants::{UDC_ADDRESS, UDC_SELECTOR}; use crate::tests::{ @@ -903,3 +905,73 @@ fn given_hardcoded_contract_set_erc20_balance_to_zero() { ); }); } + +#[test] +fn given_hardcoded_contract_run_invoke_tx_v3_with_inner_call_in_validate_then_it_fails() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + + let sender_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::InnerCall)); + let mut transaction = get_invoke_v3_dummy(Starknet::chain_id(), NONCE_ZERO); + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.signature = TransactionSignature(vec![StarkFelt::ONE, StarkFelt::ONE]); + tx.sender_address = sender_address; + }; + + let storage_key = get_storage_var_address("destination", &[]); + let destination = StarkFelt::try_from(TEST_CONTRACT_ADDRESS).unwrap(); + StorageView::::insert((sender_address, storage_key), destination); + + let storage_key = get_storage_var_address("function_selector", &[]); + let selector = get_selector_from_name("without_arg").unwrap(); + StorageView::::insert( + (sender_address, storage_key), + StarkFelt::from(Felt252Wrapper::from(selector)), + ); + + assert_err!(Starknet::invoke(none_origin, transaction), Error::::TransactionExecutionFailed); + }); +} + +#[test] +fn given_account_not_deployed_invoke_tx_v3_fails_for_nonce_not_one() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + // Wrong address (not deployed) + let contract_address = ContractAddress(PatriciaKey(StarkFelt::try_from("0x13123131").unwrap())); + + let transaction = starknet_api::transaction::InvokeTransactionV3 { + resource_bounds: create_resource_bounds(), + tip: starknet_api::transaction::Tip::default(), + calldata: Calldata::default(), + sender_address: contract_address, + nonce: Nonce(StarkFelt::ZERO), + signature: TransactionSignature::default(), + nonce_data_availability_mode: DataAvailabilityMode::L1, + fee_data_availability_mode: DataAvailabilityMode::L1, + paymaster_data: starknet_api::transaction::PaymasterData(vec![]), + account_deployment_data: starknet_api::transaction::AccountDeploymentData(vec![StarkFelt::ZERO]), + }; + + assert_eq!( + Starknet::validate_unsigned( + TransactionSource::InBlock, + &crate::Call::invoke { transaction: transaction.into() } + ), + Err(TransactionValidityError::Invalid(InvalidTransaction::BadProof)) + ); + }) +} + +#[test] +fn given_hardcoded_contract_run_invoke_tx3_then_it_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + let transaction = get_invoke_v3_dummy(Starknet::chain_id(), NONCE_ZERO); + + assert_ok!(Starknet::invoke(RuntimeOrigin::none(), transaction)); + }); +} diff --git a/crates/pallets/starknet/src/tests/utils.rs b/crates/pallets/starknet/src/tests/utils.rs index e2c83b1f6..82f82bd74 100644 --- a/crates/pallets/starknet/src/tests/utils.rs +++ b/crates/pallets/starknet/src/tests/utils.rs @@ -121,7 +121,7 @@ pub fn create_resource_bounds() -> starknet_api::transaction::ResourceBoundsMapp let mut map = BTreeMap::new(); map.insert( starknet_api::transaction::Resource::L1Gas, - starknet_api::transaction::ResourceBounds { max_amount: 10000, max_price_per_unit: 12000 }, + starknet_api::transaction::ResourceBounds { max_amount: 150000, max_price_per_unit: 12000 }, ); map.insert( starknet_api::transaction::Resource::L2Gas, diff --git a/package-lock.json b/package-lock.json index cccf51844..a75dfcab6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,14 +4,16 @@ "requires": true, "packages": { "": { - "dependencies": { - "prettier": "^3.2.5" + "devDependencies": { + "prettier": "3.3.0" } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", + "integrity": "sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==", + "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -25,9 +27,10 @@ }, "dependencies": { "prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", + "integrity": "sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==", + "dev": true } } } diff --git a/package.json b/package.json index edf7bed5f..d85dc9d61 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "dependencies": { - "prettier": "^3.2.5" + "devDependencies": { + "prettier": "3.3.0" } }