snarkvm_console_algorithms/poseidon/
hash_to_group.rs

1// Copyright 2024 Aleo Network Foundation
2// This file is part of the snarkVM 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
16use super::*;
17
18impl<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)]
36mod tests {
37    use super::*;
38    use snarkvm_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}