diff --git a/programs/system/src/errors.rs b/programs/system/src/errors.rs index 7d0cd1ef6b..cf012e61dc 100644 --- a/programs/system/src/errors.rs +++ b/programs/system/src/errors.rs @@ -69,4 +69,5 @@ pub enum SystemProgramError { #[msg("Output merkle tree indices are not in ascending order.")] OutputMerkleTreeIndicesNotInOrder, OutputMerkleTreeNotUnique, + DataFieldUndefined, } diff --git a/programs/system/src/invoke/verify_signer.rs b/programs/system/src/invoke/verify_signer.rs index f141eaeabf..28c6eef412 100644 --- a/programs/system/src/invoke/verify_signer.rs +++ b/programs/system/src/invoke/verify_signer.rs @@ -16,13 +16,19 @@ pub fn input_compressed_accounts_signer_check( .iter() .try_for_each( |compressed_account_with_context: &PackedCompressedAccountWithMerkleContext| { - if compressed_account_with_context.compressed_account.owner == *authority { + if compressed_account_with_context.compressed_account.owner == *authority + && compressed_account_with_context + .compressed_account + .data + .is_none() + { Ok(()) } else { msg!( - "signer check failed compressed account owner {} != authority {}", + "signer check failed compressed account owner {} != authority {} or data is not none {} (only programs can own compressed accounts with data)", compressed_account_with_context.compressed_account.owner, - authority + authority, + compressed_account_with_context.compressed_account.data.is_none() ); err!(SystemProgramError::SignerCheckFailed) } diff --git a/programs/system/src/invoke_cpi/verify_signer.rs b/programs/system/src/invoke_cpi/verify_signer.rs index 32e4a5cdb7..666369a63b 100644 --- a/programs/system/src/invoke_cpi/verify_signer.rs +++ b/programs/system/src/invoke_cpi/verify_signer.rs @@ -114,6 +114,13 @@ pub fn output_compressed_accounts_write_access_check( msg!("compressed_account: {:?}", compressed_account); return err!(SystemProgramError::WriteAccessCheckFailed); } + if compressed_account.compressed_account.data.is_none() + && compressed_account.compressed_account.owner == invoking_program_id.key() + { + msg!("For program owned compressed accounts the data field needs to be defined."); + msg!("compressed_account: {:?}", compressed_account); + return err!(SystemProgramError::DataFieldUndefined); + } } Ok(()) } diff --git a/test-programs/system-cpi-test/src/create_pda.rs b/test-programs/system-cpi-test/src/create_pda.rs index 8b8516306f..985a0c0c4c 100644 --- a/test-programs/system-cpi-test/src/create_pda.rs +++ b/test-programs/system-cpi-test/src/create_pda.rs @@ -17,6 +17,7 @@ pub enum CreatePdaMode { InvalidSignerSeeds, InvalidInvokingProgram, WriteToAccountNotOwned, + NoData, } pub fn process_create_pda<'info>( @@ -87,6 +88,17 @@ pub fn process_create_pda<'info>( CreatePdaMode::WriteToAccountNotOwned, )?; } + CreatePdaMode::NoData => { + cpi_compressed_pda_transfer_as_program( + &ctx, + proof, + new_address_params, + compressed_pda, + cpi_context, + bump, + CreatePdaMode::NoData, + )?; + } } Ok(()) } @@ -163,6 +175,12 @@ fn cpi_compressed_pda_transfer_as_program<'info>( compressed_pda.compressed_account.owner = ctx.accounts.signer.key(); compressed_pda } + CreatePdaMode::NoData => { + let mut compressed_pda = compressed_pda; + + compressed_pda.compressed_account.data = None; + compressed_pda + } _ => compressed_pda, }; diff --git a/test-programs/system-cpi-test/tests/test.rs b/test-programs/system-cpi-test/tests/test.rs index 71f799ca86..d608402cd2 100644 --- a/test-programs/system-cpi-test/tests/test.rs +++ b/test-programs/system-cpi-test/tests/test.rs @@ -4,6 +4,7 @@ use anchor_lang::AnchorDeserialize; use light_compressed_token::process_transfer::InputTokenDataWithContext; use light_compressed_token::token_data::AccountState; use light_hasher::{Hasher, Poseidon}; +use light_system_program::errors::SystemProgramError; use light_system_program::sdk::address::derive_address; use light_system_program::sdk::compressed_account::{ CompressedAccountWithMerkleContext, PackedCompressedAccountWithMerkleContext, @@ -17,6 +18,7 @@ use light_test_utils::indexer::{Indexer, TestIndexer, TokenDataWithContext}; use light_test_utils::rpc::errors::{assert_rpc_error, RpcError}; use light_test_utils::rpc::rpc_connection::RpcConnection; use light_test_utils::spl::{create_mint_helper, mint_tokens_helper}; +use light_test_utils::system_program::transfer_compressed_sol_test; use light_test_utils::test_env::{setup_test_programs_with_accounts, EnvAccounts}; use light_utils::hash_to_bn254_field_size_be; use solana_sdk::signature::Keypair; @@ -44,6 +46,8 @@ use system_cpi_test::{CreatePdaMode, ID}; /// 9. test signer checks trying to insert into cpi context account (invalid signer seeds) /// 10. provide cpi context account but cpi context has a different fee payer (CpiContextFeePayerMismatch) /// 11. write data to an account that it doesn't own (WriteAccessCheckFailed) +/// 12. Spend Program owned account with program keypair (SignerCheckFailed) +/// 13. Create program owned account without data (DataFieldUndefined) #[tokio::test] async fn only_test_create_pda() { let (mut rpc, env) = @@ -259,6 +263,43 @@ async fn only_test_create_pda() { ) .await .unwrap(); + + // Failing 12 Spend with program keypair + { + const CPI_SYSTEM_TEST_PROGRAM_ID_KEYPAIR: [u8; 64] = [ + 57, 80, 188, 3, 162, 80, 232, 181, 222, 192, 247, 98, 140, 227, 70, 15, 169, 202, + 73, 184, 23, 90, 69, 95, 211, 74, 128, 232, 155, 216, 5, 230, 213, 158, 155, 203, + 26, 211, 193, 195, 11, 219, 9, 155, 58, 172, 58, 200, 254, 75, 231, 106, 31, 168, + 183, 76, 179, 113, 234, 101, 191, 99, 156, 98, + ]; + let compressed_account = test_indexer.get_compressed_accounts_by_owner(&ID)[0].clone(); + let keypair = Keypair::from_bytes(&CPI_SYSTEM_TEST_PROGRAM_ID_KEYPAIR).unwrap(); + let result = transfer_compressed_sol_test( + &mut rpc, + &mut test_indexer, + &keypair, + &[compressed_account], + &[Pubkey::new_unique()], + &[env.merkle_tree_pubkey], + None, + ) + .await; + assert_rpc_error(result, 0, SystemProgramError::SignerCheckFailed.into()).unwrap(); + } + // Failing 13 DataFieldUndefined ---------------------------------------------- + perform_create_pda_failing( + &mut test_indexer, + &mut rpc, + &env, + &payer, + seed, + &data, + &ID, + CreatePdaMode::NoData, + light_system_program::errors::SystemProgramError::DataFieldUndefined.into(), + ) + .await + .unwrap(); } } diff --git a/test-programs/system-test/tests/test.rs b/test-programs/system-test/tests/test.rs index 18c5b8ead5..d534bbbb8f 100644 --- a/test-programs/system-test/tests/test.rs +++ b/test-programs/system-test/tests/test.rs @@ -444,7 +444,7 @@ pub async fn failing_transaction_inputs_inner( payer, inputs_struct, remaining_accounts.clone(), - VerifierError::ProofVerificationFailed.into(), + SystemProgramError::SignerCheckFailed.into(), ) .await .unwrap();