Skip to content

Commit ed718cf

Browse files
committed
fix(hasher): Enforce input length to match the modulus
To avoid misunderstandings related to padding in Poseidon hash inputs, enforce them to have size 32, which is the number of bytes in `Fr` modulus.
1 parent 50778df commit ed718cf

File tree

11 files changed

+55
-43
lines changed

11 files changed

+55
-43
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ impl ZCompressedAccount<'_> {
378378
#[cfg(not(feature = "pinocchio"))]
379379
#[cfg(test)]
380380
mod tests {
381-
use light_hasher::Poseidon;
381+
use light_hasher::{to_byte_array::ToByteArray, Poseidon};
382382
use light_zero_copy::borsh::Deserialize;
383383
use num_bigint::BigUint;
384384
use rand::Rng;
@@ -750,7 +750,7 @@ mod tests {
750750
Some(CompressedAccountData {
751751
discriminator: rng.gen(),
752752
data: Vec::new(), // not used in hash
753-
data_hash: Poseidon::hash(rng.gen::<u64>().to_be_bytes().as_slice())
753+
data_hash: Poseidon::hash(&rng.get::<u64>().to_byte_array().unwrap())
754754
.unwrap(),
755755
})
756756
} else {

program-libs/hasher/src/poseidon.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ impl Hasher for Poseidon {
8383
}
8484

8585
fn hashv(vals: &[&[u8]]) -> Result<Hash, HasherError> {
86+
for val in vals {
87+
if val.len() != 32 {
88+
return Err(HasherError::InvalidInputLength(32, val.len()));
89+
}
90+
}
91+
8692
// Perform the calculation inline, calling this from within a program is
8793
// not supported.
8894
#[cfg(not(target_os = "solana"))]
@@ -99,13 +105,6 @@ impl Hasher for Poseidon {
99105
#[cfg(target_os = "solana")]
100106
{
101107
use crate::HASH_BYTES;
102-
// TODO: reenable once LightHasher refactor is merged
103-
// solana_program::msg!("remove len check onchain.");
104-
// for val in vals {
105-
// if val.len() != 32 {
106-
// return Err(HasherError::InvalidInputLength(val.len()));
107-
// }
108-
// }
109108
let mut hash_result = [0; HASH_BYTES];
110109
let result = unsafe {
111110
crate::syscalls::sol_poseidon(

program-libs/hasher/src/to_byte_array.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ impl_to_byte_array_for_integer_type!(i32);
6767
impl_to_byte_array_for_integer_type!(u32);
6868
impl_to_byte_array_for_integer_type!(i64);
6969
impl_to_byte_array_for_integer_type!(u64);
70+
impl_to_byte_array_for_integer_type!(isize);
71+
impl_to_byte_array_for_integer_type!(usize);
7072
impl_to_byte_array_for_integer_type!(i128);
7173
impl_to_byte_array_for_integer_type!(u128);
7274

program-libs/indexed-merkle-tree/src/array.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ where
463463
#[cfg(test)]
464464
mod test {
465465
use light_concurrent_merkle_tree::light_hasher::Poseidon;
466+
use light_hasher::to_byte_array::ToByteArray;
466467
use num_bigint::{RandBigInt, ToBigUint};
467468
use rand::thread_rng;
468469

@@ -561,7 +562,7 @@ mod test {
561562
bigint_to_be_bytes_array::<32>(&nullifier1)
562563
.unwrap()
563564
.as_ref(),
564-
0_usize.to_be_bytes().as_ref(),
565+
0_usize.to_byte_array().unwrap().as_ref(),
565566
bigint_to_be_bytes_array::<32>(&(0.to_biguint().unwrap()))
566567
.unwrap()
567568
.as_ref(),
@@ -631,7 +632,7 @@ mod test {
631632
bigint_to_be_bytes_array::<32>(&nullifier2)
632633
.unwrap()
633634
.as_ref(),
634-
1_usize.to_be_bytes().as_ref(),
635+
1_usize.to_byte_array().unwrap().as_ref(),
635636
bigint_to_be_bytes_array::<32>(&(30.to_biguint().unwrap()))
636637
.unwrap()
637638
.as_ref(),
@@ -711,7 +712,7 @@ mod test {
711712
bigint_to_be_bytes_array::<32>(&nullifier3)
712713
.unwrap()
713714
.as_ref(),
714-
1_usize.to_be_bytes().as_ref(),
715+
1_usize.to_byte_array().unwrap().as_ref(),
715716
bigint_to_be_bytes_array::<32>(&(30.to_biguint().unwrap()))
716717
.unwrap()
717718
.as_ref(),
@@ -806,7 +807,7 @@ mod test {
806807
bigint_to_be_bytes_array::<32>(&nullifier4)
807808
.unwrap()
808809
.as_ref(),
809-
0_usize.to_be_bytes().as_ref(),
810+
0_usize.to_byte_array().unwrap().as_ref(),
810811
bigint_to_be_bytes_array::<32>(&(0.to_biguint().unwrap()))
811812
.unwrap()
812813
.as_ref(),

program-libs/indexed-merkle-tree/tests/tests.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -639,18 +639,18 @@ pub fn functional_non_inclusion_test() {
639639
assert_eq!(
640640
leaf_0,
641641
Poseidon::hashv(&[
642-
&0_u32.to_biguint().unwrap().to_bytes_be(),
643-
&1_u32.to_biguint().unwrap().to_bytes_be(),
644-
&30_u32.to_biguint().unwrap().to_bytes_be()
642+
&bigint_to_be_bytes_array::<32>(&0_u32.to_biguint().unwrap()).unwrap(),
643+
&bigint_to_be_bytes_array::<32>(&1_u32.to_biguint().unwrap()).unwrap(),
644+
&bigint_to_be_bytes_array::<32>(&30_u32.to_biguint().unwrap()).unwrap()
645645
])
646646
.unwrap()
647647
);
648648
assert_eq!(
649649
leaf_1,
650650
Poseidon::hashv(&[
651-
&30_u32.to_biguint().unwrap().to_bytes_be(),
652-
&0_u32.to_biguint().unwrap().to_bytes_be(),
653-
&0_u32.to_biguint().unwrap().to_bytes_be()
651+
&bigint_to_be_bytes_array::<32>(&30_u32.to_biguint().unwrap()).unwrap(),
652+
&bigint_to_be_bytes_array::<32>(&0_u32.to_biguint().unwrap()).unwrap(),
653+
&bigint_to_be_bytes_array::<32>(&0_u32.to_biguint().unwrap()).unwrap()
654654
])
655655
.unwrap()
656656
);

program-tests/create-address-test-program/src/create_pda.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,10 @@ pub struct RegisteredUser {
133133
impl light_hasher::DataHasher for RegisteredUser {
134134
fn hash<H: light_hasher::Hasher>(&self) -> std::result::Result<[u8; 32], HasherError> {
135135
let truncated_user_pubkey = hash_to_bn254_field_size_be(&self.user_pubkey.to_bytes());
136+
let mut data = [0u8; 32];
137+
data[1..].copy_from_slice(&self.data);
136138

137-
H::hashv(&[truncated_user_pubkey.as_slice(), self.data.as_slice()])
139+
H::hashv(&[truncated_user_pubkey.as_slice(), data.as_slice()])
138140
}
139141
}
140142

program-tests/system-cpi-test/src/create_pda.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,9 @@ pub struct RegisteredUser {
543543
impl light_hasher::DataHasher for RegisteredUser {
544544
fn hash<H: light_hasher::Hasher>(&self) -> std::result::Result<[u8; 32], HasherError> {
545545
let truncated_user_pubkey = hash_to_bn254_field_size_be(&self.user_pubkey.to_bytes());
546+
let mut data = [0u8; 32];
547+
data[1..].copy_from_slice(&self.data);
548+
546549
H::hashv(&[truncated_user_pubkey.as_slice(), self.data.as_slice()])
547550
}
548551
}

program-tests/utils/src/mock_batched_forester.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use light_compressed_account::{
22
hash_chain::create_hash_chain_from_slice, instruction_data::compressed_proof::CompressedProof,
33
};
4-
use light_hasher::{bigint::bigint_to_be_bytes_array, Hasher, Poseidon};
4+
use light_hasher::{
5+
bigint::bigint_to_be_bytes_array, to_byte_array::ToByteArray, Hasher, Poseidon,
6+
};
57
use light_merkle_tree_reference::{indexed::IndexedMerkleTree, MerkleTree};
68
use light_prover_client::{
79
errors::ProverClientError,
@@ -186,7 +188,7 @@ impl<const HEIGHT: usize> MockBatchedForester<HEIGHT> {
186188
.iter()
187189
.find(|tx_event| tx_event.inputs.contains(leaf))
188190
.expect("No event for leaf found.");
189-
let index_bytes = index.to_be_bytes();
191+
let index_bytes = index.to_byte_array().unwrap();
190192
let nullifier = Poseidon::hashv(&[leaf, &index_bytes, &event.tx_hash]).unwrap();
191193
tx_hashes.push(event.tx_hash);
192194
nullifiers.push(nullifier);

program-tests/utils/src/test_batch_forester.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ use light_compressed_account::{
2525
hash_chain::create_hash_chain_from_slice, instruction_data::compressed_proof::CompressedProof,
2626
QueueType,
2727
};
28-
use light_hasher::{bigint::bigint_to_be_bytes_array, Poseidon};
28+
use light_hasher::{
29+
bigint::bigint_to_be_bytes_array, to_byte_array::ToByteArray, Hasher, Poseidon,
30+
};
2931
use light_prover_client::{
3032
proof_client::ProofClient,
3133
proof_types::{
@@ -269,8 +271,7 @@ pub async fn get_batched_nullify_ix_data<R: Rpc>(
269271
let proof = bundle.merkle_tree.get_proof_of_leaf(index, true).unwrap();
270272
merkle_proofs.push(proof.to_vec());
271273
bundle.input_leaf_indices.remove(0);
272-
let index_bytes = index.to_be_bytes();
273-
use light_hasher::Hasher;
274+
let index_bytes = index.to_byte_array().unwrap();
274275
let nullifier = Poseidon::hashv(&[&leaf, &index_bytes, &leaf_info.tx_hash]).unwrap();
275276

276277
tx_hashes.push(leaf_info.tx_hash);

prover/client/src/proof_types/batch_update/proof_inputs.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use light_hasher::{hash_chain::create_hash_chain_from_array, Hasher, Poseidon};
1+
use light_hasher::{
2+
hash_chain::create_hash_chain_from_array, to_byte_array::ToByteArray, Hasher, Poseidon,
3+
};
24
use light_sparse_merkle_tree::changelog::ChangelogEntry;
35
use num_bigint::{BigInt, Sign};
46

@@ -91,7 +93,7 @@ pub fn get_batch_update_inputs<const HEIGHT: usize>(
9193
let merkle_proof_array = merkle_proof.try_into().unwrap();
9294

9395
// Use the adjusted index bytes for computing the nullifier.
94-
let index_bytes = (*index).to_be_bytes();
96+
let index_bytes = index.to_byte_array().unwrap();
9597
let nullifier = Poseidon::hashv(&[leaf, &index_bytes, &tx_hashes[i]]).unwrap();
9698
let (root, changelog_entry) =
9799
compute_root_from_merkle_proof(nullifier, &merkle_proof_array, *index);

0 commit comments

Comments
 (0)