diff --git a/Cargo.lock b/Cargo.lock index 03057b4669..beaff8ba74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3677,6 +3677,7 @@ dependencies = [ "light-hasher", "light-macros", "light-zero-copy", + "solana-msg", "solana-pubkey", "thiserror 2.0.12", ] diff --git a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs index dfcf6a3787..ab37560ee0 100644 --- a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs +++ b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/escrow.rs @@ -133,11 +133,12 @@ fn cpi_compressed_pda_transfer<'info>( .clone(), ]; system_accounts.extend_from_slice(ctx.remaining_accounts); - let light_accounts = CpiAccounts::new_with_config( + let light_accounts = CpiAccounts::try_new_with_config( ctx.accounts.signer.as_ref(), &system_accounts, CpiAccountsConfig::new_with_cpi_context(crate::LIGHT_CPI_SIGNER), - ); + ) + .unwrap(); verify_borsh(light_accounts, &inputs_struct).map_err(ProgramError::from)?; diff --git a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs index 0ffdb10b1a..fb8bd8eae1 100644 --- a/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs +++ b/examples/anchor/token-escrow/src/escrow_with_compressed_pda/withdrawal.rs @@ -157,11 +157,12 @@ fn cpi_compressed_pda_withdrawal<'info>( .clone(), ]; system_accounts.extend_from_slice(ctx.remaining_accounts); - let light_accounts = CpiAccounts::new_with_config( + let light_accounts = CpiAccounts::try_new_with_config( ctx.accounts.signer.as_ref(), &system_accounts, CpiAccountsConfig::new_with_cpi_context(crate::LIGHT_CPI_SIGNER), - ); + ) + .unwrap(); verify_borsh(light_accounts, &inputs_struct).unwrap(); Ok(()) diff --git a/program-libs/account-checks/src/account_info/account_info_trait.rs b/program-libs/account-checks/src/account_info/account_info_trait.rs index 03b5cf4729..3a8e81929f 100644 --- a/program-libs/account-checks/src/account_info/account_info_trait.rs +++ b/program-libs/account-checks/src/account_info/account_info_trait.rs @@ -1,10 +1,13 @@ -use core::ops::{Deref, DerefMut}; +use core::{ + fmt::Debug, + ops::{Deref, DerefMut}, +}; use crate::error::AccountError; /// Trait to abstract over different AccountInfo implementations (pinocchio vs solana) pub trait AccountInfoTrait { - type Pubkey: Copy + Clone; + type Pubkey: Copy + Clone + Debug; type DataRef<'a>: Deref where Self: 'a; diff --git a/program-tests/create-address-test-program/src/lib.rs b/program-tests/create-address-test-program/src/lib.rs index f7c347f13c..90c6d100d3 100644 --- a/program-tests/create-address-test-program/src/lib.rs +++ b/program-tests/create-address-test-program/src/lib.rs @@ -17,7 +17,8 @@ use light_compressed_account::instruction_data::{ use light_sdk::{ constants::LIGHT_SYSTEM_PROGRAM_ID, cpi::{ - invoke_light_system_program, to_account_metas, to_account_metas_small, CpiAccountsConfig, + get_account_metas_from_config, invoke_light_system_program, to_account_metas_small, + CpiAccountsConfig, CpiInstructionConfig, }, }; @@ -78,14 +79,16 @@ pub mod system_cpi_test { use light_sdk::cpi::CpiAccounts; let cpi_accounts = CpiAccounts::new_with_config(&fee_payer, ctx.remaining_accounts, config); + let account_infos = cpi_accounts .to_account_infos() .into_iter() .cloned() .collect::>(); - let account_metas = - to_account_metas(cpi_accounts).map_err(|_| ErrorCode::AccountNotEnoughKeys)?; + let config = CpiInstructionConfig::try_from(&cpi_accounts) + .map_err(|_| ErrorCode::AccountNotEnoughKeys)?; + let account_metas = get_account_metas_from_config(config); (account_infos, account_metas) }; let instruction = Instruction { diff --git a/program-tests/sdk-pinocchio-test/src/create_pda.rs b/program-tests/sdk-pinocchio-test/src/create_pda.rs index 0b732750ea..10e6d8b1d3 100644 --- a/program-tests/sdk-pinocchio-test/src/create_pda.rs +++ b/program-tests/sdk-pinocchio-test/src/create_pda.rs @@ -21,11 +21,11 @@ pub fn create_pda( let instruction_data = CreatePdaInstructionData::deserialize(&mut instruction_data) .map_err(|_| LightSdkError::Borsh)?; let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); - let cpi_accounts = CpiAccounts::new_with_config( + let cpi_accounts = CpiAccounts::try_new_with_config( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - ); + )?; let address_tree_info = instruction_data.address_tree_info; let (address, address_seed) = if BATCHED { diff --git a/program-tests/sdk-pinocchio-test/src/update_pda.rs b/program-tests/sdk-pinocchio-test/src/update_pda.rs index 5c18ac74f1..8d8edeb0fd 100644 --- a/program-tests/sdk-pinocchio-test/src/update_pda.rs +++ b/program-tests/sdk-pinocchio-test/src/update_pda.rs @@ -37,11 +37,11 @@ pub fn update_pda( let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); sol_log_compute_units(); - let cpi_accounts = CpiAccounts::new_with_config( + let cpi_accounts = CpiAccounts::try_new_with_config( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - ); + )?; sol_log_compute_units(); let cpi_inputs = CpiInputs::new( instruction_data.proof, diff --git a/program-tests/sdk-test/src/create_pda.rs b/program-tests/sdk-test/src/create_pda.rs index ecd900fddf..95a7293589 100644 --- a/program-tests/sdk-test/src/create_pda.rs +++ b/program-tests/sdk-test/src/create_pda.rs @@ -21,11 +21,12 @@ pub fn create_pda( let instruction_data = CreatePdaInstructionData::deserialize(&mut instruction_data) .map_err(|_| LightSdkError::Borsh)?; let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); - let cpi_accounts = CpiAccounts::new_with_config( + let cpi_accounts = CpiAccounts::try_new_with_config( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - ); + ) + .unwrap(); let address_tree_info = instruction_data.address_tree_info; let (address, address_seed) = if BATCHED { diff --git a/program-tests/sdk-test/src/update_pda.rs b/program-tests/sdk-test/src/update_pda.rs index b946e3baaa..2e2fcd4257 100644 --- a/program-tests/sdk-test/src/update_pda.rs +++ b/program-tests/sdk-test/src/update_pda.rs @@ -35,11 +35,11 @@ pub fn update_pda( let config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER); sol_log_compute_units(); - let cpi_accounts = CpiAccounts::new_with_config( + let cpi_accounts = CpiAccounts::try_new_with_config( &accounts[0], &accounts[instruction_data.system_accounts_offset as usize..], config, - ); + )?; sol_log_compute_units(); let cpi_inputs = CpiInputs::new( instruction_data.proof, diff --git a/sdk-libs/sdk-pinocchio/src/error.rs b/sdk-libs/sdk-pinocchio/src/error.rs index 89663d599b..3dda6ac993 100644 --- a/sdk-libs/sdk-pinocchio/src/error.rs +++ b/sdk-libs/sdk-pinocchio/src/error.rs @@ -1,3 +1,4 @@ +use light_account_checks::error::AccountError; use light_hasher::HasherError; pub use light_sdk_types::error::LightSdkTypesError; use light_zero_copy::errors::ZeroCopyError; @@ -68,12 +69,20 @@ pub enum LightSdkError { MetaCloseInputIsNone, #[error("CPI accounts index out of bounds: {0}")] CpiAccountsIndexOutOfBounds(usize), + #[error("Invalid CPI context account")] + InvalidCpiContextAccount, + #[error("Invalid sol pool pda account")] + InvalidSolPoolPdaAccount, + #[error("CpiAccounts slice starts with an invalid account. It should start with LightSystemProgram SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7.")] + InvalidCpiAccountsOffset, #[error(transparent)] Hasher(#[from] HasherError), #[error(transparent)] ZeroCopy(#[from] ZeroCopyError), #[error("Program error: {0:?}")] ProgramError(ProgramError), + #[error(transparent)] + AccountError(#[from] AccountError), } impl From for LightSdkError { @@ -111,6 +120,10 @@ impl From for LightSdkError { LightSdkTypesError::CpiAccountsIndexOutOfBounds(index) => { LightSdkError::CpiAccountsIndexOutOfBounds(index) } + LightSdkTypesError::InvalidCpiContextAccount => LightSdkError::InvalidCpiContextAccount, + LightSdkTypesError::InvalidSolPoolPdaAccount => LightSdkError::InvalidSolPoolPdaAccount, + LightSdkTypesError::AccountError(e) => LightSdkError::AccountError(e), + LightSdkTypesError::InvalidCpiAccountsOffset => LightSdkError::InvalidCpiAccountsOffset, } } } @@ -148,9 +161,13 @@ impl From for u32 { LightSdkError::MetaCloseAddressIsNone => 16028, LightSdkError::MetaCloseInputIsNone => 16029, LightSdkError::CpiAccountsIndexOutOfBounds(_) => 16031, + LightSdkError::InvalidCpiContextAccount => 16032, + LightSdkError::InvalidSolPoolPdaAccount => 16033, + LightSdkError::InvalidCpiAccountsOffset => 16034, LightSdkError::Hasher(e) => e.into(), LightSdkError::ZeroCopy(e) => e.into(), LightSdkError::ProgramError(e) => u64::from(e) as u32, + LightSdkError::AccountError(e) => e.into(), } } } diff --git a/sdk-libs/sdk-types/Cargo.toml b/sdk-libs/sdk-types/Cargo.toml index ee4f38eca2..18b3589b66 100644 --- a/sdk-libs/sdk-types/Cargo.toml +++ b/sdk-libs/sdk-types/Cargo.toml @@ -19,6 +19,7 @@ light-hasher = { workspace = true } light-compressed-account = { workspace = true } light-macros = { workspace = true } light-zero-copy = { workspace = true } +solana-msg = { workspace = true } # External dependencies borsh = { workspace = true } diff --git a/sdk-libs/sdk-types/src/constants.rs b/sdk-libs/sdk-types/src/constants.rs index 455cbdfd22..80e36ab550 100644 --- a/sdk-libs/sdk-types/src/constants.rs +++ b/sdk-libs/sdk-types/src/constants.rs @@ -8,6 +8,9 @@ pub const LIGHT_SYSTEM_PROGRAM_ID: [u8; 32] = pubkey_array!("SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"); pub const REGISTERED_PROGRAM_PDA: [u8; 32] = pubkey_array!("35hkDgaAKwMCaxRz2ocSZ6NaUrtKkyNqU6c4RV3tYJRh"); +pub const ACCOUNT_COMPRESSION_AUTHORITY_PDA: [u8; 32] = + pubkey_array!("HwXnGK3tPkkVY6P439H2p68AxpeuWXd5PcrAxFpbmfbA"); + /// ID of the light-compressed-token program. pub const C_TOKEN_PROGRAM_ID: [u8; 32] = pubkey_array!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"); @@ -31,3 +34,7 @@ pub const TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [2, 0, 0, 0, 0, 0, 0 pub const ADDRESS_TREE_V1: [u8; 32] = pubkey_array!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"); pub const ADDRESS_QUEUE_V1: [u8; 32] = pubkey_array!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"); + +pub const CPI_CONTEXT_ACCOUNT_DISCRIMINATOR: [u8; 8] = [22, 20, 149, 218, 74, 204, 128, 166]; + +pub const SOL_POOL_PDA: [u8; 32] = pubkey_array!("CHK57ywWSDncAoRu1F8QgwYJeXuAJyyBYT4LixLXvMZ1"); diff --git a/sdk-libs/sdk-types/src/cpi_accounts.rs b/sdk-libs/sdk-types/src/cpi_accounts.rs index c50874de69..7750603a3d 100644 --- a/sdk-libs/sdk-types/src/cpi_accounts.rs +++ b/sdk-libs/sdk-types/src/cpi_accounts.rs @@ -6,7 +6,7 @@ use light_account_checks::AccountInfoTrait; use crate::{ error::{LightSdkTypesError, Result}, - CpiSigner, + CpiSigner, CPI_CONTEXT_ACCOUNT_DISCRIMINATOR, LIGHT_SYSTEM_PROGRAM_ID, SOL_POOL_PDA, }; #[derive(Debug, Copy, Clone, AnchorSerialize, AnchorDeserialize)] @@ -77,6 +77,17 @@ impl<'a, T: AccountInfoTrait> CpiAccounts<'a, T> { } } + pub fn try_new(fee_payer: &'a T, accounts: &'a [T], cpi_signer: CpiSigner) -> Result { + if accounts[0].key() != LIGHT_SYSTEM_PROGRAM_ID { + return Err(LightSdkTypesError::InvalidCpiAccountsOffset); + } + Ok(Self { + fee_payer, + accounts, + config: CpiAccountsConfig::new(cpi_signer), + }) + } + pub fn new_with_config(fee_payer: &'a T, accounts: &'a [T], config: CpiAccountsConfig) -> Self { Self { fee_payer, @@ -85,6 +96,35 @@ impl<'a, T: AccountInfoTrait> CpiAccounts<'a, T> { } } + pub fn try_new_with_config( + fee_payer: &'a T, + accounts: &'a [T], + config: CpiAccountsConfig, + ) -> Result { + let res = Self { + fee_payer, + accounts, + config, + }; + if accounts[0].key() != LIGHT_SYSTEM_PROGRAM_ID { + return Err(LightSdkTypesError::InvalidCpiAccountsOffset); + } + if res.config().cpi_context { + let cpi_context = res.cpi_context()?; + let discriminator_bytes = &cpi_context.try_borrow_data()?[..8]; + if discriminator_bytes != CPI_CONTEXT_ACCOUNT_DISCRIMINATOR.as_slice() { + solana_msg::msg!("Invalid CPI context account: {:?}", cpi_context.pubkey()); + return Err(LightSdkTypesError::InvalidCpiContextAccount); + } + } + + if res.config().sol_pool_pda && res.sol_pool_pda()?.key() != SOL_POOL_PDA { + return Err(LightSdkTypesError::InvalidSolPoolPdaAccount); + } + + Ok(res) + } + pub fn fee_payer(&self) -> &'a T { self.fee_payer } @@ -160,7 +200,13 @@ impl<'a, T: AccountInfoTrait> CpiAccounts<'a, T> { } pub fn cpi_context(&self) -> Result<&'a T> { - let index = CompressionCpiAccountIndex::CpiContext as usize; + let mut index = CompressionCpiAccountIndex::CpiContext as usize; + if !self.config.sol_pool_pda { + index -= 1; + } + if !self.config.sol_compression_recipient { + index -= 1; + } self.accounts .get(index) .ok_or(LightSdkTypesError::CpiAccountsIndexOutOfBounds(index)) diff --git a/sdk-libs/sdk-types/src/error.rs b/sdk-libs/sdk-types/src/error.rs index 6ce017258a..700ab838aa 100644 --- a/sdk-libs/sdk-types/src/error.rs +++ b/sdk-libs/sdk-types/src/error.rs @@ -1,3 +1,4 @@ +use light_account_checks::error::AccountError; use light_hasher::HasherError; use thiserror::Error; @@ -27,6 +28,14 @@ pub enum LightSdkTypesError { FewerAccountsThanSystemAccounts, #[error("CPI accounts index out of bounds: {0}")] CpiAccountsIndexOutOfBounds(usize), + #[error("Invalid CPI context account")] + InvalidCpiContextAccount, + #[error("Invalid sol pool pda account")] + InvalidSolPoolPdaAccount, + #[error("CpigAccounts accounts slice starts with an invalid account. It should start with LightSystemProgram SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7.")] + InvalidCpiAccountsOffset, + #[error(transparent)] + AccountError(#[from] AccountError), #[error(transparent)] Hasher(#[from] HasherError), } @@ -45,6 +54,10 @@ impl From for u32 { LightSdkTypesError::MetaCloseInputIsNone => 14029, LightSdkTypesError::FewerAccountsThanSystemAccounts => 14017, LightSdkTypesError::CpiAccountsIndexOutOfBounds(_) => 14031, + LightSdkTypesError::InvalidCpiContextAccount => 14032, + LightSdkTypesError::InvalidSolPoolPdaAccount => 14033, + LightSdkTypesError::InvalidCpiAccountsOffset => 14034, + LightSdkTypesError::AccountError(e) => e.into(), LightSdkTypesError::Hasher(e) => e.into(), } } diff --git a/sdk-libs/sdk/src/cpi/accounts.rs b/sdk-libs/sdk/src/cpi/accounts.rs index d1bddcb78f..a35c084c7d 100644 --- a/sdk-libs/sdk/src/cpi/accounts.rs +++ b/sdk-libs/sdk/src/cpi/accounts.rs @@ -1,109 +1,162 @@ pub use light_sdk_types::CpiAccountsConfig; -use light_sdk_types::{CpiAccounts as GenericCpiAccounts, SYSTEM_ACCOUNTS_LEN}; +use light_sdk_types::{ + CpiAccounts as GenericCpiAccounts, ACCOUNT_COMPRESSION_AUTHORITY_PDA, + ACCOUNT_COMPRESSION_PROGRAM_ID, LIGHT_SYSTEM_PROGRAM_ID, NOOP_PROGRAM_ID, + REGISTERED_PROGRAM_PDA, SYSTEM_ACCOUNTS_LEN, +}; use crate::{ error::{LightSdkError, Result}, AccountInfo, AccountMeta, Pubkey, }; +#[derive(Debug)] +pub struct CpiInstructionConfig<'a, 'info> { + pub fee_payer: Pubkey, + pub cpi_signer: Pubkey, + pub invoking_program: Pubkey, + pub sol_pool_pda_pubkey: Option, + pub sol_compression_recipient_pubkey: Option, + pub cpi_context_pubkey: Option, + pub packed_accounts: &'a [AccountInfo<'info>], +} + pub type CpiAccounts<'c, 'info> = GenericCpiAccounts<'c, AccountInfo<'info>>; -pub fn to_account_metas(cpi_accounts: CpiAccounts<'_, '_>) -> Result> { +pub fn get_account_metas_from_config(config: CpiInstructionConfig<'_, '_>) -> Vec { let mut account_metas = Vec::with_capacity(1 + SYSTEM_ACCOUNTS_LEN); + + // 1. Fee payer (signer, writable) account_metas.push(AccountMeta { - pubkey: *cpi_accounts.fee_payer().key, + pubkey: config.fee_payer, is_signer: true, is_writable: true, }); + + // 2. Authority/CPI Signer (signer, readonly) account_metas.push(AccountMeta { - pubkey: *cpi_accounts.authority()?.key, + pubkey: config.cpi_signer, is_signer: true, is_writable: false, }); + // 3. Registered Program PDA (readonly) - hardcoded constant account_metas.push(AccountMeta { - pubkey: *cpi_accounts.registered_program_pda()?.key, + pubkey: Pubkey::from(REGISTERED_PROGRAM_PDA), is_signer: false, is_writable: false, }); + + // 4. Noop Program (readonly) - hardcoded constant account_metas.push(AccountMeta { - pubkey: *cpi_accounts.noop_program()?.key, + pubkey: Pubkey::from(NOOP_PROGRAM_ID), is_signer: false, is_writable: false, }); + + // 5. Account Compression Authority (readonly) - hardcoded constant account_metas.push(AccountMeta { - pubkey: *cpi_accounts.account_compression_authority()?.key, + pubkey: Pubkey::from(ACCOUNT_COMPRESSION_AUTHORITY_PDA), is_signer: false, is_writable: false, }); + + // 6. Account Compression Program (readonly) - hardcoded constant account_metas.push(AccountMeta { - pubkey: *cpi_accounts.account_compression_program()?.key, + pubkey: Pubkey::from(ACCOUNT_COMPRESSION_PROGRAM_ID), is_signer: false, is_writable: false, }); + + // 7. Invoking Program (readonly) account_metas.push(AccountMeta { - pubkey: *cpi_accounts.invoking_program()?.key, + pubkey: config.invoking_program, is_signer: false, is_writable: false, }); - let mut current_index = 7; - let anchor_none_account_meta = AccountMeta { - pubkey: *cpi_accounts.light_system_program()?.key, + + // 8. Light System Program (readonly) - reused for optional accounts + let create_light_system_meta = || AccountMeta { + pubkey: Pubkey::from(LIGHT_SYSTEM_PROGRAM_ID), is_signer: false, is_writable: false, }; - if !cpi_accounts.config().sol_pool_pda { - account_metas.push(anchor_none_account_meta.clone()); - } else { - let account = cpi_accounts.get_account_info(current_index)?; + + // 9. Sol Pool PDA (writable) OR Light System Program (readonly) + if let Some(sol_pool_pda_pubkey) = config.sol_pool_pda_pubkey { account_metas.push(AccountMeta { - pubkey: *account.key, + pubkey: sol_pool_pda_pubkey, is_signer: false, is_writable: true, }); - current_index += 1; + } else { + account_metas.push(create_light_system_meta()); } - if !cpi_accounts.config().sol_compression_recipient { - account_metas.push(anchor_none_account_meta.clone()); - } else { - let account = cpi_accounts.get_account_info(current_index)?; + // 10. Sol Compression Recipient (writable) OR Light System Program (readonly) + if let Some(sol_compression_recipient_pubkey) = config.sol_compression_recipient_pubkey { account_metas.push(AccountMeta { - pubkey: *account.key, + pubkey: sol_compression_recipient_pubkey, is_signer: false, is_writable: true, }); - current_index += 1; + } else { + account_metas.push(create_light_system_meta()); } - // System program + + // 11. System Program (readonly) - always default pubkey account_metas.push(AccountMeta { pubkey: Pubkey::default(), is_signer: false, is_writable: false, }); - current_index += 1; - if !cpi_accounts.config().cpi_context { - account_metas.push(anchor_none_account_meta); - } else { - let account = cpi_accounts.get_account_info(current_index)?; + // 12. CPI Context (writable) OR Light System Program (readonly) + if let Some(cpi_context_pubkey) = config.cpi_context_pubkey { account_metas.push(AccountMeta { - pubkey: *account.key, + pubkey: cpi_context_pubkey, is_signer: false, is_writable: true, }); - current_index += 1; + } else { + account_metas.push(create_light_system_meta()); } - let tree_accounts = cpi_accounts - .account_infos() - .get(current_index..) - .ok_or(LightSdkError::CpiAccountsIndexOutOfBounds(current_index))?; - tree_accounts.iter().for_each(|acc| { + + for acc in config.packed_accounts { account_metas.push(AccountMeta { pubkey: *acc.key, is_signer: false, is_writable: acc.is_writable, }); - }); - Ok(account_metas) + } + + account_metas +} + +impl<'a, 'info> TryFrom<&'a CpiAccounts<'a, 'info>> for CpiInstructionConfig<'a, 'info> { + type Error = LightSdkError; + + fn try_from(cpi_accounts: &'a CpiAccounts<'a, 'info>) -> Result { + Ok(CpiInstructionConfig { + fee_payer: *cpi_accounts.fee_payer().key, + cpi_signer: cpi_accounts.config().cpi_signer().into(), + invoking_program: cpi_accounts.config().cpi_signer.program_id.into(), + sol_pool_pda_pubkey: if cpi_accounts.config().sol_pool_pda { + Some(*cpi_accounts.sol_pool_pda()?.key) + } else { + None + }, + sol_compression_recipient_pubkey: if cpi_accounts.config().sol_compression_recipient { + Some(*cpi_accounts.decompression_recipient()?.key) + } else { + None + }, + cpi_context_pubkey: if cpi_accounts.config().cpi_context { + Some(*cpi_accounts.cpi_context()?.key) + } else { + None + }, + packed_accounts: cpi_accounts.tree_accounts().unwrap_or(&[]), + }) + } } diff --git a/sdk-libs/sdk/src/cpi/invoke.rs b/sdk-libs/sdk/src/cpi/invoke.rs index 2bd9d842d4..39796a8da0 100644 --- a/sdk-libs/sdk/src/cpi/invoke.rs +++ b/sdk-libs/sdk/src/cpi/invoke.rs @@ -10,10 +10,10 @@ use light_compressed_account::{ use light_sdk_types::constants::{CPI_AUTHORITY_PDA_SEED, LIGHT_SYSTEM_PROGRAM_ID}; use crate::{ - cpi::{to_account_metas, CpiAccounts}, + cpi::{get_account_metas_from_config, CpiAccounts, CpiInstructionConfig}, error::{LightSdkError, Result}, instruction::{account_info::CompressedAccountInfoTrait, ValidityProof}, - invoke_signed, AccountInfo, AccountMeta, AnchorSerialize, Instruction, + invoke_signed, AccountInfo, AnchorSerialize, Instruction, }; #[derive(Debug, Default, PartialEq, Clone)] @@ -50,7 +50,7 @@ impl CpiInputs { } } - pub fn invoke_light_system_program(self, cpi_accounts: CpiAccounts) -> Result<()> { + pub fn invoke_light_system_program(self, cpi_accounts: CpiAccounts<'_, '_>) -> Result<()> { let bump = cpi_accounts.bump(); let account_info_refs = cpi_accounts.to_account_infos(); let instruction = create_light_system_progam_instruction_invoke_cpi(self, cpi_accounts)?; @@ -61,7 +61,7 @@ impl CpiInputs { pub fn create_light_system_progam_instruction_invoke_cpi( cpi_inputs: CpiInputs, - cpi_accounts: CpiAccounts, + cpi_accounts: CpiAccounts<'_, '_>, ) -> Result { let owner = *cpi_accounts.invoking_program()?.key; let (input_compressed_accounts_with_merkle_context, output_compressed_accounts) = @@ -114,7 +114,10 @@ pub fn create_light_system_progam_instruction_invoke_cpi( data.extend_from_slice(&(inputs.len() as u32).to_le_bytes()); data.extend(inputs); - let account_metas: Vec = to_account_metas(cpi_accounts)?; + let config = CpiInstructionConfig::try_from(&cpi_accounts)?; + + let account_metas = get_account_metas_from_config(config); + Ok(Instruction { program_id: LIGHT_SYSTEM_PROGRAM_ID.into(), accounts: account_metas, @@ -125,7 +128,7 @@ pub fn create_light_system_progam_instruction_invoke_cpi( /// Invokes the light system program to verify and apply a zk-compressed state /// transition. Serializes CPI instruction data, configures necessary accounts, /// and executes the CPI. -pub fn verify_borsh(light_system_accounts: CpiAccounts, inputs: &T) -> Result<()> +pub fn verify_borsh(cpi_accounts: CpiAccounts, inputs: &T) -> Result<()> where T: AnchorSerialize, { @@ -135,11 +138,13 @@ where data.extend_from_slice(&light_compressed_account::discriminators::DISCRIMINATOR_INVOKE_CPI); data.extend_from_slice(&(inputs.len() as u32).to_le_bytes()); data.extend(inputs); - let account_info_refs = light_system_accounts.to_account_infos(); + let account_info_refs = cpi_accounts.to_account_infos(); let account_infos: Vec = account_info_refs.into_iter().cloned().collect(); - let bump = light_system_accounts.bump(); - let account_metas: Vec = to_account_metas(light_system_accounts)?; + let bump = cpi_accounts.bump(); + let config = CpiInstructionConfig::try_from(&cpi_accounts)?; + let account_metas = get_account_metas_from_config(config); + let instruction = Instruction { program_id: LIGHT_SYSTEM_PROGRAM_ID.into(), accounts: account_metas, diff --git a/sdk-libs/sdk/src/error.rs b/sdk-libs/sdk/src/error.rs index 51c6ae3e5b..3f797a71a6 100644 --- a/sdk-libs/sdk/src/error.rs +++ b/sdk-libs/sdk/src/error.rs @@ -1,3 +1,4 @@ +use light_account_checks::error::AccountError; use light_hasher::HasherError; use light_sdk_types::error::LightSdkTypesError; use light_zero_copy::errors::ZeroCopyError; @@ -69,12 +70,20 @@ pub enum LightSdkError { MetaCloseInputIsNone, #[error("CPI accounts index out of bounds: {0}")] CpiAccountsIndexOutOfBounds(usize), + #[error("Invalid CPI context account")] + InvalidCpiContextAccount, + #[error("Invalid SolPool PDA account")] + InvalidSolPoolPdaAccount, + #[error("CpigAccounts accounts slice starts with an invalid account. It should start with LightSystemProgram SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7.")] + InvalidCpiAccountsOffset, #[error(transparent)] Hasher(#[from] HasherError), #[error(transparent)] ZeroCopy(#[from] ZeroCopyError), #[error("Program error: {0}")] ProgramError(#[from] ProgramError), + #[error(transparent)] + AccountError(#[from] AccountError), } impl From for ProgramError { @@ -105,6 +114,10 @@ impl From for LightSdkError { LightSdkTypesError::CpiAccountsIndexOutOfBounds(index) => { LightSdkError::CpiAccountsIndexOutOfBounds(index) } + LightSdkTypesError::InvalidSolPoolPdaAccount => LightSdkError::InvalidSolPoolPdaAccount, + LightSdkTypesError::InvalidCpiContextAccount => LightSdkError::InvalidCpiContextAccount, + LightSdkTypesError::InvalidCpiAccountsOffset => LightSdkError::InvalidCpiAccountsOffset, + LightSdkTypesError::AccountError(e) => LightSdkError::AccountError(e), LightSdkTypesError::Hasher(e) => LightSdkError::Hasher(e), } } @@ -143,6 +156,10 @@ impl From for u32 { LightSdkError::MetaCloseAddressIsNone => 16028, LightSdkError::MetaCloseInputIsNone => 16029, LightSdkError::CpiAccountsIndexOutOfBounds(_) => 16031, + LightSdkError::InvalidCpiContextAccount => 16032, + LightSdkError::InvalidSolPoolPdaAccount => 16033, + LightSdkError::InvalidCpiAccountsOffset => 16034, + LightSdkError::AccountError(e) => e.into(), LightSdkError::Hasher(e) => e.into(), LightSdkError::ZeroCopy(e) => e.into(), LightSdkError::ProgramError(e) => u64::from(e) as u32,