1use light_hasher::{Hasher, Poseidon};
2use light_zero_copy::{ZeroCopy, ZeroCopyMut};
3
4use crate::{
5 instruction_data::zero_copy::ZCompressedAccount, CompressedAccountError, Pubkey, TreeType, Vec,
6};
7
8#[repr(C)]
9#[cfg_attr(
10 all(feature = "std", feature = "anchor"),
11 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
12)]
13#[cfg_attr(
14 not(feature = "anchor"),
15 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
16)]
17#[derive(Debug, PartialEq, Default, Clone, ZeroCopyMut)]
18pub struct PackedCompressedAccountWithMerkleContext {
19 pub compressed_account: CompressedAccount,
20 pub merkle_context: PackedMerkleContext,
21 pub root_index: u16,
23 pub read_only: bool,
24}
25
26#[cfg_attr(
27 all(feature = "std", feature = "anchor"),
28 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
29)]
30#[cfg_attr(
31 not(feature = "anchor"),
32 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
33)]
34#[derive(Debug, PartialEq, Default, Clone)]
35pub struct InCompressedAccountWithMerkleContext {
36 pub compressed_account: InCompressedAccount,
37 pub merkle_context: MerkleContext,
38}
39
40impl From<CompressedAccount> for InCompressedAccount {
41 fn from(value: CompressedAccount) -> Self {
42 let data = value.data.unwrap_or_default();
43 InCompressedAccount {
44 owner: value.owner,
45 lamports: value.lamports,
46 address: value.address,
47 discriminator: data.discriminator,
48 data_hash: data.data_hash,
49 }
50 }
51}
52
53#[cfg_attr(
54 all(feature = "std", feature = "anchor"),
55 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
56)]
57#[cfg_attr(
58 not(feature = "anchor"),
59 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
60)]
61#[derive(Debug, PartialEq, Default, Clone)]
62pub struct PackedInCompressedAccountWithMerkleContext {
63 pub compressed_account: InCompressedAccount,
64 pub merkle_context: PackedMerkleContext,
65 pub root_index: u16,
67}
68
69impl From<PackedCompressedAccountWithMerkleContext> for PackedInCompressedAccountWithMerkleContext {
70 fn from(value: PackedCompressedAccountWithMerkleContext) -> Self {
71 Self {
72 compressed_account: value.compressed_account.into(),
73 merkle_context: value.merkle_context,
74 root_index: value.root_index,
75 }
76 }
77}
78
79impl From<CompressedAccountWithMerkleContext> for InCompressedAccountWithMerkleContext {
80 fn from(value: CompressedAccountWithMerkleContext) -> Self {
81 Self {
82 compressed_account: value.compressed_account.into(),
83 merkle_context: value.merkle_context,
84 }
85 }
86}
87
88#[cfg_attr(
89 all(feature = "std", feature = "anchor"),
90 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
91)]
92#[cfg_attr(
93 not(feature = "anchor"),
94 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
95)]
96#[derive(Debug, PartialEq, Default, Clone)]
97pub struct CompressedAccountWithMerkleContext {
98 pub compressed_account: CompressedAccount,
99 pub merkle_context: MerkleContext,
100}
101
102impl CompressedAccountWithMerkleContext {
103 pub fn hash(&self) -> Result<[u8; 32], CompressedAccountError> {
104 self.compressed_account.hash(
105 &self.merkle_context.merkle_tree_pubkey,
106 &self.merkle_context.leaf_index,
107 self.merkle_context.tree_type == TreeType::StateV2,
108 )
109 }
110}
111
112impl CompressedAccountWithMerkleContext {
113 pub fn into_read_only(
114 &self,
115 root_index: Option<u16>,
116 ) -> Result<ReadOnlyCompressedAccount, CompressedAccountError> {
117 let account_hash = self.hash()?;
118 let merkle_context = if root_index.is_none() {
119 let mut merkle_context = self.merkle_context;
120 merkle_context.prove_by_index = true;
121 merkle_context
122 } else {
123 self.merkle_context
124 };
125 Ok(ReadOnlyCompressedAccount {
126 account_hash,
127 merkle_context,
128 root_index: root_index.unwrap_or_default(),
129 })
130 }
131}
132
133#[cfg_attr(
134 all(feature = "std", feature = "anchor"),
135 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
136)]
137#[cfg_attr(
138 not(feature = "anchor"),
139 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
140)]
141#[derive(Debug, PartialEq, Default, Clone)]
142pub struct ReadOnlyCompressedAccount {
143 pub account_hash: [u8; 32],
144 pub merkle_context: MerkleContext,
145 pub root_index: u16,
146}
147
148#[repr(C)]
149#[cfg_attr(
150 all(feature = "std", feature = "anchor"),
151 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
152)]
153#[cfg_attr(
154 not(feature = "anchor"),
155 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
156)]
157#[derive(Debug, PartialEq, Default, Clone, ZeroCopyMut)]
158pub struct PackedReadOnlyCompressedAccount {
159 pub account_hash: [u8; 32],
160 pub merkle_context: PackedMerkleContext,
161 pub root_index: u16,
162}
163
164#[cfg_attr(
165 all(feature = "std", feature = "anchor"),
166 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
167)]
168#[cfg_attr(
169 not(feature = "anchor"),
170 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
171)]
172#[derive(Debug, Clone, Copy, PartialEq, Default)]
173pub struct MerkleContext {
174 pub merkle_tree_pubkey: Pubkey,
175 pub queue_pubkey: Pubkey,
176 pub leaf_index: u32,
177 pub prove_by_index: bool,
178 pub tree_type: TreeType,
179}
180
181#[repr(C)]
182#[cfg_attr(
183 all(feature = "std", feature = "anchor"),
184 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
185)]
186#[cfg_attr(
187 not(feature = "anchor"),
188 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
189)]
190#[derive(Debug, Clone, Copy, PartialEq, Default, ZeroCopy, ZeroCopyMut)]
191pub struct PackedMerkleContext {
192 pub merkle_tree_pubkey_index: u8,
193 pub queue_pubkey_index: u8,
194 pub leaf_index: u32,
195 pub prove_by_index: bool,
196}
197
198#[repr(C)]
199#[cfg_attr(
200 all(feature = "std", feature = "anchor"),
201 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
202)]
203#[cfg_attr(
204 not(feature = "anchor"),
205 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
206)]
207#[derive(Debug, PartialEq, Default, Clone, ZeroCopyMut)]
208pub struct CompressedAccount {
209 pub owner: Pubkey,
210 pub lamports: u64,
211 pub address: Option<[u8; 32]>,
212 pub data: Option<CompressedAccountData>,
213}
214
215#[cfg_attr(
216 all(feature = "std", feature = "anchor"),
217 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
218)]
219#[cfg_attr(
220 not(feature = "anchor"),
221 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
222)]
223#[derive(Debug, PartialEq, Default, Clone)]
224pub struct InCompressedAccount {
225 pub owner: Pubkey,
226 pub lamports: u64,
227 pub discriminator: [u8; 8],
228 pub data_hash: [u8; 32],
229 pub address: Option<[u8; 32]>,
230}
231
232#[repr(C)]
233#[cfg_attr(
234 all(feature = "std", feature = "anchor"),
235 derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
236)]
237#[cfg_attr(
238 not(feature = "anchor"),
239 derive(borsh::BorshDeserialize, borsh::BorshSerialize)
240)]
241#[derive(Debug, PartialEq, Default, Clone, ZeroCopyMut)]
242pub struct CompressedAccountData {
243 pub discriminator: [u8; 8],
244 pub data: Vec<u8>,
245 pub data_hash: [u8; 32],
246}
247
248pub fn hash_with_hashed_values(
249 lamports: &u64,
250 address: Option<&[u8]>,
251 data: Option<(&[u8], &[u8])>,
252 owner_hashed: &[u8; 32],
253 merkle_tree_hashed: &[u8; 32],
254 leaf_index: &u32,
255 is_batched: bool,
256) -> Result<[u8; 32], CompressedAccountError> {
257 let mut vec: tinyvec::ArrayVec<[&[u8]; 7]> = tinyvec::ArrayVec::new();
260 vec.push(owner_hashed.as_slice());
261
262 let mut leaf_index_bytes = [0u8; 32];
264 if is_batched {
265 leaf_index_bytes[28..].copy_from_slice(&leaf_index.to_be_bytes());
266 } else {
267 leaf_index_bytes[28..].copy_from_slice(&leaf_index.to_le_bytes());
268 };
269 vec.push(leaf_index_bytes.as_slice());
270
271 vec.push(merkle_tree_hashed.as_slice());
272
273 let mut lamports_bytes = [0u8; 32];
277 if *lamports != 0 {
278 if is_batched {
279 lamports_bytes[24..].copy_from_slice(&lamports.to_be_bytes());
280 } else {
281 lamports_bytes[24..].copy_from_slice(&lamports.to_le_bytes());
282 };
283 lamports_bytes[23] = 1;
284
285 vec.push(lamports_bytes.as_slice());
286 }
287 if let Some(address) = address {
288 vec.push(address);
289 }
290
291 let mut discriminator_bytes = [0u8; 32];
292 if let Some((discriminator, data_hash)) = data {
293 discriminator_bytes[24..].copy_from_slice(discriminator);
294 discriminator_bytes[23] = 2;
295 vec.push(&discriminator_bytes);
296 vec.push(data_hash);
297 }
298 Ok(Poseidon::hashv(&vec)?)
299}
300
301impl CompressedAccount {
304 pub fn hash_with_hashed_values(
305 &self,
306 owner_hashed: &[u8; 32],
307 merkle_tree_hashed: &[u8; 32],
308 leaf_index: &u32,
309 is_batched: bool,
310 ) -> Result<[u8; 32], CompressedAccountError> {
311 hash_with_hashed_values(
312 &self.lamports,
313 self.address.as_ref().map(|x| x.as_slice()),
314 self.data
315 .as_ref()
316 .map(|x| (x.discriminator.as_slice(), x.data_hash.as_slice())),
317 owner_hashed,
318 merkle_tree_hashed,
319 leaf_index,
320 is_batched,
321 )
322 }
323
324 pub fn hash(
325 &self,
326 &merkle_tree_pubkey: &Pubkey,
327 leaf_index: &u32,
328 is_batched: bool,
329 ) -> Result<[u8; 32], CompressedAccountError> {
330 use light_hasher::hash_to_field_size::hash_to_bn254_field_size_be;
331 let hashed_mt = hash_to_bn254_field_size_be(merkle_tree_pubkey.as_ref());
332
333 self.hash_with_hashed_values(
334 &hash_to_bn254_field_size_be(self.owner.as_ref()),
335 &hashed_mt,
336 leaf_index,
337 is_batched,
338 )
339 }
340}
341
342impl ZCompressedAccount<'_> {
345 pub fn hash_with_hashed_values(
346 &self,
347 owner_hashed: &[u8; 32],
348 merkle_tree_hashed: &[u8; 32],
349 leaf_index: &u32,
350 is_batched: bool,
351 ) -> Result<[u8; 32], CompressedAccountError> {
352 hash_with_hashed_values(
353 &(self.lamports.into()),
354 self.address.as_ref().map(|x| x.as_slice()),
355 self.data
356 .as_ref()
357 .map(|x| (x.discriminator.as_slice(), x.data_hash.as_slice())),
358 owner_hashed,
359 merkle_tree_hashed,
360 leaf_index,
361 is_batched,
362 )
363 }
364 pub fn hash(
365 &self,
366 &merkle_tree_pubkey: &[u8; 32],
367 leaf_index: &u32,
368 is_batched: bool,
369 ) -> Result<[u8; 32], CompressedAccountError> {
370 use light_hasher::hash_to_field_size::hash_to_bn254_field_size_be;
371 self.hash_with_hashed_values(
372 &hash_to_bn254_field_size_be(&self.owner.to_bytes()),
373 &hash_to_bn254_field_size_be(merkle_tree_pubkey.as_slice()),
374 leaf_index,
375 is_batched,
376 )
377 }
378}
379
380#[cfg(all(not(feature = "pinocchio"), test, feature = "poseidon"))]
381mod tests {
382 use borsh::BorshSerialize;
383 use light_hasher::{hash_to_field_size::hash_to_bn254_field_size_be, Poseidon};
384 use light_zero_copy::traits::ZeroCopyAt;
385 use num_bigint::BigUint;
386 use rand::Rng;
387
388 use super::*;
389 #[test]
397 fn test_compressed_account_hash() {
398 let owner = Pubkey::new_unique();
399 let address = [1u8; 32];
400 let data = CompressedAccountData {
401 discriminator: [1u8; 8],
402 data: vec![2u8; 32],
403 data_hash: [3u8; 32],
404 };
405 let lamports = 100;
406 let compressed_account = CompressedAccount {
407 owner,
408 lamports,
409 address: Some(address),
410 data: Some(data.clone()),
411 };
412 let merkle_tree_pubkey = Pubkey::new_unique();
413 let leaf_index: u32 = 1;
414 let hash = compressed_account
415 .hash(&merkle_tree_pubkey, &leaf_index, false)
416 .unwrap();
417 let hash_manual = Poseidon::hashv(&[
418 hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(),
419 leaf_index.to_le_bytes().as_slice(),
420 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(),
421 [&[1u8], lamports.to_le_bytes().as_slice()]
422 .concat()
423 .as_slice(),
424 address.as_slice(),
425 [&[2u8], data.discriminator.as_slice()].concat().as_slice(),
426 &data.data_hash,
427 ])
428 .unwrap();
429 assert_eq!(hash, hash_manual);
430 assert_eq!(hash.len(), 32);
431
432 let compressed_account = CompressedAccount {
434 owner,
435 lamports,
436 address: Some(address),
437 data: None,
438 };
439 let no_data_hash = compressed_account
440 .hash(&merkle_tree_pubkey, &leaf_index, false)
441 .unwrap();
442
443 let hash_manual = Poseidon::hashv(&[
444 hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(),
445 leaf_index.to_le_bytes().as_slice(),
446 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(),
447 [&[1u8], lamports.to_le_bytes().as_slice()]
448 .concat()
449 .as_slice(),
450 address.as_slice(),
451 ])
452 .unwrap();
453 assert_eq!(no_data_hash, hash_manual);
454 assert_ne!(hash, no_data_hash);
455
456 let compressed_account = CompressedAccount {
458 owner,
459 lamports,
460 address: None,
461 data: Some(data.clone()),
462 };
463 let no_address_hash = compressed_account
464 .hash(&merkle_tree_pubkey, &leaf_index, false)
465 .unwrap();
466 let hash_manual = Poseidon::hashv(&[
467 hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(),
468 leaf_index.to_le_bytes().as_slice(),
469 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(),
470 [&[1u8], lamports.to_le_bytes().as_slice()]
471 .concat()
472 .as_slice(),
473 [&[2u8], data.discriminator.as_slice()].concat().as_slice(),
474 &data.data_hash,
475 ])
476 .unwrap();
477 assert_eq!(no_address_hash, hash_manual);
478 assert_ne!(hash, no_address_hash);
479 assert_ne!(no_data_hash, no_address_hash);
480
481 let compressed_account = CompressedAccount {
483 owner,
484 lamports: 0,
485 address: None,
486 data: Some(data.clone()),
487 };
488 let no_address_no_lamports_hash = compressed_account
489 .hash(&merkle_tree_pubkey, &leaf_index, false)
490 .unwrap();
491 let hash_manual = Poseidon::hashv(&[
492 hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(),
493 leaf_index.to_le_bytes().as_slice(),
494 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(),
495 [&[2u8], data.discriminator.as_slice()].concat().as_slice(),
496 &data.data_hash,
497 ])
498 .unwrap();
499 assert_eq!(no_address_no_lamports_hash, hash_manual);
500 assert_ne!(hash, no_address_no_lamports_hash);
501 assert_ne!(no_data_hash, no_address_no_lamports_hash);
502 assert_ne!(no_address_hash, no_address_no_lamports_hash);
503
504 let compressed_account = CompressedAccount {
506 owner,
507 lamports,
508 address: None,
509 data: None,
510 };
511 let no_address_no_data_hash = compressed_account
512 .hash(&merkle_tree_pubkey, &leaf_index, false)
513 .unwrap();
514 let hash_manual = Poseidon::hashv(&[
515 hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(),
516 leaf_index.to_le_bytes().as_slice(),
517 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(),
518 [&[1u8], lamports.to_le_bytes().as_slice()]
519 .concat()
520 .as_slice(),
521 ])
522 .unwrap();
523 assert_eq!(no_address_no_data_hash, hash_manual);
524 assert_ne!(hash, no_address_no_data_hash);
525 assert_ne!(no_data_hash, no_address_no_data_hash);
526 assert_ne!(no_address_hash, no_address_no_data_hash);
527 assert_ne!(no_address_no_lamports_hash, no_address_no_data_hash);
528
529 let compressed_account = CompressedAccount {
531 owner,
532 lamports: 0,
533 address: None,
534 data: None,
535 };
536 let no_address_no_data_no_lamports_hash = compressed_account
537 .hash(&merkle_tree_pubkey, &leaf_index, false)
538 .unwrap();
539 let hash_manual = Poseidon::hashv(&[
540 hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(),
541 leaf_index.to_le_bytes().as_slice(),
542 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(),
543 ])
544 .unwrap();
545 assert_eq!(no_address_no_data_no_lamports_hash, hash_manual);
546 assert_ne!(no_address_no_data_hash, no_address_no_data_no_lamports_hash);
547 assert_ne!(hash, no_address_no_data_no_lamports_hash);
548 assert_ne!(no_data_hash, no_address_no_data_no_lamports_hash);
549 assert_ne!(no_address_hash, no_address_no_data_no_lamports_hash);
550 assert_ne!(
551 no_address_no_lamports_hash,
552 no_address_no_data_no_lamports_hash
553 );
554 }
555
556 #[test]
557 fn reference() {
558 let owner = Pubkey::new_from_array([
559 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
560 0, 0, 0,
561 ]);
562 let address = [
563 0, 21, 245, 15, 61, 157, 224, 84, 69, 48, 190, 72, 43, 19, 47, 25, 14, 118, 20, 147,
564 40, 141, 175, 33, 233, 58, 36, 179, 73, 137, 84, 99,
565 ];
566
567 let data = CompressedAccountData {
568 discriminator: [0, 0, 0, 0, 0, 0, 0, 1],
569 data: vec![2u8; 31],
570 data_hash: Poseidon::hash(&[vec![2u8; 31], vec![0u8]].concat()).unwrap(),
571 };
572 let lamports = 100;
573 let compressed_account = CompressedAccount {
574 owner,
575 lamports,
576 address: Some(address),
577 data: Some(data.clone()),
578 };
579 let bytes: Vec<u8> = compressed_account.try_to_vec().unwrap();
580 let merkle_tree_pubkey = Pubkey::new_from_array([
581 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
582 0, 0, 0,
583 ]);
584
585 let leaf_index: u32 = 1;
586 let hash = compressed_account
587 .hash(&merkle_tree_pubkey, &leaf_index, false)
588 .unwrap();
589 let (z_account, _) = ZCompressedAccount::zero_copy_at(&bytes).unwrap();
590 let z_hash = z_account
591 .hash(&merkle_tree_pubkey.to_bytes(), &leaf_index, false)
592 .unwrap();
593 let manual_hash = {
594 let mut hasher = light_poseidon::Poseidon::<Fr>::new_circom(7).unwrap();
595 use ark_bn254::Fr;
596 use ark_ff::{BigInteger, PrimeField};
597 let hashed_owner = hash_to_bn254_field_size_be(&owner.to_bytes());
598 let owner = Fr::from_be_bytes_mod_order(hashed_owner.as_slice());
599 let leaf_index = Fr::from_be_bytes_mod_order(leaf_index.to_le_bytes().as_ref());
600 let hashed_mt = hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes());
601 let merkle_tree_pubkey = Fr::from_be_bytes_mod_order(hashed_mt.as_slice());
602 let lamports = Fr::from_be_bytes_mod_order(lamports.to_le_bytes().as_ref())
603 + Fr::from_be_bytes_mod_order(&[1u8, 0, 0, 0, 0, 0, 0, 0, 0]);
604 let address = Fr::from_be_bytes_mod_order(address.as_slice());
605 let discriminator = Fr::from_be_bytes_mod_order(data.discriminator.as_ref());
606 let domain_separated_discriminator =
607 Fr::from_be_bytes_mod_order(&[2, 0, 0, 0, 0, 0, 0, 0, 0]);
608 let data_discriminator = discriminator + domain_separated_discriminator;
609 use light_poseidon::PoseidonHasher;
610 let inputs = [
611 owner,
612 leaf_index,
613 merkle_tree_pubkey,
614 lamports,
615 address,
616 data_discriminator,
617 Fr::from_be_bytes_mod_order(data.data_hash.as_ref()),
618 ];
619 hasher.hash(&inputs).unwrap().into_bigint().to_bytes_be()
620 };
621 assert_eq!(hash.to_vec(), manual_hash);
622 assert_eq!(z_hash.to_vec(), manual_hash);
623 assert_eq!(hash.len(), 32);
624
625 let manual_hash_new = {
626 let mut hasher = light_poseidon::Poseidon::<Fr>::new_circom(7).unwrap();
627 use ark_bn254::Fr;
628 use ark_ff::{BigInteger, PrimeField};
629 let hashed_owner = hash_to_bn254_field_size_be(&owner.to_bytes());
630 let owner = Fr::from_be_bytes_mod_order(hashed_owner.as_slice());
631 let leaf_index = Fr::from_be_bytes_mod_order(leaf_index.to_be_bytes().as_ref());
632 let hashed_mt = hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes());
633 let merkle_tree_pubkey = Fr::from_be_bytes_mod_order(hashed_mt.as_slice());
634 let lamports = Fr::from_be_bytes_mod_order(lamports.to_be_bytes().as_ref())
635 + Fr::from_be_bytes_mod_order(&[1u8, 0, 0, 0, 0, 0, 0, 0, 0]);
636 let address = Fr::from_be_bytes_mod_order(address.as_slice());
637 let discriminator = Fr::from_be_bytes_mod_order(data.discriminator.as_ref());
638 let domain_separated_discriminator =
639 Fr::from_be_bytes_mod_order(&[2, 0, 0, 0, 0, 0, 0, 0, 0]);
640 let data_discriminator = discriminator + domain_separated_discriminator;
641 use light_poseidon::PoseidonHasher;
642 let inputs = [
643 owner,
644 leaf_index,
645 merkle_tree_pubkey,
646 lamports,
647 address,
648 data_discriminator,
649 Fr::from_be_bytes_mod_order(data.data_hash.as_ref()),
650 ];
651 hasher.hash(&inputs).unwrap().into_bigint().to_bytes_be()
652 };
653 let hash = compressed_account
654 .hash(&merkle_tree_pubkey, &leaf_index, true)
655 .unwrap();
656 let z_hash = z_account
657 .hash(&merkle_tree_pubkey.to_bytes(), &leaf_index, true)
658 .unwrap();
659 assert_ne!(hash.to_vec(), manual_hash);
660 assert_eq!(hash.to_vec(), manual_hash_new);
661 assert_eq!(z_hash.to_vec(), manual_hash_new);
662 assert_eq!(hash.len(), 32);
663 use std::str::FromStr;
664 let circuit_reference_value = BigUint::from_str(
665 "15638319165413000277907073391141043184436601830909724248083671155000605125280",
666 )
667 .unwrap()
668 .to_bytes_be();
669 println!(
670 "lamports domain: {:?}",
671 BigUint::from_bytes_be(&[1u8, 0, 0, 0, 0, 0, 0, 0, 0]).to_string()
672 );
673 println!(
674 "discriminator domain: {:?}",
675 BigUint::from_bytes_be(&[2u8, 0, 0, 0, 0, 0, 0, 0, 0]).to_string()
676 );
677 assert_eq!(hash.to_vec(), circuit_reference_value);
678 }
679
680 impl CompressedAccount {
681 pub fn legacy_hash_with_values<H: Hasher>(
682 &self,
683 &owner_hashed: &[u8; 32],
684 &merkle_tree_hashed: &[u8; 32],
685 leaf_index: &u32,
686 ) -> Result<[u8; 32], CompressedAccountError> {
687 let capacity = 3
688 + std::cmp::min(self.lamports, 1) as usize
689 + self.address.is_some() as usize
690 + self.data.is_some() as usize * 2;
691 let mut vec: Vec<&[u8]> = Vec::with_capacity(capacity);
692 vec.push(owner_hashed.as_slice());
693
694 let leaf_index = leaf_index.to_le_bytes();
696 vec.push(leaf_index.as_slice());
697
698 vec.push(merkle_tree_hashed.as_slice());
699
700 let mut lamports_bytes = [1, 0, 0, 0, 0, 0, 0, 0, 0];
704 if self.lamports != 0 {
705 lamports_bytes[1..].copy_from_slice(&self.lamports.to_le_bytes());
706 vec.push(lamports_bytes.as_slice());
707 }
708
709 if self.address.is_some() {
710 vec.push(self.address.as_ref().unwrap().as_slice());
711 }
712
713 let mut discriminator_bytes = [2, 0, 0, 0, 0, 0, 0, 0, 0];
714 if let Some(data) = &self.data {
715 discriminator_bytes[1..].copy_from_slice(&data.discriminator);
716 vec.push(&discriminator_bytes);
717 vec.push(&data.data_hash);
718 }
719 let hash = H::hashv(&vec)?;
720 Ok(hash)
721 }
722
723 pub fn hash_legacy<H: Hasher>(
724 &self,
725 &merkle_tree_pubkey: &Pubkey,
726 leaf_index: &u32,
727 ) -> Result<[u8; 32], CompressedAccountError> {
728 let hashed_mt = hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes());
729 self.legacy_hash_with_values::<H>(
730 &hash_to_bn254_field_size_be(&self.owner.to_bytes()),
731 &hashed_mt,
732 leaf_index,
733 )
734 }
735 }
736
737 fn equivalency_of_hash_functions_rnd_iters<const ITERS: usize>() {
738 let mut rng = rand::thread_rng();
739
740 for _ in 0..ITERS {
741 let account = CompressedAccount {
742 owner: Pubkey::new_unique(),
743 lamports: rng.gen::<u64>(),
744 address: if rng.gen_bool(0.5) {
745 let mut address = rng.gen::<[u8; 32]>();
746 address[0] = 0;
747 Some(address)
748 } else {
749 None
750 },
751 data: if rng.gen_bool(0.5) {
752 Some(CompressedAccountData {
753 discriminator: rng.gen(),
754 data: Vec::new(), data_hash: Poseidon::hash(rng.gen::<u64>().to_be_bytes().as_slice())
756 .unwrap(),
757 })
758 } else {
759 None
760 },
761 };
762 let leaf_index = rng.gen::<u32>();
763 let merkle_tree_pubkey = Pubkey::new_unique();
764 let hash_legacy = account
765 .hash_legacy::<Poseidon>(&merkle_tree_pubkey, &leaf_index)
766 .unwrap();
767 let hash = account
768 .hash(&merkle_tree_pubkey, &leaf_index, false)
769 .unwrap();
770 let bytes: Vec<u8> = account.try_to_vec().unwrap();
771 let (z_account, _) = ZCompressedAccount::zero_copy_at(bytes.as_slice()).unwrap();
772 let z_hash = z_account
773 .hash(&merkle_tree_pubkey.to_bytes(), &leaf_index, false)
774 .unwrap();
775 assert_eq!(hash_legacy, hash);
776 assert_eq!(hash, z_hash);
777 }
778 }
779
780 #[test]
781 fn equivalency_of_hash_functions() {
782 equivalency_of_hash_functions_rnd_iters::<100>();
783 }
784}