hash_to_group.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 use super::*; 17 18 impl<E: Environment, const RATE: usize> HashToGroup for Poseidon<E, RATE> { 19 type Input = Field<E>; 20 type Output = Group<E>; 21 22 /// Returns a group element from hashing the input. 23 #[inline] 24 fn hash_to_group(&self, input: &[Self::Input]) -> Result<Self::Output> { 25 // Ensure that the input is not empty. 26 ensure!(!input.is_empty(), "Input to hash to group cannot be empty"); 27 // Compute the group element as `MapToGroup(HashMany(input)[0]) + MapToGroup(HashMany(input)[1])`. 28 match self.hash_many(input, 2).iter().map(Elligator2::<E>::encode).collect_tuple() { 29 Some((Ok((h0, _)), Ok((h1, _)))) => Ok(h0 + h1), 30 _ => bail!("Poseidon failed to compute hash to group on the given input"), 31 } 32 } 33 } 34 35 #[cfg(test)] 36 mod tests { 37 use super::*; 38 use alphavm_console_types::environment::Console; 39 40 type CurrentEnvironment = Console; 41 42 const ITERATIONS: u64 = 1000; 43 44 macro_rules! check_hash_to_group { 45 ($poseidon:ident) => {{ 46 // Initialize Poseidon. 47 let poseidon = $poseidon::<CurrentEnvironment>::setup("HashToGroupTest")?; 48 49 // Ensure an empty input fails. 50 assert!(poseidon.hash_to_group(&[]).is_err()); 51 52 let mut rng = TestRng::default(); 53 54 for _ in 0..ITERATIONS { 55 for num_inputs in 1..8 { 56 // Sample random field elements. 57 let inputs = (0..num_inputs).map(|_| Uniform::rand(&mut rng)).collect::<Vec<_>>(); 58 59 // Compute the hash to group. 60 let candidate = poseidon.hash_to_group(&inputs)?; 61 assert!((*candidate).to_affine().is_on_curve()); 62 assert!((*candidate).to_affine().is_in_correct_subgroup_assuming_on_curve()); 63 assert_ne!(Group::<CurrentEnvironment>::zero(), candidate); 64 assert_ne!(Group::<CurrentEnvironment>::generator(), candidate); 65 66 let candidate_cofactor_inv = candidate.div_by_cofactor(); 67 assert_eq!(candidate, candidate_cofactor_inv.mul_by_cofactor()); 68 } 69 } 70 Ok(()) 71 }}; 72 } 73 74 #[test] 75 fn test_poseidon2_hash_to_group() -> Result<()> { 76 check_hash_to_group!(Poseidon2) 77 } 78 79 #[test] 80 fn test_poseidon4_hash_to_group() -> Result<()> { 81 check_hash_to_group!(Poseidon4) 82 } 83 84 #[test] 85 fn test_poseidon8_hash_to_group() -> Result<()> { 86 check_hash_to_group!(Poseidon8) 87 } 88 }