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