light_compressed_account/instruction_data/
zero_copy.rs

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