1use core::ops::{Deref, DerefMut};
2
3use light_zero_copy::{errors::ZeroCopyError, slice::ZeroCopySliceBorsh, traits::ZeroCopyAt};
4use zerocopy::{
5 little_endian::{U16, U32, U64},
6 FromBytes, Immutable, IntoBytes, KnownLayout, Ref, Unaligned,
7};
8
9use super::{
10 compressed_proof::CompressedProof,
11 cpi_context::CompressedCpiContext,
12 data::{NewAddressParamsAssignedPacked, PackedReadOnlyAddress},
13 traits::{AccountOptions, InputAccount, InstructionData, NewAddress, OutputAccount},
14 with_readonly::ZInstructionDataInvokeCpiWithReadOnlyMeta,
15 zero_copy::{
16 ZNewAddressParamsAssignedPacked, ZPackedMerkleContext, ZPackedReadOnlyAddress,
17 ZPackedReadOnlyCompressedAccount,
18 },
19};
20use crate::{
21 compressed_account::{
22 hash_with_hashed_values, CompressedAccount, CompressedAccountData, PackedMerkleContext,
23 PackedReadOnlyCompressedAccount,
24 },
25 discriminators::INVOKE_CPI_WITH_ACCOUNT_INFO_INSTRUCTION,
26 instruction_data::{
27 data::OutputCompressedAccountWithPackedContext, traits::LightInstructionData,
28 with_readonly::InAccount,
29 },
30 pubkey::Pubkey,
31 CompressedAccountError, InstructionDiscriminator, Vec,
32};
33
34#[cfg_attr(
35 all(feature = "std", feature = "anchor"),
36 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
37)]
38#[cfg_attr(
39 not(feature = "anchor"),
40 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
41)]
42#[derive(Debug, Default, PartialEq, Clone)]
43pub struct InAccountInfo {
44 pub discriminator: [u8; 8],
45 pub data_hash: [u8; 32],
47 pub merkle_context: PackedMerkleContext,
49 pub root_index: u16,
51 pub lamports: u64,
53}
54
55impl From<InAccount> for InAccountInfo {
56 fn from(account: InAccount) -> Self {
57 Self {
58 discriminator: account.discriminator,
59 data_hash: account.data_hash,
60 merkle_context: account.merkle_context,
61 root_index: account.root_index,
62 lamports: account.lamports,
63 }
64 }
65}
66
67impl InAccountInfo {
68 pub fn into_in_account(&self, address: Option<[u8; 32]>) -> InAccount {
69 InAccount {
70 discriminator: self.discriminator,
71 data_hash: self.data_hash,
72 merkle_context: self.merkle_context,
73 root_index: self.root_index,
74 lamports: self.lamports,
75 address,
76 }
77 }
78}
79
80#[repr(C)]
81#[derive(
82 Debug, Default, PartialEq, Clone, Copy, FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout,
83)]
84pub struct ZInAccountInfo {
85 pub discriminator: [u8; 8],
86 pub data_hash: [u8; 32],
88 pub merkle_context: ZPackedMerkleContext,
90 pub root_index: U16,
92 pub lamports: U64,
94}
95
96#[cfg_attr(
97 all(feature = "std", feature = "anchor"),
98 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
99)]
100#[cfg_attr(
101 not(feature = "anchor"),
102 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
103)]
104#[derive(Debug, Default, PartialEq, Clone)]
105pub struct OutAccountInfo {
106 pub discriminator: [u8; 8],
107 pub data_hash: [u8; 32],
109 pub output_merkle_tree_index: u8,
110 pub lamports: u64,
112 pub data: Vec<u8>,
114}
115
116impl TryFrom<OutputCompressedAccountWithPackedContext> for OutAccountInfo {
117 type Error = CompressedAccountError;
118
119 fn try_from(output: OutputCompressedAccountWithPackedContext) -> Result<Self, Self::Error> {
120 let data = output
121 .compressed_account
122 .data
123 .as_ref()
124 .ok_or(CompressedAccountError::ExpectedDataHash)?;
125
126 Ok(Self {
127 discriminator: data.discriminator,
128 data_hash: data.data_hash,
129 output_merkle_tree_index: output.merkle_tree_index,
130 lamports: output.compressed_account.lamports,
131 data: data.data.clone(),
132 })
133 }
134}
135
136impl OutputCompressedAccountWithPackedContext {
137 pub fn from_with_owner(
138 info: &OutAccountInfo,
139 owner: Pubkey,
140 address: Option<[u8; 32]>,
141 ) -> Self {
142 Self {
143 compressed_account: CompressedAccount {
144 owner,
145 lamports: info.lamports,
146 address,
147 data: Some(CompressedAccountData {
148 discriminator: info.discriminator,
149 data: info.data.to_vec(),
150 data_hash: info.data_hash,
151 }),
152 },
153 merkle_tree_index: info.output_merkle_tree_index,
154 }
155 }
156}
157
158impl<'a> InputAccount<'a> for ZCompressedAccountInfo<'a> {
159 fn owner(&self) -> &Pubkey {
160 &self.owner
161 }
162
163 fn skip(&self) -> bool {
164 self.input.is_none()
165 }
166
167 fn lamports(&self) -> u64 {
168 self.input.as_ref().unwrap().lamports.into()
169 }
170 fn address(&self) -> Option<[u8; 32]> {
171 self.address.map(|x| *x)
172 }
173
174 fn merkle_context(&self) -> ZPackedMerkleContext {
175 self.input.as_ref().unwrap().merkle_context
176 }
177
178 fn root_index(&self) -> u16 {
179 self.input.as_ref().unwrap().root_index.into()
180 }
181
182 fn has_data(&self) -> bool {
183 true
184 }
185
186 fn data(&self) -> Option<CompressedAccountData> {
187 Some(CompressedAccountData {
188 data_hash: self.input.unwrap().data_hash,
189 discriminator: self.input.unwrap().discriminator,
190 data: Vec::new(),
191 })
192 }
193
194 fn hash_with_hashed_values(
195 &self,
196 owner_hashed: &[u8; 32],
197 merkle_tree_hashed: &[u8; 32],
198 leaf_index: &u32,
199 is_batched: bool,
200 ) -> Result<[u8; 32], CompressedAccountError> {
201 let input = self.input.as_ref().unwrap();
202 let address_slice = self.address.as_ref().map(|x| x.as_slice());
203 hash_with_hashed_values(
204 &input.lamports.into(),
205 address_slice,
206 Some((input.discriminator.as_slice(), input.data_hash.as_slice())),
207 owner_hashed,
208 merkle_tree_hashed,
209 leaf_index,
210 is_batched,
211 )
212 }
213}
214
215impl<'a> OutputAccount<'a> for ZCompressedAccountInfo<'a> {
216 fn lamports(&self) -> u64 {
217 self.output.as_ref().unwrap().lamports.into()
218 }
219
220 fn address(&self) -> Option<[u8; 32]> {
221 self.address.map(|x| *x)
222 }
223
224 fn skip(&self) -> bool {
225 self.output.is_none()
226 }
227
228 fn owner(&self) -> Pubkey {
229 self.owner
230 }
231
232 fn merkle_tree_index(&self) -> u8 {
233 self.output.as_ref().unwrap().output_merkle_tree_index
234 }
235
236 fn has_data(&self) -> bool {
237 true
238 }
239
240 fn data(&self) -> Option<CompressedAccountData> {
241 Some(CompressedAccountData {
242 discriminator: self.output.as_ref().unwrap().discriminator,
243 data_hash: self.output.as_ref().unwrap().data_hash,
244 data: self.output.as_ref().unwrap().data.to_vec(),
245 })
246 }
247
248 fn hash_with_hashed_values(
249 &self,
250 owner_hashed: &[u8; 32],
251 merkle_tree_hashed: &[u8; 32],
252 leaf_index: &u32,
253 is_batched: bool,
254 ) -> Result<[u8; 32], CompressedAccountError> {
255 let output = self.output.as_ref().unwrap();
256 let address_slice = self.address.as_ref().map(|x| x.as_slice());
257 hash_with_hashed_values(
258 &output.lamports.into(),
259 address_slice,
260 Some((output.discriminator.as_slice(), output.data_hash.as_slice())),
261 owner_hashed,
262 merkle_tree_hashed,
263 leaf_index,
264 is_batched,
265 )
266 }
267}
268
269#[repr(C)]
270#[derive(
271 Debug, Default, PartialEq, Clone, FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout,
272)]
273pub struct ZOutAccountInfoMeta {
274 pub discriminator: [u8; 8],
275 pub data_hash: [u8; 32],
277 pub output_merkle_tree_index: u8,
278 pub lamports: U64,
280}
281
282#[derive(Debug, PartialEq, Clone)]
283pub struct ZOutAccountInfo<'a> {
284 meta: Ref<&'a [u8], ZOutAccountInfoMeta>,
285 pub data: &'a [u8],
287}
288
289impl<'a> ZeroCopyAt<'a> for ZOutAccountInfo<'a> {
290 type ZeroCopyAt = ZOutAccountInfo<'a>;
291
292 fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self::ZeroCopyAt, &'a [u8]), ZeroCopyError> {
293 let (meta, bytes) = Ref::<&[u8], ZOutAccountInfoMeta>::from_prefix(bytes)?;
294 let (len, bytes) = Ref::<&'a [u8], U32>::from_prefix(bytes)?;
295 let (data, bytes) = bytes.split_at(u64::from(*len) as usize);
296 Ok((Self { meta, data }, bytes))
297 }
298}
299
300impl Deref for ZOutAccountInfo<'_> {
301 type Target = ZOutAccountInfoMeta;
302
303 fn deref(&self) -> &Self::Target {
304 &self.meta
305 }
306}
307
308#[derive(Debug, PartialEq)]
309pub struct ZOutAccountInfoMut<'a> {
310 meta: Ref<&'a mut [u8], ZOutAccountInfoMeta>,
311 pub data: &'a mut [u8],
313}
314
315impl Deref for ZOutAccountInfoMut<'_> {
316 type Target = ZOutAccountInfoMeta;
317
318 fn deref(&self) -> &Self::Target {
319 &self.meta
320 }
321}
322
323impl DerefMut for ZOutAccountInfoMut<'_> {
324 fn deref_mut(&mut self) -> &mut Self::Target {
325 &mut self.meta
326 }
327}
328
329#[cfg_attr(
330 all(feature = "std", feature = "anchor"),
331 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
332)]
333#[cfg_attr(
334 not(feature = "anchor"),
335 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
336)]
337#[derive(Debug, PartialEq, Clone, Default)]
338pub struct CompressedAccountInfo {
339 pub address: Option<[u8; 32]>,
341 pub input: Option<InAccountInfo>,
343 pub output: Option<OutAccountInfo>,
345}
346
347#[derive(Debug, PartialEq)]
348pub struct ZCompressedAccountInfo<'a> {
349 pub owner: Pubkey,
350 pub address: Option<Ref<&'a [u8], [u8; 32]>>,
352 pub input: Option<Ref<&'a [u8], ZInAccountInfo>>,
354 pub output: Option<ZOutAccountInfo<'a>>,
356}
357
358impl<'a> CompressedAccountInfo {
359 pub fn zero_copy_at_with_owner(
360 bytes: &'a [u8],
361 owner: Pubkey,
362 ) -> Result<(ZCompressedAccountInfo<'a>, &'a [u8]), ZeroCopyError> {
363 let (address, bytes) = Option::<Ref<&[u8], [u8; 32]>>::zero_copy_at(bytes)?;
364 let (input, bytes) = Option::<Ref<&[u8], ZInAccountInfo>>::zero_copy_at(bytes)?;
365 let (output, bytes) = Option::<ZOutAccountInfo<'a>>::zero_copy_at(bytes)?;
366 Ok((
367 ZCompressedAccountInfo {
368 owner,
369 address,
370 input,
371 output,
372 },
373 bytes,
374 ))
375 }
376}
377
378#[cfg_attr(
379 all(feature = "std", feature = "anchor"),
380 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
381)]
382#[cfg_attr(
383 not(feature = "anchor"),
384 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
385)]
386#[derive(Debug, PartialEq, Default, Clone)]
387pub struct InstructionDataInvokeCpiWithAccountInfo {
388 pub mode: u8,
391 pub bump: u8,
392 pub invoking_program_id: Pubkey,
393 pub compress_or_decompress_lamports: u64,
395 pub is_compress: bool,
397 pub with_cpi_context: bool,
398 pub with_transaction_hash: bool,
399 pub cpi_context: CompressedCpiContext,
400 pub proof: Option<CompressedProof>,
401 pub new_address_params: Vec<NewAddressParamsAssignedPacked>,
402 pub account_infos: Vec<CompressedAccountInfo>,
403 pub read_only_addresses: Vec<PackedReadOnlyAddress>,
404 pub read_only_accounts: Vec<PackedReadOnlyCompressedAccount>,
405}
406
407impl InstructionDataInvokeCpiWithAccountInfo {
408 pub fn new(invoking_program_id: Pubkey, bump: u8, proof: Option<CompressedProof>) -> Self {
409 Self {
410 invoking_program_id,
411 bump,
412 proof,
413 mode: 1,
414 ..Default::default()
415 }
416 }
417
418 #[must_use = "mode_v1 returns a new value"]
419 pub fn mode_v1(mut self) -> Self {
420 self.mode = 0;
421 self
422 }
423
424 #[must_use = "write_to_cpi_context_set returns a new value"]
425 pub fn write_to_cpi_context_set(mut self) -> Self {
426 self.with_cpi_context = true;
427 self.cpi_context = CompressedCpiContext::set();
428 self
429 }
430
431 #[must_use = "write_to_cpi_context_first returns a new value"]
432 pub fn write_to_cpi_context_first(mut self) -> Self {
433 self.with_cpi_context = true;
434 self.cpi_context = CompressedCpiContext::first();
435 self
436 }
437
438 #[must_use = "execute_with_cpi_context returns a new value"]
439 pub fn execute_with_cpi_context(mut self) -> Self {
440 self.with_cpi_context = true;
441 self
442 }
443
444 #[must_use = "with_cpi_context returns a new value"]
445 pub fn with_cpi_context(mut self, cpi_context: CompressedCpiContext) -> Self {
446 self.cpi_context = cpi_context;
447 self
448 }
449
450 #[must_use = "with_with_transaction_hash returns a new value"]
451 pub fn with_with_transaction_hash(mut self, with_transaction_hash: bool) -> Self {
452 self.with_transaction_hash = with_transaction_hash;
453 self
454 }
455
456 #[must_use = "compress_lamports returns a new value"]
457 pub fn compress_lamports(mut self, lamports: u64) -> Self {
458 self.compress_or_decompress_lamports = lamports;
459 self.is_compress = true;
460 self
461 }
462
463 #[must_use = "decompress_lamports returns a new value"]
464 pub fn decompress_lamports(mut self, lamports: u64) -> Self {
465 self.compress_or_decompress_lamports = lamports;
466 self.is_compress = false;
467 self
468 }
469
470 #[must_use = "with_new_addresses returns a new value"]
471 pub fn with_new_addresses(
472 mut self,
473 new_address_params: &[NewAddressParamsAssignedPacked],
474 ) -> Self {
475 if !new_address_params.is_empty() {
476 self.new_address_params
477 .extend_from_slice(new_address_params);
478 }
479 self
480 }
481
482 #[must_use = "with_account_infos returns a new value"]
483 pub fn with_account_infos(mut self, account_infos: &[CompressedAccountInfo]) -> Self {
484 if !account_infos.is_empty() {
485 self.account_infos.extend_from_slice(account_infos);
486 }
487 self
488 }
489
490 #[must_use = "with_read_only_addresses returns a new value"]
491 pub fn with_read_only_addresses(
492 mut self,
493 read_only_addresses: &[PackedReadOnlyAddress],
494 ) -> Self {
495 if !read_only_addresses.is_empty() {
496 self.read_only_addresses
497 .extend_from_slice(read_only_addresses);
498 }
499 self
500 }
501
502 #[must_use = "with_read_only_accounts returns a new value"]
503 pub fn with_read_only_accounts(
504 mut self,
505 read_only_accounts: &[PackedReadOnlyCompressedAccount],
506 ) -> Self {
507 if !read_only_accounts.is_empty() {
508 self.read_only_accounts
509 .extend_from_slice(read_only_accounts);
510 }
511 self
512 }
513}
514
515impl InstructionDiscriminator for InstructionDataInvokeCpiWithAccountInfo {
516 fn discriminator(&self) -> &'static [u8] {
517 &INVOKE_CPI_WITH_ACCOUNT_INFO_INSTRUCTION
518 }
519}
520
521impl LightInstructionData for InstructionDataInvokeCpiWithAccountInfo {}
522
523impl<'a> InstructionData<'a> for ZInstructionDataInvokeCpiWithAccountInfo<'a> {
524 fn bump(&self) -> Option<u8> {
525 Some(self.bump)
526 }
527
528 fn account_option_config(
529 &self,
530 ) -> Result<super::traits::AccountOptions, CompressedAccountError> {
531 let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
532 let decompression_recipient = sol_pool_pda && !self.is_compress();
533 let cpi_context_account = self.cpi_context().is_some();
534 let write_to_cpi_context =
535 self.cpi_context.first_set_context() || self.cpi_context.set_context();
536
537 if write_to_cpi_context && !cpi_context_account {
539 return Err(CompressedAccountError::InvalidCpiContext);
540 }
541
542 Ok(AccountOptions {
543 sol_pool_pda,
544 decompression_recipient,
545 cpi_context_account,
546 write_to_cpi_context,
547 })
548 }
549
550 fn with_transaction_hash(&self) -> bool {
551 self.meta.with_transaction_hash()
552 }
553
554 fn read_only_accounts(&self) -> Option<&[ZPackedReadOnlyCompressedAccount]> {
555 Some(self.read_only_accounts.as_slice())
556 }
557
558 fn read_only_addresses(&self) -> Option<&[ZPackedReadOnlyAddress]> {
559 Some(self.read_only_addresses.as_slice())
560 }
561
562 fn owner(&self) -> Pubkey {
563 self.meta.invoking_program_id
564 }
565
566 fn new_addresses(&self) -> &[impl NewAddress<'a>] {
567 self.new_address_params.as_slice()
568 }
569
570 fn proof(&self) -> Option<Ref<&'a [u8], CompressedProof>> {
571 self.proof
572 }
573
574 fn cpi_context(&self) -> Option<CompressedCpiContext> {
575 if self.meta.with_cpi_context() {
576 Some(CompressedCpiContext {
577 set_context: self.meta.cpi_context.set_context(),
578 first_set_context: self.meta.cpi_context.first_set_context(),
579 cpi_context_account_index: self.meta.cpi_context.cpi_context_account_index,
580 })
581 } else {
582 None
583 }
584 }
585
586 fn is_compress(&self) -> bool {
587 self.meta.is_compress()
588 }
589
590 fn input_accounts(&self) -> &[impl InputAccount<'a>] {
591 self.account_infos.as_slice()
592 }
593
594 fn output_accounts(&self) -> &[impl super::traits::OutputAccount<'a>] {
595 self.account_infos.as_slice()
596 }
597
598 fn compress_or_decompress_lamports(&self) -> Option<u64> {
599 if self.meta.compress_or_decompress_lamports != U64::from(0) {
600 Some(self.meta.compress_or_decompress_lamports.into())
601 } else {
602 None
603 }
604 }
605}
606
607pub struct ZInstructionDataInvokeCpiWithAccountInfo<'a> {
608 meta: Ref<&'a [u8], ZInstructionDataInvokeCpiWithReadOnlyMeta>,
609 pub proof: Option<Ref<&'a [u8], CompressedProof>>,
610 pub new_address_params: ZeroCopySliceBorsh<'a, ZNewAddressParamsAssignedPacked>,
611 pub account_infos: Vec<ZCompressedAccountInfo<'a>>,
612 pub read_only_addresses: ZeroCopySliceBorsh<'a, ZPackedReadOnlyAddress>,
613 pub read_only_accounts: ZeroCopySliceBorsh<'a, ZPackedReadOnlyCompressedAccount>,
614}
615
616impl<'a> Deref for ZInstructionDataInvokeCpiWithAccountInfo<'a> {
617 type Target = Ref<&'a [u8], ZInstructionDataInvokeCpiWithReadOnlyMeta>;
618
619 fn deref(&self) -> &Self::Target {
620 &self.meta
621 }
622}
623
624impl<'a> ZeroCopyAt<'a> for InstructionDataInvokeCpiWithAccountInfo {
625 type ZeroCopyAt = ZInstructionDataInvokeCpiWithAccountInfo<'a>;
626 fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self::ZeroCopyAt, &'a [u8]), ZeroCopyError> {
627 let (meta, bytes) =
628 Ref::<&[u8], ZInstructionDataInvokeCpiWithReadOnlyMeta>::from_prefix(bytes)?;
629 let (proof, bytes) = Option::<Ref<&[u8], CompressedProof>>::zero_copy_at(bytes)?;
630 let (new_address_params, bytes) =
631 ZeroCopySliceBorsh::<'a, ZNewAddressParamsAssignedPacked>::from_bytes_at(bytes)?;
632 let (account_infos, bytes) = {
633 let (num_slices, mut bytes) = Ref::<&[u8], U32>::from_prefix(bytes)?;
634 let num_slices = u32::from(*num_slices) as usize;
635 let mut slices = Vec::with_capacity(num_slices);
636 if bytes.len() < num_slices {
637 return Err(ZeroCopyError::InsufficientMemoryAllocated(
638 bytes.len(),
639 num_slices,
640 ));
641 }
642 for _ in 0..num_slices {
643 let (slice, _bytes) = CompressedAccountInfo::zero_copy_at_with_owner(
644 bytes,
645 meta.invoking_program_id,
646 )?;
647 bytes = _bytes;
648 slices.push(slice);
649 }
650 (slices, bytes)
651 };
652 let (read_only_addresses, bytes) =
653 ZeroCopySliceBorsh::<'a, ZPackedReadOnlyAddress>::from_bytes_at(bytes)?;
654 let (read_only_accounts, bytes) =
655 ZeroCopySliceBorsh::<'a, ZPackedReadOnlyCompressedAccount>::from_bytes_at(bytes)?;
656 Ok((
657 ZInstructionDataInvokeCpiWithAccountInfo {
658 meta,
659 proof,
660 new_address_params,
661 account_infos,
662 read_only_addresses,
663 read_only_accounts,
664 },
665 bytes,
666 ))
667 }
668}
669
670#[cfg(all(not(feature = "pinocchio"), feature = "new-unique"))]
671#[cfg(test)]
672pub mod test {
673 use borsh::BorshSerialize;
674 use rand::{
675 rngs::{StdRng, ThreadRng},
676 Rng, SeedableRng,
677 };
678
679 use super::*;
680 use crate::{
681 compressed_account::{PackedMerkleContext, PackedReadOnlyCompressedAccount},
682 instruction_data::{
683 compressed_proof::CompressedProof, cpi_context::CompressedCpiContext,
684 data::NewAddressParamsAssignedPacked,
685 },
686 CompressedAccountError,
687 };
688
689 fn get_rnd_instruction_data_invoke_cpi_with_account_info(
690 rng: &mut StdRng,
691 ) -> InstructionDataInvokeCpiWithAccountInfo {
692 let with_cpi_context = rng.gen();
693 InstructionDataInvokeCpiWithAccountInfo {
694 mode: rng.gen_range(0..2),
695 bump: rng.gen(),
696 invoking_program_id: Pubkey::new_unique(),
697 compress_or_decompress_lamports: rng.gen(),
698 is_compress: rng.gen(),
699 with_cpi_context,
700 with_transaction_hash: rng.gen(),
701 cpi_context: get_rnd_cpi_context(rng, with_cpi_context),
702 proof: Some(CompressedProof {
703 a: rng.gen(),
704 b: (0..64)
705 .map(|_| rng.gen())
706 .collect::<Vec<u8>>()
707 .try_into()
708 .unwrap(),
709 c: rng.gen(),
710 }),
711 new_address_params: vec![
712 get_rnd_new_address_params_assigned(rng);
713 rng.gen_range(0..10)
714 ],
715 account_infos: vec![get_rnd_test_account_info(rng); rng.gen_range(0..10)],
716 read_only_addresses: vec![get_rnd_read_only_address(rng); rng.gen_range(0..10)],
717 read_only_accounts: vec![get_rnd_read_only_account(rng); rng.gen_range(0..10)],
718 }
719 }
720
721 fn get_rnd_cpi_context(rng: &mut StdRng, with_cpi_context: bool) -> CompressedCpiContext {
722 CompressedCpiContext {
723 first_set_context: rng.gen() && with_cpi_context,
724 set_context: rng.gen() && with_cpi_context,
725 cpi_context_account_index: rng.gen(),
726 }
727 }
728
729 fn get_rnd_read_only_address(rng: &mut StdRng) -> PackedReadOnlyAddress {
730 PackedReadOnlyAddress {
731 address: rng.gen(),
732 address_merkle_tree_root_index: rng.gen(),
733 address_merkle_tree_account_index: rng.gen(),
734 }
735 }
736
737 fn get_rnd_read_only_account(rng: &mut StdRng) -> PackedReadOnlyCompressedAccount {
738 PackedReadOnlyCompressedAccount {
739 account_hash: rng.gen(),
740 merkle_context: PackedMerkleContext {
741 merkle_tree_pubkey_index: rng.gen(),
742 queue_pubkey_index: rng.gen(),
743 leaf_index: rng.gen(),
744 prove_by_index: rng.gen(),
745 },
746 root_index: rng.gen(),
747 }
748 }
749
750 fn get_rnd_in_account_info(rng: &mut StdRng) -> InAccountInfo {
751 InAccountInfo {
752 discriminator: rng.gen(),
753 data_hash: rng.gen(),
754 merkle_context: PackedMerkleContext {
755 merkle_tree_pubkey_index: rng.gen(),
756 queue_pubkey_index: rng.gen(),
757 leaf_index: rng.gen(),
758 prove_by_index: rng.gen(),
759 },
760 root_index: rng.gen(),
761 lamports: rng.gen(),
762 }
763 }
764
765 pub fn get_rnd_out_account_info(rng: &mut StdRng) -> OutAccountInfo {
766 OutAccountInfo {
767 discriminator: rng.gen(),
768 data_hash: rng.gen(),
769 output_merkle_tree_index: rng.gen(),
770 lamports: rng.gen(),
771 data: (0..rng.gen_range(0..100)).map(|_| rng.gen()).collect(),
772 }
773 }
774
775 pub fn get_rnd_test_account_info(rng: &mut StdRng) -> CompressedAccountInfo {
776 CompressedAccountInfo {
777 address: if rng.gen() { Some(rng.gen()) } else { None },
778 input: if rng.gen() {
779 Some(get_rnd_in_account_info(rng))
780 } else {
781 None
782 },
783 output: if rng.gen() {
784 Some(get_rnd_out_account_info(rng))
785 } else {
786 None
787 },
788 }
789 }
790
791 pub fn get_rnd_new_address_params_assigned(rng: &mut StdRng) -> NewAddressParamsAssignedPacked {
792 NewAddressParamsAssignedPacked {
793 seed: rng.gen(),
794 address_queue_account_index: rng.gen(),
795 address_merkle_tree_account_index: rng.gen(),
796 address_merkle_tree_root_index: rng.gen(),
797 assigned_to_account: rng.gen(),
798 assigned_account_index: rng.gen(),
799 }
800 }
801
802 fn compare_invoke_cpi_with_account_info(
803 reference: &InstructionDataInvokeCpiWithAccountInfo,
804 z_copy: &ZInstructionDataInvokeCpiWithAccountInfo,
805 ) -> Result<(), CompressedAccountError> {
806 if reference.mode != z_copy.meta.mode {
808 return Err(CompressedAccountError::InvalidArgument);
809 }
810 if reference.bump != z_copy.meta.bump {
811 return Err(CompressedAccountError::InvalidArgument);
812 }
813 if reference.invoking_program_id != z_copy.meta.invoking_program_id {
814 return Err(CompressedAccountError::InvalidArgument);
815 }
816 if reference.compress_or_decompress_lamports
817 != u64::from(z_copy.meta.compress_or_decompress_lamports)
818 {
819 return Err(CompressedAccountError::InvalidArgument);
820 }
821 if reference.is_compress != z_copy.meta.is_compress() {
822 return Err(CompressedAccountError::InvalidArgument);
823 }
824 if reference.with_cpi_context != z_copy.meta.with_cpi_context() {
825 return Err(CompressedAccountError::InvalidArgument);
826 }
827 if reference.with_transaction_hash != z_copy.meta.with_transaction_hash() {
828 return Err(CompressedAccountError::InvalidArgument);
829 }
830
831 if reference.cpi_context.first_set_context != z_copy.meta.cpi_context.first_set_context() {
833 return Err(CompressedAccountError::InvalidArgument);
834 }
835 if reference.cpi_context.set_context != z_copy.meta.cpi_context.set_context() {
836 return Err(CompressedAccountError::InvalidArgument);
837 }
838 if reference.cpi_context.cpi_context_account_index
839 != z_copy.meta.cpi_context.cpi_context_account_index
840 {
841 return Err(CompressedAccountError::InvalidArgument);
842 }
843
844 if reference.proof.is_some() && z_copy.proof.is_none() {
846 return Err(CompressedAccountError::InvalidArgument);
847 }
848 if reference.proof.is_none() && z_copy.proof.is_some() {
849 return Err(CompressedAccountError::InvalidArgument);
850 }
851 if reference.proof.is_some() && z_copy.proof.is_some() {
852 let ref_proof = reference.proof.as_ref().unwrap();
853 let z_proof = *z_copy.proof.as_ref().unwrap();
854 if ref_proof.a != z_proof.a || ref_proof.b != z_proof.b || ref_proof.c != z_proof.c {
855 return Err(CompressedAccountError::InvalidArgument);
856 }
857 }
858
859 if reference.new_address_params.len() != z_copy.new_address_params.len() {
861 return Err(CompressedAccountError::InvalidArgument);
862 }
863 for i in 0..reference
864 .new_address_params
865 .len()
866 .min(z_copy.new_address_params.len())
867 {
868 let ref_param = &reference.new_address_params[i];
869 let z_param = &z_copy.new_address_params[i];
870
871 if ref_param.seed != z_param.seed {
872 return Err(CompressedAccountError::InvalidArgument);
873 }
874 if ref_param.address_queue_account_index != z_param.address_queue_account_index {
875 return Err(CompressedAccountError::InvalidArgument);
876 }
877 if ref_param.address_merkle_tree_account_index
878 != z_param.address_merkle_tree_account_index
879 {
880 return Err(CompressedAccountError::InvalidArgument);
881 }
882 if ref_param.address_merkle_tree_root_index
883 != u16::from(z_param.address_merkle_tree_root_index)
884 {
885 return Err(CompressedAccountError::InvalidArgument);
886 }
887 let z_assigned_to_account_bool = z_param.assigned_to_account > 0;
890 if ref_param.assigned_to_account != z_assigned_to_account_bool {
891 return Err(CompressedAccountError::InvalidArgument);
892 }
893 if ref_param.assigned_account_index != z_param.assigned_account_index {
894 return Err(CompressedAccountError::InvalidArgument);
895 }
896 }
897
898 if reference.account_infos.len() != z_copy.account_infos.len() {
900 return Err(CompressedAccountError::InvalidArgument);
901 }
902 if reference.read_only_addresses.len() != z_copy.read_only_addresses.len() {
907 return Err(CompressedAccountError::InvalidArgument);
908 }
909 for i in 0..reference
910 .read_only_addresses
911 .len()
912 .min(z_copy.read_only_addresses.len())
913 {
914 let ref_addr = &reference.read_only_addresses[i];
915 let z_addr = &z_copy.read_only_addresses[i];
916
917 if ref_addr.address != z_addr.address {
918 return Err(CompressedAccountError::InvalidArgument);
919 }
920 if ref_addr.address_merkle_tree_account_index
921 != z_addr.address_merkle_tree_account_index
922 {
923 return Err(CompressedAccountError::InvalidArgument);
924 }
925 if ref_addr.address_merkle_tree_root_index
926 != u16::from(z_addr.address_merkle_tree_root_index)
927 {
928 return Err(CompressedAccountError::InvalidArgument);
929 }
930 }
931
932 if reference.read_only_accounts.len() != z_copy.read_only_accounts.len() {
934 return Err(CompressedAccountError::InvalidArgument);
935 }
936 for i in 0..reference
937 .read_only_accounts
938 .len()
939 .min(z_copy.read_only_accounts.len())
940 {
941 let ref_acc = &reference.read_only_accounts[i];
942 let z_acc = &z_copy.read_only_accounts[i];
943
944 if ref_acc.account_hash != z_acc.account_hash {
945 return Err(CompressedAccountError::InvalidArgument);
946 }
947
948 if ref_acc.merkle_context.merkle_tree_pubkey_index
950 != z_acc.merkle_context.merkle_tree_pubkey_index
951 {
952 return Err(CompressedAccountError::InvalidArgument);
953 }
954 if ref_acc.merkle_context.queue_pubkey_index != z_acc.merkle_context.queue_pubkey_index
955 {
956 return Err(CompressedAccountError::InvalidArgument);
957 }
958 if ref_acc.merkle_context.leaf_index != u32::from(z_acc.merkle_context.leaf_index) {
959 return Err(CompressedAccountError::InvalidArgument);
960 }
961 if ref_acc.merkle_context.prove_by_index != z_acc.merkle_context.prove_by_index() {
962 return Err(CompressedAccountError::InvalidArgument);
963 }
964
965 if ref_acc.root_index != u16::from(z_acc.root_index) {
966 return Err(CompressedAccountError::InvalidArgument);
967 }
968 }
969
970 assert_eq!(
972 z_copy.with_transaction_hash(),
973 reference.with_transaction_hash
974 );
975 assert_eq!(z_copy.bump(), Some(reference.bump));
976 assert_eq!(z_copy.owner(), reference.invoking_program_id);
977
978 if reference.compress_or_decompress_lamports > 0 {
980 assert_eq!(
981 z_copy.compress_or_decompress_lamports(),
982 Some(reference.compress_or_decompress_lamports)
983 );
984 } else {
985 assert_eq!(z_copy.compress_or_decompress_lamports(), None);
986 }
987
988 let expected_is_compress =
990 reference.is_compress && reference.compress_or_decompress_lamports > 0;
991 assert_eq!(z_copy.is_compress(), expected_is_compress);
992
993 if reference.with_cpi_context {
995 let context = z_copy.cpi_context();
996 assert!(context.is_some());
997 if let Some(ctx) = context {
998 assert_eq!(
999 ctx.first_set_context,
1000 reference.cpi_context.first_set_context
1001 );
1002 assert_eq!(ctx.set_context, reference.cpi_context.set_context);
1003 assert_eq!(
1004 ctx.cpi_context_account_index,
1005 reference.cpi_context.cpi_context_account_index
1006 );
1007 }
1008 } else {
1009 assert!(z_copy.cpi_context().is_none());
1010 }
1011
1012 let account_options = z_copy.account_option_config().unwrap();
1014 assert_eq!(
1015 account_options.sol_pool_pda,
1016 z_copy.compress_or_decompress_lamports().is_some()
1017 );
1018 assert_eq!(
1019 account_options.decompression_recipient,
1020 z_copy.compress_or_decompress_lamports().is_some() && !z_copy.is_compress()
1021 );
1022 assert_eq!(
1023 account_options.cpi_context_account,
1024 z_copy.cpi_context().is_some()
1025 );
1026
1027 Ok(())
1028 }
1029
1030 #[test]
1031 fn test_instruction_data_invoke_cpi_with_account_info_rnd() {
1032 let mut thread_rng = ThreadRng::default();
1033 let seed = thread_rng.gen();
1034 println!("\n\ne2e test seed {}\n\n", seed);
1035 let mut rng = StdRng::seed_from_u64(seed);
1036
1037 let num_iters = 1000;
1038 for _ in 0..num_iters {
1039 let value = get_rnd_instruction_data_invoke_cpi_with_account_info(&mut rng);
1042
1043 let mut vec = Vec::new();
1044 value.serialize(&mut vec).unwrap();
1045 let (zero_copy, _) =
1046 InstructionDataInvokeCpiWithAccountInfo::zero_copy_at(&vec).unwrap();
1047 compare_invoke_cpi_with_account_info(&value, &zero_copy).unwrap();
1048 }
1049 }
1050}