diff --git a/CHANGELOG.md b/CHANGELOG.md index 33eabbe4e7..7a291d6fb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ - feat: Remove generic hasher from block hash computation - refacto: git submodules removed - test: Add pallet test for transaction declare V0 +- test: Adding tests for txv3 ## v0.7.0 diff --git a/crates/pallets/starknet/src/tests/invoke_tx.rs b/crates/pallets/starknet/src/tests/invoke_tx.rs index baa0292918..c0d333ec64 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,13 +29,15 @@ 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::{ get_invoke_argent_dummy, get_invoke_braavos_dummy, get_invoke_dummy, get_invoke_emit_event_dummy, - get_invoke_nonce_dummy, get_invoke_openzeppelin_dummy, get_invoke_v3_dummy, get_storage_read_write_dummy, - set_infinite_tokens, set_nonce, + get_invoke_nonce_dummy, get_invoke_openzeppelin_dummy, get_invoke_v3_argent_dummy, get_invoke_v3_braavos_dummy, + get_invoke_v3_dummy, get_invoke_v3_openzeppelin_dummy, get_storage_read_write_dummy, set_infinite_tokens, + set_nonce, }; use crate::{Call, Error, StorageView}; @@ -904,3 +907,196 @@ fn given_hardcoded_contract_set_erc20_balance_to_zero() { ); }); } + +#[test] +fn given_hardcoded_contract_run_invoke_tx_v3_fails_sender_not_deployed() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + let none_origin = RuntimeOrigin::none(); + + // Wrong address (not deployed) + let contract_address = ContractAddress(PatriciaKey( + StarkFelt::try_from("0x03e437FB56Bb213f5708Fcd6966502070e276c093ec271aA33433b89E21fd32f").unwrap(), + )); + + let mut transaction = get_invoke_v3_dummy(Starknet::chain_id(), NONCE_ZERO); + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.sender_address = contract_address; + }; + + assert_err!(Starknet::invoke(none_origin, transaction), Error::::AccountNotDeployed); + }) +} + +#[test] +fn given_hardcoded_contract_run_invoke_tx_v3_on_argent_account_then_it_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + // NOT WORKING + let chain_id = Starknet::chain_id(); + let mut transaction = get_invoke_v3_argent_dummy(chain_id); + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.signature = sign_message_hash(transaction.tx_hash); + }; + + assert_ok!(Starknet::invoke(none_origin, transaction)); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_tx_v3_on_openzeppelin_account_then_it_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + // NOT WORKING + let transaction = get_invoke_v3_openzeppelin_dummy(Starknet::chain_id()); + + assert_ok!(Starknet::invoke(none_origin, transaction)); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_tx_v3_on_argent_account_with_incorrect_signature_then_it_fails() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + + let mut transaction = get_invoke_v3_argent_dummy(Starknet::chain_id()); + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.signature = TransactionSignature(vec![StarkFelt::ONE, StarkFelt::ONE]); + }; + + let validate_result = Starknet::validate_unsigned( + TransactionSource::InBlock, + &crate::Call::invoke { transaction: transaction.clone() }, + ); + assert!(matches!(validate_result.unwrap_err(), TransactionValidityError::Invalid(_))); + + assert_err!(Starknet::invoke(none_origin, transaction), Error::::TransactionExecutionFailed); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_tx_v3_on_braavos_account_then_it_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + // NOT WORKING + let mut transaction = get_invoke_v3_braavos_dummy(Starknet::chain_id()); + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.signature = sign_message_hash(transaction.tx_hash); + }; + + assert_ok!(Starknet::invoke(none_origin, transaction)); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_tx_v3_on_braavos_account_with_incorrect_signature_then_it_fails() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + + let mut transaction = get_invoke_v3_braavos_dummy(Starknet::chain_id()); + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.signature = TransactionSignature(vec![StarkFelt::ONE, StarkFelt::ONE]); + }; + + let validate_result = Starknet::validate_unsigned( + TransactionSource::InBlock, + &crate::Call::invoke { transaction: transaction.clone() }, + ); + assert!(matches!(validate_result.unwrap_err(), TransactionValidityError::Invalid(_))); + + assert_err!(Starknet::invoke(none_origin, transaction), Error::::TransactionExecutionFailed); + }); +} + +#[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_validate_works_for_nonce_one() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + // Wrong address (not deployed) + let contract_address = ContractAddress(PatriciaKey(StarkFelt::try_from("0x13123131").unwrap())); + // NOT WORKING + 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![StarkFelt::ZERO]), + account_deployment_data: starknet_api::transaction::AccountDeploymentData(vec![StarkFelt::ZERO]), + }; + + set_infinite_tokens::(&contract_address); + assert_ok!(Starknet::validate_unsigned( + TransactionSource::InBlock, + &crate::Call::invoke { transaction: transaction.into() } + )); + }) +} + +#[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![StarkFelt::ZERO]), + 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)) + ); + }) +} diff --git a/crates/pallets/starknet/src/tests/mod.rs b/crates/pallets/starknet/src/tests/mod.rs index bc18e7cf0a..7ae5564e70 100644 --- a/crates/pallets/starknet/src/tests/mod.rs +++ b/crates/pallets/starknet/src/tests/mod.rs @@ -144,6 +144,40 @@ fn get_invoke_argent_dummy(chain_id: Felt252Wrapper) -> blockifier::transaction: blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } } +fn get_invoke_v3_argent_dummy(chain_id: Felt252Wrapper) -> blockifier::transaction::transactions::InvokeTransaction { + let sender_address = ContractAddress(PatriciaKey( + StarkFelt::try_from("0x02e63de215f650e9d7e2313c6e9ed26b4f920606fb08576b1663c21a7c4a28c5").unwrap(), + )); + let nonce = Nonce(StarkFelt::ZERO); + let signature = TransactionSignature::default(); + let calldata = Calldata(Arc::new(vec![ + StarkFelt::try_from("0x1").unwrap(), // call_array_len + StarkFelt::try_from("0x024d1e355f6b9d27a5a420c8f4b50cea9154a8e34ad30fc39d7c98d3c177d0d7").unwrap(), // to + StarkFelt::try_from("0x00e7def693d16806ca2a2f398d8de5951344663ba77f340ed7a958da731872fc").unwrap(), // selector + StarkFelt::try_from("0x0").unwrap(), // data_offset + StarkFelt::try_from("0x1").unwrap(), // data_len + StarkFelt::try_from("0x1").unwrap(), // calldata_len + StarkFelt::try_from("0x19").unwrap(), // calldata[0] + ])); + + let tx = starknet_api::transaction::InvokeTransaction::V3(starknet_api::transaction::InvokeTransactionV3 { + resource_bounds: create_resource_bounds(), + tip: starknet_api::transaction::Tip::default(), + calldata, + sender_address, + nonce, + signature, + 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); + + blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } +} + // ref: https://github.com/myBraavos/braavos-account-cairo/blob/develop/src/account/Account.cairo fn get_invoke_braavos_dummy(chain_id: Felt252Wrapper) -> blockifier::transaction::transactions::InvokeTransaction { let signature = TransactionSignature(vec![ @@ -177,6 +211,43 @@ fn get_invoke_braavos_dummy(chain_id: Felt252Wrapper) -> blockifier::transaction blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } } +fn get_invoke_v3_braavos_dummy(chain_id: Felt252Wrapper) -> blockifier::transaction::transactions::InvokeTransaction { + let signature = TransactionSignature(vec![ + StarkFelt::try_from("0x00f513fe663ffefb9ad30058bb2d2f7477022b149a0c02fb63072468d3406168").unwrap(), + StarkFelt::try_from("0x02e29e92544d31c03e89ecb2005941c88c28b4803a3647a7834afda12c77f096").unwrap(), + ]); + let sender_address = ContractAddress(PatriciaKey( + StarkFelt::try_from("0x05ef3fba22df259bf84890945352df711bcc9a4e3b6858cb93e9c90d053cf122").unwrap(), + )); + let nonce = Nonce(StarkFelt::ZERO); + let calldata = Calldata(Arc::new(vec![ + StarkFelt::try_from("0x1").unwrap(), // call_array_len + StarkFelt::try_from("0x024d1e355f6b9d27a5a420c8f4b50cea9154a8e34ad30fc39d7c98d3c177d0d7").unwrap(), // to + StarkFelt::try_from("0x00e7def693d16806ca2a2f398d8de5951344663ba77f340ed7a958da731872fc").unwrap(), // selector + StarkFelt::try_from("0x0").unwrap(), // data_offset + StarkFelt::try_from("0x1").unwrap(), // data_len + StarkFelt::try_from("0x1").unwrap(), // calldata_len + StarkFelt::try_from("0x19").unwrap(), // calldata[0] + ])); + + let tx = starknet_api::transaction::InvokeTransaction::V3(starknet_api::transaction::InvokeTransactionV3 { + resource_bounds: create_resource_bounds(), + tip: starknet_api::transaction::Tip::default(), + calldata, + sender_address, + nonce, + signature, + 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); + + blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } +} + // ref: https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/openzeppelin/token/erc20/IERC20.cairo fn get_invoke_emit_event_dummy(chain_id: Felt252Wrapper) -> blockifier::transaction::transactions::InvokeTransaction { let signature = TransactionSignature(vec![ @@ -295,6 +366,45 @@ fn get_invoke_openzeppelin_dummy(chain_id: Felt252Wrapper) -> blockifier::transa blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } } +fn get_invoke_v3_openzeppelin_dummy( + chain_id: Felt252Wrapper, +) -> blockifier::transaction::transactions::InvokeTransaction { + let signature = TransactionSignature(vec![ + StarkFelt::try_from("0x028ef1ae6c37314bf9df65663db1cf68f95d67c4b4cf7f6590654933a84912b0").unwrap(), + StarkFelt::try_from("0x0625aae99c58b18e5161c719fef0f99579c6468ca6c1c866f9b2b968a5447e4").unwrap(), + ]); + let sender_address = ContractAddress(PatriciaKey( + StarkFelt::try_from("0x06e2616a2dceff4355997369246c25a78e95093df7a49e5ca6a06ce1544ffd50").unwrap(), + )); + let nonce = Nonce(StarkFelt::ZERO); + let calldata = Calldata(Arc::new(vec![ + StarkFelt::try_from("0x1").unwrap(), // call_array_len + StarkFelt::try_from("0x024d1e355f6b9d27a5a420c8f4b50cea9154a8e34ad30fc39d7c98d3c177d0d7").unwrap(), // to + StarkFelt::try_from("0x00e7def693d16806ca2a2f398d8de5951344663ba77f340ed7a958da731872fc").unwrap(), // selector + StarkFelt::try_from("0x0").unwrap(), // data offset + StarkFelt::try_from("0x1").unwrap(), // data length + StarkFelt::try_from("0x1").unwrap(), // calldata_len + StarkFelt::try_from("0x19").unwrap(), // calldata[0] + ])); + + let tx = starknet_api::transaction::InvokeTransaction::V3(starknet_api::transaction::InvokeTransactionV3 { + resource_bounds: create_resource_bounds(), + tip: starknet_api::transaction::Tip::default(), + calldata, + sender_address, + nonce, + signature, + 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); + + blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } +} + /// Returns a dummy declare transaction for the given account type. /// The declared class hash is a ERC20 contract, class hash calculated /// with starkli.