1use std::ops::{Deref, DerefMut};
2
3use aligned_sized::aligned_sized;
4use light_account_checks::{
5 checks::{check_account_info, set_discriminator},
6 discriminator::{Discriminator, DISCRIMINATOR_LEN},
7 AccountInfoTrait,
8};
9use light_compressed_account::{
10 hash_to_bn254_field_size_be, pubkey::Pubkey, QueueType, OUTPUT_STATE_QUEUE_TYPE_V2,
11};
12use light_merkle_tree_metadata::{errors::MerkleTreeMetadataError, queue::QueueMetadata};
13use light_zero_copy::{errors::ZeroCopyError, vec::ZeroCopyVecU64};
14use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref};
15
16use super::batch::BatchState;
18use crate::{
19 batch::Batch,
20 constants::{ACCOUNT_COMPRESSION_PROGRAM_ID, NUM_BATCHES},
21 errors::BatchedMerkleTreeError,
22 queue_batch_metadata::QueueBatches,
23 BorshDeserialize, BorshSerialize,
24};
25
26#[repr(C)]
27#[derive(
28 BorshDeserialize,
29 BorshSerialize,
30 Debug,
31 PartialEq,
32 Default,
33 Clone,
34 Copy,
35 KnownLayout,
36 Immutable,
37 FromBytes,
38 IntoBytes,
39)]
40#[aligned_sized(anchor)]
41pub struct BatchedQueueMetadata {
42 pub metadata: QueueMetadata,
43 pub batch_metadata: QueueBatches,
44 pub tree_capacity: u64,
47 pub hashed_merkle_tree_pubkey: [u8; 32],
48 pub hashed_queue_pubkey: [u8; 32],
49}
50
51impl BatchedQueueMetadata {
52 pub fn init(
53 &mut self,
54 meta_data: QueueMetadata,
55 batch_size: u64,
56 zkp_batch_size: u64,
57 bloom_filter_capacity: u64,
58 num_iters: u64,
59 queue_pubkey: &Pubkey,
60 ) -> Result<(), BatchedMerkleTreeError> {
61 self.metadata = meta_data;
62 self.batch_metadata.init(batch_size, zkp_batch_size)?;
63 self.batch_metadata.bloom_filter_capacity = bloom_filter_capacity;
64 for (i, batches) in self.batch_metadata.batches.iter_mut().enumerate() {
65 *batches = Batch::new(
66 num_iters,
67 bloom_filter_capacity,
68 batch_size,
69 zkp_batch_size,
70 batch_size * (i as u64),
71 );
72 }
73
74 self.hashed_merkle_tree_pubkey =
80 hash_to_bn254_field_size_be(&meta_data.associated_merkle_tree.to_bytes());
81 self.hashed_queue_pubkey = hash_to_bn254_field_size_be(&queue_pubkey.to_bytes());
82 Ok(())
83 }
84}
85
86#[derive(Debug, PartialEq)]
128pub struct BatchedQueueAccount<'a> {
129 pubkey: Pubkey,
130 metadata: Ref<&'a mut [u8], BatchedQueueMetadata>,
131 pub value_vecs: [ZeroCopyVecU64<'a, [u8; 32]>; 2],
132 pub hash_chain_stores: [ZeroCopyVecU64<'a, [u8; 32]>; 2],
133}
134
135impl Discriminator for BatchedQueueAccount<'_> {
136 const LIGHT_DISCRIMINATOR: [u8; 8] = *b"queueacc";
137 const LIGHT_DISCRIMINATOR_SLICE: &'static [u8] = b"queueacc";
138}
139
140impl<'a> BatchedQueueAccount<'a> {
141 pub fn output_from_account_info<A: AccountInfoTrait>(
148 account_info: &A,
149 ) -> Result<BatchedQueueAccount<'a>, BatchedMerkleTreeError> {
150 Self::from_account_info::<OUTPUT_STATE_QUEUE_TYPE_V2, A>(
151 &Pubkey::new_from_array(ACCOUNT_COMPRESSION_PROGRAM_ID),
152 account_info,
153 )
154 }
155
156 fn from_account_info<const QUEUE_TYPE: u64, A: AccountInfoTrait>(
160 program_id: &Pubkey,
161 account_info: &A,
162 ) -> Result<BatchedQueueAccount<'a>, BatchedMerkleTreeError> {
163 check_account_info::<Self, A>(&program_id.to_bytes(), account_info)?;
164 let account_data = &mut account_info.try_borrow_mut_data()?;
165 let account_data: &'a mut [u8] = unsafe {
167 std::slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len())
168 };
169 Self::from_bytes::<QUEUE_TYPE>(account_data, account_info.key().into())
170 }
171
172 #[cfg(not(target_os = "solana"))]
176 pub fn output_from_bytes(
177 account_data: &'a mut [u8],
178 ) -> Result<BatchedQueueAccount<'a>, BatchedMerkleTreeError> {
179 light_account_checks::checks::check_discriminator::<BatchedQueueAccount>(account_data)?;
180 Self::from_bytes::<OUTPUT_STATE_QUEUE_TYPE_V2>(account_data, Pubkey::default())
181 }
182
183 fn from_bytes<const QUEUE_TYPE: u64>(
184 account_data: &'a mut [u8],
185 pubkey: Pubkey,
186 ) -> Result<BatchedQueueAccount<'a>, BatchedMerkleTreeError> {
187 let (_discriminator, account_data) = account_data.split_at_mut(DISCRIMINATOR_LEN);
188 let (metadata, account_data) =
189 Ref::<&'a mut [u8], BatchedQueueMetadata>::from_prefix(account_data)
190 .map_err(ZeroCopyError::from)?;
191
192 if metadata.metadata.queue_type != QUEUE_TYPE {
193 return Err(MerkleTreeMetadataError::InvalidQueueType.into());
194 }
195
196 let (value_vec0, account_data) = ZeroCopyVecU64::from_bytes_at(account_data)?;
197 let (value_vec1, account_data) = ZeroCopyVecU64::from_bytes_at(account_data)?;
198
199 let (hash_chain_store0, account_data) = ZeroCopyVecU64::from_bytes_at(account_data)?;
200 let hash_chain_store1 = ZeroCopyVecU64::from_bytes(account_data)?;
201
202 Ok(BatchedQueueAccount {
203 pubkey,
204 metadata,
205 value_vecs: [value_vec0, value_vec1],
206 hash_chain_stores: [hash_chain_store0, hash_chain_store1],
207 })
208 }
209
210 pub fn init(
211 account_data: &'a mut [u8],
212 metadata: QueueMetadata,
213 output_queue_batch_size: u64,
214 output_queue_zkp_batch_size: u64,
215 num_iters: u64,
216 bloom_filter_capacity: u64,
217 pubkey: Pubkey,
218 ) -> Result<BatchedQueueAccount<'a>, BatchedMerkleTreeError> {
219 let account_data_len = account_data.len();
220 let (discriminator, account_data) = account_data.split_at_mut(DISCRIMINATOR_LEN);
221 set_discriminator::<Self>(discriminator)?;
222
223 let (mut account_metadata, account_data) =
224 Ref::<&mut [u8], BatchedQueueMetadata>::from_prefix(account_data)
225 .map_err(ZeroCopyError::from)?;
226
227 account_metadata.init(
228 metadata,
229 output_queue_batch_size,
230 output_queue_zkp_batch_size,
231 bloom_filter_capacity,
232 num_iters,
233 &pubkey,
234 )?;
235
236 if account_data_len
237 != account_metadata
238 .batch_metadata
239 .queue_account_size(account_metadata.metadata.queue_type)?
240 {
241 #[cfg(feature = "solana")]
242 solana_msg::msg!("account_data.len() {:?}", account_data_len);
243 #[cfg(feature = "solana")]
244 solana_msg::msg!(
245 "queue_account_size {:?}",
246 account_metadata
247 .batch_metadata
248 .queue_account_size(account_metadata.metadata.queue_type)?
249 );
250 return Err(ZeroCopyError::Size.into());
251 }
252
253 let value_vec_capacity = account_metadata.batch_metadata.batch_size;
254 let hash_chain_capacity = account_metadata.batch_metadata.get_num_zkp_batches();
255 let (value_vecs_0, account_data) =
256 ZeroCopyVecU64::new_at(value_vec_capacity, account_data)?;
257 let (value_vecs_1, account_data) =
258 ZeroCopyVecU64::new_at(value_vec_capacity, account_data)?;
259 let (hash_chain_0, account_data) =
260 ZeroCopyVecU64::new_at(hash_chain_capacity, account_data)?;
261 let hash_chain_1 = ZeroCopyVecU64::new(hash_chain_capacity, account_data)?;
262 Ok(BatchedQueueAccount {
263 pubkey,
264 metadata: account_metadata,
265 value_vecs: [value_vecs_0, value_vecs_1],
266 hash_chain_stores: [hash_chain_0, hash_chain_1],
267 })
268 }
269
270 pub fn insert_into_current_batch(
275 &mut self,
276 hash_chain_value: &[u8; 32],
277 current_slot: &u64,
278 ) -> Result<(), BatchedMerkleTreeError> {
279 let current_index = self.batch_metadata.next_index;
280
281 insert_into_current_queue_batch(
282 self.metadata.metadata.queue_type,
283 &mut self.metadata.batch_metadata,
284 &mut self.value_vecs,
285 &mut [],
286 &mut self.hash_chain_stores,
287 hash_chain_value,
288 None,
289 Some(current_index),
290 current_slot,
291 )?;
292 self.metadata.batch_metadata.next_index += 1;
293
294 Ok(())
295 }
296
297 pub fn prove_inclusion_by_index(
307 &mut self,
308 leaf_index: u64,
309 hash_chain_value: &[u8; 32],
310 ) -> Result<bool, BatchedMerkleTreeError> {
311 self.internal_prove_inclusion_by_index::<false>(leaf_index, hash_chain_value)
312 }
313
314 fn internal_prove_inclusion_by_index<const ZERO_OUT: bool>(
315 &mut self,
316 leaf_index: u64,
317 hash_chain_value: &[u8; 32],
318 ) -> Result<bool, BatchedMerkleTreeError> {
319 if leaf_index >= self.batch_metadata.next_index {
320 return Err(BatchedMerkleTreeError::InvalidIndex);
321 }
322 for (batch_index, batch) in self.batch_metadata.batches.iter().enumerate() {
323 if batch.leaf_index_exists(leaf_index) {
324 let index = batch.get_value_index_in_batch(leaf_index)?;
325 let element = self.value_vecs[batch_index]
326 .get_mut(index as usize)
327 .ok_or(BatchedMerkleTreeError::InclusionProofByIndexFailed)?;
328
329 if *element == *hash_chain_value {
330 if ZERO_OUT {
331 *element = [0u8; 32];
332 }
333 return Ok(true);
334 } else {
335 #[cfg(target_os = "solana")]
336 {
337 solana_msg::msg!(
338 "Index found but value doesn't match leaf_index {} compressed account hash: {:?} expected compressed account hash {:?}. (If the expected element is [0u8;32] it was already spent. Other possibly causes, data hash, discriminator, leaf index, or Merkle tree mismatch.)",
339 leaf_index,
340 hash_chain_value,*element
341 );
342 }
343 return Err(BatchedMerkleTreeError::InclusionProofByIndexFailed);
344 }
345 }
346 }
347 Ok(false)
348 }
349
350 pub fn prove_inclusion_by_index_and_zero_out_leaf(
353 &mut self,
354 leaf_index: u64,
355 hash_chain_value: &[u8; 32],
356 prove_by_index: bool,
357 ) -> Result<(), BatchedMerkleTreeError> {
358 let is_proven_by_index =
360 self.internal_prove_inclusion_by_index::<true>(leaf_index, hash_chain_value)?;
361 if is_proven_by_index {
362 return Ok(());
363 }
364 if prove_by_index {
366 #[cfg(target_os = "solana")]
367 {
368 solana_msg::msg!(
369 "leaf_index {} compressed account hash: {:?}. Possibly causes, leaf index, or Merkle tree mismatch.)",
370 leaf_index,
371 hash_chain_value
372 );
373 }
374 Err(BatchedMerkleTreeError::InclusionProofByIndexFailed)
375 } else {
376 Ok(())
377 }
378 }
379
380 pub fn get_metadata(&self) -> &BatchedQueueMetadata {
381 &self.metadata
382 }
383
384 pub fn get_metadata_mut(&mut self) -> &mut BatchedQueueMetadata {
385 &mut self.metadata
386 }
387
388 pub fn get_num_inserted_in_current_batch(&self) -> u64 {
391 let current_batch = self.batch_metadata.currently_processing_batch_index as usize;
392 if self.batch_metadata.batches[current_batch].get_state() == BatchState::Inserted {
393 0
394 } else {
395 self.batch_metadata.batches[current_batch].get_num_inserted_elements()
396 }
397 }
398
399 pub fn is_associated(&self, pubkey: &Pubkey) -> bool {
401 self.metadata.metadata.associated_merkle_tree == *pubkey
402 }
403
404 pub fn check_is_associated(&self, pubkey: &Pubkey) -> Result<(), BatchedMerkleTreeError> {
406 if !self.is_associated(pubkey) {
407 return Err(MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into());
408 }
409 Ok(())
410 }
411
412 pub fn tree_is_full(&self) -> bool {
414 self.tree_capacity == self.batch_metadata.next_index
415 }
416
417 pub fn check_tree_is_full(&self) -> Result<(), BatchedMerkleTreeError> {
419 if self.tree_is_full() {
420 return Err(BatchedMerkleTreeError::TreeIsFull);
421 }
422 Ok(())
423 }
424
425 pub fn pubkey(&self) -> &Pubkey {
426 &self.pubkey
427 }
428}
429
430impl Deref for BatchedQueueAccount<'_> {
431 type Target = BatchedQueueMetadata;
432
433 fn deref(&self) -> &Self::Target {
434 &self.metadata
435 }
436}
437
438impl DerefMut for BatchedQueueAccount<'_> {
439 fn deref_mut(&mut self) -> &mut Self::Target {
440 &mut self.metadata
441 }
442}
443
444#[allow(clippy::too_many_arguments)]
454#[allow(clippy::type_complexity)]
455pub(crate) fn insert_into_current_queue_batch(
456 queue_type: u64,
457 batch_metadata: &mut QueueBatches,
458 value_vecs: &mut [ZeroCopyVecU64<[u8; 32]>],
459 bloom_filter_stores: &mut [&mut [u8]],
460 hash_chain_stores: &mut [ZeroCopyVecU64<[u8; 32]>],
461 hash_chain_value: &[u8; 32],
462 bloom_filter_value: Option<&[u8; 32]>,
463 current_index: Option<u64>,
464 current_slot: &u64,
465) -> Result<(), BatchedMerkleTreeError> {
466 let batch_index = batch_metadata.currently_processing_batch_index as usize;
467 let mut value_store = value_vecs.get_mut(batch_index);
468 let mut hash_chain_stores = hash_chain_stores.get_mut(batch_index);
469 let current_batch = batch_metadata.get_current_batch_mut();
470 {
473 let clear_batch = current_batch.get_state() == BatchState::Inserted;
474 if current_batch.get_state() == BatchState::Fill {
475 } else if clear_batch {
477 if queue_type != QueueType::OutputStateV2 as u64
481 && !current_batch.bloom_filter_is_zeroed()
482 {
483 return Err(BatchedMerkleTreeError::BloomFilterNotZeroed);
484 }
485 if let Some(value_store) = value_store.as_mut() {
486 (*value_store).clear();
487 }
488 if let Some(hash_chain_stores) = hash_chain_stores.as_mut() {
489 (*hash_chain_stores).clear();
490 }
491 current_batch.advance_state_to_fill(current_index)?;
496 } else {
497 #[cfg(feature = "solana")]
499 for batch in batch_metadata.batches.iter() {
500 solana_msg::msg!("batch {:?}", batch);
501 }
502 return Err(BatchedMerkleTreeError::BatchNotReady);
503 }
504 }
505
506 let queue_type = QueueType::from(queue_type);
508 match queue_type {
509 QueueType::InputStateV2 | QueueType::AddressV2 => current_batch.insert(
510 bloom_filter_value.unwrap(),
511 hash_chain_value,
512 bloom_filter_stores,
513 hash_chain_stores.as_mut().unwrap(),
514 batch_index,
515 current_slot,
516 ),
517 QueueType::OutputStateV2 => current_batch.store_and_hash_value(
518 hash_chain_value,
519 value_store.unwrap(),
520 hash_chain_stores.unwrap(),
521 current_slot,
522 ),
523 _ => Err(MerkleTreeMetadataError::InvalidQueueType.into()),
524 }?;
525
526 batch_metadata.increment_currently_processing_batch_index_if_full();
528
529 Ok(())
530}
531
532#[inline(always)]
533pub(crate) fn deserialize_bloom_filter_stores(
534 bloom_filter_capacity: usize,
535 account_data: &mut [u8],
536) -> ([&mut [u8]; 2], &mut [u8]) {
537 let (slice_1, account_data) = account_data.split_at_mut(bloom_filter_capacity);
538 let (slice_2, account_data) = account_data.split_at_mut(bloom_filter_capacity);
539 ([slice_1, slice_2], account_data)
540}
541
542pub fn get_output_queue_account_size(batch_size: u64, zkp_batch_size: u64) -> usize {
543 let metadata = BatchedQueueMetadata {
544 metadata: QueueMetadata::default(),
545 batch_metadata: QueueBatches {
546 num_batches: NUM_BATCHES as u64,
547 batch_size,
548 zkp_batch_size,
549 ..Default::default()
550 },
551 ..Default::default()
552 };
553 metadata
554 .batch_metadata
555 .queue_account_size(QueueType::OutputStateV2 as u64)
556 .unwrap()
557}
558
559#[cfg(feature = "test-only")]
560pub mod test_utils {
561 use super::*;
562 use crate::{
563 constants::{NUM_BATCHES, TEST_DEFAULT_BATCH_SIZE, TEST_DEFAULT_ZKP_BATCH_SIZE},
564 initialize_state_tree::InitStateTreeAccountsInstructionData,
565 };
566 pub fn get_output_queue_account_size_default() -> usize {
567 let batch_metadata = BatchedQueueMetadata {
568 metadata: QueueMetadata::default(),
569 batch_metadata: QueueBatches {
570 num_batches: NUM_BATCHES as u64,
571 batch_size: TEST_DEFAULT_BATCH_SIZE,
572 zkp_batch_size: TEST_DEFAULT_ZKP_BATCH_SIZE,
573 ..Default::default()
574 },
575 ..Default::default()
576 };
577 batch_metadata
578 .batch_metadata
579 .queue_account_size(QueueType::OutputStateV2 as u64)
580 .unwrap()
581 }
582
583 pub fn get_output_queue_account_size_from_params(
584 ix_data: InitStateTreeAccountsInstructionData,
585 ) -> usize {
586 let metadata = BatchedQueueMetadata {
587 metadata: QueueMetadata::default(),
588 batch_metadata: QueueBatches {
589 num_batches: NUM_BATCHES as u64,
590 batch_size: ix_data.output_queue_batch_size,
591 zkp_batch_size: ix_data.output_queue_zkp_batch_size,
592 ..Default::default()
593 },
594 ..Default::default()
595 };
596 metadata
597 .batch_metadata
598 .queue_account_size(QueueType::OutputStateV2 as u64)
599 .unwrap()
600 }
601
602 #[allow(clippy::too_many_arguments)]
603 pub fn assert_queue_inited(
604 batch_metadata: QueueBatches,
605 ref_batch_metadata: QueueBatches,
606 queue_type: u64,
607 value_vecs: &mut [ZeroCopyVecU64<'_, [u8; 32]>],
608 ) {
609 assert_eq!(
610 batch_metadata, ref_batch_metadata,
611 "batch_metadata mismatch"
612 );
613
614 if queue_type == QueueType::OutputStateV2 as u64 {
615 assert_eq!(value_vecs.len(), NUM_BATCHES, "value_vecs mismatch");
616 } else {
617 assert_eq!(value_vecs.len(), 0, "value_vecs mismatch");
618 }
619 for vec in value_vecs.iter() {
620 assert_eq!(
621 vec.capacity(),
622 batch_metadata.batch_size as usize,
623 "batch_size mismatch"
624 );
625 assert_eq!(vec.len(), 0, "batch_size mismatch");
626 }
627 }
628 pub fn assert_queue_zero_copy_inited(
629 account_data: &mut [u8],
630 ref_account: BatchedQueueMetadata,
631 ) {
632 let mut account = BatchedQueueAccount::output_from_bytes(account_data)
633 .expect("from_bytes_unchecked_mut failed");
634 let batch_metadata = account.batch_metadata;
635 let queue_type = account.metadata.metadata.queue_type;
636 assert_eq!(
637 account.metadata.metadata, ref_account.metadata,
638 "metadata mismatch"
639 );
640 assert_queue_inited(
641 batch_metadata,
642 ref_account.batch_metadata,
643 queue_type,
644 &mut account.value_vecs,
645 );
646 }
647}
648
649#[cfg(feature = "test-only")]
650#[test]
651fn test_from_bytes_invalid_tree_type() {
652 use crate::queue::test_utils::get_output_queue_account_size_default;
653 let mut account_data = vec![0u8; get_output_queue_account_size_default()];
654 let account = BatchedQueueAccount::from_bytes::<6>(&mut account_data, Pubkey::default());
655 assert_eq!(
656 account.unwrap_err(),
657 MerkleTreeMetadataError::InvalidQueueType.into()
658 );
659}
660
661#[test]
662fn test_batched_queue_metadata_init() {
663 let mut metadata = BatchedQueueMetadata::default();
664 let mt_pubkey = Pubkey::new_unique();
665 let queue_metadata = QueueMetadata {
666 associated_merkle_tree: mt_pubkey,
667 ..Default::default()
668 };
669 let batch_size = 4;
670 let zkp_batch_size = 2;
671 let bloom_filter_capacity = 10;
672 let num_iters = 5;
673 let queue_pubkey = Pubkey::new_unique();
674
675 let result = metadata.init(
676 queue_metadata,
677 batch_size,
678 zkp_batch_size,
679 bloom_filter_capacity,
680 num_iters,
681 &queue_pubkey,
682 );
683
684 assert!(result.is_ok());
685 assert_eq!(metadata.metadata, queue_metadata);
686 assert_eq!(
687 metadata.batch_metadata.bloom_filter_capacity,
688 bloom_filter_capacity
689 );
690 for (i, batch) in metadata.batch_metadata.batches.iter().enumerate() {
691 assert_eq!(batch.num_iters, num_iters);
692 assert_eq!(batch.bloom_filter_capacity, bloom_filter_capacity);
693 assert_eq!(batch.batch_size, batch_size);
694 assert_eq!(batch.zkp_batch_size, zkp_batch_size);
695 assert_eq!(batch.start_index, batch_size * (i as u64));
696 }
697 let hashed_merkle_tree_pubkey = hash_to_bn254_field_size_be(&mt_pubkey.to_bytes());
698 let hashed_queue_pubkey = hash_to_bn254_field_size_be(&queue_pubkey.to_bytes());
699 assert_eq!(
700 metadata.hashed_merkle_tree_pubkey,
701 hashed_merkle_tree_pubkey
702 );
703 assert_eq!(metadata.hashed_queue_pubkey, hashed_queue_pubkey);
704}
705
706#[test]
707fn test_check_is_associated() {
708 let mut account_data = vec![0u8; 1000];
709 let mut queue_metadata = QueueMetadata::default();
710 let associated_merkle_tree = Pubkey::new_unique();
711 queue_metadata.associated_merkle_tree = associated_merkle_tree;
712 queue_metadata.queue_type = QueueType::OutputStateV2 as u64;
713 let batch_size = 4;
714 let zkp_batch_size = 2;
715 let bloom_filter_capacity = 0;
716 let num_iters = 0;
717 let account = BatchedQueueAccount::init(
718 &mut account_data,
719 queue_metadata,
720 batch_size,
721 zkp_batch_size,
722 num_iters,
723 bloom_filter_capacity,
724 Pubkey::new_unique(),
725 )
726 .unwrap();
727 {
729 account
730 .check_is_associated(&associated_merkle_tree)
731 .unwrap();
732 assert!(account.is_associated(&associated_merkle_tree));
733 }
734 {
736 let other_merkle_tree = Pubkey::new_unique();
737 assert_eq!(
738 account.check_is_associated(&other_merkle_tree),
739 Err(MerkleTreeMetadataError::MerkleTreeAndQueueNotAssociated.into())
740 );
741 assert!(!account.is_associated(&other_merkle_tree));
742 }
743}
744
745#[test]
746fn test_pubkey() {
747 let mut account_data = vec![0u8; 1000];
748 let mut queue_metadata = QueueMetadata::default();
749 let associated_merkle_tree = Pubkey::new_unique();
750 queue_metadata.associated_merkle_tree = associated_merkle_tree;
751 queue_metadata.queue_type = QueueType::OutputStateV2 as u64;
752 let batch_size = 4;
753 let zkp_batch_size = 2;
754 let bloom_filter_capacity = 0;
755 let num_iters = 0;
756 let pubkey = Pubkey::new_unique();
757 let account = BatchedQueueAccount::init(
758 &mut account_data,
759 queue_metadata,
760 batch_size,
761 zkp_batch_size,
762 num_iters,
763 bloom_filter_capacity,
764 pubkey,
765 )
766 .unwrap();
767 assert_eq!(*account.pubkey(), pubkey);
768}