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