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