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