Skip to content

Commit 7ff105c

Browse files
authored
chore: refactored insert_into_queues -> instruction_data, z_compressed_account hash -> compressed_account.rs (#1589)
1 parent 2f1164b commit 7ff105c

File tree

20 files changed

+301
-307
lines changed

20 files changed

+301
-307
lines changed

program-libs/compressed-account/src/compressed_account.rs

Lines changed: 271 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ use light_hasher::{Hasher, Poseidon};
88
use solana_program::pubkey::Pubkey;
99

1010
use crate::{
11-
address::pack_account, hash_to_bn254_field_size_be,
12-
instruction_data::data::OutputCompressedAccountWithPackedContext, CompressedAccountError,
11+
address::pack_account,
12+
hash_to_bn254_field_size_be,
13+
instruction_data::{
14+
data::OutputCompressedAccountWithPackedContext, zero_copy::ZCompressedAccount,
15+
},
16+
CompressedAccountError,
1317
};
1418

1519
#[derive(Debug, PartialEq, Default, Clone, AnchorSerialize, AnchorDeserialize)]
@@ -261,6 +265,68 @@ impl CompressedAccount {
261265
}
262266
}
263267

268+
/// Hashing scheme:
269+
/// H(owner || leaf_index || merkle_tree_pubkey || lamports || address || data.discriminator || data.data_hash)
270+
impl ZCompressedAccount<'_> {
271+
pub fn hash_with_hashed_values<H: Hasher>(
272+
&self,
273+
&owner_hashed: &[u8; 32],
274+
&merkle_tree_hashed: &[u8; 32],
275+
leaf_index: &u32,
276+
) -> Result<[u8; 32], CompressedAccountError> {
277+
let capacity = 3
278+
+ std::cmp::min(u64::from(self.lamports), 1) as usize
279+
+ self.address.is_some() as usize
280+
+ self.data.is_some() as usize * 2;
281+
let mut vec: Vec<&[u8]> = Vec::with_capacity(capacity);
282+
vec.push(owner_hashed.as_slice());
283+
284+
// leaf index and merkle tree pubkey are used to make every compressed account hash unique
285+
let leaf_index = leaf_index.to_le_bytes();
286+
vec.push(leaf_index.as_slice());
287+
288+
vec.push(merkle_tree_hashed.as_slice());
289+
290+
// Lamports are only hashed if non-zero to safe CU
291+
// For safety we prefix the lamports with 1 in 1 byte.
292+
// Thus even if the discriminator has the same value as the lamports, the hash will be different.
293+
let mut lamports_bytes = [1, 0, 0, 0, 0, 0, 0, 0, 0];
294+
if self.lamports != 0 {
295+
lamports_bytes[1..].copy_from_slice(&(u64::from(self.lamports)).to_le_bytes());
296+
vec.push(lamports_bytes.as_slice());
297+
}
298+
299+
if self.address.is_some() {
300+
vec.push(self.address.as_ref().unwrap().as_slice());
301+
}
302+
303+
let mut discriminator_bytes = [2, 0, 0, 0, 0, 0, 0, 0, 0];
304+
if let Some(data) = &self.data {
305+
discriminator_bytes[1..].copy_from_slice(data.discriminator.as_slice());
306+
vec.push(&discriminator_bytes);
307+
vec.push(data.data_hash.as_slice());
308+
}
309+
let hash = H::hashv(&vec)?;
310+
Ok(hash)
311+
}
312+
313+
pub fn hash<H: Hasher>(
314+
&self,
315+
&merkle_tree_pubkey: &Pubkey,
316+
leaf_index: &u32,
317+
) -> Result<[u8; 32], CompressedAccountError> {
318+
self.hash_with_hashed_values::<H>(
319+
&hash_to_bn254_field_size_be(&self.owner.to_bytes())
320+
.unwrap()
321+
.0,
322+
&hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
323+
.unwrap()
324+
.0,
325+
leaf_index,
326+
)
327+
}
328+
}
329+
264330
#[cfg(test)]
265331
mod tests {
266332
use light_hasher::Poseidon;
@@ -468,4 +534,207 @@ mod tests {
468534
no_address_no_data_no_lamports_hash
469535
);
470536
}
537+
538+
/// Tests:
539+
/// 1. functional with all inputs set
540+
/// 2. no data
541+
/// 3. no address
542+
/// 4. no address and no lamports
543+
/// 5. no address and no data
544+
/// 6. no address, no data, no lamports
545+
#[test]
546+
fn test_zcompressed_account_hash() {
547+
let owner = Pubkey::new_unique();
548+
let address = [1u8; 32];
549+
let data = CompressedAccountData {
550+
discriminator: [1u8; 8],
551+
data: vec![2u8; 32],
552+
data_hash: [3u8; 32],
553+
};
554+
let lamports = 100;
555+
let compressed_account = CompressedAccount {
556+
owner,
557+
lamports,
558+
address: Some(address),
559+
data: Some(data.clone()),
560+
};
561+
let merkle_tree_pubkey = Pubkey::new_unique();
562+
let leaf_index = 1;
563+
let hash = compressed_account
564+
.hash::<Poseidon>(&merkle_tree_pubkey, &leaf_index)
565+
.unwrap();
566+
let hash_manual = Poseidon::hashv(&[
567+
hash_to_bn254_field_size_be(&owner.to_bytes())
568+
.unwrap()
569+
.0
570+
.as_slice(),
571+
leaf_index.to_le_bytes().as_slice(),
572+
hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
573+
.unwrap()
574+
.0
575+
.as_slice(),
576+
[&[1u8], lamports.to_le_bytes().as_slice()]
577+
.concat()
578+
.as_slice(),
579+
address.as_slice(),
580+
[&[2u8], data.discriminator.as_slice()].concat().as_slice(),
581+
&data.data_hash,
582+
])
583+
.unwrap();
584+
assert_eq!(hash, hash_manual);
585+
assert_eq!(hash.len(), 32);
586+
587+
// no data
588+
let compressed_account = CompressedAccount {
589+
owner,
590+
lamports,
591+
address: Some(address),
592+
data: None,
593+
};
594+
let no_data_hash = compressed_account
595+
.hash::<Poseidon>(&merkle_tree_pubkey, &leaf_index)
596+
.unwrap();
597+
598+
let hash_manual = Poseidon::hashv(&[
599+
hash_to_bn254_field_size_be(&owner.to_bytes())
600+
.unwrap()
601+
.0
602+
.as_slice(),
603+
leaf_index.to_le_bytes().as_slice(),
604+
hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
605+
.unwrap()
606+
.0
607+
.as_slice(),
608+
[&[1u8], lamports.to_le_bytes().as_slice()]
609+
.concat()
610+
.as_slice(),
611+
address.as_slice(),
612+
])
613+
.unwrap();
614+
assert_eq!(no_data_hash, hash_manual);
615+
assert_ne!(hash, no_data_hash);
616+
617+
// no address
618+
let compressed_account = CompressedAccount {
619+
owner,
620+
lamports,
621+
address: None,
622+
data: Some(data.clone()),
623+
};
624+
let no_address_hash = compressed_account
625+
.hash::<Poseidon>(&merkle_tree_pubkey, &leaf_index)
626+
.unwrap();
627+
let hash_manual = Poseidon::hashv(&[
628+
hash_to_bn254_field_size_be(&owner.to_bytes())
629+
.unwrap()
630+
.0
631+
.as_slice(),
632+
leaf_index.to_le_bytes().as_slice(),
633+
hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
634+
.unwrap()
635+
.0
636+
.as_slice(),
637+
[&[1u8], lamports.to_le_bytes().as_slice()]
638+
.concat()
639+
.as_slice(),
640+
[&[2u8], data.discriminator.as_slice()].concat().as_slice(),
641+
&data.data_hash,
642+
])
643+
.unwrap();
644+
assert_eq!(no_address_hash, hash_manual);
645+
assert_ne!(hash, no_address_hash);
646+
assert_ne!(no_data_hash, no_address_hash);
647+
648+
// no address no lamports
649+
let compressed_account = CompressedAccount {
650+
owner,
651+
lamports: 0,
652+
address: None,
653+
data: Some(data.clone()),
654+
};
655+
let no_address_no_lamports_hash = compressed_account
656+
.hash::<Poseidon>(&merkle_tree_pubkey, &leaf_index)
657+
.unwrap();
658+
let hash_manual = Poseidon::hashv(&[
659+
hash_to_bn254_field_size_be(&owner.to_bytes())
660+
.unwrap()
661+
.0
662+
.as_slice(),
663+
leaf_index.to_le_bytes().as_slice(),
664+
hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
665+
.unwrap()
666+
.0
667+
.as_slice(),
668+
[&[2u8], data.discriminator.as_slice()].concat().as_slice(),
669+
&data.data_hash,
670+
])
671+
.unwrap();
672+
assert_eq!(no_address_no_lamports_hash, hash_manual);
673+
assert_ne!(hash, no_address_no_lamports_hash);
674+
assert_ne!(no_data_hash, no_address_no_lamports_hash);
675+
assert_ne!(no_address_hash, no_address_no_lamports_hash);
676+
677+
// no address and no data
678+
let compressed_account = CompressedAccount {
679+
owner,
680+
lamports,
681+
address: None,
682+
data: None,
683+
};
684+
let no_address_no_data_hash = compressed_account
685+
.hash::<Poseidon>(&merkle_tree_pubkey, &leaf_index)
686+
.unwrap();
687+
let hash_manual = Poseidon::hashv(&[
688+
hash_to_bn254_field_size_be(&owner.to_bytes())
689+
.unwrap()
690+
.0
691+
.as_slice(),
692+
leaf_index.to_le_bytes().as_slice(),
693+
hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
694+
.unwrap()
695+
.0
696+
.as_slice(),
697+
[&[1u8], lamports.to_le_bytes().as_slice()]
698+
.concat()
699+
.as_slice(),
700+
])
701+
.unwrap();
702+
assert_eq!(no_address_no_data_hash, hash_manual);
703+
assert_ne!(hash, no_address_no_data_hash);
704+
assert_ne!(no_data_hash, no_address_no_data_hash);
705+
assert_ne!(no_address_hash, no_address_no_data_hash);
706+
assert_ne!(no_address_no_lamports_hash, no_address_no_data_hash);
707+
708+
// no address, no data, no lamports
709+
let compressed_account = CompressedAccount {
710+
owner,
711+
lamports: 0,
712+
address: None,
713+
data: None,
714+
};
715+
let no_address_no_data_no_lamports_hash = compressed_account
716+
.hash::<Poseidon>(&merkle_tree_pubkey, &leaf_index)
717+
.unwrap();
718+
let hash_manual = Poseidon::hashv(&[
719+
hash_to_bn254_field_size_be(&owner.to_bytes())
720+
.unwrap()
721+
.0
722+
.as_slice(),
723+
leaf_index.to_le_bytes().as_slice(),
724+
hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
725+
.unwrap()
726+
.0
727+
.as_slice(),
728+
])
729+
.unwrap();
730+
assert_eq!(no_address_no_data_no_lamports_hash, hash_manual);
731+
assert_ne!(no_address_no_data_hash, no_address_no_data_no_lamports_hash);
732+
assert_ne!(hash, no_address_no_data_no_lamports_hash);
733+
assert_ne!(no_data_hash, no_address_no_data_no_lamports_hash);
734+
assert_ne!(no_address_hash, no_address_no_data_no_lamports_hash);
735+
assert_ne!(
736+
no_address_no_lamports_hash,
737+
no_address_no_data_no_lamports_hash
738+
);
739+
}
471740
}

