1use super::*;
17
18impl<E: Environment, const NUM_WINDOWS: u8, const WINDOW_SIZE: u8> HashUncompressed
19 for BHP<E, NUM_WINDOWS, WINDOW_SIZE>
20{
21 type Input = Boolean<E>;
22 type Output = Group<E>;
23
24 fn hash_uncompressed(&self, input: &[Self::Input]) -> Self::Output {
29 let num_hasher_bits = NUM_WINDOWS as usize * WINDOW_SIZE as usize * BHP_CHUNK_SIZE;
31 let num_data_bits = E::BaseField::size_in_data_bits();
33 let max_input_bits_per_iteration = num_hasher_bits - num_data_bits;
35
36 debug_assert!(num_data_bits < num_hasher_bits);
37 debug_assert_eq!(num_data_bits - 64, self.domain.len());
38
39 let mut digest = Group::zero();
41
42 let mut preimage = Vec::with_capacity(num_hasher_bits);
44
45 for (i, input_bits) in input.chunks(max_input_bits_per_iteration).enumerate() {
47 match i == 0 {
49 true => {
51 preimage.extend(self.domain.clone());
53 U64::constant(console::U64::new(input.len() as u64)).write_bits_le(&mut preimage);
54 preimage.extend_from_slice(input_bits);
55 }
56 false => {
58 digest.to_x_coordinate().write_bits_le(&mut preimage);
60 preimage.truncate(num_data_bits);
61 preimage.extend_from_slice(input_bits);
62 }
63 }
64 digest = self.hasher.hash_uncompressed(&preimage);
66 preimage.clear();
68 }
69
70 digest
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use snarkvm_circuit_types::environment::Circuit;
78 use snarkvm_curves::{AffineCurve, ProjectiveCurve};
79 use snarkvm_utilities::{TestRng, Uniform};
80
81 use anyhow::Result;
82
83 const ITERATIONS: u64 = 100;
84 const DOMAIN: &str = "BHPCircuit0";
85
86 macro_rules! check_hash_uncompressed {
87 ($bhp:ident, $mode:ident, $num_bits:expr, ($num_constants:expr, $num_public:expr, $num_private:expr, $num_constraints:expr), $rng:expr) => {{
88 let native = console::$bhp::<<Circuit as Environment>::Network>::setup(DOMAIN)?;
90 let circuit = $bhp::<Circuit>::constant(native.clone());
91
92 for i in 0..ITERATIONS {
93 let input = (0..$num_bits).map(|_| Uniform::rand($rng)).collect::<Vec<_>>();
95 let expected = console::HashUncompressed::hash_uncompressed(&native, &input)?;
97 let circuit_input: Vec<Boolean<_>> = Inject::new(Mode::$mode, input);
99
100 Circuit::scope(format!("BHP {i}"), || {
101 let candidate = circuit.hash_uncompressed(&circuit_input);
103 assert_scope!($num_constants, $num_public, $num_private, $num_constraints);
104 assert_eq!(expected, candidate.eject_value());
105 assert!(candidate.eject_value().to_affine().is_on_curve());
106 assert!(candidate.eject_value().to_affine().is_in_correct_subgroup_assuming_on_curve());
107 });
108 Circuit::reset();
109 }
110 Ok::<_, anyhow::Error>(())
111 }};
112 }
113
114 fn check_hash_uncompressed<const NUM_WINDOWS: u8, const WINDOW_SIZE: u8>(
115 mode: Mode,
116 num_constants: u64,
117 num_public: u64,
118 num_private: u64,
119 num_constraints: u64,
120 ) -> Result<()> {
121 use console::HashUncompressed as H;
122
123 let native = console::BHP::<<Circuit as Environment>::Network, NUM_WINDOWS, WINDOW_SIZE>::setup(DOMAIN)?;
125 let circuit = BHP::<Circuit, NUM_WINDOWS, WINDOW_SIZE>::new(Mode::Constant, native.clone());
126 let num_input_bits = NUM_WINDOWS as usize * WINDOW_SIZE as usize * BHP_CHUNK_SIZE;
128
129 let mut rng = TestRng::default();
130
131 for i in 0..ITERATIONS {
132 let input = (0..num_input_bits).map(|_| bool::rand(&mut rng)).collect::<Vec<bool>>();
134 let expected = native.hash_uncompressed(&input).expect("Failed to hash native input");
136 let circuit_input: Vec<Boolean<_>> = Inject::new(mode, input);
138
139 Circuit::scope(format!("BHP {mode} {i}"), || {
140 let candidate = circuit.hash_uncompressed(&circuit_input);
142 assert_scope!(num_constants, num_public, num_private, num_constraints);
143 assert_eq!(expected, candidate.eject_value());
144 });
145 Circuit::reset();
146 }
147 Ok(())
148 }
149
150 #[test]
151 fn test_hash_uncompressed_constant() -> Result<()> {
152 check_hash_uncompressed::<32, 48>(Mode::Constant, 7239, 0, 0, 0)
153 }
154
155 #[test]
156 fn test_hash_uncompressed_public() -> Result<()> {
157 check_hash_uncompressed::<32, 48>(Mode::Public, 470, 0, 8774, 8776)
158 }
159
160 #[test]
161 fn test_hash_uncompressed_private() -> Result<()> {
162 check_hash_uncompressed::<32, 48>(Mode::Private, 470, 0, 8774, 8776)
163 }
164
165 #[test]
166 fn test_hash_uncompressed_bhp256_constant() -> Result<()> {
167 let mut rng = TestRng::default();
168 check_hash_uncompressed!(BHP256, Constant, 261, (756, 0, 0, 0), &mut rng)
169 }
170
171 #[test]
172 fn test_hash_uncompressed_bhp256_public() -> Result<()> {
173 let mut rng = TestRng::default();
174 check_hash_uncompressed!(BHP256, Public, 261, (403, 0, 445, 445), &mut rng)
175 }
176
177 #[test]
178 fn test_hash_uncompressed_bhp256_private() -> Result<()> {
179 let mut rng = TestRng::default();
180 check_hash_uncompressed!(BHP256, Private, 261, (403, 0, 445, 445), &mut rng)
181 }
182
183 #[test]
184 fn test_hash_uncompressed_bhp512_constant() -> Result<()> {
185 let mut rng = TestRng::default();
186 check_hash_uncompressed!(BHP512, Constant, 522, (1113, 0, 0, 0), &mut rng)
187 }
188
189 #[test]
190 fn test_hash_uncompressed_bhp512_public() -> Result<()> {
191 let mut rng = TestRng::default();
192 check_hash_uncompressed!(BHP512, Public, 522, (409, 0, 895, 895), &mut rng)
193 }
194
195 #[test]
196 fn test_hash_uncompressed_bhp512_private() -> Result<()> {
197 let mut rng = TestRng::default();
198 check_hash_uncompressed!(BHP512, Private, 522, (409, 0, 895, 895), &mut rng)
199 }
200
201 #[test]
202 fn test_hash_uncompressed_bhp768_constant() -> Result<()> {
203 let mut rng = TestRng::default();
204 check_hash_uncompressed!(BHP768, Constant, 783, (1488, 0, 0, 0), &mut rng)
205 }
206
207 #[test]
208 fn test_hash_uncompressed_bhp768_public() -> Result<()> {
209 let mut rng = TestRng::default();
210 check_hash_uncompressed!(BHP768, Public, 783, (429, 0, 1365, 1365), &mut rng)
211 }
212
213 #[test]
214 fn test_hash_uncompressed_bhp768_private() -> Result<()> {
215 let mut rng = TestRng::default();
216 check_hash_uncompressed!(BHP768, Private, 783, (429, 0, 1365, 1365), &mut rng)
217 }
218
219 #[test]
220 fn test_hash_uncompressed_bhp1024_constant() -> Result<()> {
221 let mut rng = TestRng::default();
222 check_hash_uncompressed!(BHP1024, Constant, 1043, (1815, 0, 0, 0), &mut rng)?;
223 check_hash_uncompressed!(BHP1024, Constant, 1044, (1815, 0, 0, 0), &mut rng)?;
224 check_hash_uncompressed!(BHP1024, Constant, 1046, (2413, 0, 0, 0), &mut rng)
225 }
226
227 #[test]
228 fn test_hash_uncompressed_bhp1024_public() -> Result<()> {
229 let mut rng = TestRng::default();
230 check_hash_uncompressed!(BHP1024, Public, 1043, (413, 0, 1775, 1775), &mut rng)?;
231 check_hash_uncompressed!(BHP1024, Public, 1044, (413, 0, 1775, 1775), &mut rng)?;
232 check_hash_uncompressed!(BHP1024, Public, 1046, (418, 0, 2709, 2711), &mut rng)
233 }
234
235 #[test]
236 fn test_hash_uncompressed_bhp1024_private() -> Result<()> {
237 let mut rng = TestRng::default();
238 check_hash_uncompressed!(BHP1024, Private, 1043, (413, 0, 1775, 1775), &mut rng)?;
239 check_hash_uncompressed!(BHP1024, Private, 1044, (413, 0, 1775, 1775), &mut rng)?;
240 check_hash_uncompressed!(BHP1024, Private, 1046, (418, 0, 2709, 2711), &mut rng)
241 }
242
243 #[test]
244 fn test_hash_uncompressed_cost_comparison() -> Result<()> {
245 let mut rng = TestRng::default();
247 check_hash_uncompressed!(BHP256, Private, 512, (410, 0, 1799, 1801), &mut rng)?;
248 check_hash_uncompressed!(BHP512, Private, 512, (409, 0, 880, 880), &mut rng)?;
249 check_hash_uncompressed!(BHP768, Private, 512, (423, 0, 900, 900), &mut rng)?;
250 check_hash_uncompressed!(BHP1024, Private, 512, (407, 0, 875, 875), &mut rng)
251 }
252}