snarkvm_circuit_algorithms/poseidon/
hash_to_group.rs1use super::*;
17
18impl<E: Environment, const RATE: usize> HashToGroup for Poseidon<E, RATE> {
19 type Group = Group<E>;
20 type Input = Field<E>;
21 type Scalar = Scalar<E>;
22
23 #[inline]
25 fn hash_to_group(&self, input: &[Self::Input]) -> Self::Group {
26 if input.is_empty() {
28 E::halt("Input to hash to group cannot be empty")
29 }
30 match self.hash_many(input, 2).iter().collect_tuple() {
32 Some((h0, h1)) => Elligator2::encode(h1) + Elligator2::encode(h0),
34 None => E::halt("Failed to compute the hash to group"),
35 }
36 }
37}
38
39#[cfg(all(test, feature = "console"))]
40mod tests {
41 use super::*;
42 use snarkvm_circuit_types::environment::Circuit;
43 use snarkvm_curves::{AffineCurve, ProjectiveCurve};
44
45 use anyhow::Result;
46
47 const ITERATIONS: u64 = 100;
48 const DOMAIN: &str = "PoseidonCircuit0";
49
50 macro_rules! check_hash_to_group {
51 ($poseidon:ident, $mode:ident, $num_fields:expr, ($num_constants:expr, $num_public:expr, $num_private:expr, $num_constraints:expr)) => {{
52 let native = console::$poseidon::<<Circuit as Environment>::Network>::setup(DOMAIN)?;
54 let circuit = $poseidon::<Circuit>::constant(native.clone());
55
56 let rng = &mut TestRng::default();
57
58 for i in 0..ITERATIONS {
59 let input = (0..$num_fields).map(|_| Uniform::rand(rng)).collect::<Vec<_>>();
61 let expected = console::HashToGroup::hash_to_group(&native, &input)?;
63 let circuit_input: Vec<Field<_>> = Inject::new(Mode::$mode, input);
65
66 Circuit::scope(format!("Poseidon HashToGroup {i}"), || {
67 let candidate = circuit.hash_to_group(&circuit_input);
69 assert_scope!($num_constants, $num_public, $num_private, $num_constraints);
70 assert_eq!(expected, candidate.eject_value());
71
72 let candidate = candidate.eject_value();
74 assert!((*candidate).to_affine().is_on_curve());
75 assert!((*candidate).to_affine().is_in_correct_subgroup_assuming_on_curve());
76 assert_ne!(console::Group::<<Circuit as Environment>::Network>::zero(), candidate);
77 assert_ne!(console::Group::<<Circuit as Environment>::Network>::generator(), candidate);
78
79 let candidate_cofactor_inv = candidate.div_by_cofactor();
80 assert_eq!(candidate, candidate_cofactor_inv.mul_by_cofactor());
81 });
82 Circuit::reset();
83 }
84 Ok::<_, anyhow::Error>(())
85 }};
86 }
87
88 #[test]
89 fn test_poseidon2_hash_to_group_constant() -> Result<()> {
90 check_hash_to_group!(Poseidon2, Constant, 2, (1059, 0, 0, 0))
91 }
92
93 #[test]
94 fn test_poseidon2_hash_to_group_public() -> Result<()> {
95 check_hash_to_group!(Poseidon2, Public, 2, (529, 0, 2026, 2036))
96 }
97
98 #[test]
99 fn test_poseidon2_hash_to_group_private() -> Result<()> {
100 check_hash_to_group!(Poseidon2, Private, 2, (529, 0, 2026, 2036))
101 }
102
103 #[test]
104 fn test_poseidon4_hash_to_group_constant() -> Result<()> {
105 check_hash_to_group!(Poseidon4, Constant, 2, (1059, 0, 0, 0))
106 }
107
108 #[test]
109 fn test_poseidon4_hash_to_group_public() -> Result<()> {
110 check_hash_to_group!(Poseidon4, Public, 2, (529, 0, 2096, 2106))
111 }
112
113 #[test]
114 fn test_poseidon4_hash_to_group_private() -> Result<()> {
115 check_hash_to_group!(Poseidon4, Private, 2, (529, 0, 2096, 2106))
116 }
117
118 #[test]
119 fn test_poseidon8_hash_to_group_constant() -> Result<()> {
120 check_hash_to_group!(Poseidon8, Constant, 2, (1059, 0, 0, 0))
121 }
122
123 #[test]
124 fn test_poseidon8_hash_to_group_public() -> Result<()> {
125 check_hash_to_group!(Poseidon8, Public, 2, (529, 0, 2236, 2246))
126 }
127
128 #[test]
129 fn test_poseidon8_hash_to_group_private() -> Result<()> {
130 check_hash_to_group!(Poseidon8, Private, 2, (529, 0, 2236, 2246))
131 }
132}