program-libs/compressed-account/src/event.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use borsh::{BorshDeserialize, BorshSerialize};
22
use light_zero_copy::{borsh::Deserialize, errors::ZeroCopyError};
33
use solana_program::pubkey::Pubkey;
44

5-
use super::{discriminators::*, insert_into_queues::InsertIntoQueuesInstructionData};
5+
use super::discriminators::*;
66
use crate::instruction_data::{
77
data::OutputCompressedAccountWithPackedContext,
8+
insert_into_queues::InsertIntoQueuesInstructionData,
89
zero_copy::{
910
ZInstructionDataInvoke, ZInstructionDataInvokeCpi, ZInstructionDataInvokeCpiWithReadOnly,
1011
},
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod compressed_proof;
22
pub mod cpi_context;
33
pub mod data;
4+
pub mod insert_into_queues;
45
pub mod invoke_cpi;
56
pub mod zero_copy;

program-libs/compressed-account/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ pub mod compressed_account;
1212
pub mod discriminators;
1313
pub mod event;
1414
pub mod hash_chain;
15-
pub mod insert_into_queues;
1615
pub mod instruction_data;
1716
pub mod pubkey;
18-
pub mod z_compressed_account;
1917

2018
#[derive(Debug, Error, PartialEq)]
2119
pub enum CompressedAccountError {

0 commit comments

Comments
 (0)