diff --git a/program-libs/ctoken-types/src/instructions/create_associated_token_account.rs b/program-libs/ctoken-types/src/instructions/create_associated_token_account.rs index 987f844390..be853bea62 100644 --- a/program-libs/ctoken-types/src/instructions/create_associated_token_account.rs +++ b/program-libs/ctoken-types/src/instructions/create_associated_token_account.rs @@ -13,7 +13,6 @@ pub struct CreateAssociatedTokenAccountInstructionData { pub owner: Pubkey, /// The mint for the associated token account pub mint: Pubkey, - pub bump: u8, /// Optional compressible configuration for the token account pub compressible_config: Option, } diff --git a/program-libs/ctoken-types/src/instructions/create_associated_token_account2.rs b/program-libs/ctoken-types/src/instructions/create_associated_token_account2.rs index 26abc36583..fc4c1960b2 100644 --- a/program-libs/ctoken-types/src/instructions/create_associated_token_account2.rs +++ b/program-libs/ctoken-types/src/instructions/create_associated_token_account2.rs @@ -8,7 +8,6 @@ use crate::{ #[repr(C)] #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy)] pub struct CreateAssociatedTokenAccount2InstructionData { - pub bump: u8, /// Optional compressible configuration for the token account pub compressible_config: Option, } diff --git a/program-tests/compressed-token-test/tests/ctoken/create_ata.rs b/program-tests/compressed-token-test/tests/ctoken/create_ata.rs index d306180aa1..2af626d553 100644 --- a/program-tests/compressed-token-test/tests/ctoken/create_ata.rs +++ b/program-tests/compressed-token-test/tests/ctoken/create_ata.rs @@ -311,7 +311,7 @@ async fn test_create_ata_failing() { // Use different mint for this test context.mint_pubkey = solana_sdk::pubkey::Pubkey::new_unique(); - let (ata_pubkey, bump) = + let (ata_pubkey, _bump) = derive_ctoken_ata(&context.owner_keypair.pubkey(), &context.mint_pubkey); // Manually build instruction data with compress_to_account_pubkey (forbidden) @@ -324,7 +324,6 @@ async fn test_create_ata_failing() { let instruction_data = CreateAssociatedTokenAccountInstructionData { owner: context.owner_keypair.pubkey().into(), mint: context.mint_pubkey.into(), - bump, compressible_config: Some(CompressibleExtensionInstructionData { token_account_version: light_ctoken_types::state::TokenDataVersion::ShaFlat as u8, rent_payment: 2, @@ -364,9 +363,9 @@ async fn test_create_ata_failing() { light_program_test::utils::assert::assert_rpc_error(result, 0, 2).unwrap(); } - // Test 5: Invalid PDA derivation (wrong bump) - // ATAs must use the correct bump derived from [owner, program_id, mint] - // Error: 21 (ProgramFailedToComplete - provided seeds do not result in valid address) + // Test 5: Invalid PDA derivation (wrong ATA address) + // ATAs must use the correct address derived from [owner, program_id, mint] + // Error: InvalidSeeds (14) { use anchor_lang::prelude::borsh::BorshSerialize; use light_ctoken_types::instructions::{ @@ -377,20 +376,15 @@ async fn test_create_ata_failing() { // Use different mint for this test context.mint_pubkey = solana_sdk::pubkey::Pubkey::new_unique(); - let (ata_pubkey, correct_bump) = + let (_ata_pubkey, _correct_bump) = derive_ctoken_ata(&context.owner_keypair.pubkey(), &context.mint_pubkey); - // Manually build instruction data with WRONG bump - let wrong_bump = if correct_bump == 255 { - 254 - } else { - correct_bump + 1 - }; + // Use a WRONG ATA address (just generate a random one) + let wrong_ata_pubkey = solana_sdk::pubkey::Pubkey::new_unique(); let instruction_data = CreateAssociatedTokenAccountInstructionData { owner: context.owner_keypair.pubkey().into(), mint: context.mint_pubkey.into(), - bump: wrong_bump, // Wrong bump! compressible_config: Some(CompressibleExtensionInstructionData { token_account_version: light_ctoken_types::state::TokenDataVersion::ShaFlat as u8, rent_payment: 2, @@ -407,7 +401,7 @@ async fn test_create_ata_failing() { program_id: light_compressed_token::ID, accounts: vec![ solana_sdk::instruction::AccountMeta::new(payer_pubkey, true), - solana_sdk::instruction::AccountMeta::new(ata_pubkey, false), + solana_sdk::instruction::AccountMeta::new(wrong_ata_pubkey, false), solana_sdk::instruction::AccountMeta::new_readonly( solana_sdk::pubkey::Pubkey::default(), false, @@ -426,16 +420,8 @@ async fn test_create_ata_failing() { .create_and_send_transaction(&[ix], &payer_pubkey, &[&context.payer]) .await; - // Wrong bump can trigger either ProgramFailedToComplete (21) or PrivilegeEscalation (19) - // depending on runtime state - accept either - let is_valid_error = - light_program_test::utils::assert::assert_rpc_error(result.clone(), 0, 21).is_ok() - || light_program_test::utils::assert::assert_rpc_error(result, 0, 19).is_ok(); - - assert!( - is_valid_error, - "Expected either ProgramFailedToComplete (21) or PrivilegeEscalation (19)" - ); + // Should fail with InvalidSeeds (14) + light_program_test::utils::assert::assert_rpc_error(result, 0, 14).unwrap(); } // Test 6: Invalid config account owner diff --git a/programs/compressed-token/program/src/create_associated_token_account.rs b/programs/compressed-token/program/src/create_associated_token_account.rs index dc19e6c5cd..fb3d12938c 100644 --- a/programs/compressed-token/program/src/create_associated_token_account.rs +++ b/programs/compressed-token/program/src/create_associated_token_account.rs @@ -15,7 +15,6 @@ use crate::{ shared::{ convert_program_error, create_pda_account, initialize_ctoken_account::initialize_ctoken_account, transfer_lamports_via_cpi, - validate_ata_derivation, }, }; @@ -57,7 +56,6 @@ pub(crate) fn process_create_associated_token_account_with_mode, ) -> Result<(), ProgramError> { let mut iter = AccountIterator::new(account_infos); @@ -78,10 +75,23 @@ pub(crate) fn process_create_associated_token_account_inner( token_account_size: usize, fee_payer: &'info AccountInfo, associated_token_account: &'info AccountInfo, - ata_bump: u8, + ata_bump_seed: &[u8; 1], owner_bytes: &[u8; 32], mint_bytes: &[u8; 32], ) -> Result<(&'info CompressibleConfig, Option), ProgramError> { @@ -186,7 +196,6 @@ fn process_compressible_config<'info>( ); // Build ATA seeds - let ata_bump_seed = [ata_bump]; let ata_seeds = [ Seed::from(owner_bytes.as_ref()), Seed::from(crate::LIGHT_CPI_SIGNER.program_id.as_ref()), diff --git a/programs/compressed-token/program/src/create_associated_token_account2.rs b/programs/compressed-token/program/src/create_associated_token_account2.rs index 3dd686443c..67d29b077c 100644 --- a/programs/compressed-token/program/src/create_associated_token_account2.rs +++ b/programs/compressed-token/program/src/create_associated_token_account2.rs @@ -56,7 +56,6 @@ fn process_create_associated_token_account2_inner( remaining_accounts, owner.key(), mint.key(), - instruction_inputs.bump, instruction_inputs.compressible_config, ) } diff --git a/programs/compressed-token/program/src/shared/mod.rs b/programs/compressed-token/program/src/shared/mod.rs index 1b0f097c71..1ac604ef91 100644 --- a/programs/compressed-token/program/src/shared/mod.rs +++ b/programs/compressed-token/program/src/shared/mod.rs @@ -9,7 +9,6 @@ pub mod owner_validation; pub mod token_input; pub mod token_output; pub mod transfer_lamports; -pub mod validate_ata_derivation; // Re-export AccountIterator from light-account-checks pub use convert_program_error::convert_program_error; @@ -17,4 +16,3 @@ pub use create_pda_account::{create_pda_account, verify_pda}; pub use light_account_checks::AccountIterator; pub use mint_to_token_pool::mint_to_token_pool; pub use transfer_lamports::*; -pub use validate_ata_derivation::validate_ata_derivation; diff --git a/programs/compressed-token/program/src/shared/validate_ata_derivation.rs b/programs/compressed-token/program/src/shared/validate_ata_derivation.rs deleted file mode 100644 index 3d1a3f7a7b..0000000000 --- a/programs/compressed-token/program/src/shared/validate_ata_derivation.rs +++ /dev/null @@ -1,29 +0,0 @@ -use anchor_lang::prelude::ProgramError; -use light_program_profiler::profile; -use pinocchio::account_info::AccountInfo; - -/// Validates that an account is the correct Associated Token Account PDA -/// -/// Returns Ok(()) if the account key matches the expected PDA derivation. -/// This is used by both the regular and idempotent create ATA instructions. -#[inline(always)] -#[profile] -pub fn validate_ata_derivation( - account: &AccountInfo, - owner: &[u8; 32], - mint: &[u8; 32], - bump: u8, -) -> Result<(), ProgramError> { - let seeds = &[ - owner.as_ref(), - crate::LIGHT_CPI_SIGNER.program_id.as_ref(), - mint.as_ref(), - ]; - - crate::shared::verify_pda( - account.key(), - seeds, - bump, - &crate::LIGHT_CPI_SIGNER.program_id, - ) -} diff --git a/sdk-libs/compressed-token-sdk/src/instructions/create_associated_token_account.rs b/sdk-libs/compressed-token-sdk/src/instructions/create_associated_token_account.rs index f7e7280654..e7d19c9679 100644 --- a/sdk-libs/compressed-token-sdk/src/instructions/create_associated_token_account.rs +++ b/sdk-libs/compressed-token-sdk/src/instructions/create_associated_token_account.rs @@ -58,35 +58,12 @@ pub fn create_compressible_associated_token_account_idempotent( pub fn create_compressible_associated_token_account_with_mode( inputs: CreateCompressibleAssociatedTokenAccountInputs, ) -> Result { - let (ata_pubkey, bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint); - create_compressible_associated_token_account_with_bump_and_mode::( - inputs, ata_pubkey, bump, - ) -} - -/// Creates a compressible associated token account instruction with a specified bump (non-idempotent) -pub fn create_compressible_associated_token_account_with_bump( - inputs: CreateCompressibleAssociatedTokenAccountInputs, - ata_pubkey: Pubkey, - bump: u8, -) -> Result { - create_compressible_associated_token_account_with_bump_and_mode::( - inputs, ata_pubkey, bump, - ) -} - -/// Creates a compressible associated token account instruction with a specified bump and mode -pub fn create_compressible_associated_token_account_with_bump_and_mode( - inputs: CreateCompressibleAssociatedTokenAccountInputs, - ata_pubkey: Pubkey, - bump: u8, -) -> Result { + let (ata_pubkey, _bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint); create_ata_instruction_unified::( inputs.payer, inputs.owner, inputs.mint, ata_pubkey, - bump, Some(( inputs.pre_pay_num_epochs, inputs.lamports_per_write, @@ -121,34 +98,8 @@ pub fn create_associated_token_account_with_mode( owner: Pubkey, mint: Pubkey, ) -> Result { - let (ata_pubkey, bump) = derive_ctoken_ata(&owner, &mint); - create_associated_token_account_with_bump_and_mode::( - payer, owner, mint, ata_pubkey, bump, - ) -} - -/// Creates a basic associated token account instruction with a specified bump (non-idempotent) -pub fn create_associated_token_account_with_bump( - payer: Pubkey, - owner: Pubkey, - mint: Pubkey, - ata_pubkey: Pubkey, - bump: u8, -) -> Result { - create_associated_token_account_with_bump_and_mode::( - payer, owner, mint, ata_pubkey, bump, - ) -} - -/// Creates a basic associated token account instruction with specified bump and mode -pub fn create_associated_token_account_with_bump_and_mode( - payer: Pubkey, - owner: Pubkey, - mint: Pubkey, - ata_pubkey: Pubkey, - bump: u8, -) -> Result { - create_ata_instruction_unified::(payer, owner, mint, ata_pubkey, bump, None) + let (ata_pubkey, _bump) = derive_ctoken_ata(&owner, &mint); + create_ata_instruction_unified::(payer, owner, mint, ata_pubkey, None) } /// Unified function to create ATA instructions with compile-time configuration @@ -157,7 +108,6 @@ fn create_ata_instruction_unified, Pubkey, Pubkey, TokenDataVersion)>, // (pre_pay_num_epochs, lamports_per_write, rent_sponsor, compressible_config_account, token_account_version) ) -> Result { // Select discriminator based on idempotent mode @@ -189,7 +139,6 @@ fn create_ata_instruction_unified( inputs: CreateCompressibleAssociatedTokenAccountInputs, ) -> Result { - let (ata_pubkey, bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint); - create_compressible_associated_token_account2_with_bump_and_mode::( - inputs, ata_pubkey, bump, - ) -} - -/// Creates a compressible associated token account instruction v2 with specified bump and mode -fn create_compressible_associated_token_account2_with_bump_and_mode( - inputs: CreateCompressibleAssociatedTokenAccountInputs, - ata_pubkey: Pubkey, - bump: u8, -) -> Result { + let (ata_pubkey, _bump) = derive_ctoken_ata(&inputs.owner, &inputs.mint); create_ata2_instruction_unified::( inputs.payer, inputs.owner, inputs.mint, ata_pubkey, - bump, Some(( inputs.pre_pay_num_epochs, inputs.lamports_per_write, @@ -315,21 +252,8 @@ fn create_associated_token_account2_with_mode( owner: Pubkey, mint: Pubkey, ) -> Result { - let (ata_pubkey, bump) = derive_ctoken_ata(&owner, &mint); - create_associated_token_account2_with_bump_and_mode::( - payer, owner, mint, ata_pubkey, bump, - ) -} - -/// Creates a basic associated token account instruction v2 with specified bump and mode -fn create_associated_token_account2_with_bump_and_mode( - payer: Pubkey, - owner: Pubkey, - mint: Pubkey, - ata_pubkey: Pubkey, - bump: u8, -) -> Result { - create_ata2_instruction_unified::(payer, owner, mint, ata_pubkey, bump, None) + let (ata_pubkey, _bump) = derive_ctoken_ata(&owner, &mint); + create_ata2_instruction_unified::(payer, owner, mint, ata_pubkey, None) } /// Unified function to create ATA2 instructions with compile-time configuration @@ -339,7 +263,6 @@ fn create_ata2_instruction_unified, Pubkey, Pubkey, TokenDataVersion)>, ) -> Result { let discriminator = if IDEMPOTENT { @@ -367,7 +290,6 @@ fn create_ata2_instruction_unified( - payer, owner, mint, ata_pubkey, bump, - ) - .unwrap(); - assert_eq!(ix_with_bump_false.data[0], CREATE_ATA_DISCRIMINATOR); - - let ix_with_bump_true = create_associated_token_account_with_bump_and_mode::( - payer, owner, mint, ata_pubkey, bump, - ) - .unwrap(); + let (ata_pubkey, _bump) = derive_ctoken_ata(&owner, &mint); + + // Verify that the derived ATA address is consistent + let (ata_pubkey2, _bump2) = derive_ctoken_ata(&owner, &mint); assert_eq!( - ix_with_bump_true.data[0], - CREATE_ATA_IDEMPOTENT_DISCRIMINATOR + ata_pubkey, ata_pubkey2, + "ATA derivation should be deterministic" + ); + + // Verify that different owners produce different ATAs + let owner2 = Pubkey::new_unique(); + let (ata_pubkey3, _bump3) = derive_ctoken_ata(&owner2, &mint); + assert_ne!( + ata_pubkey, ata_pubkey3, + "Different owners should have different ATAs" + ); + + // Verify that different mints produce different ATAs + let mint2 = Pubkey::new_unique(); + let (ata_pubkey4, _bump4) = derive_ctoken_ata(&owner, &mint2); + assert_ne!( + ata_pubkey, ata_pubkey4, + "Different mints should have different ATAs" ); }