light_system_program/invoke/
nullify_state.rs

1use account_compression::utils::constants::CPI_AUTHORITY_PDA_SEED;
2use anchor_lang::{prelude::*, solana_program::pubkey::Pubkey, Bumps, InstructionData};
3use light_macros::heap_neutral;
4
5use crate::{
6    constants::CPI_AUTHORITY_PDA_BUMP,
7    invoke::InstructionDataInvoke,
8    invoke_cpi::verify_signer::check_program_owner_state_merkle_tree,
9    sdk::accounts::{InvokeAccounts, SignerAccounts},
10};
11
12/// 1. Checks that if nullifier queue has program_owner it invoking_program is
13///    program_owner.
14/// 2. Inserts nullifiers into the queue.
15#[heap_neutral]
16pub fn insert_nullifiers<
17    'a,
18    'b,
19    'c: 'info,
20    'info,
21    A: InvokeAccounts<'info> + SignerAccounts<'info> + Bumps,
22>(
23    inputs: &'a InstructionDataInvoke,
24    ctx: &'a Context<'a, 'b, 'c, 'info, A>,
25    nullifiers: &'a [[u8; 32]],
26    invoking_program: &Option<Pubkey>,
27) -> Result<Option<(u8, u64)>> {
28    light_heap::bench_sbf_start!("cpda_insert_nullifiers_prep_accs");
29    let mut account_infos = vec![
30        ctx.accounts.get_fee_payer().to_account_info(),
31        ctx.accounts
32            .get_account_compression_authority()
33            .to_account_info(),
34        ctx.accounts.get_registered_program_pda().to_account_info(),
35        ctx.accounts.get_system_program().to_account_info(),
36    ];
37    let mut accounts = vec![
38        AccountMeta {
39            pubkey: account_infos[0].key(),
40            is_signer: true,
41            is_writable: true,
42        },
43        AccountMeta::new_readonly(account_infos[1].key(), true),
44        AccountMeta::new_readonly(account_infos[2].key(), false),
45        AccountMeta::new_readonly(account_infos[3].key(), false),
46    ];
47    // If the transaction contains at least one input compressed account a
48    // network fee is paid. This network fee is paid in addition to the address
49    // network fee. The network fee is paid once per transaction, defined in the
50    // state Merkle tree and transferred to the nullifier queue because the
51    // nullifier queue is mutable. The network fee field in the queue is not
52    // used.
53    let mut network_fee_bundle = None;
54    for account in inputs.input_compressed_accounts_with_merkle_context.iter() {
55        let account_info =
56            &ctx.remaining_accounts[account.merkle_context.nullifier_queue_pubkey_index as usize];
57        accounts.push(AccountMeta {
58            pubkey: account_info.key(),
59            is_signer: false,
60            is_writable: true,
61        });
62        account_infos.push(account_info.clone());
63        let (_, network_fee, _) = check_program_owner_state_merkle_tree(
64            &ctx.remaining_accounts[account.merkle_context.merkle_tree_pubkey_index as usize],
65            invoking_program,
66        )?;
67        if network_fee_bundle.is_none() && network_fee.is_some() {
68            network_fee_bundle = Some((
69                account.merkle_context.nullifier_queue_pubkey_index,
70                network_fee.unwrap(),
71            ));
72        }
73        let account_info =
74            &ctx.remaining_accounts[account.merkle_context.merkle_tree_pubkey_index as usize];
75        accounts.push(AccountMeta {
76            pubkey: account_info.key(),
77            is_signer: false,
78            is_writable: false,
79        });
80        account_infos.push(account_info.clone());
81    }
82
83    light_heap::bench_sbf_end!("cpda_insert_nullifiers_prep_accs");
84    light_heap::bench_sbf_start!("cpda_instruction_data");
85
86    let instruction_data = account_compression::instruction::InsertIntoNullifierQueues {
87        nullifiers: nullifiers.to_vec(),
88    };
89
90    let data = instruction_data.data();
91    light_heap::bench_sbf_end!("cpda_instruction_data");
92    let bump = &[CPI_AUTHORITY_PDA_BUMP];
93    let seeds = &[&[CPI_AUTHORITY_PDA_SEED, bump][..]];
94    let instruction = anchor_lang::solana_program::instruction::Instruction {
95        program_id: account_compression::ID,
96        accounts,
97        data,
98    };
99    anchor_lang::solana_program::program::invoke_signed(
100        &instruction,
101        account_infos.as_slice(),
102        seeds,
103    )?;
104    Ok(network_fee_bundle)
105}