diff --git a/Cargo.lock b/Cargo.lock index c3bda37256..945ead2f98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3853,7 +3853,7 @@ dependencies = [ "light-hasher", "light-heap", "light-macros", - "light-poseidon 0.3.0", + "light-poseidon 0.4.0", "light-program-profiler", "light-zero-copy", "num-bigint 0.4.6", @@ -4011,7 +4011,7 @@ dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", "borsh 0.10.4", - "light-poseidon 0.3.0", + "light-poseidon 0.4.0", "num-bigint 0.4.6", "pinocchio", "rand 0.8.5", @@ -4142,9 +4142,9 @@ dependencies = [ [[package]] name = "light-poseidon" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3d87542063daaccbfecd78b60f988079b6ec4e089249658b9455075c78d42" +checksum = "47a1ccadd0bb5a32c196da536fd72c59183de24a055f6bf0513bf845fefab862" dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", @@ -4326,7 +4326,7 @@ dependencies = [ "light-compressed-account", "light-hasher", "light-macros", - "light-poseidon 0.3.0", + "light-poseidon 0.4.0", "light-sdk-types", "prettyplease", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 1e7640db6d..979b51d65a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -252,7 +252,7 @@ light-merkle-tree-metadata = { path = "program-libs/merkle-tree-metadata", versi aligned-sized = { path = "program-libs/aligned-sized", version = "1.1.0" } light-bloom-filter = { path = "program-libs/bloom-filter", version = "0.6.0" } light-bounded-vec = { version = "2.0.1" } -light-poseidon = { version = "0.3.0" } +light-poseidon = { version = "0.4.0" } light-test-utils = { path = "program-tests/utils", version = "1.2.1" } light-indexed-array = { path = "program-libs/indexed-array", version = "0.3.0" } light-array-map = { path = "program-libs/array-map", version = "0.2.0" } diff --git a/program-libs/compressed-account/src/compressed_account.rs b/program-libs/compressed-account/src/compressed_account.rs index 08e87b7437..01a81292e8 100644 --- a/program-libs/compressed-account/src/compressed_account.rs +++ b/program-libs/compressed-account/src/compressed_account.rs @@ -411,18 +411,27 @@ mod tests { }; let merkle_tree_pubkey = Pubkey::new_unique(); let leaf_index: u32 = 1; + + // Precompute padded 32-byte arrays matching the hash implementation + let mut leaf_index_bytes = [0u8; 32]; + leaf_index_bytes[28..].copy_from_slice(&leaf_index.to_le_bytes()); + let mut lamports_bytes = [0u8; 32]; + lamports_bytes[24..].copy_from_slice(&lamports.to_le_bytes()); + lamports_bytes[23] = 1; + let mut discriminator_bytes = [0u8; 32]; + discriminator_bytes[24..].copy_from_slice(&data.discriminator); + discriminator_bytes[23] = 2; + let hash = compressed_account .hash(&merkle_tree_pubkey, &leaf_index, false) .unwrap(); let hash_manual = Poseidon::hashv(&[ hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(), - leaf_index.to_le_bytes().as_slice(), + &leaf_index_bytes, hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(), - [&[1u8], lamports.to_le_bytes().as_slice()] - .concat() - .as_slice(), + &lamports_bytes, address.as_slice(), - [&[2u8], data.discriminator.as_slice()].concat().as_slice(), + &discriminator_bytes, &data.data_hash, ]) .unwrap(); @@ -442,11 +451,9 @@ mod tests { let hash_manual = Poseidon::hashv(&[ hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(), - leaf_index.to_le_bytes().as_slice(), + &leaf_index_bytes, hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(), - [&[1u8], lamports.to_le_bytes().as_slice()] - .concat() - .as_slice(), + &lamports_bytes, address.as_slice(), ]) .unwrap(); @@ -465,12 +472,10 @@ mod tests { .unwrap(); let hash_manual = Poseidon::hashv(&[ hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(), - leaf_index.to_le_bytes().as_slice(), + &leaf_index_bytes, hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(), - [&[1u8], lamports.to_le_bytes().as_slice()] - .concat() - .as_slice(), - [&[2u8], data.discriminator.as_slice()].concat().as_slice(), + &lamports_bytes, + &discriminator_bytes, &data.data_hash, ]) .unwrap(); @@ -490,9 +495,9 @@ mod tests { .unwrap(); let hash_manual = Poseidon::hashv(&[ hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(), - leaf_index.to_le_bytes().as_slice(), + &leaf_index_bytes, hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(), - [&[2u8], data.discriminator.as_slice()].concat().as_slice(), + &discriminator_bytes, &data.data_hash, ]) .unwrap(); @@ -513,11 +518,9 @@ mod tests { .unwrap(); let hash_manual = Poseidon::hashv(&[ hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(), - leaf_index.to_le_bytes().as_slice(), + &leaf_index_bytes, hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(), - [&[1u8], lamports.to_le_bytes().as_slice()] - .concat() - .as_slice(), + &lamports_bytes, ]) .unwrap(); assert_eq!(no_address_no_data_hash, hash_manual); @@ -538,7 +541,7 @@ mod tests { .unwrap(); let hash_manual = Poseidon::hashv(&[ hash_to_bn254_field_size_be(&owner.to_bytes()).as_slice(), - leaf_index.to_le_bytes().as_slice(), + &leaf_index_bytes, hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes()).as_slice(), ]) .unwrap(); @@ -752,8 +755,11 @@ mod tests { Some(CompressedAccountData { discriminator: rng.gen(), data: Vec::new(), // not used in hash - data_hash: Poseidon::hash(rng.gen::().to_be_bytes().as_slice()) - .unwrap(), + data_hash: { + let mut random_bytes = [0u8; 32]; + random_bytes[24..].copy_from_slice(&rng.gen::().to_be_bytes()); + Poseidon::hash(&random_bytes).unwrap() + }, }) } else { None diff --git a/program-libs/hasher/src/poseidon.rs b/program-libs/hasher/src/poseidon.rs index 3c9f92b6fb..4df63d478b 100644 --- a/program-libs/hasher/src/poseidon.rs +++ b/program-libs/hasher/src/poseidon.rs @@ -108,11 +108,11 @@ impl Hasher for Poseidon { use crate::HASH_BYTES; // TODO: reenable once LightHasher refactor is merged // solana_program::msg!("remove len check onchain."); - // for val in vals { - // if val.len() != 32 { - // return Err(HasherError::InvalidInputLength(val.len())); - // } - // } + for val in _vals { + if val.len() != 32 { + return Err(HasherError::InvalidInputLength(val.len(), 32)); + } + } let mut hash_result = [0; HASH_BYTES]; let result = unsafe { crate::syscalls::sol_poseidon( diff --git a/program-libs/indexed-merkle-tree/src/array.rs b/program-libs/indexed-merkle-tree/src/array.rs index 3812541db4..26986e5e05 100644 --- a/program-libs/indexed-merkle-tree/src/array.rs +++ b/program-libs/indexed-merkle-tree/src/array.rs @@ -557,11 +557,13 @@ mod test { indexed_array.find_element(&nullifier1), Some(&bundle1.new_element), ); + let mut next_index_bytes = [0u8; 32]; + next_index_bytes[24..].copy_from_slice(&0_usize.to_be_bytes()); let expected_hash = Poseidon::hashv(&[ bigint_to_be_bytes_array::<32>(&nullifier1) .unwrap() .as_ref(), - 0_usize.to_be_bytes().as_ref(), + &next_index_bytes, bigint_to_be_bytes_array::<32>(&(0.to_biguint().unwrap())) .unwrap() .as_ref(), @@ -627,11 +629,13 @@ mod test { indexed_array.find_element(&nullifier2), Some(&bundle2.new_element), ); + let mut next_index_bytes = [0u8; 32]; + next_index_bytes[24..].copy_from_slice(&1_usize.to_be_bytes()); let expected_hash = Poseidon::hashv(&[ bigint_to_be_bytes_array::<32>(&nullifier2) .unwrap() .as_ref(), - 1_usize.to_be_bytes().as_ref(), + &next_index_bytes, bigint_to_be_bytes_array::<32>(&(30.to_biguint().unwrap())) .unwrap() .as_ref(), @@ -707,11 +711,13 @@ mod test { indexed_array.find_element(&nullifier3), Some(&bundle3.new_element), ); + let mut next_index_bytes = [0u8; 32]; + next_index_bytes[24..].copy_from_slice(&1_usize.to_be_bytes()); let expected_hash = Poseidon::hashv(&[ bigint_to_be_bytes_array::<32>(&nullifier3) .unwrap() .as_ref(), - 1_usize.to_be_bytes().as_ref(), + &next_index_bytes, bigint_to_be_bytes_array::<32>(&(30.to_biguint().unwrap())) .unwrap() .as_ref(), @@ -802,11 +808,13 @@ mod test { indexed_array.find_element(&nullifier4), Some(&bundle4.new_element), ); + let mut next_index_bytes = [0u8; 32]; + next_index_bytes[24..].copy_from_slice(&0_usize.to_be_bytes()); let expected_hash = Poseidon::hashv(&[ bigint_to_be_bytes_array::<32>(&nullifier4) .unwrap() .as_ref(), - 0_usize.to_be_bytes().as_ref(), + &next_index_bytes, bigint_to_be_bytes_array::<32>(&(0.to_biguint().unwrap())) .unwrap() .as_ref(), diff --git a/program-libs/indexed-merkle-tree/tests/tests.rs b/program-libs/indexed-merkle-tree/tests/tests.rs index cf18deee24..5a4a1d86c3 100644 --- a/program-libs/indexed-merkle-tree/tests/tests.rs +++ b/program-libs/indexed-merkle-tree/tests/tests.rs @@ -639,18 +639,30 @@ pub fn functional_non_inclusion_test() { assert_eq!( leaf_0, Poseidon::hashv(&[ - &0_u32.to_biguint().unwrap().to_bytes_be(), - &1_u32.to_biguint().unwrap().to_bytes_be(), - &30_u32.to_biguint().unwrap().to_bytes_be() + bigint_to_be_bytes_array::<32>(&0_u32.to_biguint().unwrap()) + .unwrap() + .as_ref(), + bigint_to_be_bytes_array::<32>(&1_u32.to_biguint().unwrap()) + .unwrap() + .as_ref(), + bigint_to_be_bytes_array::<32>(&30_u32.to_biguint().unwrap()) + .unwrap() + .as_ref(), ]) .unwrap() ); assert_eq!( leaf_1, Poseidon::hashv(&[ - &30_u32.to_biguint().unwrap().to_bytes_be(), - &0_u32.to_biguint().unwrap().to_bytes_be(), - &0_u32.to_biguint().unwrap().to_bytes_be() + bigint_to_be_bytes_array::<32>(&30_u32.to_biguint().unwrap()) + .unwrap() + .as_ref(), + bigint_to_be_bytes_array::<32>(&0_u32.to_biguint().unwrap()) + .unwrap() + .as_ref(), + bigint_to_be_bytes_array::<32>(&0_u32.to_biguint().unwrap()) + .unwrap() + .as_ref(), ]) .unwrap() ); diff --git a/program-tests/batched-merkle-tree-test/tests/merkle_tree.rs b/program-tests/batched-merkle-tree-test/tests/merkle_tree.rs index f06ad2e867..da3c8d047d 100644 --- a/program-tests/batched-merkle-tree-test/tests/merkle_tree.rs +++ b/program-tests/batched-merkle-tree-test/tests/merkle_tree.rs @@ -61,9 +61,10 @@ pub fn assert_nullifier_queue_insert( ) -> Result<(), BatchedMerkleTreeError> { let mut leaf_hash_chain_insert_values = vec![]; for (insert_value, leaf_index) in bloom_filter_insert_values.iter().zip(leaf_indices.iter()) { + let mut leaf_index_bytes = [0u8; 32]; + leaf_index_bytes[24..].copy_from_slice(&leaf_index.to_be_bytes()); let nullifier = - Poseidon::hashv(&[insert_value.as_slice(), &leaf_index.to_be_bytes(), &tx_hash]) - .unwrap(); + Poseidon::hashv(&[insert_value.as_slice(), &leaf_index_bytes, &tx_hash]).unwrap(); leaf_hash_chain_insert_values.push(nullifier); } assert_input_queue_insert( diff --git a/program-tests/create-address-test-program/src/create_pda.rs b/program-tests/create-address-test-program/src/create_pda.rs index eceefaceeb..10660a0d4c 100644 --- a/program-tests/create-address-test-program/src/create_pda.rs +++ b/program-tests/create-address-test-program/src/create_pda.rs @@ -134,7 +134,9 @@ impl light_hasher::DataHasher for RegisteredUser { fn hash(&self) -> std::result::Result<[u8; 32], HasherError> { let truncated_user_pubkey = hash_to_bn254_field_size_be(&self.user_pubkey.to_bytes()); - H::hashv(&[truncated_user_pubkey.as_slice(), self.data.as_slice()]) + let mut data_bytes = [0u8; 32]; + data_bytes[1..].copy_from_slice(&self.data); + H::hashv(&[truncated_user_pubkey.as_slice(), &data_bytes]) } } diff --git a/program-tests/system-cpi-test/src/create_pda.rs b/program-tests/system-cpi-test/src/create_pda.rs index 0009c4a0ca..ad0a68f638 100644 --- a/program-tests/system-cpi-test/src/create_pda.rs +++ b/program-tests/system-cpi-test/src/create_pda.rs @@ -541,7 +541,9 @@ pub struct RegisteredUser { impl light_hasher::DataHasher for RegisteredUser { fn hash(&self) -> std::result::Result<[u8; 32], HasherError> { let truncated_user_pubkey = hash_to_bn254_field_size_be(&self.user_pubkey.to_bytes()); - H::hashv(&[truncated_user_pubkey.as_slice(), self.data.as_slice()]) + let mut data_bytes = [0u8; 32]; + data_bytes[1..].copy_from_slice(&self.data); + H::hashv(&[truncated_user_pubkey.as_slice(), &data_bytes]) } } diff --git a/program-tests/system-cpi-test/tests/test.rs b/program-tests/system-cpi-test/tests/test.rs index ec6d61721f..885eadbd9d 100644 --- a/program-tests/system-cpi-test/tests/test.rs +++ b/program-tests/system-cpi-test/tests/test.rs @@ -1906,10 +1906,11 @@ pub async fn assert_created_pda( ); let truncated_user_pubkey = hash_to_bn254_field_size_be(&compressed_escrow_pda_data.user_pubkey.to_bytes()); - + let mut data_bytes = [0u8; 32]; + data_bytes[1..].copy_from_slice(data); assert_eq!( compressed_escrow_pda_deserialized.data_hash, - Poseidon::hashv(&[truncated_user_pubkey.as_slice(), data.as_slice()]).unwrap(), + Poseidon::hashv(&[truncated_user_pubkey.as_slice(), &data_bytes]).unwrap(), ); } diff --git a/program-tests/utils/src/mock_batched_forester.rs b/program-tests/utils/src/mock_batched_forester.rs index 732ed0af16..4458aa03b3 100644 --- a/program-tests/utils/src/mock_batched_forester.rs +++ b/program-tests/utils/src/mock_batched_forester.rs @@ -186,7 +186,8 @@ impl MockBatchedForester { .iter() .find(|tx_event| tx_event.inputs.contains(leaf)) .expect("No event for leaf found."); - let index_bytes = index.to_be_bytes(); + let mut index_bytes = [0u8; 32]; + index_bytes[24..].copy_from_slice(&(index as u64).to_be_bytes()); let nullifier = Poseidon::hashv(&[leaf, &index_bytes, &event.tx_hash]).unwrap(); tx_hashes.push(event.tx_hash); nullifiers.push(nullifier); diff --git a/program-tests/utils/src/test_batch_forester.rs b/program-tests/utils/src/test_batch_forester.rs index 576b887447..8e6909704f 100644 --- a/program-tests/utils/src/test_batch_forester.rs +++ b/program-tests/utils/src/test_batch_forester.rs @@ -269,7 +269,8 @@ pub async fn get_batched_nullify_ix_data( let proof = bundle.merkle_tree.get_proof_of_leaf(index, true).unwrap(); merkle_proofs.push(proof.to_vec()); bundle.input_leaf_indices.remove(0); - let index_bytes = index.to_be_bytes(); + let mut index_bytes = [0u8; 32]; + index_bytes[24..].copy_from_slice(&(index as u64).to_be_bytes()); use light_hasher::Hasher; let nullifier = Poseidon::hashv(&[&leaf, &index_bytes, &leaf_info.tx_hash]).unwrap(); diff --git a/prover/client/src/proof_types/batch_update/proof_inputs.rs b/prover/client/src/proof_types/batch_update/proof_inputs.rs index e63f50ff23..2136d01d10 100644 --- a/prover/client/src/proof_types/batch_update/proof_inputs.rs +++ b/prover/client/src/proof_types/batch_update/proof_inputs.rs @@ -171,7 +171,8 @@ pub fn get_batch_update_inputs( let merkle_proof_array = merkle_proof.try_into().unwrap(); // Use the adjusted index bytes for computing the nullifier. - let index_bytes = (*index).to_be_bytes(); + let mut index_bytes = [0u8; 32]; + index_bytes[28..].copy_from_slice(&(*index).to_be_bytes()); let nullifier = Poseidon::hashv(&[leaf, &index_bytes, &tx_hashes[i]]).unwrap(); let (root, changelog_entry) = compute_root_from_merkle_proof(nullifier, &merkle_proof_array, *index); diff --git a/prover/client/tests/batch_update.rs b/prover/client/tests/batch_update.rs index 31b7a809c4..c4aeeaa1c0 100644 --- a/prover/client/tests/batch_update.rs +++ b/prover/client/tests/batch_update.rs @@ -31,9 +31,9 @@ async fn prove_batch_update() { old_leaves.push(leaf); merkle_tree.append(&leaf).unwrap(); - #[allow(clippy::unnecessary_cast)] - let nullifier = - Poseidon::hashv(&[&leaf, &(i as usize).to_be_bytes(), &tx_hash]).unwrap(); + let mut index_bytes = [0u8; 32]; + index_bytes[28..].copy_from_slice(&(i as u32).to_be_bytes()); + let nullifier = Poseidon::hashv(&[&leaf, &index_bytes, &tx_hash]).unwrap(); nullifiers.push(nullifier); } diff --git a/xtask/src/zero_indexed_leaf.rs b/xtask/src/zero_indexed_leaf.rs index c63b27ebe7..27a085af1e 100644 --- a/xtask/src/zero_indexed_leaf.rs +++ b/xtask/src/zero_indexed_leaf.rs @@ -1,4 +1,4 @@ -use std::{fs::File, io::prelude::*, mem, path::PathBuf}; +use std::{fs::File, io::prelude::*, path::PathBuf}; use clap::Parser; use light_hasher::{Hasher, Keccak, Poseidon, Sha256}; @@ -26,8 +26,7 @@ fn generate_zero_indexed_leaf_for_hasher(opts: Options) -> anyhow::Result<()> where H: Hasher, { - let zero_indexed_leaf = - H::hashv(&[&[0u8; 32], &[0u8; mem::size_of::()], &[0u8; 32]]).unwrap(); + let zero_indexed_leaf = H::hashv(&[&[0u8; 32], &[0u8; 32], &[0u8; 32]]).unwrap(); let code = quote! { pub const ZERO_INDEXED_LEAF: [u8; 32] = [ #(#zero_indexed_leaf),* ];