light_compressed_account/instruction_data/
zero_copy.rs

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