Skip to main content

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