account_compression/processor/
insert_nullifiers.rs1use anchor_lang::prelude::*;
2use light_batched_merkle_tree::queue::BatchedQueueAccount;
3use light_compressed_account::instruction_data::insert_into_queues::InsertNullifierInput;
4use num_bigint::BigUint;
5
6use crate::{
7 context::AcpAccount, errors::AccountCompressionErrorCode,
8 insert_into_queues::get_queue_and_tree_accounts, queue_from_bytes_zero_copy_mut, QueueAccount,
9};
10
11#[inline(always)]
12pub fn insert_nullifiers(
13 num_queues: u8,
14 tx_hash: [u8; 32],
15 nullifiers: &[InsertNullifierInput],
16 accounts: &mut [AcpAccount<'_, '_>],
17 current_slot: &u64,
18) -> Result<()> {
19 if nullifiers.is_empty() {
20 return Ok(());
21 }
22
23 let mut visited = Vec::with_capacity(num_queues as usize);
26 visited.push((nullifiers[0].tree_index, nullifiers[0].queue_index));
28
29 for nf in nullifiers.iter().skip(1) {
30 if visited.len() == num_queues as usize {
32 break;
33 }
34 if visited.iter().all(|&(_, q)| q != nf.queue_index) {
36 visited.push((nf.tree_index, nf.queue_index));
37 }
38 }
39
40 let mut inserted_nullifiers = 0;
41
42 for &(tree_index, queue_index) in &visited {
44 let (queue_account, merkle_tree_account) =
46 get_queue_and_tree_accounts(accounts, queue_index as usize, tree_index as usize)?;
47
48 match queue_account {
50 AcpAccount::OutputQueue(queue) => {
51 inserted_nullifiers += batched_nullifiers(
52 merkle_tree_account,
53 queue,
54 &tx_hash,
55 nullifiers,
56 queue_index,
57 tree_index,
58 current_slot,
59 )?;
60 anchor_lang::Result::Ok(())
61 }
62 AcpAccount::V1Queue(queue_account_info) => {
63 inserted_nullifiers += process_nullifiers_v1(
64 merkle_tree_account,
65 queue_account_info,
66 nullifiers,
67 queue_index,
68 tree_index,
69 )?;
70 Ok(())
71 }
72 AcpAccount::BatchedStateTree(_) => {
73 msg!("BatchedStateTree, expected output queue.");
74 Err(AccountCompressionErrorCode::InvalidAccount.into())
75 }
76 AcpAccount::StateTree(_) => {
77 msg!("StateTree, expected v1 nullifier queue.");
78 Err(AccountCompressionErrorCode::InvalidAccount.into())
79 }
80 AcpAccount::BatchedAddressTree(_) => {
81 msg!("BatchedAddressTree, expected v1 nullifier or output queue.");
82 Err(AccountCompressionErrorCode::InvalidAccount.into())
83 }
84 _ => Err(AccountCompressionErrorCode::InvalidAccount.into()),
85 }?;
86 }
87
88 if inserted_nullifiers != nullifiers.len() {
90 msg!("inserted_nullifiers {:?}", inserted_nullifiers);
91 msg!("nullifiers.len() {:?}", nullifiers.len());
92 return err!(AccountCompressionErrorCode::NotAllLeavesProcessed);
93 }
94
95 Ok(())
96}
97
98#[inline(always)]
106fn batched_nullifiers<'info>(
107 merkle_tree: &mut AcpAccount<'_, 'info>,
108 output_queue: &mut BatchedQueueAccount<'info>,
109 tx_hash: &[u8; 32],
110 nullifiers: &[InsertNullifierInput],
111 current_queue_index: u8,
112 current_tree_index: u8,
113 current_slot: &u64,
114) -> Result<usize> {
115 let nullifiers = nullifiers
117 .iter()
118 .filter(|x| x.queue_index == current_queue_index && x.tree_index == current_tree_index);
119 let merkle_tree = if let AcpAccount::BatchedStateTree(tree) = merkle_tree {
120 tree
121 } else {
122 return err!(AccountCompressionErrorCode::StateMerkleTreeAccountDiscriminatorMismatch);
123 };
124 output_queue
126 .check_is_associated(merkle_tree.pubkey())
127 .map_err(ProgramError::from)?;
128
129 let mut num_elements = 0;
130
131 for nullifier in nullifiers {
132 num_elements += 1;
133 let leaf_index = nullifier.leaf_index.into();
136 output_queue
137 .prove_inclusion_by_index_and_zero_out_leaf(
138 leaf_index,
139 &nullifier.account_hash,
140 nullifier.prove_by_index(),
141 )
142 .map_err(ProgramError::from)?;
143
144 merkle_tree
146 .insert_nullifier_into_queue(&nullifier.account_hash, leaf_index, tx_hash, current_slot)
147 .map_err(ProgramError::from)?;
148 }
149 Ok(num_elements)
150}
151
152fn process_nullifiers_v1<'info>(
158 merkle_tree: &mut AcpAccount<'_, 'info>,
159 nullifier_queue: &mut AccountInfo<'info>,
160 nullifiers: &[InsertNullifierInput],
161 current_queue_index: u8,
162 current_tree_index: u8,
163) -> Result<usize> {
164 let nullifiers = nullifiers
165 .iter()
166 .filter(|x| x.queue_index == current_queue_index && x.tree_index == current_tree_index);
167 let (merkle_pubkey, merkle_tree) = if let AcpAccount::StateTree(tree) = merkle_tree {
168 tree
169 } else {
170 return err!(AccountCompressionErrorCode::StateMerkleTreeAccountDiscriminatorMismatch);
171 };
172
173 {
174 let queue_data = nullifier_queue.try_borrow_data()?;
175 let queue = bytemuck::from_bytes::<QueueAccount>(&queue_data[8..QueueAccount::LEN]);
177 if queue.metadata.associated_merkle_tree != *merkle_pubkey {
179 msg!(
180 "Queue account {:?} is not associated with Merkle tree {:?}",
181 nullifier_queue.key(),
182 *merkle_pubkey
183 );
184 return err!(AccountCompressionErrorCode::MerkleTreeAndQueueNotAssociated);
185 }
186 }
187 let mut num_elements = 0;
188 let sequence_number = merkle_tree.sequence_number();
191 let mut queue = nullifier_queue.try_borrow_mut_data()?;
192 let mut queue = unsafe { queue_from_bytes_zero_copy_mut(&mut queue).unwrap() };
193 #[cfg(feature = "bench-sbf")]
194 light_heap::bench_sbf_start!("acp_insert_nf_into_queue");
195 for nullifier in nullifiers {
196 if nullifier.prove_by_index() {
197 return Err(AccountCompressionErrorCode::V1AccountMarkedAsProofByIndex.into());
198 }
199 num_elements += 1;
200 let element = BigUint::from_bytes_be(nullifier.account_hash.as_slice());
201 queue
202 .insert(&element, sequence_number)
203 .map_err(ProgramError::from)?;
204 }
205 #[cfg(feature = "bench-sbf")]
206 light_heap::bench_sbf_end!("acp_insert_nf_into_queue");
207 Ok(num_elements)
208}