snarkvm_circuit_algorithms/pedersen/
hash_uncompressed.rs1use super::*;
17
18use std::borrow::Cow;
19
20impl<E: Environment, const NUM_BITS: u8> HashUncompressed for Pedersen<E, NUM_BITS> {
21 type Input = Boolean<E>;
22 type Output = Group<E>;
23
24 fn hash_uncompressed(&self, input: &[Self::Input]) -> Self::Output {
26 let mut input = Cow::Borrowed(input);
28 match input.len() <= NUM_BITS as usize {
29 true => input.to_mut().resize(NUM_BITS as usize, Boolean::constant(false)),
31 false => E::halt(format!("The Pedersen hash input cannot exceed {NUM_BITS} bits.")),
33 }
34
35 input
37 .iter()
38 .zip_eq(&self.base_window)
39 .map(|(bit, base)| Group::ternary(bit, base, &Group::zero()))
40 .fold(Group::<E>::zero(), |acc, x| acc + x)
41 }
42}
43
44impl<E: Environment, const NUM_BITS: u8> Metrics<dyn HashUncompressed<Input = Boolean<E>, Output = Group<E>>>
45 for Pedersen<E, NUM_BITS>
46{
47 type Case = Vec<Mode>;
48
49 #[inline]
50 fn count(case: &Self::Case) -> Count {
51 let group_initialization_counts = case
53 .iter()
54 .map(|mode| {
55 count!(
56 Group<E>,
57 Ternary<Boolean = Boolean<E>, Output = Group<E>>,
58 &(*mode, Mode::Constant, Mode::Constant)
59 )
60 })
61 .fold(Count::zero(), |cumulative, count| cumulative + count);
62
63 let mut modes = case.iter().map(|mode| {
65 match mode.is_constant() {
68 true => Mode::Constant,
69 false => Mode::Private,
70 }
71 });
72
73 let sum_counts = match modes.next() {
75 Some(start_mode) => {
76 modes
77 .fold((start_mode, Count::zero()), |(prev_mode, cumulative), curr_mode| {
78 let mode = output_mode!(Group<E>, Add<Group<E>, Output = Group<E>>, &(prev_mode, curr_mode));
79 let sum_count = count!(Group<E>, Add<Group<E>, Output = Group<E>>, &(prev_mode, curr_mode));
80 (mode, cumulative + sum_count)
81 })
82 .1
83 }
84 None => Count::zero(),
85 };
86
87 group_initialization_counts + sum_counts
88 }
89}
90
91impl<E: Environment, const NUM_BITS: u8> OutputMode<dyn HashUncompressed<Input = Boolean<E>, Output = Group<E>>>
92 for Pedersen<E, NUM_BITS>
93{
94 type Case = Vec<Mode>;
95
96 #[inline]
97 fn output_mode(parameter: &Self::Case) -> Mode {
98 match parameter.iter().all(|mode| mode.is_constant()) {
99 true => Mode::Constant,
100 false => Mode::Private,
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use snarkvm_circuit_types::environment::Circuit;
109 use snarkvm_utilities::{TestRng, Uniform};
110
111 const ITERATIONS: u64 = 10;
112 const MESSAGE: &str = "PedersenCircuit0";
113 const NUM_BITS_MULTIPLIER: u8 = 8;
114
115 fn check_hash_uncompressed<const NUM_BITS: u8>(mode: Mode, rng: &mut TestRng) {
116 use console::HashUncompressed as H;
117
118 let native = console::Pedersen::<<Circuit as Environment>::Network, NUM_BITS>::setup(MESSAGE);
120 let circuit = Pedersen::<Circuit, NUM_BITS>::constant(native.clone());
121
122 for i in 0..ITERATIONS {
123 let input = (0..NUM_BITS).map(|_| bool::rand(rng)).collect::<Vec<bool>>();
125 let expected = native.hash_uncompressed(&input).expect("Failed to hash native input");
127 let circuit_input: Vec<Boolean<_>> = Inject::new(mode, input);
129
130 Circuit::scope(format!("Pedersen {mode} {i}"), || {
131 let candidate = circuit.hash_uncompressed(&circuit_input);
133 assert_eq!(expected, candidate.eject_value());
134
135 let modes = circuit_input.iter().map(|b| b.eject_mode()).collect::<Vec<_>>();
137 assert_count!(
138 Pedersen<Circuit, NUM_BITS>,
139 HashUncompressed<Input = Boolean<Circuit>, Output = Group<Circuit>>,
140 &modes
141 );
142 assert_output_mode!(
143 Pedersen<Circuit, NUM_BITS>,
144 HashUncompressed<Input = Boolean<Circuit>, Output = Group<Circuit>>,
145 &modes,
146 candidate
147 );
148 });
149 }
150 }
151
152 fn check_homomorphic_addition<C: Display + Eject + Add<Output = C> + ToBits<Boolean = Boolean<Circuit>>>(
153 pedersen: &impl HashUncompressed<Input = Boolean<Circuit>, Output = Group<Circuit>>,
154 first: C,
155 second: C,
156 ) {
157 println!("Checking homomorphic addition on {first} + {second}");
158
159 let a = pedersen.hash_uncompressed(&first.to_bits_le());
161 let b = pedersen.hash_uncompressed(&second.to_bits_le());
162 let expected = a + b;
163
164 let candidate = pedersen.hash_uncompressed(&(first + second).to_bits_le());
166 assert_eq!(expected.eject(), candidate.eject());
167 assert!(Circuit::is_satisfied());
168 }
169
170 #[test]
171 fn test_hash_uncompressed_constant() {
172 let mut rng = TestRng::default();
174 check_hash_uncompressed::<NUM_BITS_MULTIPLIER>(Mode::Constant, &mut rng);
175 check_hash_uncompressed::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
176 check_hash_uncompressed::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
177 check_hash_uncompressed::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
178 check_hash_uncompressed::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
179 }
180
181 #[test]
182 fn test_hash_uncompressed_public() {
183 let mut rng = TestRng::default();
185 check_hash_uncompressed::<NUM_BITS_MULTIPLIER>(Mode::Public, &mut rng);
186 check_hash_uncompressed::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
187 check_hash_uncompressed::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
188 check_hash_uncompressed::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
189 check_hash_uncompressed::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
190 }
191
192 #[test]
193 fn test_hash_uncompressed_private() {
194 let mut rng = TestRng::default();
196 check_hash_uncompressed::<NUM_BITS_MULTIPLIER>(Mode::Private, &mut rng);
197 check_hash_uncompressed::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
198 check_hash_uncompressed::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
199 check_hash_uncompressed::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
200 check_hash_uncompressed::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
201 }
202
203 #[test]
204 fn test_pedersen64_homomorphism_private() {
205 let pedersen = Pedersen64::constant(console::Pedersen64::setup("Pedersen64HomomorphismTest"));
207
208 let mut rng = TestRng::default();
209
210 for _ in 0..ITERATIONS {
211 let first = U8::<Circuit>::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
213 let second = U8::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
214 check_homomorphic_addition(&pedersen, first, second);
215
216 let first = U16::<Circuit>::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
218 let second = U16::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
219 check_homomorphic_addition(&pedersen, first, second);
220
221 let first = U32::<Circuit>::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
223 let second = U32::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
224 check_homomorphic_addition(&pedersen, first, second);
225
226 let first = U64::<Circuit>::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
228 let second = U64::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
229 check_homomorphic_addition(&pedersen, first, second);
230 }
231 }
232
233 #[test]
234 fn test_pedersen_homomorphism_private() {
235 fn check_pedersen_homomorphism(
236 pedersen: &impl HashUncompressed<Input = Boolean<Circuit>, Output = Group<Circuit>>,
237 ) {
238 let mut rng = TestRng::default();
239
240 for _ in 0..ITERATIONS {
241 let first = U8::<Circuit>::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
243 let second = U8::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
244 check_homomorphic_addition(pedersen, first, second);
245
246 let first = U16::<Circuit>::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
248 let second = U16::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
249 check_homomorphic_addition(pedersen, first, second);
250
251 let first = U32::<Circuit>::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
253 let second = U32::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
254 check_homomorphic_addition(pedersen, first, second);
255
256 let first = U64::<Circuit>::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
258 let second = U64::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
259 check_homomorphic_addition(pedersen, first, second);
260
261 let first = U128::<Circuit>::new(Mode::Private, console::U128::new(u128::rand(&mut rng) >> 1));
263 let second = U128::new(Mode::Private, console::U128::new(u128::rand(&mut rng) >> 1));
264 check_homomorphic_addition(pedersen, first, second);
265 }
266 }
267
268 let pedersen128 = Pedersen128::constant(console::Pedersen128::setup("Pedersen128HomomorphismTest"));
270 check_pedersen_homomorphism(&pedersen128);
271 }
272}