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 Group = Group<E>; 20 type Input = Field<E>; 21 type Scalar = Scalar<E>; 22 23 /// Returns an affine group element from hashing the input. 24 #[inline] 25 fn hash_to_group(&self, input: &[Self::Input]) -> Self::Group { 26 // Ensure that the input is not empty. 27 if input.is_empty() { 28 E::halt("Input to hash to group cannot be empty") 29 } 30 // Compute `HashMany(input, 2)`. 31 match self.hash_many(input, 2).iter().collect_tuple() { 32 // Compute the group element as `MapToGroup(h0) + MapToGroup(h1)`. 33 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(test)] 40 mod tests { 41 use super::*; 42 use alphavm_circuit_types::environment::Circuit; 43 use alphavm_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 // Initialize Poseidon. 53 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 // Sample a random input. 60 let input = (0..$num_fields).map(|_| Uniform::rand(rng)).collect::<Vec<_>>(); 61 // Compute the expected hash. 62 let expected = console::HashToGroup::hash_to_group(&native, &input)?; 63 // Prepare the circuit input. 64 let circuit_input: Vec<Field<_>> = Inject::new(Mode::$mode, input); 65 66 Circuit::scope(format!("Poseidon HashToGroup {i}"), || { 67 // Perform the hash operation. 68 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 // Eject the value to inspect it further. 73 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 }