light_compressed_account/instruction_data/
zero_copy.rs

1use std::{mem::size_of, ops::Deref};
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    invoke_cpi::InstructionDataInvokeCpi,
11    traits::{AccountOptions, InputAccount, InstructionData, NewAddress, OutputAccount},
12};
13use crate::{
14    compressed_account::{
15        CompressedAccount, CompressedAccountData, PackedCompressedAccountWithMerkleContext,
16        PackedMerkleContext,
17    },
18    instruction_data::{
19        compressed_proof::CompressedProof, cpi_context::CompressedCpiContext,
20        data::OutputCompressedAccountWithPackedContext,
21    },
22    pubkey::Pubkey,
23    CompressedAccountError,
24};
25
26#[repr(C)]
27#[derive(
28    Debug, PartialEq, Default, Clone, Copy, KnownLayout, Immutable, FromBytes, IntoBytes, Unaligned,
29)]
30pub struct ZPackedReadOnlyAddress {
31    pub address: [u8; 32],
32    pub address_merkle_tree_root_index: U16,
33    pub address_merkle_tree_account_index: u8,
34}
35
36impl<'a> ZeroCopyAt<'a> for ZPackedReadOnlyAddress {
37    type ZeroCopyAt = Self;
38    fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ZeroCopyError> {
39        let (address, bytes) = bytes.split_at(size_of::<[u8; 32]>());
40        let (address_merkle_tree_root_index, bytes) = U16::ref_from_prefix(bytes)?;
41        let (address_merkle_tree_account_index, bytes) = u8::zero_copy_at(bytes)?;
42
43        Ok((
44            ZPackedReadOnlyAddress {
45                address: address.try_into().unwrap(),
46                address_merkle_tree_root_index: *address_merkle_tree_root_index,
47                address_merkle_tree_account_index,
48            },
49            bytes,
50        ))
51    }
52}
53
54#[repr(C)]
55#[derive(
56    Debug, PartialEq, Default, Clone, Copy, KnownLayout, Immutable, FromBytes, IntoBytes, Unaligned,
57)]
58pub struct ZNewAddressParamsPacked {
59    pub seed: [u8; 32],
60    pub address_queue_account_index: u8,
61    pub address_merkle_tree_account_index: u8,
62    pub address_merkle_tree_root_index: U16,
63}
64
65impl NewAddress<'_> for ZNewAddressParamsPacked {
66    fn seed(&self) -> [u8; 32] {
67        self.seed
68    }
69    fn address_queue_index(&self) -> u8 {
70        self.address_queue_account_index
71    }
72
73    fn address_merkle_tree_account_index(&self) -> u8 {
74        self.address_merkle_tree_account_index
75    }
76
77    fn assigned_compressed_account_index(&self) -> Option<usize> {
78        None
79    }
80
81    fn address_merkle_tree_root_index(&self) -> u16 {
82        self.address_merkle_tree_root_index.into()
83    }
84}
85
86#[repr(C)]
87#[derive(
88    Debug, Default, PartialEq, Clone, Copy, KnownLayout, Immutable, FromBytes, IntoBytes, Unaligned,
89)]
90pub struct ZPackedMerkleContext {
91    pub merkle_tree_pubkey_index: u8,
92    pub queue_pubkey_index: u8,
93    pub leaf_index: U32,
94    pub prove_by_index: u8,
95}
96
97impl ZPackedMerkleContext {
98    pub fn prove_by_index(&self) -> bool {
99        self.prove_by_index == 1
100    }
101}
102
103impl<'a> ZeroCopyAt<'a> for ZPackedMerkleContext {
104    type ZeroCopyAt = Ref<&'a [u8], Self>;
105    fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self::ZeroCopyAt, &'a [u8]), ZeroCopyError> {
106        let (ref_value, bytes) = Ref::<&[u8], Self>::from_prefix(bytes)?;
107        Ok((ref_value, bytes))
108    }
109}
110
111#[repr(C)]
112#[derive(Debug, PartialEq, Clone)]
113pub struct ZOutputCompressedAccountWithPackedContext<'a> {
114    pub compressed_account: ZCompressedAccount<'a>,
115    pub merkle_tree_index: u8,
116}
117
118impl<'a> OutputAccount<'a> for ZOutputCompressedAccountWithPackedContext<'a> {
119    fn skip(&self) -> bool {
120        false
121    }
122    fn lamports(&self) -> u64 {
123        self.compressed_account.lamports.into()
124    }
125    fn owner(&self) -> Pubkey {
126        self.compressed_account.owner
127    }
128
129    fn merkle_tree_index(&self) -> u8 {
130        self.merkle_tree_index
131    }
132
133    fn address(&self) -> Option<[u8; 32]> {
134        self.compressed_account.address.map(|x| *x)
135    }
136
137    fn has_data(&self) -> bool {
138        self.compressed_account.data.is_some()
139    }
140
141    fn data(&self) -> Option<CompressedAccountData> {
142        self.compressed_account
143            .data
144            .as_ref()
145            .map(|data| data.into())
146    }
147
148    fn hash_with_hashed_values(
149        &self,
150        owner_hashed: &[u8; 32],
151        merkle_tree_hashed: &[u8; 32],
152        leaf_index: &u32,
153        is_batched: bool,
154    ) -> Result<[u8; 32], crate::CompressedAccountError> {
155        self.compressed_account.hash_with_hashed_values(
156            owner_hashed,
157            merkle_tree_hashed,
158            leaf_index,
159            is_batched,
160        )
161    }
162}
163
164impl<'a> From<&ZOutputCompressedAccountWithPackedContext<'a>>
165    for OutputCompressedAccountWithPackedContext
166{
167    fn from(output_compressed_account: &ZOutputCompressedAccountWithPackedContext<'a>) -> Self {
168        OutputCompressedAccountWithPackedContext {
169            compressed_account: (&output_compressed_account.compressed_account).into(),
170            merkle_tree_index: output_compressed_account.merkle_tree_index,
171        }
172    }
173}
174
175impl<'a> ZeroCopyAt<'a> for ZOutputCompressedAccountWithPackedContext<'a> {
176    type ZeroCopyAt = Self;
177
178    #[inline]
179    fn zero_copy_at(vec: &'a [u8]) -> Result<(Self, &'a [u8]), ZeroCopyError> {
180        let (compressed_account, bytes) = ZCompressedAccount::zero_copy_at(vec)?;
181        let (merkle_tree_index, bytes) = u8::zero_copy_at(bytes)?;
182        Ok((
183            ZOutputCompressedAccountWithPackedContext {
184                compressed_account,
185                merkle_tree_index,
186            },
187            bytes,
188        ))
189    }
190}
191
192#[derive(Debug, PartialEq, Clone)]
193pub struct ZCompressedAccountData<'a> {
194    pub discriminator: Ref<&'a [u8], [u8; 8]>,
195    pub data: &'a [u8],
196    pub data_hash: Ref<&'a [u8], [u8; 32]>,
197}
198
199impl From<ZCompressedAccountData<'_>> for CompressedAccountData {
200    fn from(compressed_account_data: ZCompressedAccountData) -> Self {
201        CompressedAccountData {
202            discriminator: *compressed_account_data.discriminator,
203            data: compressed_account_data.data.to_vec(),
204            data_hash: *compressed_account_data.data_hash,
205        }
206    }
207}
208
209impl From<&ZCompressedAccountData<'_>> for CompressedAccountData {
210    fn from(compressed_account_data: &ZCompressedAccountData) -> Self {
211        CompressedAccountData {
212            discriminator: *compressed_account_data.discriminator,
213            data: compressed_account_data.data.to_vec(),
214            data_hash: *compressed_account_data.data_hash,
215        }
216    }
217}
218
219impl<'a> ZeroCopyAt<'a> for ZCompressedAccountData<'a> {
220    type ZeroCopyAt = Self;
221
222    #[inline]
223    fn zero_copy_at(
224        bytes: &'a [u8],
225    ) -> Result<(ZCompressedAccountData<'a>, &'a [u8]), ZeroCopyError> {
226        let (discriminator, bytes) = Ref::<&'a [u8], [u8; 8]>::from_prefix(bytes)?;
227        let (len, bytes) = Ref::<&'a [u8], U32>::from_prefix(bytes)?;
228        let (data, bytes) = bytes.split_at(u64::from(*len) as usize);
229        let (data_hash, bytes) = Ref::<&'a [u8], [u8; 32]>::from_prefix(bytes)?;
230
231        Ok((
232            ZCompressedAccountData {
233                discriminator,
234                data,
235                data_hash,
236            },
237            bytes,
238        ))
239    }
240}
241
242#[repr(C)]
243#[derive(Debug, PartialEq, KnownLayout, FromBytes, IntoBytes, Immutable)]
244pub struct AccountDesMeta {
245    pub owner: Pubkey,
246    pub lamports: U64,
247    address_option: u8,
248}
249
250#[derive(Debug, PartialEq, Clone)]
251pub struct ZCompressedAccount<'a> {
252    meta: Ref<&'a [u8], AccountDesMeta>,
253    pub address: Option<Ref<&'a [u8], [u8; 32]>>,
254    pub data: Option<ZCompressedAccountData<'a>>,
255}
256
257impl Deref for ZCompressedAccount<'_> {
258    type Target = AccountDesMeta;
259
260    fn deref(&self) -> &Self::Target {
261        &self.meta
262    }
263}
264
265impl From<&ZCompressedAccount<'_>> for CompressedAccount {
266    fn from(compressed_account: &ZCompressedAccount) -> Self {
267        let data: Option<CompressedAccountData> =
268            compressed_account
269                .data
270                .as_ref()
271                .map(|data| CompressedAccountData {
272                    discriminator: *data.discriminator,
273                    data: data.data.to_vec(),
274                    data_hash: *data.data_hash,
275                });
276        CompressedAccount {
277            owner: compressed_account.owner,
278            lamports: compressed_account.lamports.into(),
279            address: compressed_account.address.map(|x| *x),
280            data,
281        }
282    }
283}
284
285impl<'a> ZeroCopyAt<'a> for ZCompressedAccount<'a> {
286    type ZeroCopyAt = Self;
287
288    #[inline]
289    fn zero_copy_at(bytes: &'a [u8]) -> Result<(ZCompressedAccount<'a>, &'a [u8]), ZeroCopyError> {
290        let (meta, bytes) = Ref::<&[u8], AccountDesMeta>::from_prefix(bytes)?;
291        let (address, bytes) = if meta.address_option == 1 {
292            let (address, bytes) = Ref::<&[u8], [u8; 32]>::zero_copy_at(bytes)?;
293            (Some(address), bytes)
294        } else {
295            (None, bytes)
296        };
297        let (data, bytes) = Option::<ZCompressedAccountData>::zero_copy_at(bytes)?;
298        Ok((
299            ZCompressedAccount {
300                meta,
301                address,
302                data,
303            },
304            bytes,
305        ))
306    }
307}
308
309#[repr(C)]
310#[derive(Debug, PartialEq, Immutable, KnownLayout, IntoBytes, FromBytes)]
311pub struct ZPackedCompressedAccountWithMerkleContextMeta {
312    pub merkle_context: ZPackedMerkleContext,
313    /// Index of root used in inclusion validity proof.
314    pub root_index: U16,
315    /// Placeholder to mark accounts read-only unimplemented set to false.
316    read_only: u8,
317}
318
319impl From<ZPackedMerkleContext> for PackedMerkleContext {
320    fn from(merkle_context: ZPackedMerkleContext) -> Self {
321        PackedMerkleContext {
322            merkle_tree_pubkey_index: merkle_context.merkle_tree_pubkey_index,
323            queue_pubkey_index: merkle_context.queue_pubkey_index,
324            leaf_index: merkle_context.leaf_index.into(),
325            prove_by_index: merkle_context.prove_by_index == 1,
326        }
327    }
328}
329
330#[derive(Debug, PartialEq, Clone)]
331pub struct ZPackedCompressedAccountWithMerkleContext<'a> {
332    pub compressed_account: ZCompressedAccount<'a>,
333    meta: Ref<&'a [u8], ZPackedCompressedAccountWithMerkleContextMeta>,
334}
335
336impl<'a> InputAccount<'a> for ZPackedCompressedAccountWithMerkleContext<'a> {
337    fn skip(&self) -> bool {
338        false
339    }
340    fn owner(&self) -> &crate::pubkey::Pubkey {
341        &self.compressed_account.owner
342    }
343    fn lamports(&self) -> u64 {
344        self.compressed_account.lamports.into()
345    }
346    fn address(&self) -> Option<[u8; 32]> {
347        self.compressed_account.address.map(|x| *x)
348    }
349
350    fn merkle_context(&self) -> ZPackedMerkleContext {
351        self.meta.merkle_context
352    }
353
354    fn root_index(&self) -> u16 {
355        self.meta.root_index.into()
356    }
357
358    fn has_data(&self) -> bool {
359        self.compressed_account.data.is_some()
360    }
361
362    fn data(&self) -> Option<CompressedAccountData> {
363        self.compressed_account.data.as_ref().map(|x| x.into())
364    }
365
366    fn hash_with_hashed_values(
367        &self,
368        owner_hashed: &[u8; 32],
369        merkle_tree_hashed: &[u8; 32],
370        leaf_index: &u32,
371        is_batched: bool,
372    ) -> Result<[u8; 32], crate::CompressedAccountError> {
373        self.compressed_account.hash_with_hashed_values(
374            owner_hashed,
375            merkle_tree_hashed,
376            leaf_index,
377            is_batched,
378        )
379    }
380}
381
382impl From<&ZPackedCompressedAccountWithMerkleContext<'_>>
383    for PackedCompressedAccountWithMerkleContext
384{
385    fn from(packed_compressed_account: &ZPackedCompressedAccountWithMerkleContext<'_>) -> Self {
386        PackedCompressedAccountWithMerkleContext {
387            compressed_account: (&packed_compressed_account.compressed_account).into(),
388            merkle_context: packed_compressed_account.merkle_context.into(),
389            root_index: packed_compressed_account.root_index.into(),
390            read_only: packed_compressed_account.read_only == 1,
391        }
392    }
393}
394
395impl Deref for ZPackedCompressedAccountWithMerkleContext<'_> {
396    type Target = ZPackedCompressedAccountWithMerkleContextMeta;
397
398    fn deref(&self) -> &Self::Target {
399        &self.meta
400    }
401}
402
403impl<'a> ZeroCopyAt<'a> for ZPackedCompressedAccountWithMerkleContext<'a> {
404    type ZeroCopyAt = Self;
405    fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ZeroCopyError> {
406        let (compressed_account, bytes) = ZCompressedAccount::zero_copy_at(bytes)?;
407        let (meta, bytes) =
408            Ref::<&[u8], ZPackedCompressedAccountWithMerkleContextMeta>::from_prefix(bytes)?;
409        if meta.read_only == 1 {
410            unimplemented!("Read only accounts are implemented as a separate instruction.");
411        }
412
413        Ok((
414            ZPackedCompressedAccountWithMerkleContext {
415                compressed_account,
416                meta,
417            },
418            bytes,
419        ))
420    }
421}
422
423#[derive(Debug, PartialEq)]
424pub struct ZInstructionDataInvoke<'a> {
425    pub proof: Option<Ref<&'a [u8], CompressedProof>>,
426    pub input_compressed_accounts_with_merkle_context:
427        Vec<ZPackedCompressedAccountWithMerkleContext<'a>>,
428    pub output_compressed_accounts: Vec<ZOutputCompressedAccountWithPackedContext<'a>>,
429    pub relay_fee: Option<Ref<&'a [u8], U64>>,
430    pub new_address_params: ZeroCopySliceBorsh<'a, ZNewAddressParamsPacked>,
431    pub compress_or_decompress_lamports: Option<Ref<&'a [u8], U64>>,
432    pub is_compress: bool,
433}
434
435impl<'a> InstructionData<'a> for ZInstructionDataInvoke<'a> {
436    fn bump(&self) -> Option<u8> {
437        None
438    }
439    fn with_transaction_hash(&self) -> bool {
440        true
441    }
442    fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
443        unimplemented!()
444    }
445    fn read_only_accounts(&self) -> Option<&[ZPackedReadOnlyCompressedAccount]> {
446        None
447    }
448    fn read_only_addresses(&self) -> Option<&[ZPackedReadOnlyAddress]> {
449        None
450    }
451    fn proof(&self) -> Option<Ref<&'a [u8], CompressedProof>> {
452        self.proof
453    }
454    fn is_compress(&self) -> bool {
455        self.is_compress
456    }
457    fn compress_or_decompress_lamports(&self) -> Option<u64> {
458        self.compress_or_decompress_lamports.map(|x| (*x).into())
459    }
460    fn owner(&self) -> Pubkey {
461        // TODO: investigate why this is called if there are no inputs when using mint_to.
462        if self
463            .input_compressed_accounts_with_merkle_context
464            .is_empty()
465        {
466            Pubkey::default()
467        } else {
468            self.input_compressed_accounts_with_merkle_context[0]
469                .compressed_account
470                .owner
471        }
472    }
473
474    fn new_addresses(&self) -> &[impl NewAddress<'a>] {
475        self.new_address_params.as_slice()
476    }
477
478    fn input_accounts(&self) -> &[impl InputAccount<'a>] {
479        self.input_compressed_accounts_with_merkle_context
480            .as_slice()
481    }
482
483    fn output_accounts(&self) -> &[impl OutputAccount<'a>] {
484        self.output_compressed_accounts.as_slice()
485    }
486
487    fn cpi_context(&self) -> Option<CompressedCpiContext> {
488        unimplemented!()
489    }
490}
491impl<'a> ZeroCopyAt<'a> for ZInstructionDataInvoke<'a> {
492    type ZeroCopyAt = Self;
493    fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ZeroCopyError> {
494        let (proof, bytes) = Option::<CompressedProof>::zero_copy_at(bytes)?;
495        let (input_compressed_accounts_with_merkle_context, bytes) =
496            Vec::<ZPackedCompressedAccountWithMerkleContext>::zero_copy_at(bytes)?;
497        let (output_compressed_accounts, bytes) =
498            Vec::<ZOutputCompressedAccountWithPackedContext>::zero_copy_at(bytes)?;
499        let (relay_fee, bytes) = Option::<Ref<&'a [u8], U64>>::zero_copy_at(bytes)?;
500        if relay_fee.is_some() {
501            return Err(ZeroCopyError::InvalidConversion);
502        }
503        let (new_address_params, bytes) = ZeroCopySliceBorsh::from_bytes_at(bytes)?;
504        let (compress_or_decompress_lamports, bytes) =
505            Option::<Ref<&'a [u8], U64>>::zero_copy_at(bytes)?;
506        let (is_compress, bytes) = u8::zero_copy_at(bytes)?;
507
508        Ok((
509            ZInstructionDataInvoke {
510                proof,
511                input_compressed_accounts_with_merkle_context,
512                output_compressed_accounts,
513                relay_fee: None,
514                new_address_params,
515                compress_or_decompress_lamports,
516                is_compress: is_compress == 1,
517            },
518            bytes,
519        ))
520    }
521}
522
523#[derive(Debug, PartialEq)]
524pub struct ZInstructionDataInvokeCpi<'a> {
525    pub proof: Option<Ref<&'a [u8], CompressedProof>>,
526    pub new_address_params: ZeroCopySliceBorsh<'a, ZNewAddressParamsPacked>,
527    pub input_compressed_accounts_with_merkle_context:
528        Vec<ZPackedCompressedAccountWithMerkleContext<'a>>,
529    pub output_compressed_accounts: Vec<ZOutputCompressedAccountWithPackedContext<'a>>,
530    pub relay_fee: Option<Ref<&'a [u8], U64>>,
531    pub compress_or_decompress_lamports: Option<Ref<&'a [u8], U64>>,
532    pub is_compress: bool,
533    pub cpi_context: Option<Ref<&'a [u8], ZCompressedCpiContext>>,
534}
535
536impl ZInstructionDataInvokeCpi<'_> {
537    pub fn owner(&self) -> Pubkey {
538        if self
539            .input_compressed_accounts_with_merkle_context
540            .is_empty()
541        {
542            Pubkey::default()
543        } else {
544            self.input_compressed_accounts_with_merkle_context[0]
545                .compressed_account
546                .owner
547        }
548    }
549}
550
551impl<'a> InstructionData<'a> for ZInstructionDataInvokeCpi<'a> {
552    fn bump(&self) -> Option<u8> {
553        None
554    }
555
556    fn with_transaction_hash(&self) -> bool {
557        true
558    }
559
560    fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
561        let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
562        let decompression_recipient = sol_pool_pda && !self.is_compress();
563        let cpi_context_account = self.cpi_context().is_some();
564        let write_to_cpi_context = false; // Not used
565
566        Ok(AccountOptions {
567            sol_pool_pda,
568            decompression_recipient,
569            cpi_context_account,
570            write_to_cpi_context,
571        })
572    }
573
574    fn read_only_accounts(&self) -> Option<&[ZPackedReadOnlyCompressedAccount]> {
575        None
576    }
577
578    fn read_only_addresses(&self) -> Option<&[ZPackedReadOnlyAddress]> {
579        None
580    }
581
582    fn owner(&self) -> Pubkey {
583        if self
584            .input_compressed_accounts_with_merkle_context
585            .is_empty()
586        {
587            Pubkey::default()
588        } else {
589            self.input_compressed_accounts_with_merkle_context[0]
590                .compressed_account
591                .owner
592        }
593    }
594
595    fn is_compress(&self) -> bool {
596        self.is_compress
597    }
598
599    fn proof(&self) -> Option<Ref<&'a [u8], CompressedProof>> {
600        self.proof
601    }
602
603    fn new_addresses(&self) -> &[impl NewAddress<'a>] {
604        self.new_address_params.as_slice()
605    }
606
607    fn output_accounts(&self) -> &[impl OutputAccount<'a>] {
608        self.output_compressed_accounts.as_slice()
609    }
610
611    fn input_accounts(&self) -> &[impl InputAccount<'a>] {
612        self.input_compressed_accounts_with_merkle_context
613            .as_slice()
614    }
615
616    fn cpi_context(&self) -> Option<CompressedCpiContext> {
617        self.cpi_context
618            .as_ref()
619            .map(|cpi_context| CompressedCpiContext {
620                set_context: cpi_context.set_context(),
621                first_set_context: cpi_context.first_set_context(),
622                cpi_context_account_index: cpi_context.cpi_context_account_index,
623            })
624    }
625
626    fn compress_or_decompress_lamports(&self) -> Option<u64> {
627        self.compress_or_decompress_lamports.map(|x| (*x).into())
628    }
629}
630
631#[repr(C)]
632#[derive(
633    Debug,
634    Clone,
635    Copy,
636    PartialEq,
637    Eq,
638    Default,
639    FromBytes,
640    IntoBytes,
641    Immutable,
642    Unaligned,
643    KnownLayout,
644)]
645pub struct ZCompressedCpiContext {
646    /// Is set by the program that is invoking the CPI to signal that is should
647    /// set the cpi context.
648    set_context: u8,
649    /// Is set to clear the cpi context since someone could have set it before
650    /// with unrelated data.
651    first_set_context: u8,
652    /// Index of cpi context account in remaining accounts.
653    pub cpi_context_account_index: u8,
654}
655
656impl ZCompressedCpiContext {
657    pub fn set_context(&self) -> bool {
658        self.set_context == 1
659    }
660
661    pub fn first_set_context(&self) -> bool {
662        self.first_set_context == 1
663    }
664}
665
666impl<'a> From<ZInstructionDataInvokeCpi<'a>> for ZInstructionDataInvoke<'a> {
667    fn from(instruction_data_invoke: ZInstructionDataInvokeCpi<'a>) -> Self {
668        ZInstructionDataInvoke {
669            proof: instruction_data_invoke.proof,
670            new_address_params: instruction_data_invoke.new_address_params,
671            input_compressed_accounts_with_merkle_context: instruction_data_invoke
672                .input_compressed_accounts_with_merkle_context,
673            output_compressed_accounts: instruction_data_invoke.output_compressed_accounts,
674            relay_fee: instruction_data_invoke.relay_fee,
675            compress_or_decompress_lamports: instruction_data_invoke
676                .compress_or_decompress_lamports,
677            is_compress: instruction_data_invoke.is_compress,
678        }
679    }
680}
681
682impl<'a> ZeroCopyAt<'a> for ZInstructionDataInvokeCpi<'a> {
683    type ZeroCopyAt = Self;
684    fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ZeroCopyError> {
685        let (proof, bytes) = Option::<CompressedProof>::zero_copy_at(bytes)?;
686        let (new_address_params, bytes) = ZeroCopySliceBorsh::from_bytes_at(bytes)?;
687        let (input_compressed_accounts_with_merkle_context, bytes) =
688            Vec::<ZPackedCompressedAccountWithMerkleContext>::zero_copy_at(bytes)?;
689        let (output_compressed_accounts, bytes) =
690            Vec::<ZOutputCompressedAccountWithPackedContext>::zero_copy_at(bytes)?;
691        let (option_relay_fee, bytes) = bytes.split_at(1);
692        if option_relay_fee[0] == 1 {
693            return Err(ZeroCopyError::InvalidConversion);
694        }
695        let (compress_or_decompress_lamports, bytes) =
696            Option::<Ref<&'a [u8], U64>>::zero_copy_at(bytes)?;
697        let (is_compress, bytes) = u8::zero_copy_at(bytes)?;
698        let (cpi_context, bytes) =
699            Option::<Ref<&[u8], ZCompressedCpiContext>>::zero_copy_at(bytes)?;
700
701        Ok((
702            ZInstructionDataInvokeCpi {
703                proof,
704                new_address_params,
705                input_compressed_accounts_with_merkle_context,
706                output_compressed_accounts,
707                relay_fee: None,
708                compress_or_decompress_lamports,
709                is_compress: is_compress == 1,
710                cpi_context,
711            },
712            bytes,
713        ))
714    }
715}
716
717impl ZeroCopyAt<'_> for CompressedCpiContext {
718    type ZeroCopyAt = Self;
719    fn zero_copy_at(bytes: &[u8]) -> Result<(Self, &[u8]), ZeroCopyError> {
720        let (set_context, bytes) = u8::zero_copy_at(bytes)?;
721        let (first_set_context, bytes) = u8::zero_copy_at(bytes)?;
722        let (cpi_context_account_index, bytes) = u8::zero_copy_at(bytes)?;
723
724        Ok((
725            CompressedCpiContext {
726                first_set_context: first_set_context == 1,
727                set_context: set_context == 1,
728                cpi_context_account_index,
729            },
730            bytes,
731        ))
732    }
733}
734
735#[repr(C)]
736#[derive(
737    Debug, PartialEq, Clone, Copy, KnownLayout, Immutable, FromBytes, IntoBytes, Unaligned,
738)]
739pub struct ZPackedReadOnlyCompressedAccount {
740    pub account_hash: [u8; 32],
741    pub merkle_context: ZPackedMerkleContext,
742    pub root_index: U16,
743}
744
745impl<'a> ZeroCopyAt<'a> for ZPackedReadOnlyCompressedAccount {
746    type ZeroCopyAt = Ref<&'a [u8], Self>;
747    fn zero_copy_at(bytes: &'a [u8]) -> Result<(Self::ZeroCopyAt, &'a [u8]), ZeroCopyError> {
748        Ok(Ref::<&[u8], Self>::from_prefix(bytes)?)
749    }
750}
751
752impl From<&ZInstructionDataInvokeCpi<'_>> for InstructionDataInvokeCpi {
753    fn from(data: &ZInstructionDataInvokeCpi<'_>) -> Self {
754        Self {
755            proof: None,
756            new_address_params: vec![],
757            input_compressed_accounts_with_merkle_context: data
758                .input_compressed_accounts_with_merkle_context
759                .iter()
760                .map(PackedCompressedAccountWithMerkleContext::from)
761                .collect::<Vec<_>>(),
762            output_compressed_accounts: data
763                .output_compressed_accounts
764                .iter()
765                .map(OutputCompressedAccountWithPackedContext::from)
766                .collect::<Vec<_>>(),
767            relay_fee: None,
768            compress_or_decompress_lamports: None,
769            is_compress: data.is_compress,
770            cpi_context: None,
771        }
772    }
773}
774
775#[repr(C)]
776#[derive(
777    Debug, PartialEq, Default, Clone, Copy, KnownLayout, Immutable, FromBytes, IntoBytes, Unaligned,
778)]
779pub struct ZNewAddressParamsAssignedPacked {
780    pub seed: [u8; 32],
781    pub address_queue_account_index: u8,
782    pub address_merkle_tree_account_index: u8,
783    pub address_merkle_tree_root_index: U16,
784    pub assigned_to_account: u8,
785    pub assigned_account_index: u8,
786}
787
788impl NewAddress<'_> for ZNewAddressParamsAssignedPacked {
789    fn seed(&self) -> [u8; 32] {
790        self.seed
791    }
792    fn address_queue_index(&self) -> u8 {
793        self.address_queue_account_index
794    }
795
796    fn address_merkle_tree_account_index(&self) -> u8 {
797        self.address_merkle_tree_account_index
798    }
799
800    fn assigned_compressed_account_index(&self) -> Option<usize> {
801        if self.assigned_to_account > 0 {
802            Some(self.assigned_account_index as usize)
803        } else {
804            None
805        }
806    }
807
808    fn address_merkle_tree_root_index(&self) -> u16 {
809        self.address_merkle_tree_root_index.into()
810    }
811}
812
813#[cfg(all(not(feature = "pinocchio"), feature = "new-unique"))]
814#[cfg(test)]
815pub mod test {
816    use borsh::BorshSerialize;
817    use rand::{
818        rngs::{StdRng, ThreadRng},
819        Rng,
820    };
821
822    use super::*;
823    use crate::{
824        compressed_account::{
825            CompressedAccount, CompressedAccountData, PackedCompressedAccountWithMerkleContext,
826            PackedMerkleContext,
827        },
828        instruction_data::{
829            data::{InstructionDataInvoke, NewAddressParamsPacked},
830            invoke_cpi::InstructionDataInvokeCpi,
831        },
832        CompressedAccountError,
833    };
834
835    fn get_instruction_data_invoke_cpi() -> InstructionDataInvokeCpi {
836        InstructionDataInvokeCpi {
837            proof: Some(CompressedProof {
838                a: [1; 32],
839                b: [2; 64],
840                c: [3; 32],
841            }),
842            new_address_params: vec![get_new_address_params(); 3],
843            input_compressed_accounts_with_merkle_context: vec![get_test_input_account(); 3],
844            output_compressed_accounts: vec![get_test_output_account(); 2],
845            relay_fee: None,
846            compress_or_decompress_lamports: Some(1),
847            is_compress: true,
848            cpi_context: Some(get_cpi_context()),
849        }
850    }
851
852    fn get_rnd_instruction_data_invoke_cpi(rng: &mut StdRng) -> InstructionDataInvokeCpi {
853        InstructionDataInvokeCpi {
854            proof: Some(CompressedProof {
855                a: rng.gen(),
856                b: (0..64)
857                    .map(|_| rng.gen())
858                    .collect::<Vec<u8>>()
859                    .try_into()
860                    .unwrap(),
861                c: rng.gen(),
862            }),
863            new_address_params: vec![get_rnd_new_address_params(rng); rng.gen_range(0..10)],
864            input_compressed_accounts_with_merkle_context: vec![
865                get_rnd_test_input_account(rng);
866                rng.gen_range(0..10)
867            ],
868            output_compressed_accounts: vec![
869                get_rnd_test_output_account(rng);
870                rng.gen_range(0..10)
871            ],
872            relay_fee: None,
873            compress_or_decompress_lamports: rng.gen(),
874            is_compress: rng.gen(),
875            cpi_context: Some(get_rnd_cpi_context(rng)),
876        }
877    }
878
879    fn compare_invoke_cpi_instruction_data(
880        reference: &InstructionDataInvokeCpi,
881        z_copy: &ZInstructionDataInvokeCpi,
882    ) -> Result<(), CompressedAccountError> {
883        if reference.proof.is_some() && z_copy.proof.is_none() {
884            println!("proof is none");
885            return Err(CompressedAccountError::InvalidArgument);
886        }
887        if reference.proof.is_none() && z_copy.proof.is_some() {
888            println!("proof is some");
889            return Err(CompressedAccountError::InvalidArgument);
890        }
891        if reference.proof.is_some()
892            && z_copy.proof.is_some()
893            && reference.proof.as_ref().unwrap().a != z_copy.proof.as_ref().unwrap().a
894            || reference.proof.as_ref().unwrap().b != z_copy.proof.as_ref().unwrap().b
895            || reference.proof.as_ref().unwrap().c != z_copy.proof.as_ref().unwrap().c
896        {
897            println!("proof is not equal");
898            return Err(CompressedAccountError::InvalidArgument);
899        }
900        if reference
901            .input_compressed_accounts_with_merkle_context
902            .len()
903            != z_copy.input_compressed_accounts_with_merkle_context.len()
904        {
905            println!("input_compressed_accounts_with_merkle_context is not equal");
906            return Err(CompressedAccountError::InvalidArgument);
907        }
908        for (ref_input, z_input) in reference
909            .input_compressed_accounts_with_merkle_context
910            .iter()
911            .zip(z_copy.input_compressed_accounts_with_merkle_context.iter())
912        {
913            compare_packed_compressed_account_with_merkle_context(ref_input, z_input)?;
914        }
915        if reference.output_compressed_accounts.len() != z_copy.output_compressed_accounts.len() {
916            println!("output_compressed_accounts is not equal");
917            return Err(CompressedAccountError::InvalidArgument);
918        }
919        for (ref_output, z_output) in reference
920            .output_compressed_accounts
921            .iter()
922            .zip(z_copy.output_compressed_accounts.iter())
923        {
924            compare_compressed_output_account(ref_output, z_output)?;
925        }
926        if reference.relay_fee != z_copy.relay_fee.map(|x| (*x).into()) {
927            println!("relay_fee is not equal");
928            return Err(CompressedAccountError::InvalidArgument);
929        }
930        if reference.new_address_params.len() != z_copy.new_address_params.len() {
931            println!("new_address_params is not equal");
932            return Err(CompressedAccountError::InvalidArgument);
933        }
934        for (ref_params, z_params) in reference
935            .new_address_params
936            .iter()
937            .zip(z_copy.new_address_params.iter())
938        {
939            if ref_params.seed != z_params.seed {
940                println!("seed is not equal");
941                return Err(CompressedAccountError::InvalidArgument);
942            }
943            if ref_params.address_queue_account_index != z_params.address_queue_account_index {
944                println!("address_queue_account_index is not equal");
945                return Err(CompressedAccountError::InvalidArgument);
946            }
947            if ref_params.address_merkle_tree_account_index
948                != z_params.address_merkle_tree_account_index
949            {
950                println!("address_merkle_tree_account_index is not equal");
951                return Err(CompressedAccountError::InvalidArgument);
952            }
953            if ref_params.address_merkle_tree_root_index
954                != u16::from(z_params.address_merkle_tree_root_index)
955            {
956                println!("address_merkle_tree_root_index is not equal");
957                return Err(CompressedAccountError::InvalidArgument);
958            }
959        }
960        if reference.compress_or_decompress_lamports
961            != z_copy.compress_or_decompress_lamports.map(|x| (*x).into())
962        {
963            println!("compress_or_decompress_lamports is not equal");
964            return Err(CompressedAccountError::InvalidArgument);
965        }
966        if reference.is_compress != z_copy.is_compress {
967            println!("is_compress is not equal");
968            return Err(CompressedAccountError::InvalidArgument);
969        }
970        if reference.cpi_context.is_some() && z_copy.cpi_context.is_none() {
971            println!("cpi_context is none");
972            return Err(CompressedAccountError::InvalidArgument);
973        }
974        if reference.cpi_context.is_none() && z_copy.cpi_context.is_some() {
975            println!("cpi_context is some");
976            println!("reference: {:?}", reference.cpi_context);
977            println!("z_copy: {:?}", z_copy.cpi_context);
978            return Err(CompressedAccountError::InvalidArgument);
979        }
980        if reference.cpi_context.is_some() && z_copy.cpi_context.is_some() {
981            let reference = reference.cpi_context.as_ref().unwrap();
982            let zcopy = z_copy.cpi_context.as_ref().unwrap();
983            if reference.first_set_context != zcopy.first_set_context()
984                || reference.set_context != zcopy.set_context()
985                || reference.cpi_context_account_index != zcopy.cpi_context_account_index
986            {
987                println!("reference: {:?}", reference);
988                println!("z_copy: {:?}", zcopy);
989                return Err(CompressedAccountError::InvalidArgument);
990            }
991        }
992        Ok(())
993    }
994
995    #[test]
996    fn test_cpi_context_instruction_data() {
997        let reference = get_instruction_data_invoke_cpi();
998
999        let mut bytes = Vec::new();
1000        reference.serialize(&mut bytes).unwrap();
1001        let (z_copy, bytes) = ZInstructionDataInvokeCpi::zero_copy_at(&bytes).unwrap();
1002        assert!(bytes.is_empty());
1003        compare_invoke_cpi_instruction_data(&reference, &z_copy).unwrap();
1004    }
1005
1006    fn get_cpi_context() -> CompressedCpiContext {
1007        CompressedCpiContext {
1008            first_set_context: true,
1009            set_context: true,
1010            cpi_context_account_index: 1,
1011        }
1012    }
1013
1014    fn get_rnd_cpi_context(rng: &mut StdRng) -> CompressedCpiContext {
1015        CompressedCpiContext {
1016            first_set_context: rng.gen(),
1017            set_context: rng.gen(),
1018            cpi_context_account_index: rng.gen(),
1019        }
1020    }
1021
1022    #[test]
1023    fn test_cpi_context_deserialize() {
1024        let cpi_context = get_cpi_context();
1025        let mut bytes = Vec::new();
1026        cpi_context.serialize(&mut bytes).unwrap();
1027
1028        let (z_copy, bytes) = CompressedCpiContext::zero_copy_at(&bytes).unwrap();
1029        assert!(bytes.is_empty());
1030        assert_eq!(z_copy, cpi_context);
1031    }
1032
1033    #[test]
1034    fn test_account_deserialize() {
1035        let test_account = get_test_account();
1036        let mut bytes = Vec::new();
1037        test_account.serialize(&mut bytes).unwrap();
1038
1039        let (z_copy, bytes) = ZCompressedAccount::zero_copy_at(&bytes).unwrap();
1040        assert!(bytes.is_empty());
1041        compare_compressed_account(&test_account, &z_copy).unwrap();
1042    }
1043
1044    fn get_test_account_data() -> CompressedAccountData {
1045        CompressedAccountData {
1046            discriminator: 1u64.to_le_bytes(),
1047            data: vec![1, 2, 3, 4, 5, 6, 7, 8],
1048            data_hash: [1; 32],
1049        }
1050    }
1051
1052    fn get_rnd_test_account_data(rng: &mut StdRng) -> CompressedAccountData {
1053        CompressedAccountData {
1054            discriminator: rng.gen(),
1055            data: (0..100).map(|_| rng.gen()).collect::<Vec<u8>>(),
1056            data_hash: rng.gen(),
1057        }
1058    }
1059
1060    fn get_test_account() -> CompressedAccount {
1061        CompressedAccount {
1062            owner: crate::Pubkey::new_unique(),
1063            lamports: 100,
1064            address: Some(Pubkey::new_unique().to_bytes()),
1065            data: Some(get_test_account_data()),
1066        }
1067    }
1068
1069    fn get_rnd_test_account(rng: &mut StdRng) -> CompressedAccount {
1070        CompressedAccount {
1071            owner: crate::Pubkey::new_unique(),
1072            lamports: rng.gen(),
1073            address: Some(Pubkey::new_unique().to_bytes()),
1074            data: Some(get_rnd_test_account_data(rng)),
1075        }
1076    }
1077
1078    fn get_test_output_account() -> OutputCompressedAccountWithPackedContext {
1079        OutputCompressedAccountWithPackedContext {
1080            compressed_account: get_test_account(),
1081            merkle_tree_index: 1,
1082        }
1083    }
1084
1085    fn get_rnd_test_output_account(rng: &mut StdRng) -> OutputCompressedAccountWithPackedContext {
1086        OutputCompressedAccountWithPackedContext {
1087            compressed_account: get_rnd_test_account(rng),
1088            merkle_tree_index: rng.gen(),
1089        }
1090    }
1091
1092    #[test]
1093    fn test_output_account_deserialize() {
1094        let test_output_account = get_test_output_account();
1095        let mut bytes = Vec::new();
1096        test_output_account.serialize(&mut bytes).unwrap();
1097
1098        let (z_copy, bytes) =
1099            ZOutputCompressedAccountWithPackedContext::zero_copy_at(&bytes).unwrap();
1100        assert!(bytes.is_empty());
1101        compare_compressed_output_account(&test_output_account, &z_copy).unwrap();
1102    }
1103
1104    fn compare_compressed_output_account(
1105        reference: &OutputCompressedAccountWithPackedContext,
1106        z_copy: &ZOutputCompressedAccountWithPackedContext,
1107    ) -> Result<(), CompressedAccountError> {
1108        compare_compressed_account(&reference.compressed_account, &z_copy.compressed_account)?;
1109        if reference.merkle_tree_index != z_copy.merkle_tree_index {
1110            return Err(CompressedAccountError::InvalidArgument);
1111        }
1112        Ok(())
1113    }
1114
1115    fn get_test_input_account() -> PackedCompressedAccountWithMerkleContext {
1116        PackedCompressedAccountWithMerkleContext {
1117            compressed_account: CompressedAccount {
1118                owner: crate::Pubkey::new_unique(),
1119                lamports: 100,
1120                address: Some(Pubkey::new_unique().to_bytes()),
1121                data: Some(CompressedAccountData {
1122                    discriminator: 1u64.to_le_bytes(),
1123                    data: vec![1, 2, 3, 4, 5, 6, 7, 8],
1124                    data_hash: [1; 32],
1125                }),
1126            },
1127            merkle_context: PackedMerkleContext {
1128                merkle_tree_pubkey_index: 1,
1129                queue_pubkey_index: 2,
1130                leaf_index: 3,
1131                prove_by_index: true,
1132            },
1133            root_index: 5,
1134            read_only: false,
1135        }
1136    }
1137
1138    fn get_rnd_test_input_account(rng: &mut StdRng) -> PackedCompressedAccountWithMerkleContext {
1139        PackedCompressedAccountWithMerkleContext {
1140            compressed_account: CompressedAccount {
1141                owner: crate::Pubkey::new_unique(),
1142                lamports: 100,
1143                address: Some(Pubkey::new_unique().to_bytes()),
1144                data: Some(get_rnd_test_account_data(rng)),
1145            },
1146            merkle_context: PackedMerkleContext {
1147                merkle_tree_pubkey_index: rng.gen(),
1148                queue_pubkey_index: rng.gen(),
1149                leaf_index: rng.gen(),
1150                prove_by_index: rng.gen(),
1151            },
1152            root_index: rng.gen(),
1153            read_only: false,
1154        }
1155    }
1156    #[test]
1157    fn test_input_account_deserialize() {
1158        let input_account = get_test_input_account();
1159
1160        let mut bytes = Vec::new();
1161        input_account.serialize(&mut bytes).unwrap();
1162
1163        let (z_copy, bytes) =
1164            ZPackedCompressedAccountWithMerkleContext::zero_copy_at(&bytes).unwrap();
1165
1166        assert!(bytes.is_empty());
1167        compare_packed_compressed_account_with_merkle_context(&input_account, &z_copy).unwrap();
1168    }
1169
1170    fn get_new_address_params() -> NewAddressParamsPacked {
1171        NewAddressParamsPacked {
1172            seed: [1; 32],
1173            address_queue_account_index: 1,
1174            address_merkle_tree_account_index: 2,
1175            address_merkle_tree_root_index: 3,
1176        }
1177    }
1178
1179    // get_instruction_data_invoke_cpi
1180    fn get_rnd_new_address_params(rng: &mut StdRng) -> NewAddressParamsPacked {
1181        NewAddressParamsPacked {
1182            seed: rng.gen(),
1183            address_queue_account_index: rng.gen(),
1184            address_merkle_tree_account_index: rng.gen(),
1185            address_merkle_tree_root_index: rng.gen(),
1186        }
1187    }
1188    #[test]
1189    fn test_account_data_deserialize() {
1190        let test_data = CompressedAccountData {
1191            discriminator: 1u64.to_le_bytes(),
1192            data: vec![1, 2, 3, 4, 5, 6, 7, 8],
1193            data_hash: [1; 32],
1194        };
1195
1196        let mut bytes = Vec::new();
1197        test_data.serialize(&mut bytes).unwrap();
1198
1199        let (z_copy, bytes) = ZCompressedAccountData::zero_copy_at(&bytes).unwrap();
1200        assert!(bytes.is_empty());
1201        assert_eq!(
1202            z_copy.discriminator.as_slice(),
1203            test_data.discriminator.as_slice()
1204        );
1205        assert_eq!(z_copy.data, test_data.data.as_slice());
1206        assert_eq!(z_copy.data_hash.as_slice(), test_data.data_hash.as_slice());
1207    }
1208
1209    #[test]
1210    fn test_invoke_ix_data_deserialize_rnd() {
1211        use rand::{rngs::StdRng, Rng, SeedableRng};
1212        let mut thread_rng = ThreadRng::default();
1213        let seed = thread_rng.gen();
1214        // Keep this print so that in case the test fails
1215        // we can use the seed to reproduce the error.
1216        println!("\n\ne2e test seed for invoke_ix_data {}\n\n", seed);
1217        let mut rng = StdRng::seed_from_u64(seed);
1218
1219        let num_iters = 1000;
1220        for i in 0..num_iters {
1221            // Create randomized instruction data
1222            let invoke_ref = InstructionDataInvoke {
1223                proof: if rng.gen() {
1224                    Some(CompressedProof {
1225                        a: rng.gen(),
1226                        b: (0..64)
1227                            .map(|_| rng.gen())
1228                            .collect::<Vec<u8>>()
1229                            .try_into()
1230                            .unwrap(),
1231                        c: rng.gen(),
1232                    })
1233                } else {
1234                    None
1235                },
1236                input_compressed_accounts_with_merkle_context: if i % 5 == 0 {
1237                    // Only add inputs occasionally to keep test manageable
1238                    vec![get_rnd_test_input_account(&mut rng); rng.gen_range(1..3)]
1239                } else {
1240                    vec![]
1241                },
1242                output_compressed_accounts: if i % 4 == 0 {
1243                    vec![get_rnd_test_output_account(&mut rng); rng.gen_range(1..3)]
1244                } else {
1245                    vec![]
1246                },
1247                relay_fee: None, // Relay fee is currently not supported
1248                new_address_params: if i % 3 == 0 {
1249                    vec![get_rnd_new_address_params(&mut rng); rng.gen_range(1..3)]
1250                } else {
1251                    vec![]
1252                },
1253                compress_or_decompress_lamports: if rng.gen() { Some(rng.gen()) } else { None },
1254                is_compress: rng.gen(),
1255            };
1256
1257            let mut bytes = Vec::new();
1258            invoke_ref.serialize(&mut bytes).unwrap();
1259
1260            let (z_copy, bytes) = ZInstructionDataInvoke::zero_copy_at(&bytes).unwrap();
1261            assert!(bytes.is_empty());
1262
1263            // Compare serialized and deserialized data
1264            compare_instruction_data(&invoke_ref, &z_copy).unwrap();
1265
1266            // Test trait methods
1267            assert!(z_copy.with_transaction_hash()); // Always true for ZInstructionDataInvoke
1268            assert!(z_copy.bump().is_none()); // Always None for ZInstructionDataInvoke
1269            assert_eq!(z_copy.is_compress(), invoke_ref.is_compress);
1270            assert_eq!(
1271                z_copy.compress_or_decompress_lamports(),
1272                invoke_ref.compress_or_decompress_lamports
1273            );
1274
1275            // The account_option_config() method will call unimplemented!(),
1276            // so we don't call it directly in the test. Instead, we'll just verify other trait methods.
1277
1278            // Additional trait method checks
1279            assert!(z_copy.read_only_accounts().is_none());
1280            assert!(z_copy.read_only_addresses().is_none());
1281
1282            // Verify new_addresses() - check that length matches
1283            assert_eq!(
1284                z_copy.new_addresses().len(),
1285                invoke_ref.new_address_params.len()
1286            );
1287
1288            // Verify input_accounts() and output_accounts() count matches
1289            assert_eq!(
1290                z_copy.input_accounts().len(),
1291                invoke_ref
1292                    .input_compressed_accounts_with_merkle_context
1293                    .len()
1294            );
1295            assert_eq!(
1296                z_copy.output_accounts().len(),
1297                invoke_ref.output_compressed_accounts.len()
1298            );
1299
1300            // Check owner() method returns expected value
1301            if !invoke_ref
1302                .input_compressed_accounts_with_merkle_context
1303                .is_empty()
1304            {
1305                let expected_owner: Pubkey = invoke_ref
1306                    .input_compressed_accounts_with_merkle_context[0]
1307                    .compressed_account
1308                    .owner;
1309                assert_eq!(z_copy.owner(), expected_owner);
1310            } else {
1311                assert_eq!(z_copy.owner(), Pubkey::default());
1312            }
1313        }
1314    }
1315
1316    fn compare_instruction_data(
1317        reference: &InstructionDataInvoke,
1318        z_copy: &ZInstructionDataInvoke,
1319    ) -> Result<(), CompressedAccountError> {
1320        if reference.proof.is_some() && z_copy.proof.is_none() {
1321            return Err(CompressedAccountError::InvalidArgument);
1322        }
1323        if reference.proof.is_none() && z_copy.proof.is_some() {
1324            return Err(CompressedAccountError::InvalidArgument);
1325        }
1326        if reference.proof.is_some() && z_copy.proof.is_some() {
1327            let ref_proof = reference.proof.as_ref().unwrap();
1328            let z_proof = z_copy.proof.as_ref().unwrap();
1329
1330            if ref_proof.a != z_proof.a || ref_proof.b != z_proof.b || ref_proof.c != z_proof.c {
1331                return Err(CompressedAccountError::InvalidArgument);
1332            }
1333        }
1334        if reference
1335            .input_compressed_accounts_with_merkle_context
1336            .len()
1337            != z_copy.input_compressed_accounts_with_merkle_context.len()
1338        {
1339            return Err(CompressedAccountError::InvalidArgument);
1340        }
1341        for (ref_input, z_input) in reference
1342            .input_compressed_accounts_with_merkle_context
1343            .iter()
1344            .zip(z_copy.input_compressed_accounts_with_merkle_context.iter())
1345        {
1346            compare_packed_compressed_account_with_merkle_context(ref_input, z_input)?;
1347        }
1348        if reference.output_compressed_accounts.len() != z_copy.output_compressed_accounts.len() {
1349            return Err(CompressedAccountError::InvalidArgument);
1350        }
1351        for (ref_output, z_output) in reference
1352            .output_compressed_accounts
1353            .iter()
1354            .zip(z_copy.output_compressed_accounts.iter())
1355        {
1356            compare_compressed_output_account(ref_output, z_output)?;
1357        }
1358        if reference.relay_fee != z_copy.relay_fee.map(|x| (*x).into()) {
1359            return Err(CompressedAccountError::InvalidArgument);
1360        }
1361        if reference.new_address_params.len() != z_copy.new_address_params.len() {
1362            return Err(CompressedAccountError::InvalidArgument);
1363        }
1364        for (ref_params, z_params) in reference
1365            .new_address_params
1366            .iter()
1367            .zip(z_copy.new_address_params.iter())
1368        {
1369            if ref_params.seed != z_params.seed {
1370                return Err(CompressedAccountError::InvalidArgument);
1371            }
1372            if ref_params.address_queue_account_index != z_params.address_queue_account_index {
1373                return Err(CompressedAccountError::InvalidArgument);
1374            }
1375            if ref_params.address_merkle_tree_account_index
1376                != z_params.address_merkle_tree_account_index
1377            {
1378                return Err(CompressedAccountError::InvalidArgument);
1379            }
1380            if ref_params.address_merkle_tree_root_index
1381                != u16::from(z_params.address_merkle_tree_root_index)
1382            {
1383                return Err(CompressedAccountError::InvalidArgument);
1384            }
1385        }
1386        Ok(())
1387    }
1388
1389    fn compare_compressed_account_data(
1390        reference: &CompressedAccountData,
1391        z_copy: &ZCompressedAccountData,
1392    ) -> Result<(), CompressedAccountError> {
1393        if reference.discriminator.as_slice() != z_copy.discriminator.as_slice() {
1394            return Err(CompressedAccountError::InvalidArgument);
1395        }
1396        if reference.data != z_copy.data {
1397            return Err(CompressedAccountError::InvalidArgument);
1398        }
1399        if reference.data_hash.as_slice() != z_copy.data_hash.as_slice() {
1400            return Err(CompressedAccountError::InvalidArgument);
1401        }
1402        Ok(())
1403    }
1404
1405    fn compare_compressed_account(
1406        reference: &CompressedAccount,
1407        z_copy: &ZCompressedAccount,
1408    ) -> Result<(), CompressedAccountError> {
1409        if reference.owner.to_bytes() != z_copy.owner.as_bytes() {
1410            return Err(CompressedAccountError::InvalidArgument);
1411        }
1412        if reference.lamports != u64::from(z_copy.lamports) {
1413            return Err(CompressedAccountError::InvalidArgument);
1414        }
1415        if reference.address != z_copy.address.map(|x| *x) {
1416            return Err(CompressedAccountError::InvalidArgument);
1417        }
1418        if reference.data.is_some() && z_copy.data.is_none() {
1419            return Err(CompressedAccountError::InvalidArgument);
1420        }
1421        if reference.data.is_none() && z_copy.data.is_some() {
1422            return Err(CompressedAccountError::InvalidArgument);
1423        }
1424        if reference.data.is_some() && z_copy.data.is_some() {
1425            compare_compressed_account_data(
1426                reference.data.as_ref().unwrap(),
1427                z_copy.data.as_ref().unwrap(),
1428            )?;
1429        }
1430        Ok(())
1431    }
1432
1433    fn compare_merkle_context(
1434        reference: PackedMerkleContext,
1435        z_copy: ZPackedMerkleContext,
1436    ) -> Result<(), CompressedAccountError> {
1437        if reference.merkle_tree_pubkey_index != z_copy.merkle_tree_pubkey_index {
1438            return Err(CompressedAccountError::InvalidArgument);
1439        }
1440        if reference.queue_pubkey_index != z_copy.queue_pubkey_index {
1441            return Err(CompressedAccountError::InvalidArgument);
1442        }
1443        if reference.leaf_index != u32::from(z_copy.leaf_index) {
1444            return Err(CompressedAccountError::InvalidArgument);
1445        }
1446        if reference.prove_by_index != (z_copy.prove_by_index == 1) {
1447            return Err(CompressedAccountError::InvalidArgument);
1448        }
1449        Ok(())
1450    }
1451
1452    fn compare_packed_compressed_account_with_merkle_context(
1453        reference: &PackedCompressedAccountWithMerkleContext,
1454        z_copy: &ZPackedCompressedAccountWithMerkleContext,
1455    ) -> Result<(), CompressedAccountError> {
1456        compare_compressed_account(&reference.compressed_account, &z_copy.compressed_account)?;
1457        compare_merkle_context(reference.merkle_context, z_copy.merkle_context)?;
1458        if reference.root_index != u16::from(z_copy.root_index) {
1459            return Err(CompressedAccountError::InvalidArgument);
1460        }
1461
1462        Ok(())
1463    }
1464
1465    #[test]
1466    fn test_instruction_data_invoke_cpi_rnd() {
1467        use rand::{rngs::StdRng, Rng, SeedableRng};
1468        let mut thread_rng = ThreadRng::default();
1469        let seed = thread_rng.gen();
1470        // Keep this print so that in case the test fails
1471        // we can use the seed to reproduce the error.
1472        println!("\n\ne2e test seed {}\n\n", seed);
1473        let mut rng = StdRng::seed_from_u64(seed);
1474
1475        let num_iters = 10000;
1476        for _ in 0..num_iters {
1477            let value = get_rnd_instruction_data_invoke_cpi(&mut rng);
1478            let mut vec = Vec::new();
1479            value.serialize(&mut vec).unwrap();
1480            let (zero_copy, _) = ZInstructionDataInvokeCpi::zero_copy_at(&vec).unwrap();
1481            compare_invoke_cpi_instruction_data(&value, &zero_copy).unwrap();
1482        }
1483    }
1484}