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