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